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.
/api/fpo/v1/<country_code>/.... Use my, sg, or id for the country.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_idwhen the path includes/outlets/(list transactions)idwhen acknowledging via/transactions/<id>detailson 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]=…).
omni_reference=PL-0001&total_amount_cents=1000&app_id=a3osyvuayt&outlet_id=11637&format=txt
{
"omni_reference": "PL-0001",
"total_amount_cents": 1000,
"app_id": "a3osyvuayt",
"outlet_id": 11637,
"format": "txt",
"sign": "4e23af6ca0c0831e9fe3524ac7943d5e8860f0843a343f7c9c4a3f172b408972"
}
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.
{
"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>"
}
{
"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.| Field | Required | Notes |
|---|---|---|
omni_reference | Required | Your unique payment reference. Format [prefix]-[id], max 28 characters. Use the prefix Fave assigned to your integration. |
total_amount_cents | Required | Amount in cents (positive integer). Singapore max: 1999900. |
app_id | Required | Your integration App ID from Fave. |
outlet_id | Required | Fave outlet ID for this payment. |
format | Optional | txt, base64 (default), web_url, grab, paynow, alipay, wcp, shopeepay, atome. |
redirect_url | Optional | Recommended for web_url so customers return to your site after payment. |
callback_url | Optional | Optional per-payment webhook URL (overrides your default callback when enabled for your account; kept for a few hours). |
payment_option | Optional | both (default), app, or pwa. |
expiry_time | Optional | Unix timestamp for QR expiry. |
shopper_details | Optional | Object with optional email, phone, location. |
test | Optional | Enable test payment without the Fave app when supported for your account. |
sign | Required | HMAC-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.
{
"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>"
}
{
"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"
}
| Field | Required | Notes |
|---|---|---|
omni_reference | Required | Unique reference, max 28 characters. |
total_amount_cents | Required | Positive integer in cents. |
app_id | Required | Your integration App ID from Fave. |
outlet_id | Required | Fave outlet ID for this payment. |
code | Required | Customer QR or barcode payload. |
callback_url | Optional | Optional per-payment webhook URL. |
details | Optional | Arbitrary JSON metadata (excluded from signature). |
sign | Required | HMAC-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.
{
"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"
}
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.
null. When looking up by receipt_id, use the same app_id that created the payment.{
"app_id": "a3osyvuayt",
"omni_reference": "PL-0001",
"receipt_id": "0118-7347",
"outlet_id": 11637,
"sign": "<hmac_sha256_sign>"
}
{
"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"
}
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_.
{
"omni_reference": "PL-0001",
"app_id": "a3osyvuayt",
"outlet_id": 11637,
"sign": "<hmac_sha256_sign>"
}
{
"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.
{
"app_id": "a3osyvuayt",
"limit": 20,
"ack": true,
"timestamp": 1720512000,
"sign": "<hmac_sha256_sign>"
}
[
{
"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"
}
]
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.
{
"omni_reference": "PL-0001",
"app_id": "a3osyvuayt",
"status": "refunded",
"partial_refund_cents": 500,
"sign": "<hmac_sha256_sign>"
}
{
"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"
}
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:
| Prefix | Payment 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):
status | status_code |
|---|---|
pending_payment | 0 |
payment_processing | 1 |
successful | 2 |
disputed | 3 |
rejected | 4 |
refunded | 5 |
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.
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.
{
"error": "validation",
"error_description": "Omni reference already exists. Please provide unique omni reference."
}
| HTTP | error | Meaning |
|---|---|---|
400 | validation / stale_transaction | Invalid params, or refund outside the allowed window. |
401 | Unauthorized | Invalid signature, or outlet not linked to this app_id. |
404 | resource_not_found | Unknown app_id, or missing transaction/outlet. |
404 | error | Empty transaction history for the query window. |
422 | provider / format codes | QR format not supported, amount limit, or payment provider rejection. |
500 | internal_error / unexpected_error | Unexpected server failure. |
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.