FavePay Omni (FPO)
Accept payments online or in-store with Fave. Use these endpoints to create QR codes, look up transactions, cancel unpaid QRs, 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.Payment flows
These sequence diagrams match the three API implementation flows in the FavePay Omni documentation: dynamic QR online, dynamic QR offline, and static QR. Actors: Vendor (integrating system), Customer, Fave Omni API, and where needed Fave app or Payment page.
1. Dynamic QR online (web_url)
E-commerce / hosted checkout. Customer pays on Fave’s payment page, then returns to the Vendor redirect_url.
sequenceDiagram autonumber actor Customer participant Vendor as Vendor participant Fave as Fave Omni API participant Pay as Payment page Customer->>Vendor: Checkout / choose Fave Vendor->>Fave: POST /qr_codes (web_url) Fave-->>Vendor: Hosted payment URL Vendor->>Customer: Redirect to payment URL Customer->>Pay: Complete payment Pay-->>Customer: Return via redirect_url Fave->>Vendor: Webhook (successful or rejected) Vendor-->>Fave: HTTP under 400 Note over Vendor,Fave: Optional GET /transactions Vendor->>Customer: Show order result
2. Dynamic QR offline (txt / base64)
Retail point of sale. The Vendor Client displays a one-time QR; the customer scans it with the Fave app.
sequenceDiagram autonumber actor Customer participant Vendor as Vendor participant Fave as Fave Omni API participant App as Fave app Customer->>Vendor: Start payment Vendor->>Fave: POST /qr_codes (txt or base64) Fave-->>Vendor: QR payload or image Vendor->>Customer: Show QR Customer->>App: Scan QR and pay App->>Fave: Payment completed Fave->>Vendor: Webhook (successful or rejected) Vendor-->>Fave: HTTP under 400 Note over Vendor,Fave: Optional GET /transactions Vendor->>Customer: Confirm sale
3. Static QR
Vendor fetches one combined static QR for the outlet (POST /qr_codes/static). Response includes supported_apms (e.g. favepay, wcp, paynow, alipay) for logos. Customer scans and pays with any listed wallet; amount is entered on the phone. After the webhook, the Vendor must call Acknowledge. Static QR is not auto-acknowledged.
sequenceDiagram
autonumber
actor Customer
participant Vendor
participant Fave
Vendor->>Fave: POST /qr_codes/static (outlet_id, app_id, sign[, format])
Fave->>Fave: Resolve enabled APMs / Assemble combined EMVCo string
Fave-->>Vendor: 200 { code, format, supported_apms }
Note over Vendor,Fave: format = txt|base64 encoding only\nsupported_apms = APMs in the QR
Vendor->>Customer: Display static QR (and show icons)
Customer->>Fave: Scan QR, enter amount, pay (Fave / WeChat / PayNow / Alipay)
Fave->>Vendor: POST callback_url (successful)
Vendor-->>Fave: HTTP 200
Vendor->>Fave: POST /transactions/{id}
Fave-->>Vendor: 200 transaction
Vendor->>Customer: Receipt / close sale
opt Webhook missed
Vendor->>Fave: GET /outlets/{outlet_id}/transactions (ack=false)
Fave-->>Vendor: Unacknowledged list
Vendor->>Fave: POST /transactions/{id}
end
sign before trusting the payload.
Fetch static QR with POST /qr_codes/static (sign in the request body — do not use GET query params for auth).
QR code formats
The format you send on Request a QR code controls what code returns and how the customer pays. Choose the format that matches your channel (online checkout, POS display, or a specific wallet QR).
Default is base64 if you omit format.
format |
What you get | Typical use | How payment happens |
|---|---|---|---|
txt |
Plaintext QR payload. You encode it into a QR image yourself. | POS that can generate QR images | Customer scans the displayed QR with the Fave app (dynamic offline flow). |
base64 |
PNG QR image as a Base64 string. Render with a data URL, or decode to a file. | POS / apps that cannot build QR images | Same as txt: customer scans with the Fave app (dynamic offline flow). |
web_url |
Hosted Fave payment URL (tokenized gateway link). | Online / e-commerce checkout | Customer opens the URL, pays on Fave’s page, then returns via redirect_url (dynamic online flow). |
favepay |
FavePay QR payload. | In-store FavePay | Customer scans with the Fave app (dynamic offline flow). |
grab |
GrabPay QR payload. | GrabPay acceptance | Customer scans with Grab / GrabPay. |
paynow |
SGQR containing PayNow payload only. | Singapore PayNow | Customer pays via a PayNow-capable banking app. |
alipay |
Alipay QR payload. | Alipay acceptance | Customer scans with Alipay. |
wcp |
SGQR containing Fave payload (WeChat Pay can detect it). | WeChat Pay | Customer scans with WeChat Pay. |
shopeepay |
ShopeePay QR payload. | ShopeePay acceptance | Customer scans with ShopeePay. |
atome |
Atome QR payload. | Atome acceptance | Customer scans with Atome. |
web_url; in-store Fave app scan → txt, base64, or favepay; wallet-specific QR → grab, paynow, alipay, wcp, shopeepay, or atome.
Availability can depend on country and what Fave enabled for your account. Unsupported formats return an error (for example service_not_supported).
See Payment flows for the online and offline sequences.
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 depends on format (see QR code formats): payment URL for web_url, plaintext payload for txt, Base64 PNG for base64, FavePay payload for favepay, or a wallet-specific QR payload. 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 | See QR code formats. Default base64. Accepted: txt, base64, web_url, favepay, grab, paynow, alipay, wcp, shopeepay, atome. |
redirect_url | Optional | Recommended for web_url so customers return to your site after payment. Fave appends encrypted transaction details as omni_ref. See Omni_ref in redirect URL. |
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 | When true, returns a test page URL in code for dynamic formats only. See Use of test parameter. |
sign | Required | HMAC-SHA256 signature. |
Use of test parameter
When test=true is provided, the API returns a test page URL in the code field so partners can simulate success or failure without the Fave app.
Supported formats: base64, txt, web_url, favepay, and paynow. Wallet-specific formats do not support test mode.
In test mode, the code field is always a test page URL (for example /web_views/favepay_online/test?omni_ref=...), regardless of the requested format.
Partners can simulate a payment success or failure scenario by clicking the corresponding buttons on the test payment page.
Partners can simulate a promo-applied payment by ticking the Promo code applied checkbox on the test payment page. This sends promo_code=true and applies promo fpoms.
test parameter in a production environment — the test page is blocked there. This mode is strictly reserved for testing purposes. Under normal circumstances, a user would use the Fave app to make payment instead.Omni_ref in redirect URL
After payment on Fave’s payment page, the customer is redirected back to your site using the redirect_url provided in the QR code request.
Fave appends encrypted transaction details as the omni_ref query parameter:
<redirect_url>?omni_ref=<encrypted_value>
https://your-site.com/payment/complete?omni_ref=7gxyZxBRE6wARrTJNx5Fe6VH/...
Decrypt omni_ref using your private API key (the same key used to sign API requests).
- Algorithm: AES-128-CBC
- Encoding: Base64
receipt_id=0871-5110&omni_reference=PL-000023&status=successful&total_amount_cents=1000
| Field | Description |
|---|---|
receipt_id | Fave receipt ID for the payment. |
omni_reference | Your payment reference from the QR code request. |
status | Payment status (for example successful). |
total_amount_cents | Charged amount in cents. |
omni_ref flow applies to standard integrations. Magento, WooCommerce, Jocom, and EasyStore use different redirect formats.Fetch static QR
Returns one persistent (static) EMVCo / SGQR payload for an outlet. Schemes enabled on the outlet (for example Fave, WeChat Pay via the Fave slot, PayNow, Alipay) are assembled into a single QR string. Unlike Request a QR code, this does not create a payment, does not take an amount or omni_reference, and does not expire.
{
"outlet_id": 11637,
"app_id": "a3osyvuayt",
"format": "txt",
"sign": "<hmac_sha256_sign>"
}
{
"code": "<EMVCo SGQR payload or base64 PNG>",
"format": "txt",
"supported_apms": ["favepay", "paynow", "alipay", "wcp"]
}
| Field | Required | Notes |
|---|---|---|
outlet_id | Required | Fave outlet ID. Outlet must belong to your app_id. |
app_id | Required | Your integration App ID from Fave. |
format | Optional | Encoding only. txt (default) = plaintext QR payload string; base64 = PNG image. Not used to select payment methods. |
sign | Required | HMAC-SHA256 signature. Send in the request body (POST). Do not put sign on the query string. |
Which APMs are included is decided by Fave from the outlet’s enabled payment methods. You do not pass APM filters in v1.
Response fields
| Field | Notes |
|---|---|
code | One static QR (plaintext or Base64 PNG per request format). Multiple APM payloads may be embedded in the EMVCo string. |
format | Echo of encoding: txt or base64. |
supported_apms | APM keys payable via this QR, using the same keys as dynamic QR format values where applicable: favepay, paynow, alipay, wcp. Use for on-screen logos. Only schemes enabled for the outlet and embedded in code are listed. |
format vs APMs: On this endpoint, format means encoding only (txt / base64). APM membership is always supported_apms. If a future revision lets partners request a subset of APMs, that will be a new request key — not format.
WeChat Pay (wcp) uses the Fave merchant-account slot in the combined SGQR when both are enabled.
After payment, handle the webhook and call Acknowledge. If the webhook is missed, list unacknowledged payments with List transactions (ack=false), then acknowledge.
See Payment flows → Static QR.
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. The callback body uses the same fields as Get a transaction, plus sign for verification.
Accepting webhook callback
The webhook callback notifies you of Fave payment status. Two statuses are sent via webhook:
| Status | Definition |
|---|---|
successful | Payment has been completed and accepted. |
rejected | Payment has been completed but not successful. |
Return any HTTP 2XX status from your webhook URL to accept the notification. Accept the callback quickly, then process the payload in the background (for example verify sign, update your order system).
Provide your webhook URL to Fave during the technical kick-off meeting. Alternatively, pass it per payment as callback_url in the Request a QR code API (or on merchant scan when supported).
If your endpoint does not return HTTP 2XX, Fave retries delivery at increasing intervals. Keep your handler idempotent — the same payment may be posted more than once.
Callback body
{
"id": "FP_792619",
"receipt_id": "0118-7347",
"outlet_name": "TTDI",
"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": "apicbpartner2",
"promo_code_cashback_value": "2.00",
"promo_code_cashback_type": "absolute_cashback",
"promo_code_cashback_issued_cents": "200",
"promo_code_cashback_funded_by": "merchant",
"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",
"sign": "56f6054ed61a3a3eb3dff2c06c320339044a4562f63f7d53cee0ff010df561a8"
}
| Field | Description |
|---|---|
id | Transaction ID (max 35 characters). Prefix indicates payment type — see Transaction IDs. |
receipt_id | Fave receipt ID (max 35 characters). |
outlet_name | Outlet display name. |
total_amount_cents | Order total in cents (string). |
payment_type | Payment method category (for example Credit Card, Debit Card, DuitNow). Present when payment-method reporting is enabled. |
payment_type_variant | Payment method variant (for example Visa, MasterCard, Touch N’Go eWallet). |
currency | ISO currency code (for example MYR). |
outlet_id | Fave outlet ID. |
mid | Merchant ID. |
omni_reference | Your payment reference from the original request. |
status | Webhook status: successful or rejected. See Accepting webhook callback. |
status_code | Numeric status code. See Transaction IDs & statuses. |
created_at | Payment timestamp (ISO 8601). |
campaign_credit_amount_cents | Campaign credit redeemed amount. |
campaign_funded_by | Who funded the campaign credit. |
aabig_points_used_amount_cents | AABig points redeemed amount. |
merchant_cashback_amount_cents | Merchant cashback redeemed amount. |
fave_credits_amount_cents | Fave credit redeemed amount. |
promo_code_value_cents | Promo code discount amount. |
e_card_credits_used_cents | User’s eCard membership credits amount. |
charged_amount_cents | Credit card or e-wallet charged amount. |
cashback_rate | Cashback rate offered by the merchant. |
merchant_cashback_issued_cents | Cashback amount the user earned. |
promo_code | Promo code used by the user. |
promo_code_cashback_value | Promo cashback value. |
promo_code_cashback_type | Promo cashback type (for example absolute_cashback). |
promo_code_cashback_issued_cents | Promo cashback amount the user earned. |
promo_code_cashback_funded_by | Who funded the promo cashback (for example merchant). |
fave_fees_percentage | Fave fee percentage. |
fave_fees_cents | Total fee charged by Fave. |
sst_on_total_fees_cents | Government tax on fees (mandated by country). |
merchant_takeback_cents | Total amount the merchant receives. |
user_id | Fave unique user ID. |
fpl_transaction | true if this is a FavePay Later transaction. |
fpl_fees_percentage | FavePay Later fee percentage (when applicable). |
fpl_fees_cents | FavePay Later fee amount (when applicable). |
fpl_merchant_cashback_reduction_percentage | FPL merchant cashback reduction percentage (when applicable). |
sign | HMAC-SHA256 signature over the callback fields. See Payload verification. |
N/A when not applicable.
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.
On a successful delivery response (HTTP 2XX), Fave may auto-acknowledge the payment for dynamic QR flows, 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.
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"
}
| Field | Required | Notes |
|---|---|---|
app_id | Required | Your integration App ID from Fave. |
omni_reference | Optional | At least one of omni_reference or receipt_id is required. |
receipt_id | Optional | At least one of omni_reference or receipt_id is required. Use the same app_id that created the payment. |
outlet_id | Optional | When provided, must match the payment’s outlet. |
sign | Required | HMAC-SHA256 signature. |
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"
}
| Field | Required | Notes |
|---|---|---|
id (path) | Required | Transaction ID with prefix: FP_, RPP_, GRAB_, PAYNOW_, or EXT_. Excluded from signature. |
omni_reference | Required | Your payment reference. |
app_id | Required | Your integration App ID from Fave. |
outlet_id | Required | Fave outlet ID for this payment. |
sign | Required | HMAC-SHA256 signature. Path id is excluded from signature. |
Cancel a QR payment
Invalidates an unpaid QR so the customer can no longer pay with it. Use when an order is abandoned or the amount changes before payment completes. This applies to QR flows only; use Refund after a successful payment.
{
"omni_reference": "PL-0001",
"app_id": "a3osyvuayt",
"outlet_id": 11637,
"sign": "<hmac_sha256_sign>"
}
{
"cancellation_status": "accepted"
}
{
"cancellation_status": "rejected",
"error": "QR_PAID",
"error_description": "Payment has been received. QR cannot be cancelled. Query the transaction with omni_reference.",
"omni_reference": "PL-0001"
}
| Field | Required | Notes |
|---|---|---|
omni_reference | Required | The reference used when creating the QR. |
app_id | Required | Your integration App ID from Fave. |
outlet_id | Optional | When provided, must match the outlet for this payment. |
sign | Required | HMAC-SHA256 signature. |
200): the QR is cancelled. Pending or processing payments are marked rejected.
If no payment record exists for omni_reference, the API still returns accepted (safe to retry).
Rejected (
409): cancellation is not allowed. Response includes cancellation_status: "rejected", an error code, error_description, and omni_reference.
error | Meaning |
|---|---|
QR_PAID | Payment received (including acknowledged). Query the transaction instead. |
QR_CANCELLED | QR was already cancelled. |
QR_REFUNDED | Payment was refunded. QR cannot be cancelled. |
Wrong
app_id for the payment → 404 resource_not_found.
A cancelled QR may trigger a rejected webhook if one was configured at QR creation.
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"
}
]
| Field | Required | Notes |
|---|---|---|
outlet_id (path) | Required | Fave outlet ID. Excluded from signature. |
app_id | Required | Your integration App ID from Fave. |
limit | Optional | Max results to return (max 1000). See defaults below. |
timestamp | Optional | Unix timestamp. Must not be earlier than ~31 days. |
ack | Optional | Default true (all payments). Set false for unacknowledged only. |
sign | Required | HMAC-SHA256 signature. Path outlet_id is excluded from signature. |
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"
}
| Field | Required | Notes |
|---|---|---|
omni_reference | Required | Payment reference to refund. |
app_id | Required | Your integration App ID from Fave. |
status | Required | Must be refunded. |
partial_refund_cents | Optional | Partial refund amount in cents. Cannot exceed charged_amount_cents. Not supported for DuitNow or PayNow. |
sign | Required | HMAC-SHA256 signature. |
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
Payload verification
The value of sign in webhook callbacks is computed using the HMAC-SHA256 algorithm. To verify payload integrity, generate an HMAC-SHA256 hash using your private API key (secret key) from Fave together with the URL-safe encoded content body of the payload on your side, then compare it with the sign value in the callback payload.
Build the signing string from all payload fields in the exact order received, excluding sign. Use URL encoding for values, and replace spaces with + rather than %20.
sign from all fields in the actual payload you receive.d25f1p1zf6ww8eja
{
"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",
"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": "apicbpartner2",
"promo_code_cashback_value": "2.00",
"promo_code_cashback_type": "absolute_cashback",
"promo_code_cashback_issued_cents": "200",
"promo_code_cashback_funded_by": "merchant",
"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",
"sign": "fc12259055f146e8bf0b587cbd8dbb9283e4db6a525243bc608e5762e43c6b29"
}
id=FP_792619&receipt_id=0118-7347&outlet_name=TTDI+Shop&total_amount_cents=2100¤cy=MYR&outlet_id=1593&mid=1166&omni_reference=PL-0001&status=successful&status_code=2&created_at=2021-03-26T18%3A14%3A59.698%2B08%3A00&campaign_credit_amount_cents=0&campaign_funded_by=N%2FA&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=apicbpartner2&promo_code_cashback_value=2.00&promo_code_cashback_type=absolute_cashback&promo_code_cashback_issued_cents=200&promo_code_cashback_funded_by=merchant&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%2FA&fpl_fees_cents=N%2FA&fpl_merchant_cashback_reduction_percentage=N%2FA
Generate an HMAC-SHA256 hash using the private API key as the secret and the signing string above as the message. For this example, the generated sign is fc12259055f146e8bf0b587cbd8dbb9283e4db6a525243bc608e5762e43c6b29.
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. |
409 | QR_PAID / QR_CANCELLED / QR_REFUNDED | Cancel rejected — payment already received, cancelled, or refunded. Body includes cancellation_status: "rejected". |
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.