Sandbox & Testing Guide
Guide for testing your PSP integration in the staging environment before go-live.
Staging Environment
Base URL (Lookup API) : https://api-merchant-staging.kesles.com
Base URL (Events API) : https://api-merchant-staging.kesles.com
Staging credentials (API Key ID + secret) are provided separately by the Kesles team during onboarding. Do not use production credentials on staging.
- Merchant lookup calls (
/api/psp/v1/merchants/*): useX-API-Key-ID/X-Timestamp/X-Signature - Event forwarding calls (
/psp/v1/events,/psp/v1/settlements): useX-PSP-Key-ID/X-PSP-Timestamp/X-PSP-Signature
Test Merchant Data
Staging provides dummy merchants you can use for testing:
| NMID | Bank Code | Status | Description |
|---|---|---|---|
ID102326873TEST1 | matches your bank | active | Normal merchant |
ID102326873TEST2 | matches your bank | active | Merchant with many outlets |
ID102326873TEST3 | matches your bank | suspended | Suspended merchant (returns 404) |
Staging NMIDs are different from production. All staging NMIDs have a TEST suffix.
Required Test Cases
1. Merchant Lookup by NMID
curl -X GET \
"https://api-merchant-staging.kesles.com/api/psp/v1/merchants/by-nmid/ID102326873TEST1" \
-H "X-API-Key-ID: $STAGING_KEY_ID" \
-H "X-Timestamp: $(date +%s)" \
-H "X-Signature: hmac-sha256=$COMPUTED_SIG" \
-H "X-Request-ID: $(uuidgen)"
Expected: 200 with the full merchant payload.
2. Merchant Lookup by UUID
curl -X GET \
"https://api-merchant-staging.kesles.com/api/psp/v1/merchants/{merchant_uuid}" \
-H "X-API-Key-ID: $STAGING_KEY_ID" \
-H "X-Timestamp: $(date +%s)" \
-H "X-Signature: hmac-sha256=$COMPUTED_SIG" \
-H "X-Request-ID: $(uuidgen)"
Expected: 200 with merchant payload + outlets.
3. NMID Not Found
# Non-existent NMID
curl "...by-nmid/ID999999999NOTEXIST" \
-H "X-API-Key-ID: $STAGING_KEY_ID" ...
Expected: 404 merchant_not_found.
4. Forward a Transaction Event (Events API)
Signed_payload for events API uses format: method\npath\ntimestamp\nbody where timestamp is RFC3339 (not unix seconds). The X-PSP-Signature header carries the raw hex signature with no hmac-sha256= prefix.
TIMESTAMP=$(date -u +%FT%TZ)
BODY='{"external_event_id":"test-evt-001","event_type":"transaction.success","nmid":"ID102326873TEST1","gross_amount":75000,"transaction_code":"TRX-TEST-001","transaction_at":"2026-05-23T14:23:45Z","status":"success"}'
# Compute signature with format: method\npath\ntimestamp\nbody
STRING_TO_SIGN="POST\n/psp/v1/events\n${TIMESTAMP}\n${BODY}"
SIGNATURE=$(printf "%s" "$STRING_TO_SIGN" | openssl dgst -sha256 -hmac "$STAGING_PSP_SECRET" -hex | awk '{print $2}')
curl -X POST \
"https://api-merchant-staging.kesles.com/psp/v1/events" \
-H "X-PSP-Key-ID: $STAGING_PSP_KEY_ID" \
-H "X-PSP-Timestamp: $TIMESTAMP" \
-H "X-PSP-Signature: $SIGNATURE" \
-H "X-Request-ID: $(uuidgen)" \
-H "Content-Type: application/json" \
-d "$BODY"
Expected: 200 with { "status": "success", "flow": "A", "event_id": "<uuid>", "merchant_id": "<uuid>" }.
5. Idempotency — Duplicate Event
Send the same event twice with the same external_event_id.
Expected:
- First call:
{ "status": "success", "flow": "A", "event_id": "<uuid>", "merchant_id": "<uuid>" } - Second call:
{ "status": "duplicate_skipped" }
6. Timestamp Expired (Replay Attack Prevention)
Send a request with X-Timestamp (or X-PSP-Timestamp) more than 5 minutes in the past.
Expected: 401 timestamp_expired.
7. Invalid Signature
Send a request with an incorrect signature header.
Expected: 401 invalid_signature.
8. Data Isolation (Must Be Verified)
Try to look up an NMID not issued by your bank.
Expected: 404 merchant_not_found (not 403 — the server does not confirm whether a merchant exists for another bank).
9. Rate Limit
Send more than 1,000 by-nmid requests within 1 minute.
Expected: 429 rate_limit_exceeded with a Retry-After header.
10. Delta Sync with Cursor Pagination
# First page
curl "...merchants?limit=100&updated_since=2026-01-01T00:00:00Z" ...
# Take next_cursor from the response, then the next page
curl "...merchants?cursor=eyJpZCI6ImJmYTMifQ==" ...
Expected: merchants paginate correctly, has_more: false on the last page.
11. Webhook Signature Verification
The Kesles team will send a test webhook to your endpoint on staging. Verify that:
- The
X-Kesles-Signatureis successfully verified - The
merchant.activatedevent is processed correctly - Duplicate webhooks (same
X-Kesles-Event-ID) are ignored
12. Settlement Forwarding
curl -X POST \
"https://api-merchant-staging.kesles.com/psp/v1/settlements" \
-H "X-PSP-Key-ID: $STAGING_PSP_KEY_ID" \
-H "X-PSP-Timestamp: $TIMESTAMP" \
-H "X-PSP-Signature: $SIGNATURE" \
-H "Content-Type: application/json" \
-d '{
"external_event_id": "sett-test-001",
"nmid": "ID102326873TEST1",
"gross_amount": 1500000,
"settlement_date": "2026-05-23",
"completed_at": "2026-05-24T08:00:00Z"
}'
Expected: 200 with { "status": "success", "event_id": "<uuid>", "settlement_id": "<uuid>" }.
UAT Sign-off Checklist
Before go-live, all of the test cases above must pass:
- Merchant lookup by NMID — 200 OK
- Merchant lookup by UUID — 200 OK
- 404 for non-existent NMID
- Transaction event forwarded — Flow A response
- Idempotent duplicate event —
status: duplicate_skipped - 401 for expired timestamp
- 401 for invalid signature
- Data isolation verified
- Rate limit handling implemented
- Cursor pagination works
- Webhook signature verification implemented
- Settlement forwarded successfully
- Exponential backoff implemented
- Sign-off from Kesles technical PIC
Kesles Technical Team Contact
For questions during testing or UAT, contact:
- Email: support@kesles.com
- Include: bank code, API Key ID, request ID, and the error received