FavePay (FavePay Omni) Payment Gateway
Use this skill for FavePay Omni (FPO) integration work.
Source
Read references/favepay-api.md before giving factual API guidance or writing integration code. It holds environments, the signing algorithm with a verified worked example, the endpoint catalogue, full field tables for create, merchant scan, static QR, webhook, lookup, acknowledge, cancel, list and refund, statuses, errors and plugin behaviour.
Read references/account-setup.md when the user asks how to register, which credentials Fave issues, sandbox UAT and sign-off, env var names, payment methods, plugins, pricing or settlement.
Read references/source-map.md when choosing which official page or plugin file supports a fact.
Core Workflow
- Identify environment: Sandbox (
https://omni.dino.fave.ninja) or Production (https://omni.myfave.com). Credentials are separate per environment. - Identify country path segment:
my(MYR) orsg(SGD). Currency follows the path; there is no currency field. - Identify flow: online checkout (
format=web_url), in-store dynamic QR (txt,base64,favepay), wallet QR (grab,alipay,wcp,shopeepay,atome,paynow), merchant scan, or static QR (planned). - Create a local pending payment with a unique
omni_reference(<Fave prefix>-<id>, max 28 chars) before calling Fave. POST /api/fpo/v1/<cc>/qr_codesserver-side withtotal_amount_cents,app_id,outlet_id,format,redirect_url,callback_url, andsignlast.- Redirect the customer to
code(forweb_url), or render the QR payload or image. - Treat the redirect back to
redirect_urlas browser UX only. - On the webhook, verify
signover the received fields in received order, then return HTTP 2XX quickly and process idempotently. - Confirm with
GET /api/fpo/v1/<cc>/transactions(byomni_referenceorreceipt_id) when the webhook is late or missing. - Cancel unpaid QRs with
POST .../transactions/cancel; refund paid ones withPOST .../transactionsandstatus=refunded(same calendar day by default).
Core Facts
- Production base:
https://omni.myfave.com. Sandbox base:https://omni.dino.fave.ninja. All paths start/api/fpo/v1/<country_code>/. - Auth is a
signfield: lowercase hex HMAC-SHA256 of the URL-encoded fields (form encoding, space as+) in the order sent, keyed with the Fave secret key. - Always exclude
signandcountry_codefrom the signing string; also exclude pathoutlet_id(list), pathid(acknowledge) anddetails(merchant scan). - Do not sort keys. Nested objects flatten to bracket form, for example
shopper_details[email]=.... - Amounts are integer cents (
total_amount_cents). Most response amounts are strings. - Create response:
code,format,expires_in(seconds). Defaultformatisbase64. - Webhook statuses:
successfulandrejected. Expired or cancelled unpaid QRs can sendrejected. status_code: 0pending_payment, 1payment_processing, 2successful, 3disputed, 4rejected, 5refunded.- Transaction
idprefixes:FP_FavePay,RPP_DuitNow,GRAB_,PAYNOW_,EXT_other wallets. - Fave retries non-2XX webhooks at increasing intervals; the same payment can arrive more than once.
test=trueon create (sandbox only) returns a test page URL incodeto simulate success or failure. Never send it to production.- Errors use
erroranderror_description. Bad sign is 401; unknownapp_idis 404resource_not_found; duplicateomni_referenceis 400validation. - Refund window: same calendar day by default (
stale_transactionafter). Partial refunds not supported for DuitNow or PayNow. - Rate limits and webhook source IPs: not documented in official docs (checked 2026-10-21).
Critical Rules
- Keep the secret key and
app_idserver-side; the key signs requests, verifies webhooks and decryptsomni_ref. - Build the signing string from the exact fields you send, in the exact order you serialise them; put
signlast. - Encode like PHP
urlencode/http_build_query: space to+, everything exceptA-Z a-z 0-9 - _ .percent-encoded (uppercase hex). - Verify webhooks over the received JSON keys in received order (use an order-preserving parser), and compare with a constant-time check.
- Do not mark paid from the redirect alone. Mark paid only after a verified webhook or a signed
GET /transactionsreturnssuccessful. - Check
omni_reference,total_amount_cents,currency,outlet_idandstatusagainst the local record before settling. - Settle idempotently keyed on
receipt_idorid; duplicate webhooks are expected. - Generate a new
omni_referencefor every new QR on the same order; references must be unique. - Use sandbox keys only with the sandbox base URL; Fave issues new production keys after UAT sign-off.
- Confirm static QR availability with Fave before use; the docs mark it as planned.
Completion Criteria
A FavePay integration is done only when:
- QR or
web_urlcreation runs server-side with a correctsign, and returnscode. - The redirect page shows status only, or re-queries the transaction server-side before showing success.
- The webhook URL is registered with Fave (kick-off meeting) or passed as
callback_url, per environment. - The webhook handler verifies
sign, returns 2XX fast, and settles once per payment. - Settlement checks reference, amount, currency, outlet and
successfulstatus. - A scheduled requery handles missed webhooks; static QR flows call Acknowledge.
- Tests cover bad sign, duplicate webhook, wrong amount,
rejected, expired QR, cancel, full refund, andstale_transaction. - The UAT checklist (A1 to A4, B1 to B2, C1, D1 to D3) is passed in sandbox before asking for production keys.
Loophole Review Loop
When asked whether a FavePay strategy is "100% confident", run this loop until no factual gaps remain:
- List trust boundaries: browser redirect,
omni_refdecryption, backend QR creator, Fave Omni API, webhook receiver, JSON parser key order, database, fulfilment job, refund job, POS display, and FaveBiz dashboard. - Check bypasses: forged redirect, forged webhook, sign computed over sorted or re-serialised fields, wrong encoding (
%20vs+,~,*), null or boolean values, sandbox key in production,test=truein production, reusedomni_reference, wrong outlet, amount mismatch, duplicate webhook, late success after cancel, refund after the same-day window. - Add fixes: server-side create, local pending record first, order-preserving verification, constant-time compare, strict amount and reference checks, idempotent settlement, requery fallback, per-environment config, cancel before reissuing a QR.
- Re-read
references/favepay-api.mdsections relevant to any gap. - State remaining uncertainty where docs are silent:
omni_refIV and padding, boolean and null encoding insign, rate limits, webhook IPs, retry schedule, settlement timing, pricing.


