API reference

FavePay Omni (FPO)

Accept payments online or in-store with Fave. Use these endpoints to create QR codes, look up transactions, refund, and receive payment status via webhook.

MY SG ID
All paths are under /api/fpo/v1/<country_code>/.... Use my, sg, or id for the country.
Typical flow: authenticate → create a payment (QR or merchant scan) → handle the webhook → look up or acknowledge as needed → refund if required.

Authentication

Sign every request with HMAC-SHA256 using the secret key Fave issued for your integration. Some setups use an outlet-specific secret instead, so use the key Fave confirms for your account.

Build the signing string from URL-encoded request fields. Always exclude sign and country_code. Also exclude:

  • outlet_id when the path includes /outlets/ (list transactions)
  • id when acknowledging via /transactions/<id>
  • details on merchant scan (metadata is not part of the signature)

Field order matters. Do not sort keys alphabetically; encode fields in the same order you send them. Nested objects flatten to bracket form (for example shopper_details[email]=…).

Signing string example
omni_reference=PL-0001&total_amount_cents=1000&app_id=a3osyvuayt&outlet_id=11637&format=txt
Example request body
{
  "omni_reference": "PL-0001",
  "total_amount_cents": 1000,
  "app_id": "a3osyvuayt",
  "outlet_id": 11637,
  "format": "txt",
  "sign": "4e23af6ca0c0831e9fe3524ac7943d5e8860f0843a343f7c9c4a3f172b408972"
}
Unknown app_id typically returns 404 resource_not_found. Invalid signature returns 401 Unauthorized.

Request a QR code

Creates a single-use QR (or payment URL) for an outlet. Currency follows the country code in the path.

POST
/api/fpo/v1/<country_code>/qr_codes
Request body
{
  "omni_reference": "PL-0001",
  "total_amount_cents": 1000,
  "app_id": "a3osyvuayt",
  "outlet_id": 11637,
  "format": "web_url",
  "redirect_url": "https://partner.example/return",
  "callback_url": "https://partner.example/webhook",
  "sign": "<hmac_sha256_sign>"
}
Sample response
{
  "code": "https://omni.myfave.com/web_views/favepay_online/gateway?omni_ref=<token>",
  "format": "web_url",
  "expires_in": 900
}
code is a payment URL when format is web_url (a Fave-hosted checkout link with a short-lived token, not your raw omni_reference), plaintext QR payload for txt, or a Base64 PNG for base64. expires_in is seconds until expiry. Default format is base64.
FieldRequiredNotes
omni_referenceRequiredYour unique payment reference. Format [prefix]-[id], max 28 characters. Use the prefix Fave assigned to your integration.
total_amount_centsRequiredAmount in cents (positive integer). Singapore max: 1999900.
app_idRequiredYour integration App ID from Fave.
outlet_idRequiredFave outlet ID for this payment.
formatOptionaltxt, base64 (default), web_url, grab, paynow, alipay, wcp, shopeepay, atome.
redirect_urlOptionalRecommended for web_url so customers return to your site after payment.
callback_urlOptionalOptional per-payment webhook URL (overrides your default callback when enabled for your account; kept for a few hours).
payment_optionOptionalboth (default), app, or pwa.
expiry_timeOptionalUnix timestamp for QR expiry.
shopper_detailsOptionalObject with optional email, phone, location.
testOptionalEnable test payment without the Fave app when supported for your account.
signRequiredHMAC-SHA256 signature.

Merchant scan

Charge a customer-presented QR or barcode (your POS scans the customer’s code). Returns a transaction object after the charge attempt.

POST
/api/fpo/v1/<country_code>/merchant_scan
Request body
{
  "omni_reference": "PL-0001",
  "total_amount_cents": 1000,
  "app_id": "a3osyvuayt",
  "outlet_id": 11637,
  "code": "<customer_qr_or_barcode>",
  "callback_url": "https://partner.example/webhook",
  "details": { "order_id": "ORD-1001" },
  "sign": "<hmac_sha256_sign>"
}
Sample response
{
  "id": "FP_792619",
  "receipt_id": "0118-7347",
  "outlet_name": "TTDI Shop",
  "total_amount_cents": "1000",
  "currency": "MYR",
  "outlet_id": "11637",
  "mid": "1166",
  "omni_reference": "PL-0001",
  "status": "successful",
  "status_code": "2",
  "created_at": "2026-07-10T10:15:22.100+08:00",
  "charged_amount_cents": "1000",
  "user_id": "2818771",
  "fpl_transaction": "false"
}
FieldRequiredNotes
omni_referenceRequiredUnique reference, max 28 characters.
total_amount_centsRequiredPositive integer in cents.
app_idRequiredYour integration App ID from Fave.
outlet_idRequiredFave outlet ID for this payment.
codeRequiredCustomer QR or barcode payload.
callback_urlOptionalOptional per-payment webhook URL.
detailsOptionalArbitrary JSON metadata (excluded from signature).
signRequiredHMAC-SHA256 signature over fields excluding details.

Webhooks

Fave posts the transaction object (including sign) to your callback URL when payment status changes. Respond with any HTTP status < 400 quickly, then process asynchronously.

Sample callback body
{
  "id": "FP_792619",
  "receipt_id": "0118-7347",
  "outlet_name": "TTDI Shop",
  "total_amount_cents": "2100",
  "payment_type": "Credit Card",
  "payment_type_variant": "Visa",
  "currency": "MYR",
  "outlet_id": "1593",
  "mid": "1166",
  "omni_reference": "PL-0001",
  "status": "successful",
  "status_code": "2",
  "created_at": "2021-03-26T18:14:59.698+08:00",
  "charged_amount_cents": "1550",
  "merchant_takeback_cents": "1881",
  "user_id": "2818771",
  "fpl_transaction": "false",
  "sign": "fc12259055f146e8bf0b587cbd8dbb9283e4db6a525243bc608e5762e43c6b29"
}
Verify sign before trusting the callback. On a successful delivery response, Fave may auto-acknowledge the payment, so you may not need the Acknowledge API unless the callback failed or you use static QR. Expired unpaid QRs can trigger a rejected callback. Status may also be successful, rejected, or refunded. Retries use increasing intervals; keep your endpoint idempotent.

Get a transaction

Look up one payment by omni_reference or receipt_id (at least one required). Optional outlet_id must match the payment’s outlet when provided.

If the QR was created but not yet paid, you may receive a pending transaction object with many fields set to null. When looking up by receipt_id, use the same app_id that created the payment.
GET
/api/fpo/v1/<country_code>/transactions
Parameters (provide omni_reference or receipt_id)
{
  "app_id": "a3osyvuayt",
  "omni_reference": "PL-0001",
  "receipt_id": "0118-7347",
  "outlet_id": 11637,
  "sign": "<hmac_sha256_sign>"
}
Sample response
{
  "id": "FP_792619",
  "receipt_id": "0118-7347",
  "outlet_name": "TTDI Shop",
  "total_amount_cents": "2100",
  "payment_type": "Credit Card",
  "payment_type_variant": "Visa",
  "currency": "MYR",
  "outlet_id": "1593",
  "mid": "1166",
  "omni_reference": "PL-0001",
  "status": "successful",
  "status_code": "2",
  "created_at": "2021-03-26T18:14:59.698+08:00",
  "campaign_credit_amount_cents": "0",
  "campaign_funded_by": "N/A",
  "aabig_points_used_amount_cents": "0",
  "merchant_cashback_amount_cents": "350",
  "fave_credits_amount_cents": "200",
  "promo_code_value_cents": "0",
  "e_card_credits_used_cents": "0",
  "charged_amount_cents": "1550",
  "cashback_rate": "0.0",
  "merchant_cashback_issued_cents": "0",
  "promo_code": "N/A",
  "promo_code_cashback_value": "N/A",
  "promo_code_cashback_type": "N/A",
  "promo_code_cashback_issued_cents": "0",
  "promo_code_cashback_funded_by": "N/A",
  "fave_fees_percentage": "1.0",
  "fave_fees_cents": "18",
  "sst_on_total_fees_cents": "1",
  "merchant_takeback_cents": "1881",
  "user_id": "2818771",
  "fpl_transaction": "false",
  "fpl_fees_percentage": "N/A",
  "fpl_fees_cents": "N/A",
  "fpl_merchant_cashback_reduction_percentage": "N/A"
}
Most amount fields are returned as strings. payment_type / payment_type_variant (and sometimes minimum_fee_applied) appear when Fave has enabled payment-method reporting for your account. shopper_details may appear when your integration supports it.

Acknowledge a transaction

Marks a payment as acknowledged on your side. Use this for static-QR flows, or when a webhook was missed and the payment was not auto-acknowledged.

id in the path is the transaction id prefix form: FP_, RPP_, GRAB_, PAYNOW_, or EXT_.

POST
/api/fpo/v1/<country_code>/transactions/<id>
Request body
{
  "omni_reference": "PL-0001",
  "app_id": "a3osyvuayt",
  "outlet_id": 11637,
  "sign": "<hmac_sha256_sign>"
}
Sample response
{
  "id": "FP_792619",
  "receipt_id": "0118-7347",
  "outlet_name": "TTDI Shop",
  "total_amount_cents": "2100",
  "currency": "MYR",
  "outlet_id": "1593",
  "mid": "1166",
  "omni_reference": "PL-0001",
  "status": "successful",
  "status_code": "2",
  "created_at": "2021-03-26T18:14:59.698+08:00",
  "charged_amount_cents": "1550",
  "merchant_takeback_cents": "1881",
  "user_id": "2818771",
  "fpl_transaction": "false"
}

List transactions

Returns recent transactions for an outlet as a JSON array. Use timestamp and limit to narrow results.

GET
/api/fpo/v1/<country_code>/outlets/<outlet_id>/transactions
Parameters
{
  "app_id": "a3osyvuayt",
  "limit": 20,
  "ack": true,
  "timestamp": 1720512000,
  "sign": "<hmac_sha256_sign>"
}
Sample response
[
  {
    "id": "FP_792619",
    "receipt_id": "0118-7347",
    "outlet_name": "TTDI Shop",
    "total_amount_cents": "2100",
    "currency": "MYR",
    "outlet_id": "1593",
    "mid": "1166",
    "omni_reference": "PL-0001",
    "status": "successful",
    "status_code": "2",
    "created_at": "2021-03-26T18:14:59.698+08:00",
    "charged_amount_cents": "1550",
    "merchant_takeback_cents": "1881",
    "user_id": "2818771",
    "fpl_transaction": "false"
  },
  {
    "id": "FP_792620",
    "receipt_id": "0118-7348",
    "outlet_name": "TTDI Shop",
    "total_amount_cents": "1500",
    "currency": "MYR",
    "outlet_id": "1593",
    "mid": "1166",
    "omni_reference": "PL-0002",
    "status": "successful",
    "status_code": "2",
    "created_at": "2021-03-26T19:02:11.120+08:00",
    "charged_amount_cents": "1500",
    "merchant_takeback_cents": "1470",
    "user_id": "2819002",
    "fpl_transaction": "false"
  }
]
Defaults & limits: if both limit and timestamp are omitted → last 10 since 24 hours ago. If only limit is set → since 30 days ago. timestamp must not be earlier than ~31 days. Max limit is 1000. ack defaults to true (return all payments in range). Set ack=false to return only unacknowledged payments. Empty result → 404 with error: "error" and description No records were found that match your query. Response is a bare array (not wrapped in a transactions key).

Refund a transaction

Refunds a successful payment. Set status to refunded. Optional partial_refund_cents for a partial refund.

POST
/api/fpo/v1/<country_code>/transactions
Request body
{
  "omni_reference": "PL-0001",
  "app_id": "a3osyvuayt",
  "status": "refunded",
  "partial_refund_cents": 500,
  "sign": "<hmac_sha256_sign>"
}
Sample response
{
  "id": "FP_792619",
  "receipt_id": "0118-7347",
  "outlet_name": "TTDI Shop",
  "total_amount_cents": "2100",
  "refund_amount_cents": "500",
  "currency": "MYR",
  "outlet_id": "1593",
  "mid": "1166",
  "omni_reference": "PL-0001",
  "status": "refunded",
  "status_code": "5",
  "created_at": "2021-03-26T18:14:59.698+08:00",
  "charged_amount_cents": "1550",
  "fave_fees_cents": "18",
  "sst_on_total_fees_cents": "1",
  "merchant_takeback_cents": "1881",
  "user_id": "2818771",
  "fpl_transaction": "false",
  "fpl_fees_percentage": "N/A",
  "fpl_fees_cents": "N/A",
  "fpl_merchant_cashback_reduction_percentage": "N/A"
}
Refund window: Refunds are typically allowed only on the same calendar day as the payment (stale_transaction after that). Some merchants may have a different policy window. Partial refunds cannot exceed charged_amount_cents, and are not supported for DuitNow or PayNow. If already refunded, the API returns the transaction object with HTTP 200 (not an error body).

Transaction IDs & statuses

Transaction id values use a type prefix:

PrefixPayment type
FP_FavePay / in-app payment
RPP_DuitNow
GRAB_Grab
PAYNOW_PayNow
EXT_External wallet or other payment method

Common payment status / status_code values (FavePay / in-app):

statusstatus_code
pending_payment0
payment_processing1
successful2
disputed3
rejected4
refunded5

Payment type fields

Transaction responses may include payment_type and payment_type_variant so you can show how the customer paid. For Fave in-app payments these fields are returned when payment-method reporting is enabled for your account.

Common values
Fave in-app: type and variant from the payment method used (when enabled)
DuitNow: payment_type="DuitNow", variant=bank name or BIC
Grab: payment_type="grab", variant from Grab payment method
PayNow: type and variant from PayNow payment method details
Other wallets: payment_type=provider name, variant from channel when available

Errors

Error responses use error and error_description.

Example
{
  "error": "validation",
  "error_description": "Omni reference already exists. Please provide unique omni reference."
}
HTTPerrorMeaning
400validation / stale_transactionInvalid params, or refund outside the allowed window.
401UnauthorizedInvalid signature, or outlet not linked to this app_id.
404resource_not_foundUnknown app_id, or missing transaction/outlet.
404errorEmpty transaction history for the query window.
422provider / format codesQR format not supported, amount limit, or payment provider rejection.
500internal_error / unexpected_errorUnexpected server failure.
Already-refunded payments return HTTP 200 with the transaction object (not an error). Named codes you may also see include failed_request and already_refunded (handled as success body).

Support

Need credentials, sandbox access, or help with an integration issue? Contact your Fave technical contact.

Platform-specific checkout integrations (for example certain e-commerce plugins) are separate from the core Omni REST endpoints above. Ask Fave if your integration needs them.