Overview
All API requests to Waypay payment endpoints must include a valid signature for security verification. This guide explains how to generate signatures correctly.Quick Reference
| Property | Value |
|---|---|
| Algorithm | MD5 |
| Output Format | Lowercase hexadecimal (32 characters) |
| Secret Key Format | mer_sk_xxxxxxxxxxxxx (20 characters) |
| Key Normalization | All keys converted to lowercase |
| Parameter Sorting | Alphabetical (ASCII order) on lowercase keys |
Signature Generation Algorithm
Step-by-Step Process
- Collect all request parameters (excluding ‘signature’)
- Remove null values, empty strings, objects, and arrays
- Convert all parameter keys to LOWERCASE
- Sort parameters alphabetically by lowercase key name (ASCII order)
- Build query string:
key1=value1&key2=value2&... - Append your secret key directly (no & prefix)
- Compute MD5 hash of the combined string
- Convert to lowercase hexadecimal
Visual Example
Value Formatting Rules
| Type | Format | Example Input | Signature Value |
|---|---|---|---|
| String | As-is | "PKR" | PKR |
| Integer | As-is (plain number) | 100 | 100 |
| Long | As-is (plain number) | 1000 | 1000 |
| Decimal | .NET ToString() | 1000.50 | 1000.50 |
| Double | .NET ToString() | 99.99 | 99.99 |
| Float | .NET ToString() | 50.5 | 50.5 |
| Boolean | Lowercase | true | true |
| Enum | Integer value | WalletProvider.JazzCash (value: 2) | 2 |
| DateTime | .NET ToString() | 2024-01-15T10:30:00 | 1/15/2024 10:30:00 AM |
| DateTimeOffset | .NET ToString() | 2024-01-15T10:30:00+05:00 | 1/15/2024 10:30:00 AM +05:00 |
| Null | SKIP | null | (not included) |
| Empty String | SKIP | "" | (not included) |
| Object | SKIP | { "name": "..." } | (not included) |
| Array | SKIP | ["a", "b"] | (not included) |
Important Notes
🔑 ALL Keys Must Be Lowercase
🔑 ALL Keys Must Be Lowercase
All parameter keys must be converted to lowercase before sorting and building the query string:
orderRef→orderrefcallbackUrl→callbackurlpaymentMethod→paymentmethod
Numbers: Format depends on type
Numbers: Format depends on type
- Integers (int, long): Plain number →
100,1000,20 - Decimals (decimal, double, float): Use .NET ToString() →
1000.50,99.99 - Enums: Convert to integer value →
WalletProvider.JazzCash(2) →"2"
Booleans: Must be lowercase
Booleans: Must be lowercase
✅ Correct:
true, false❌ Incorrect: True, FALSE, TRUEObjects & Arrays: Handling varies by type
Objects & Arrays: Handling varies by type
- Most objects (like
customerRef): Excluded from signature calculation - Required parameter objects (like
orderRef): Serialized to JSON string and included - Arrays: Always excluded from signature calculation
DateTime Values: Use .NET default ToString()
DateTime Values: Use .NET default ToString()
The format will vary based on culture settings.Recommendation: Use ISO 8601 strings for consistency across different systems.
Code Examples
Testing Your Implementation
Test Endpoint
Use our signature testing API to verify your implementation:Verify Endpoint
Common Mistakes & Troubleshooting
Common Errors
| Error | Cause | Solution |
|---|---|---|
| Signature length ≠ 32 | Wrong hash algorithm or encoding | Use MD5, output as hex |
| Uppercase letters in signature | Not converting to lowercase | Call .toLowerCase() on result |
| Wrong parameter order | Not sorting alphabetically | Use ASCII/lexicographic sort |
| Including signature field | Signature included in params | Exclude ‘signature’ key |
| Wrong number format | Incorrect formatting | Use plain numbers for integers |
| Including nested objects | Objects/arrays in signature | Skip complex types |
Debugging Checklist
Is your signature 32 characters?
MD5 produces 32 hex characters. If shorter/longer, check your hash function.
Are all parameter keys converted to lowercase?
orderRef→orderrefcallbackUrl→callbackurlAmount→amount
Are parameters sorted correctly?
Alphabetical (ASCII) order on lowercase keys. All keys must be lowercase before sorting.
Are numbers formatted correctly?
- Integers: Plain number →
1000,20,5 - Decimals: Use ToString() →
1000.50,99.99 - Enums: Integer value →
2,5
Are you skipping objects and arrays?
customerRef: { name: "..." }→ SKIPorderRef: { orderRef: "..." }→ INCLUDE (serialize to JSON string)items: [...]→ SKIP
API Request Example
Complete Payment Request
Security Best Practices
Protect Your Secret Key
Never expose in client-side code
Never expose in client-side code
Don’t include in JavaScript, mobile apps, or browser code. Generate signatures on your server only.
Store securely
Store securely
- Use environment variables
- Use secret management services (Azure Key Vault, AWS Secrets Manager)
- Never commit to version control
Rotate if compromised
Rotate if compromised
Contact support to regenerate your secret key and update all integrations with the new key.
Use HTTPS only
Use HTTPS only
All API calls must use HTTPS. Never send signatures over HTTP.
Secret Key Format
Getting Help
Test Your Implementation
- Use
/api/v1/signature/exampleto see a complete example - Use
/api/v1/signature/generateto generate signatures for testing - Use
/api/v1/signature/verifyto debug signature mismatches
Contact Support
If you continue to have issues:- Email: support@waypay.com
- Include: Your merchant ID, request payload (without signature), and the signature you generated
Migration from v1.1 to v2.0
Breaking Changes
1. All keys are now lowercase in signature calculation
1. All keys are now lowercase in signature calculation
Old:
orderRef, callbackUrl, paymentMethodNew: orderref, callbackurl, paymentmethod2. Integer formatting changed
2. Integer formatting changed
Old:
1000 → "1000.00"New: 1000 → "1000"3. Enum handling
3. Enum handling
New: Enums are converted to integer valuesExample:
WalletProvider.JazzCash (value: 2) → "2"4. DateTime formatting
4. DateTime formatting
Old: ISO 8601 format
"2024-01-15T10:30:00"New: .NET default ToString() (culture-dependent)Migration Steps
Update your signature generation code
Convert all parameter keys to lowercase before sorting and building the query string.
Remove forced 2-decimal formatting for integers
Use plain numbers for integer values (e.g.,
1000 instead of 1000.00).Changelog
| Version | Date | Changes |
|---|---|---|
| 1.0 | 2024-01-15 | Initial release |
| 1.1 | 2024-01-20 | Added: Skip objects and arrays from signature calculation |
| 2.0 | 2026-01-26 | BREAKING: Keys normalized to lowercase; Integer formatting changed (plain numbers); Enum handling added; DateTime uses default ToString() |
Document Version: 2.0
Last Updated: 2026-01-26
API Version: v1