Overview
All webhook payloads from Modulus Labs are encrypted using JWE (JSON Web Encryption) to protect sensitive transaction data. This page provides a complete reference for the payload structure, encryption details, and all available fields.Webhook Request Structure
When Modulus Labs sends a webhook to your endpoint, the request looks like this:HTTP Request
Request Body
data field contains a JWE-encrypted string that you must decrypt to access the actual transaction details.
JWE Encryption Specification
Webhook payloads use JSON Web Encryption with the following algorithms:Content Encryption
Algorithm:
A256CBC-HS512AES-256-CBC with HMAC SHA-512 for authenticated encryptionKey Encryption
Algorithm:
A256KWAES-256 Key Wrap for secure key encryptionSerialization Format
Format: Compact SerializationStandard JWE compact format (five base64url segments separated by dots)
Secret Key
Your Secret: Modulus Labs Secret KeySame key used for QR API authentication
JWE Structure
Decrypting the Payload
Use your secret key to decrypt the JWE payload.Required Libraries
- Node.js
- Python
- PHP
- Ruby
- Go
Decryption Implementation
Decrypted Payload Structure
After decryption, the webhook payload is a JSON object with the following structure:Complete Field Reference
The webhook action that triggered this notification.Possible values:
QRPH_SUCCESS- Payment completed successfullyQRPH_DECLINED- Payment failed or was declined
"QRPH_SUCCESS"Unique reference number for this transaction. This matches the
referenceNumber you provided when creating the Dynamic QR Ph code.Use this field to:- Match webhook to original QR creation request
- Prevent duplicate processing (idempotency)
- Look up order in your database
"REF-20240115-ABC123"Transaction amount in the smallest currency unit.
- For PHP: Amount in centavos (100 = ₱1.00)
- For USD: Amount in cents (100 = $1.00)
100000 represents ₱1,000.00Three-letter ISO 4217 currency code.Possible values:
"PHP", "USD"Example: "PHP"ISO 8601 timestamp indicating when the transaction was processed by the payment network.Format:
YYYY-MM-DDTHH:mm:ssZExample: "2024-01-15T14:30:00Z"Current transaction status.Possible values:
SUCCESS- Payment completed successfullyFAILED- Payment failedPENDING- Payment processing (rare for webhooks)REQUIRES_ACTION- Additional action needed (rare for webhooks)
"SUCCESS"Your merchant identifier assigned by Modulus Labs.Example:
"MERCH12345"Name of the customer who made the payment, as provided by their bank.Availability: May be
null if the bank doesn’t provide this information.Example: "Juan Dela Cruz"Name of the bank or payment provider the customer used to complete the payment.Availability: May be
null if not provided by the payment network.Example: "BDO Unibank", "GCash", "Maya"Bank’s internal reference number for this transaction.Availability: Present only for successful payments, may be
null for declined payments.Use case: Reconciliation with bank statements, dispute resolutionExample: "BANK-REF-987654321"Reason the payment was declined (only present when
action is QRPH_DECLINED).Possible values:"Insufficient funds""Invalid account""Transaction limit exceeded""Card expired""Declined by bank""Unknown error"
"Insufficient funds"Custom key-value pairs you included when creating the Dynamic QR Ph code. This is returned exactly as you sent it.Use case: Store order ID, customer ID, or other identifiers to link webhook to your systemExample:
Payload Examples
Successful Payment
Declined Payment
E-wallet Payment (GCash)
Field Validation
Validate the decrypted payload before processing:Common Validation Patterns
Verify Reference Number Exists
Verify Reference Number Exists
Check that the referenceNumber matches a QR code you actually created:
Validate Amount Matches
Validate Amount Matches
Verify the payment amount matches what you expected:
Check Timestamp Freshness
Check Timestamp Freshness
Detect replayed or delayed webhooks:
Validate Status Consistency
Validate Status Consistency
Ensure action and status fields are consistent:
Security Considerations
Always Decrypt
Never trust the encrypted payload without decryption. An attacker could send fake webhook requests.
Verify Signature
JWE provides authenticated encryption - the decryption process validates data integrity.
Use HTTPS
Your webhook endpoint must use HTTPS to prevent man-in-the-middle attacks.
Rate Limiting
Implement rate limiting on your webhook endpoint to prevent abuse.
Log Everything
Log all webhook receipts, decryption attempts, and processing results.
Handle Errors Gracefully
Don’t expose sensitive information in error messages.
Troubleshooting Decryption Issues
Decryption Fails with 'Invalid Key' Error
Decryption Fails with 'Invalid Key' Error
Possible causes:
- Wrong secret key
- Secret key for different environment (sandbox vs production)
- Secret key has extra whitespace or newline characters
Payload is Undefined or Null After Decryption
Payload is Undefined or Null After Decryption
Possible causes:
- Decryption succeeded but JSON parsing failed
- Empty payload
- Incorrect plaintext handling
Algorithm Not Supported Error
Algorithm Not Supported Error
Possible causes:
- JWE library doesn’t support A256CBC-HS512 or A256KW
- Outdated library version
Webhook Data is Corrupted
Webhook Data is Corrupted
Possible causes:
- Middleware modifying request body
- Character encoding issues
- Request body not properly buffered
Testing with Sample Payloads
Use the Simulate API to generate test webhooks with real encrypted payloads:Simulate API Reference
Complete documentation for testing webhooks
Best Practices
Cache Secret Key
Cache Secret Key
Load your secret key once at startup, not on every webhook:
Validate Before Processing
Validate Before Processing
Always validate payload structure before business logic:
Store Raw Encrypted Data
Store Raw Encrypted Data
Save the encrypted payload for audit and debugging:
Handle Partial Data Gracefully
Handle Partial Data Gracefully
Some fields like
customerName or bankName may be null: