Documentation Index Fetch the complete documentation index at: https://docs.waypay.live/llms.txt
Use this file to discover all available pages before exploring further.
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
Request:
{
"amount": 1000,
"currency": "PKR",
"orderRef": {
"orderRef": "ORD123456"
},
"description": "Payment for order",
"customerRef": { "name": "Ayesha" }, ✗ SKIPPED (object)
"items": ["item1", "item2"], ✗ SKIPPED (array)
"signature": "..." ✗ SKIPPED (signature field)
}
Secret Key: mer_sk_abc123def456
Step 1-2: Filter parameters
✓ amount, currency, description, orderRef
Step 3: Normalize keys to lowercase
✓ amount → amount
✓ currency → currency
✓ description → description
✓ orderRef → orderref (lowercase!)
Step 4: Sort alphabetically (lowercase keys)
✓ amount, currency, description, orderref
Step 5: Build query string
✓ amount=1000¤cy=PKR&description=Payment for order&orderref={"orderRef":"ORD123456"}
Step 6: Append secret key
✓ amount=1000¤cy=PKR&description=Payment for order&orderref={"orderRef":"ORD123456"}mer_sk_abc123def456
Step 7-8: MD5 hash (lowercase)
✓ a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6
Critical: Format Values Correctly Different data types require specific formatting rules to ensure signature consistency.
Type Format Example Input Signature Value String As-is "PKR"PKRInteger As-is (plain number) 100100Long As-is (plain number) 10001000Decimal .NET ToString() 1000.501000.50Double .NET ToString() 99.9999.99Float .NET ToString() 50.550.5Boolean Lowercase truetrueEnum Integer value WalletProvider.JazzCash (value: 2)2DateTime .NET ToString() 2024-01-15T10:30:001/15/2024 10:30:00 AMDateTimeOffset .NET ToString() 2024-01-15T10:30:00+05:001/15/2024 10:30:00 AM +05:00Null 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 parameter keys must be converted to lowercase before sorting and building the query string:
orderRef → orderref
callbackUrl → callbackurl
paymentMethod → paymentmethod
This prevents signature mismatches due to casing differences.
Numbers: Format depends on type
Booleans: Must be lowercase
✅ Correct: true, false ❌ Incorrect: True, FALSE, TRUE
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
Example: {
"orderRef" : { "orderRef" : "ORD123456" }, // → included as JSON string
"customerRef" : { "name" : "..." }, // → excluded
"items" : [ "a" , "b" ] // → excluded
}
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
using System . Security . Cryptography ;
using System . Text ;
public static string GenerateSignature ( Dictionary < string , object > parameters , string secretKey )
{
// Filter, normalize keys to lowercase, and sort parameters
var sortedParams = new SortedDictionary < string , string >( StringComparer . Ordinal );
foreach ( var kvp in parameters )
{
// Skip signature field
if ( kvp . Key . Equals ( "signature" , StringComparison . OrdinalIgnoreCase ))
continue ;
// Skip null values
if ( kvp . Value == null )
continue ;
// Skip complex types (objects, arrays)
if ( IsComplexType ( kvp . Value ))
continue ;
// Normalize key to lowercase and format value
var normalizedKey = kvp . Key . ToLowerInvariant ();
sortedParams [ normalizedKey ] = FormatValue ( kvp . Value );
}
// Build query string
var queryString = string . Join ( "&" ,
sortedParams . Select ( kvp => $" { kvp . Key } = { kvp . Value } " ));
// Append secret key and compute MD5
var signatureInput = queryString + secretKey ;
using var md5 = MD5 . Create ();
var hashBytes = md5 . ComputeHash ( Encoding . UTF8 . GetBytes ( signatureInput ));
return BitConverter . ToString ( hashBytes ). Replace ( "-" , "" ). ToLowerInvariant ();
}
private static bool IsComplexType ( object value )
{
if ( value == null ) return false ;
var type = value . GetType ();
// Check for arrays
if ( type . IsArray ) return true ;
// Check for collections (but not string)
if ( typeof ( System . Collections . IEnumerable ). IsAssignableFrom ( type ) && type != typeof ( string ))
return true ;
// Check for complex objects (classes, but not string or decimal)
return type . IsClass && type != typeof ( string ) && ! type . IsPrimitive && type != typeof ( decimal );
}
private static string FormatValue ( object value )
{
return value switch
{
// Integers: plain number
int i => i . ToString (),
long l => l . ToString (),
// Decimals: use ToString() (preserves precision)
decimal d => d . ToString (),
double dbl => dbl . ToString (),
float f => f . ToString (),
// Booleans: lowercase
bool b => b . ToString (). ToLowerInvariant (),
// Enums: convert to integer
Enum e => Convert . ToInt32 ( e ). ToString (),
// DateTime: default ToString()
DateTime dt => dt . ToString (),
DateTimeOffset dto => dto . ToString (),
// Default: ToString()
_ => value . ToString ()
};
}
Testing Your Implementation
Test Endpoint
Use our signature testing API to verify your implementation:
POST /api/v1/signature/generate
Request:
{
"parameters" : {
"amount" : 1000 ,
"currency" : "PKR" ,
"orderRef" : {
"orderRef" : "ORD123456"
}
},
"secretKey" : "mer_sk_abc123def456"
}
Response:
{
"signature" : "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6" ,
"queryString" : "amount=1000¤cy=PKR&orderref={ \" orderRef \" : \" ORD123456 \" }" ,
"sortedParameters" : {
"amount" : "1000" ,
"currency" : "PKR" ,
"orderref" : "{ \" orderRef \" : \" ORD123456 \" }"
},
"signatureInput" : "amount=1000¤cy=PKR&orderref={ \" orderRef \" : \" ORD123456 \" }mer_sk_abc...456"
}
Verify Endpoint
POST /api/v1/signature/verify
Request:
{
"parameters" : {
"amount" : 1000 ,
"currency" : "PKR" ,
"orderRef" : {
"orderRef" : "ORD123456"
}
},
"secretKey" : "mer_sk_abc123def456" ,
"providedSignature" : "your_generated_signature"
}
Response (Success):
{
"isValid" : true ,
"providedSignature" : "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6" ,
"expectedSignature" : "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6" ,
"queryString" : "amount=1000¤cy=PKR&orderref={ \" orderRef \" : \" ORD123456 \" }" ,
"issues" : []
}
Response (Failure):
{
"isValid" : false ,
"providedSignature" : "wrong_signature" ,
"expectedSignature" : "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6" ,
"queryString" : "amount=1000¤cy=PKR&orderref={ \" orderRef \" : \" ORD123456 \" }" ,
"sortedParameters" : {
"amount" : "1000" ,
"currency" : "PKR" ,
"orderref" : "{ \" orderRef \" : \" ORD123456 \" }"
},
"issues" : [
"Signature length is 15, expected 32 characters for MD5" ,
"Signature contains uppercase letters - should be lowercase"
]
}
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.
Is your signature lowercase?
Must be abcdef123456... not ABCDEF123456...
Are all parameter keys converted to lowercase?
orderRef → orderref
callbackUrl → callbackurl
Amount → 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: "..." } → SKIP
orderRef: { orderRef: "..." } → INCLUDE (serialize to JSON string)
items: [...] → SKIP
Is the secret key appended correctly?
NO & before secret key. Directly append: ...orderId=123mer_sk_xxx
API Request Example
Complete Payment Request
curl -X POST "https://gateway.dev.waypay.live/Gateway/v1/Payment/initiate-checkout" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-d '{
"amount": 1500,
"currency": "PKR",
"orderRef": {
"orderRef": "ORD123456"
},
"description": "Payment for Order #2024-001",
"callbackUrl": "https://yoursite.com/callback",
"customerRef": {
"name": "Ayesha Khan",
"email": "ayesha@example.com"
},
"signature": "a1b2c3d4e5f6789012345678abcdef12"
}'
Signature Calculation:
Parameters (after filtering, customerRef and orderRef are skipped as they're objects):
amount=1500
callbackUrl=https://yoursite.com/callback
currency=PKR
description=Payment for Order #2024-001
Keys normalized to lowercase:
amount → amount
callbackUrl → callbackurl
currency → currency
description → description
Query String (sorted by lowercase keys):
amount=1500&callbackurl=https://yoursite.com/callback¤cy=PKR&description=Payment for Order #2024-001
Signature Input:
amount=1500&callbackurl=https://yoursite.com/callback¤cy=PKR&description=Payment for Order #2024-001mer_sk_your_secret_key
MD5 Hash:
a1b2c3d4e5f6789012345678abcdef12
Security Best Practices
Protect Your Secret Key
Never expose your secret key in client-side code or commit it to version control.
Never expose in client-side code
Don’t include in JavaScript, mobile apps, or browser code. Generate signatures on your server only.
Use environment variables
Use secret management services (Azure Key Vault, AWS Secrets Manager)
Never commit to version control
Contact support to regenerate your secret key and update all integrations with the new key.
All API calls must use HTTPS. Never send signatures over HTTP.
Format: mer_sk_xxxxxxxxxxxxx
Length: 20 characters
Prefix: mer_sk_ (7 characters)
Random: 13 alphanumeric characters (lowercase)
Example: mer_sk_a1b2c3d4e5f6g
Getting Help
Test Your Implementation
Use /api/v1/signature/example to see a complete example
Use /api/v1/signature/generate to generate signatures for testing
Use /api/v1/signature/verify to debug signature mismatches
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 in v2.0 If you have existing integrations, you must update your signature generation code.
Breaking Changes
1. All keys are now lowercase in signature calculation
Old: orderRef, callbackUrl, paymentMethodNew: orderref, callbackurl, paymentmethod
2. Integer formatting changed
Old: 1000 → "1000.00"New: 1000 → "1000"
New: Enums are converted to integer valuesExample: WalletProvider.JazzCash (value: 2) → "2"
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).
Test your signatures
Use the /api/v1/signature/generate endpoint to verify your implementation.
Verify before deploying
Use /api/v1/signature/verify to validate signatures before deploying to production.
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