api.md 9.4 KB

API Contract: 创建 OMG AIO 支付订单

POST /pay/omg/create

Authentication

  • Header: token: <user-login-token>
  • Spring annotations: @Anonymous + project @Auth
  • Controller method must declare @RequestHeader String token.

Request

POST /pay/omg/create
Content-Type: application/json
token: <login-token>

{
  "orderId": "991786433092835",
  "paymentMethod": "CREDIT"
}

Rules:

  • DTO has exactly two fields: orderId and paymentMethod.
  • Controller uses explicit @RequestBody(required = false).
  • orderId is trimmed, non-empty and at most 64 characters.
  • paymentMethod must be exactly CREDIT or APPLE_PAY.
  • Client cannot provide amount, MerchantID, gateway URL, ReturnURL, text, raw OMG payment parameters or signature fields.

Success

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.

Business failure

{
  "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.

POST /pay/omg/notify

Request

POST /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:

  • No login token is required.
  • Controller receives one explicit OmgNotifyRequest DTO; it does not accept Map or HttpServletRequest.
  • The request boundary preserves the exact raw body plus every decoded parameter, including unknown fields and empty values.
  • Duplicate parameter names, malformed percent encoding, unsupported content type or an oversized body are invalid.
  • Signature input contains every actual parameter except CheckMacValue; no field whitelist is used.
  • Credential lookup begins from the locked MerchantTradeNo attempt and uses its credential snapshot.
  • MerchantID and TradeAmt must equal the creation snapshot.

Acknowledged

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.

Retryable rejection

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.

IPN logging

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.

POST /pay/omg/query

Header 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.

  • POST MerchantID, MerchantTradeNo, current Unix TimeStamp and CheckMacValue to the OMG stage QueryTradeInfo/V5 endpoint.
  • Use the attempt credential snapshot, parse and sign every actual response field (including additional and empty fields), excluding only CheckMacValue.
  • Verify 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.
  • User-triggered query is not an incoming IPN and is not inserted into 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.

POST /pay/omg/retry

Header 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.

POST /pay/omg/refund

Current 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.