Security & Signatures
How MyTPE Pay secures webhook deliveries using HMAC-SHA256 signatures, timestamps, and delivery IDs.
Webhook Security
Every webhook sent by MyTPE Pay is cryptographically signed using the same pattern as Stripe, PayPal, and GitHub. This ensures:
- Authenticity — The webhook was sent by MyTPE Pay, not an attacker
- Integrity — The payload was not modified in transit
- Replay Protection — Old webhooks cannot be replayed
Signing Model
Signature = HMAC-SHA256(timestamp + "." + payload, webhook_secret)Each webhook request includes these headers:
| Header | Description | Example |
|---|---|---|
X-MytpePay-Signature | HMAC-SHA256 signature | sha256=a1b2c3d4... |
X-MytpePay-Timestamp | Unix epoch seconds when sent | 1712678400 |
X-MytpePay-Event | Event type | transaction.completed |
X-MytpePay-Delivery-Id | Unique delivery UUID | f47ac10b-58cc-... |
Content-Type | Always JSON | application/json |
User-Agent | Identifies the sender | MytpePay-Webhook/1.0 |
How Signing Works
- MyTPE Pay captures the current Unix timestamp
- The JSON payload is serialized
- They are joined with a dot:
timestamp.payload - An HMAC-SHA256 hash is computed using your webhook secret
- The result is sent as
sha256={hash}in theX-MytpePay-Signatureheader
Your Webhook Secret
When you configure a webhook URL, MyTPE Pay automatically generates a secret key:
whsec_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6- Prefixed with
whsec_for easy identification - 48 hex characters of cryptographic randomness
- Unique per instance
- Can be regenerated at any time (invalidates the old one)
Keep Your Secret Safe
Store your webhook secret in environment variables. Never expose it in client-side code, logs, or version control.
Replay Protection
To prevent attackers from capturing and resending old webhook requests:
- Check the
X-MytpePay-Timestampheader - Reject requests older than 5 minutes
- The timestamp is part of the signature, so it cannot be modified
$timestamp = $_SERVER['HTTP_X_MYTPEPAY_TIMESTAMP'];
if (abs(time() - (int)$timestamp) > 300) {
http_response_code(403);
exit('Timestamp too old — possible replay attack');
}Idempotency
Each webhook delivery includes a unique X-MytpePay-Delivery-Id (UUID v4). Use this to:
- Detect duplicates — If you receive the same delivery ID twice, skip processing
- Log deliveries — Track which webhooks you've processed
- Debug issues — Reference specific deliveries in support requests
Retry Policy
If your endpoint returns a non-2xx status code or times out (10 second limit):
| Attempt | Delay | Total Time |
|---|---|---|
| 1st retry | 1 minute | ~1 min after failure |
| 2nd retry | 5 minutes | ~6 min after failure |
| 3rd retry | 15 minutes | ~21 min after failure |
After 3 failed attempts, the webhook is marked as permanently failed and logged.
Best Practice
Always return a 200 OK response quickly — process the webhook data asynchronously (e.g., via a queue) to avoid timeouts.