Payment intents
The core unit of a payment. An intent is created with amount (FJD cents),
currency (always FJD for the MVP), an optional reference, and a
return_url for redirect rails. It exposes one state machine regardless of
rail:
requires_action → processing → succeeded | failed
requires_action expires after 24h; processing auto-fails after a
rail-specific TTL unless it resolves first. A terminal intent never transitions
again.
Platform-wide concepts (auth, idempotency, versioning, errors) live on the
Create an intent
POST /payment-intents
Request:
POST /payment-intents
Authorization: Bearer sk_test_...
Idempotency-Key: order-1234
Content-Type: application/json
{
"amount": 2500,
"reference": "order-1234",
"return_url": "https://you.example.com/thanks"
}
Parameters:
| Name | Type | Required | Description |
|---|---|---|---|
amount | integer (FJD cents) | ✅ | Positive integer in FJD cents |
reference | string | ❌ | Merchant order ID |
return_url | string (https://) | ❌ | URL the customer returns to after a redirect rail |
payment_method | string | ❌ | mpaisa (default), mycash, or card |
Transxact-Version | header, YYYY-MM-DD | ❌ | Pins the API version |
Response (200):
{
"intent": {
"id": "pi_...",
"status": "requires_action",
"mode": "test",
"next_action": {
"type": "redirect",
"url": "/simulate/pi_..."
}
}
}
Drive the next action
The next_action type tells you the single next step, independent of rail:
redirect— M-PAiSA and cards: send the customer tonext_action.url;
they act on the hosted page and return_url brings them back.
collect_otp— MyCash: collect the customer's mobile number and OTP and
confirm them server-side (below).
Confirm the mobile number
POST /payment-intents/{id}/confirm
Authorization: Bearer sk_test_...
Content-Type: application/json
{ "mobile": "+6797000000" }
MyCash then sends an OTP to that number.
Confirm the OTP
POST /payment-intents/{id}/confirm
Authorization: Bearer sk_test_...
Content-Type: application/json
{ "otp": "123456" }
Any OTP approves in test mode except the magic 000000; see
test mode for
the full magic-value set.
Simulate an outcome (test mode)
POST /simulate/{id}
Force a predetermined outcome for a test-mode intent instead of walking the
redirect:
| Input | Outcome |
|---|---|
reference: "test_success" or amount 1 | succeeded |
reference: "test_fail" or amount 2 | failed |
reference: "test_pending" | processing, then succeeds on next simulate |
Code examples:
| Language | Example |
|---|---|
| Python | transxact.test.simulate(intent["id"], magic=Magic.REFERENCE_SUCCESS) |
| Node.js | await transxact.test.simulate(intent.id, Magic.referenceSuccess) |
| Go | client.Test.Simulate(ctx, "pi_...", transxact.ReferenceTestSuccess) |
| Java | client.test().simulate("pi_...", Magic.REFERENCE_SUCCESS) |
Code examples
| Language | Example |
|---|---|
| Python | transxact.payment_intents.create({"amount": 2500, "reference": "order-123"}) |
| Node.js | await transxact.paymentIntents.create({ amount: 2500, reference: "order-123" }) |
| Go | client.PaymentIntents.Create(ctx, &transxact.CreatePaymentIntent{Amount: 2500, Reference: "order-123"}, "") |
| Java | client.paymentIntents().create(new PaymentIntents.CreateRequest(2500).reference("order-123"), "order-123") |
Related: Webhook endpoints — get
notified when an intent reaches a terminal state.