Testing Guide
Use the staging replica API, generate dummy keys, and configure staging webhooks.
What the testing API is
The testing environment is a staging-only replica of the Challanwala organization API. It mirrors the public contract, but uses deterministic dummy data and a simulated payment flow.
- Host:
https://cms-staging.challanwala.com - API root:
/api/v1/staging - Auth type: bearer secret
- Data source: dummy challans generated from the RC number you search
Generate a staging API secret
This testing route is public and mints a dummy bearer secret for the staging Hono API. It never creates a production organization key.
Staging key generation
Testing keys are public self-serve credentials intended only for the staging environment. They do not expose production organization data and they do not work against production routes.
/api/v1/staging/api-keysCreate a dummy staging bearer secret that can be used against the replica API.
curl --request POST 'https://cms-staging.challanwala.com/api/v1/staging/api-keys' \ --header 'Content-Type: application/json' \ --data '{"name": "Partner QA Sandbox"}'const response = await fetch('https://cms-staging.challanwala.com/api/v1/staging/api-keys', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({name: "Partner QA Sandbox"}),});const data = await response.json();console.log(data);{"success": true,"message": "Organization API secret generated","data": { "key": { "id": "8c9d962c-56cb-4b5d-998c-5a2a23c07e71", "name": "Partner QA Sandbox", "keyPrefix": "cms_test_2b15d8d31", "status": "ACTIVE", "createdAt": "2026-05-14T09:32:00.000Z", "maskedKey": "cms_test_2b15d8d31************************" }, "apiSecret": "cms_test_2b15d8d3156f98f1c1234ab5678ef901234567"}}Replica routes
The staging API includes the same primary business capabilities as production, plus testing-only webhook configuration helpers:
| Status | Meaning | Notes |
|---|---|---|
| GET | /api/v1/staging/metadata/rc-types | Lists supported RC types for testing searches. |
| GET | /api/v1/staging/locations | Returns fixed organization-scoped city/state fixtures. |
| POST | /api/v1/staging/challans/search | Creates or reuses deterministic dummy challans for the RC number. |
| POST | /api/v1/staging/payment-links | Returns ACTIVE first, then the stored link transitions immediately to its final test status. |
| GET | /api/v1/staging/payment-links/:id | Poll the stored payment-link status after creation. |
| GET | /api/v1/staging/webhook-config | Reads the current staging webhook configuration and recent deliveries. |
| PUT | /api/v1/staging/webhook-config | Sets the staging webhook URL, activation state, and optional secret rotation. |
| POST | /api/v1/staging/webhook-config/test | Triggers a signed webhook.test event to your configured receiver. |
Configure staging webhooks
Use the protected webhook-config routes after generating a staging key.
Public HTTPS endpoints only
The hosted staging API only accepts public HTTPS webhook URLs. Localhost, private-network addresses, link-local targets, and redirecting webhook URLs are rejected for security reasons.
/api/v1/staging/webhook-configSave a staging webhook URL, enable or disable delivery, and optionally rotate the webhook secret.
curl --request PUT 'https://cms-staging.challanwala.com/api/v1/staging/webhook-config' \ --header 'Authorization: Bearer YOUR_API_SECRET' \ --header 'Content-Type: application/json' \ --data '{"webhookUrl": "https://example.com/challanwala/webhook","isActive": true,"rotateSecret": true}'const response = await fetch('https://cms-staging.challanwala.com/api/v1/staging/webhook-config', { method: 'PUT', headers: { Authorization: 'Bearer YOUR_API_SECRET', 'Content-Type': 'application/json', }, body: JSON.stringify({webhookUrl: "https://example.com/challanwala/webhook",isActive: true,rotateSecret: true}),});const data = await response.json();console.log(data);{"success": true,"message": "Webhook configuration saved","data": { "config": { "id": "71414ce7-84da-43d6-bdb6-1c9fce1f9760", "webhookUrl": "https://example.com/challanwala/webhook", "isActive": true, "createdAt": "2026-05-14T09:41:00.000Z", "updatedAt": "2026-05-14T09:41:00.000Z" }, "webhookSecret": "whsec_54123f6a8dc9eaf73125cda94c336d98c701d1"}}If the callback is unsafe, the staging API rejects it before saving:
{"success": false,"message": "Webhook URL must be a public HTTPS endpoint","errors": [ { "path": "webhookUrl", "message": "Localhost and mDNS webhook targets are not allowed." }]}curl --request POST 'https://cms-staging.challanwala.com/api/v1/staging/webhook-config/test' \ --header 'Authorization: Bearer YOUR_API_SECRET'const response = await fetch('https://cms-staging.challanwala.com/api/v1/staging/webhook-config/test', { method: 'POST', headers: { Authorization: 'Bearer YOUR_API_SECRET', },});const data = await response.json();console.log(data);{"success": true,"message": "Webhook test event queued","data": { "eventType": "webhook.test"}}What webhook events fire in testing
The staging replica does more than store a webhook URL. It also emits predictable test events so you can validate your receiver end to end.
| Status | Meaning | Notes |
|---|---|---|
| POST /webhook-config/test | webhook.test | Use this first to confirm your endpoint is reachable and your signature verification is working. |
| POST /challans/search | challan_search.completed or challan_search.failed | A normal RC search sends challan_search.completed. Reserved failure RC numbers send challan_search.failed. |
| POST /payment-links | payment_link.created followed by a final payment event | Standard staging RC numbers emit payment_link.created and then payment_link.paid almost immediately. |
| Reserved payment RCs | payment_link.failed or payment_link.expired | STGPAYFAILED and STGPAYEXPIRE let you test alternate payment webhook branches deterministically. |
Immediate paid flow in testing
For normal staging RC numbers, payment-link creation returns an ACTIVE
response first for contract compatibility, but the stored link transitions to
PAID immediately after creation. Your webhook receiver should therefore be
ready to process payment_link.created and payment_link.paid back to back.
Recommended staging webhook test sequence:
- Generate a staging API key.
- Save a public HTTPS webhook URL with
rotateSecret: true. - Verify the returned
whsec_...secret in your receiver. - Trigger
POST /webhook-config/testand confirmwebhook.test. - Run a challan search and confirm
challan_search.completed. - Create a payment link and confirm
payment_link.createdand the final payment event.
Simulated payments
Generated staging payment links use a real testing page under the same host. Opening the paymentUrl shows a branded result page, but no real money movement happens.
- Standard RC numbers: create returns
ACTIVE, then status lookup becomesPAID STGPAYEXPIRE: create returnsACTIVE, then status becomesEXPIREDSTGPAYFAILED: create returnsACTIVE, then status becomesFAILED
Use the reserved testing inputs on the next page for predictable edge-case validation.
