/pay/omg/createtoken: <user-login-token>@Anonymous + project @Auth@RequestHeader String token.POST /pay/omg/create
Content-Type: application/json
token: <login-token>
{
"orderId": "991786433092835",
"paymentMethod": "CREDIT"
}
Rules:
orderId and paymentMethod.@RequestBody(required = false).orderId is trimmed, non-empty and at most 64 characters.paymentMethod must be exactly CREDIT or APPLE_PAY.Outer response keeps the project AjaxResult format. data is:
{
"status": "CREATED",
"gatewayUrl": "https://payment-stage.funpoint.com.tw/Cashier/AioCheckOut/V5",
"formFields": {
"MerchantID": "1000031",
"MerchantTradeNo": "OMGR8K3P7W2M9C4X6A1B",
"MerchantTradeDate": "2026/08/13 15:30:23",
"PaymentType": "aio",
"TotalAmount": "100",
"TradeDesc": "Foodie order 991786433092835",
"ItemName": "Order 991786433092835",
"ReturnURL": "https://foodieapi.waimai-paotui.com/pay/omg/notify",
"OrderResultURL": "https://foodieapi.waimai-paotui.com/pay/omg/result",
"ChoosePayment": "Credit",
"UnionPay": "2",
"EncryptType": "1",
"InvoiceMark": "N",
"NeedExtraPaidInfo": "Y",
"CheckMacValue": "<64 uppercase hexadecimal characters>"
}
}
The example above is the CREDIT variant. For APPLE_PAY, ChoosePayment is ApplePay, and UnionPay and IgnorePayment are absent. ChoosePayment=ALL is never generated.
The client must submit every formFields entry in the current page as an application/x-www-form-urlencoded POST to gatewayUrl. It must not use iframe, a new window or a GET link.
{
"code": 500,
"msg": "<localized message>",
"data": {
"status": "PAYMENT_ATTEMPT_EXISTS"
}
}
Stable statuses:
| Status | Meaning |
|---|---|
AUTH_REQUIRED |
No usable user identity reached the new Controller |
ORDER_REQUIRED |
Request or orderId is missing/invalid |
ORDER_NOT_AVAILABLE |
Order does not exist or is not owned by this user |
MULTI_STORE_ORDER_NOT_SUPPORTED |
Matched row is a multi-store child order |
ORDER_STATE_NOT_PAYABLE |
State is not 0, 1 or 2 |
ORDER_ALREADY_PAID |
pay_status is not 0 |
ORDER_AMOUNT_INVALID |
Amount is null or not positive |
PAYMENT_TYPE_INVALID |
pay_type is not "2" |
PAYMENT_METHOD_INVALID |
paymentMethod is missing or is not CREDIT/APPLE_PAY |
STORE_CREDENTIAL_UNAVAILABLE |
No enabled credential exists for the order store |
PAYMENT_ATTEMPT_EXISTS |
The order already has an active CREATED attempt |
PAYMENT_CONFIGURATION_INVALID |
Stage/ReturnURL safety validation failed |
PAYMENT_CREATION_FAILED |
Controlled generation/insert attempts could not complete |
No error response includes token, HashKey, HashIV, CheckMacValue, form fields, SQL or stack trace.
/pay/omg/notifyPOST /pay/omg/notify
Content-Type: application/x-www-form-urlencoded
MerchantID=1000031&MerchantTradeNo=OMGR8K3P7W2M9C4X6A1B&RtnCode=1&RtnMsg=Succeeded&TradeNo=26081300000000000001&TradeAmt=100&PaymentDate=2026%2F08%2F13+16%3A20%3A00&PaymentType=Credit_CreditCard&PaymentTypeChargeFee=3&TradeDate=2026%2F08%2F13+16%3A18%3A00&SimulatePaid=1&CustomField1=&CustomField2=&CustomField3=&CustomField4=&card4no=4242&CheckMacValue=<CHECK_MAC_VALUE>
Rules:
OmgNotifyRequest DTO; it does not accept Map or HttpServletRequest.CheckMacValue; no field whitelist is used.MerchantTradeNo attempt and uses its credential snapshot.MerchantID and TradeAmt must equal the creation snapshot.HTTP/1.1 200 OK
Content-Type: text/plain;charset=UTF-8
1|OK
This response is used for a verified success, verified final failure, or an idempotently repeated fact. RtnCode=1 marks the attempt and order paid even when SimulatePaid=1. RtnCode!=1 marks a non-paid attempt failed and leaves the order unpaid.
HTTP/1.1 200 OK
Content-Type: text/plain;charset=UTF-8
0|ERROR
This response is used when the request cannot be trusted or atomically processed: unknown trade number, invalid/missing fields, duplicate fields, credential snapshot unavailable, signature mismatch, MerchantID/amount mismatch, conflicting gateway TradeNo, or database transaction failure. No order/payment state is changed.
Every HTTP request is inserted before business handling into existing ipn_log with type=omg, request IP, receive time and the full raw form body. This insert uses an independent transaction. Failure to write ipn_log is logged but does not block payment processing.
/pay/omg/queryHeader token is required. The JSON body contains only the business order ID:
{"orderId":"991786433092835"}
The server verifies ownership and chooses the only current payment attempt. An unpaid order queries its unique CREATED attempt; an already-paid order queries its first PAID attempt. The client cannot provide MerchantTradeNo, amount, credentials or gateway URL.
MerchantID, MerchantTradeNo, current Unix TimeStamp and CheckMacValue to the OMG stage QueryTradeInfo/V5 endpoint.CheckMacValue.MerchantID, MerchantTradeNo and TradeAmt against the attempt snapshot.TradeStatus=1 reuses the callback's locked idempotent settlement transaction and compensates a lost callback by marking the attempt/order paid.TradeStatus=0 changes no local state; 10200095 synchronizes an unpaid attempt to failed; paid facts are irreversible.ipn_log.The response whitelist includes normalized status (PAID, UNPAID, FAILED, UNKNOWN), raw tradeStatus, trade numbers, amount, gateway dates, payment type and fees. Invalid/tampered responses return PAYMENT_QUERY_FAILED; no eligible attempt returns PAYMENT_QUERY_NOT_AVAILABLE.
/pay/omg/retryHeader token is required. The explicit JSON DTO contains only:
{"orderId":"991786433092835","paymentMethod":"APPLE_PAY"}
The endpoint is used after the App has destroyed the original payment WebView and the user explicitly starts payment again. The server never returns or replays the old form. If no local attempt is currently queryable (a terminal FAILED/SUPERSEDED attempt has released its active pointer), skip the gateway query and create a fresh attempt directly; an already-paid order is still rejected by the create-side validation with ORDER_ALREADY_PAID. Otherwise it first executes the same authenticated, signed gateway query as /query:
PAID: return ORDER_ALREADY_PAID and do not create a payment.FAILED: create a fresh attempt and form.UNPAID with blank paymentType: lock the order, require the active attempt's MerchantTradeNo to equal the verified query result, atomically mark that exact attempt SUPERSEDED, and create a fresh attempt/form in the same transaction.UNPAID with a nonblank paymentType, UNKNOWN, query failure, or a concurrent active-attempt change: do not replace the attempt and return a stable error.At most one retry request can replace a given active attempt. A late trusted paid callback for a SUPERSEDED attempt remains eligible for the irreversible paid transition and closes any newer active attempt.
Success has the same response contract as /create: {status:"CREATED", gatewayUrl, formFields} with a new MerchantTradeNo and fields for the newly selected paymentMethod. Business errors include the existing query/create errors plus PAYMENT_RETRY_NOT_AVAILABLE; unexpected failures return PAYMENT_RETRY_FAILED. The client cannot send MerchantTradeNo, OMG paymentType, raw OMG payment parameters, amount, credentials, gateway URL, or old form fields.
/pay/omg/refundCurrent scope is a test-environment safety boundary. Header token is required and the JSON body contains only:
{"orderId":"991786433092835"}
The client cannot provide refund amount, MerchantTradeNo, TradeNo, Action, credentials or gateway URL. After authentication and basic order-ID validation, the endpoint always returns:
{
"code":500,
"msg":"OMG 测试环境不支持退款,请在正式环境启用后操作",
"data":{"status":"PAYMENT_REFUND_UNAVAILABLE_IN_TEST_ENVIRONMENT"}
}
The endpoint does not call the production-only OMG CreditDetail/DoAction API, does not read or update order/payment/refund state, does not insert ipn_log, and does not create a refund record. Formal refund orchestration remains out of scope until the production environment is explicitly enabled.