Skip to main content

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.

Two Auth Header Sets
  • Merchant lookup calls (/api/psp/v1/merchants/*): use X-API-Key-ID / X-Timestamp / X-Signature
  • Event forwarding calls (/psp/v1/events, /psp/v1/settlements): use X-PSP-Key-ID / X-PSP-Timestamp / X-PSP-Signature

Test Merchant Data

Staging provides dummy merchants you can use for testing:

NMIDBank CodeStatusDescription
ID102326873TEST1matches your bankactiveNormal merchant
ID102326873TEST2matches your bankactiveMerchant with many outlets
ID102326873TEST3matches your banksuspendedSuspended merchant (returns 404)
note

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-Signature is successfully verified
  • The merchant.activated event 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