Ganap compatibility
Already integrated with Ganap? You don't have to touch your integration code. JCPay exposes a Ganap-shaped endpoint so you change one URL constant and ship. Same request body, same sk: header, same webhook callback shape.
The one-line migration
Replace your base URL:
- https://api.ganap.net + https://jcpay.innoserver.cloud
That's it. Your externalId, amount constraints (0 or 200–50000), success/failure redirects, payer email/name, and sk: auth continue to work unchanged.
Create a checkout
curl -X POST "https://jcpay.innoserver.cloud/api/merchant-project/<YOUR_PROJECT_UUID>/webhook-checkout" \
-H "Content-Type: application/json" \
-H "sk: sk_gnp_<YOUR_SECRET>" \
-d '{
"externalId": "ORDER-123",
"amount": 500,
"successRedirectURL": "https://yourgame.example/payment/success",
"failureRedirectURL": "https://yourgame.example/payment/failed",
"payerEmail": "player@example.com",
"payerName": "Player Handle"
}'Response is dual-shaped — whichever key your existing Ganap client reads, it still works:
{
"status": "pending",
"externalId": "ORDER-123",
"external_id": "ORDER-123",
"amount": 500,
"checkoutUrl": "https://jcpay.innoserver.cloud/sandbox/checkout/pay_...",
"checkout_url": "https://jcpay.innoserver.cloud/sandbox/checkout/pay_...",
"reference": "mck_...",
"referenceNumber": "mck_...",
"reference_number": "mck_..."
}Rules
externalIdmust be unique per transaction; doubles as your idempotency key — replays with the same value return the existing payment.amountmust be0(free) or between200and50000, PHP.- Path id accepts either the project UUID or its slug.
- Authorization is
sk: sk_gnp_<secret>— note the custom header name, notAuthorization: Bearer.
Webhook callback
When the payment reaches a terminal state, JCPay POSTs a Ganap-shaped payload to your configured webhook URL with the static key in a query param:
POST https://your-domain.example/topup/webhook?key=<YOUR_STATIC_KEY>
Content-Type: application/json
{
"status": "success",
"externalId": "ORDER-123",
"referenceNumber": "mck_...",
"amount": 500
}Auth is the query param — no HMAC signature header. Verify that keymatches the one you registered, and use externalId to reconcile against your order.
Status enum
| JCPay internal | Delivered as | When |
|---|---|---|
succeeded | success | Customer paid; funds settled to your ledger. |
failed | failed | Customer attempt declined / cancelled. |
expired | expired | Checkout window elapsed without a decision (15 min default). |
processing | processing | Provider accepted but not yet finalized. Rare intermediate state. |
Delivery + retries
- Webhook deliveries retry on non-2xx at 1m, 5m, 30m, 2h, 6h, 24h, then mark
failed. - Delivery timeout: 10 seconds per attempt.
- No HMAC in this mode. If you want signed webhooks, switch the endpoint to JCPay's native
/v1/paymentsflow (see Webhooks).
Getting a project
Ganap-compat projects are provisioned by the JCPay team today. Give us (a) the merchant account you want to bind, (b) your current Ganap webhook URL, (c) the static key you use on that URL. We return your project_uuid and sk_gnp_<secret>— the secret is shown once.
Upgrading later
Once you're on JCPay, you can migrate to the native /v1/payments API at your own pace to gain HMAC-signed webhooks, Idempotency-Key headers, multiple payment methods per merchant, and a self-service dashboard. Compat mode stays running in parallel — no forced cutover.