Skip to content

Make Payment ​

Process transactions across the IIMMPACT platform. This is a unified endpoint for all product offerings, including JomPAY, mobile reload, gift cards, vouchers, eSIM purchases, and more.

TIP

For a step-by-step integration walkthrough, see the Make Payment Guide.

Idempotency ​

Once a transaction is created, its refid is permanent: resending the same request returns that transaction's current status and never creates a duplicate. Store and re-use the same refid for the same transaction.

Check Final Transaction Status: Call this API again with the identical request parameters and the original refid used during initiation. The API will return the current status of the transaction. This works for any refid that created a transaction (the response has a transaction_id). After a rejection (HTTP 400 without transaction_id) nothing was created, so resending that refid is a new purchase attempt.

API Endpoint ​

http
POST https://api.iimmpact.com/v2/topup

DANGER

Staging server statuses and failure reasons might not match production due to limited validation.

Request Headers ​

HeaderDescriptionRequired
X-Api-KeyYour API keyYes
X-TimestampUnix timestamp in secondsYes
X-NonceUnique request identifierYes
X-SignatureHMAC-SHA256 signature (v1=...)Yes
X-API-VersionAPI version date for opting into upcoming response format changes (e.g. 2026-04-01). Dates later than tomorrow (UTC) and malformed values are ignored. Optional.No

See API Key Authentication for how to sign your requests.

Request Body ​

ParameterTypeRequiredDescription
refidstringYesYour unique reference ID for this transaction. See refid constraints.
productstringYesProduct code
remarksstringNoOptional field for any additional remarks
accountstringYesRecipient's account number or identifier (e.g., bill account number, mobile number)
amountstring | numberYesTransaction face value or quantity. For a pricing select, send the selected option's numeric denomination, not price.amount; for a direct money field, send the entered amount. Internally parsed as decimal. The response always returns a number (integer if whole, decimal otherwise).
extrasobjectNoAdditional parameters required by specific products (see below)
extras.subproduct_codestringConditionalExact option code from /v2/options for the product's pricing field. Required for mobile internet products. Must correspond to the plan whose denomination equals amount.
extras.ic_numberstringNoIC number without dashes
extras.biller_codestringNoBiller code
extras.ref2stringConditionalRequired by some JomPAY billers. The ref2 value is typically printed on the user's JomPAY bill. Use bill presentment to validate. The API returns an error if ref2 is required but not provided.

refid Constraints ​

ConstraintValue
Max length50 characters
Allowed charactersAny non-empty string
Uniqueness scopePer reseller account (not global)
IdempotencyPermanent — re-querying with an existing refid always returns the original transaction

Example Request Body ​

jsonc
{
  "refid": "321479-0-30f4f209-ee",
  "product": "GC",
  "account": "0123456789",
  "amount": 10.00, // Strings also work, e.g. "10.00"
  "remarks": "",
  "extras": {}
}

Extras Requirements by Product ​

ParameterJomPAYPTPTNMobile DataOthers
subproduct_code-MandatoryMandatory-
ic_numberMandatory (payee IC)Mandatory (recipient IC)--
biller_codeMandatory---
ref2Conditional---

Plan code validation

A supplied plan code must exactly match (case-sensitive) an active option code for the product's pricing field. The option's denomination must equal amount.

Validation failures return HTTP 400 with data.statusCode 44, create no transaction, and leave the refid available for reuse. The possible remarks are:

  • Multiple plans are available for this amount. Please provide a plan code.
  • unknown or inactive option code '<code>'. Select an available plan and try again.
  • option code '<code>' does not match amount <amount>. Select the plan for this amount and try again.

This validation does not depend on X-API-Version.

Backward compatibility

Existing integrations that send amount only continue to work while the amount identifies exactly one active plan. New integrations must send subproduct_code.

JomPAY Testing

For staging test biller codes that simulate JomPAY payment outcomes, see JomPAY Test Biller Codes.

AMLA Compliance — JomPAY

Due to AMLA (Anti-Money Laundering Act 2001) requirements, it is mandatory to perform eKYC (electronic Know Your Customer) on your end-users for all JomPAY transactions. Pass the verified IC number (Malaysians) or Passport number (non-Malaysians) via the ic_number field. Failure to comply, or providing fictitious/invalid numbers, may lead to account suspension.

Response 200 ​

FieldTypeDescription
dataobjectTransaction result
data.statusCodenumberStatus code — see Payment Status Codes
data.statusstringOutcome: Accepted, Processing, Succesful, or Failed
data.transaction_idstringIIMMPACT transaction ID. Present on every response that describes a transaction; omitted on rejected requests
data.accountstringAccount number
data.productstringProduct code
data.productNamestringProduct name
data.amountnumberAmount paid
data.snstringSerial number from the provider/operator
data.pinstringPIN for vouchers, gift cards, etc.
data.expirystringVoucher expiration date (yyyymmdd)
data.costnumberYour wholesale cost for this transaction (deducted from your IIMMPACT wallet balance).
data.balancenumberCurrent wallet balance
data.remarksstringAdditional notes about the transaction
data.refidstringYour unique reference ID
data.timestampstringTransaction timestamp
data.notestringInstructions or information for the user regarding the product
data.voucherlinkstringLink to access or redeem the voucher

Spelling Note

The success status value is spelled Succesful (single 's'), not Successful. This is a legacy quirk that cannot be changed. Match this exact string in your code.

Example Response:

json
{
  "data": {
    "statusCode": 20,
    "status": "Succesful",
    "transaction_id": "123871821",
    "account": "0123456789",
    "product": "GC",
    "productName": "Grab Gift Code",
    "amount": 5,
    "sn": "106648697",
    "pin": "MPHE39G3WL",
    "expiry": "20251116",
    "cost": 5,
    "balance": 50.54,
    "remarks": "",
    "refid": "321479-0-30f4f209-ee",
    "timestamp": "2025-05-20 13:09:34",
    "note": "Insert voucher code into Use Grab Gifts under Use Offers section upon check out",
    "voucherlink": "https://api.grab.com/gifts/v2/go?id=7464957319334bb0afbd5980738a2b50"
  }
}

Response 400 ​

Rejected requests (invalid product, insufficient balance, invalid denomination, invalid account, a temporary system error, etc.) return HTTP 400 with the same {"data": {...}} body shape as HTTP 200, without a transaction_id.

A transaction that fails as soon as it is created can also come back with HTTP 400 (or 5xx), but then the response carries its transaction_id. Treat any response with a transaction_id as that transaction's status, not as a rejection.

Key Difference from HTTP 200

HTTP 400 without transaction_id means no transaction was created and the refid is not consumed. You can safely retry with the same refid after fixing the cause: top up for 43, correct the request for 44/58, or wait a few seconds for 49. This is different from an HTTP 200 Failed response, where a transaction was created. See Not processed.

FieldTypeDescription
dataobjectValidation error result
data.statusCodenumberStatus code — see Payment Status Codes
data.statusstringAlways Failed for validation errors
data.accountstringAccount number from the request
data.productstringProduct code from the request
data.amountnumberAmount from the request
data.costnumberAlways 0 (no transaction was processed)
data.balancenumberCurrent wallet balance (unchanged)
data.remarksstringReason for the validation failure (e.g., Invalid_Denomination, Insufficient_Balance)
data.refidstringYour reference ID (not consumed — can be reused)
data.timestampstringTimestamp of the response

Example Response:

json
{
  "data": {
    "statusCode": 58,
    "status": "Failed",
    "account": "0123456789",
    "product": "DB",
    "amount": 22.23,
    "cost": 0,
    "balance": 50.54,
    "remarks": "Invalid_Denomination",
    "refid": "my-ref-id",
    "timestamp": "2026-03-16 13:09:34"
  }
}

Transaction Status Values ​

HTTPStatusFinal?Transaction Created?What To Do
400, no transaction_idFailedNo — request rejectedNoFix the cause, retry with same refid (or close the order and never reuse the refid)
200AcceptedNoYesRe-query with same refid
200ProcessingNoYesRe-query with same refid
200SuccesfulYesYesDone
200FailedYesYesFinal failure — use new refid
200Failed, statusCode 53, remarks RefundYesYesTransaction was voided

Distinguish HTTP 400 Failed vs HTTP 200 Failed

  • HTTP 400 + Failed without transaction_id = no transaction was created, refid not consumed. Retry with the same refid after fixing the cause.
  • Any response with transaction_id (HTTP 200, 400 or 5xx) = a transaction exists. Follow its status; a Failed transaction is final — use a new refid for the next attempt.
  • HTTP 200 + Failed = a transaction was created and failed at the provider level. This is final — use a new refid for the next attempt.

Recommended Polling Strategy

When a transaction returns Processing, poll every 5-10 seconds by re-querying with the same refid. Most transactions resolve within 30 seconds. Always combine polling with callbacks — polling provides immediate UX feedback, while callbacks are the reliable final status delivery mechanism.

IIMMPACT API Documentation