Integrate with an AI agent
This page is one linear recipe for adding Transxact payments to an app. It’s
written so a coding agent (Claude Code, Cursor, Copilot and the like) can
follow it end to end, but it reads just as well for a person. Paste its URL
into your agent, or point the agent at
llms.txt for the whole site. Every page
also has a Markdown version: add .md to its URL.
The shape of every Transxact integration:
- Your server creates a Checkout Session for the order.
- You redirect the Customer to the session’s
hostedUrl. Transxact takes the payment with M-PAiSA or MyCash. - Transxact sends your server a signed webhook with the outcome.
- You fulfil the order from the webhook, not from the Customer’s redirect.
Before you start
- A Test mode secret key,
sk_test_..., from signing up with the Business account. Test mode simulates payments, so nothing here charges anyone. Webhooks need a Business account. - A webhook signing secret, from the Webhooks section of the dashboard, once you’ve added an endpoint there. Each endpoint has its own.
- Put both in environment variables,
TRANSXACT_SECRET_KEYandTRANSXACT_WEBHOOK_SECRET. Never in source code, a browser or a mobile app.
1. Install the SDK
Any other language can call the HTTP API directly: see the API Reference or the OpenAPI spec.
2. Create a Checkout Session on your server
amountis an integer in FJD cents, under FJD 5,000.00 (at most499999).- The idempotency key is required. Derive it from the order, so a retried request replays the same session instead of creating a second one.
metadatacomes back on every retrieve and webhook, so you can find the order without keeping your own mapping.
3. Send the Customer to hostedUrl
The redirect above is the whole step. The hosted page offers every Provider and
handles each one’s own steps. Afterwards it sends the Customer to successUrl
or offers a way back to cancelUrl, with ?session_id=cs_... appended.
Your successUrl page should say “thanks, we’re confirming your payment”,
not “paid”. Anyone can type that URL. If the page needs the real outcome,
retrieve the session:
4. Receive and verify the webhook
In PHP, the same check is Transxact\Webhooks::verifySignature($rawBody, $signatureHeader, $secret). See Verifying webhooks.
- Answer
2xxquickly. Anything else is retried for about a day, with the same eventid. - Delivery is at least once, so skip event
ids you’ve already handled. - The dashboard’s Send test sends a signed
webhook.testevent. Return2xxfor types you don’t handle.
5. Fulfil from the webhook
Ship, unlock or mark the order paid only in the
checkout_session.succeeded handler, after checking the session’s amount
matches the order. That’s the only signal that money moved. failed and
cancelled are final for that session. To let the Customer try again, create
a new Checkout Session with a new idempotency key.
6. Test it end to end
- Expose your server with a tunnel (
cloudflared tunnel --url http://localhost:3000orngrok http 3000) and set itshttps://address plus your webhook path as an endpoint in the dashboard. - Press Send test and check your endpoint returns
2xx. - Start a checkout, open the
hostedUrland pay with the practice screens: Approve payment for M-PAiSA, or any 7-digit number such as7123456with code123456for MyCash. - Check the order is marked paid by the webhook, and try Decline payment for the failure path.
7. Go live
Once your Verification is approved, create a Live mode key on the dashboard
and swap sk_test_... for sk_live_.... Nothing else changes. Until a
Provider takes Live mode payments, creating a session returns 409 live_mode_unavailable. See Test vs Live mode.
Common mistakes
- Amounts in dollars.
amount: 50is 50 cents. FJD 50.00 is5000. - Fulfilling on the redirect. The
successUrlvisit proves nothing. Fulfil from the verifiedcheckout_session.succeededwebhook. - Secret key in the client.
sk_keys belong on your server only. A mobile app asks your server to create the session (see Mobile integration). - Skipping signature verification, or verifying re-serialized JSON instead of the raw body.
- A random idempotency key per request. A retry then creates a second session. Tie the key to the order.
- Mixing modes. A Test mode session can’t be retrieved with a Live mode
key, and the reverse. Each returns
404. - Treating duplicate webhooks as new payments. Deduplicate on the event
id. - Branching on error messages. Branch on
error.code.messagecan change.