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

reference index.

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:

NameTypeRequiredDescription
amountinteger (FJD cents)Positive integer in FJD cents
referencestringMerchant order ID
return_urlstring (https://)URL the customer returns to after a redirect rail
payment_methodstringmpaisa (default), mycash, or card
Transxact-Versionheader, YYYY-MM-DDPins 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 to next_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:

InputOutcome
reference: "test_success" or amount 1succeeded
reference: "test_fail" or amount 2failed
reference: "test_pending"processing, then succeeds on next simulate

Code examples:

LanguageExample
Pythontransxact.test.simulate(intent["id"], magic=Magic.REFERENCE_SUCCESS)
Node.jsawait transxact.test.simulate(intent.id, Magic.referenceSuccess)
Goclient.Test.Simulate(ctx, "pi_...", transxact.ReferenceTestSuccess)
Javaclient.test().simulate("pi_...", Magic.REFERENCE_SUCCESS)

Code examples

LanguageExample
Pythontransxact.payment_intents.create({"amount": 2500, "reference": "order-123"})
Node.jsawait transxact.paymentIntents.create({ amount: 2500, reference: "order-123" })
Goclient.PaymentIntents.Create(ctx, &transxact.CreatePaymentIntent{Amount: 2500, Reference: "order-123"}, "")
Javaclient.paymentIntents().create(new PaymentIntents.CreateRequest(2500).reference("order-123"), "order-123")

Related: Webhook endpoints — get

notified when an intent reaches a terminal state.