AinePay
EN中文
Webhooks

Payment Notification

When an order reaches its final status, AinePay pushes a notification of the order status change to the merchant's system so the merchant can process it in a timely manner.

Endpoint

  • Method: POST
  • Target URL: {notifyUrl}/ainepay/notify
  • Authentication: Signature verification with timestamp window check
  • Content-Type: application/x-www-form-urlencoded
  • Headers:
    • x-api-signature: lowercase hex HMAC-SHA256
    • x-api-timestamp: millisecond timestamp, included in the signature
    • x-api-recv-window: accept window in milliseconds, included in the signature

Delivery Behavior

  • A webhook is created when the order status changes in a way that needs to notify the merchant.
  • In the current implementation, AinePay sends notifications for PAID, EXPIRED, CANCEL, and REFUND.
  • Orders created through a payment link (PAYMENT_LINK) do not trigger callbacks.
  • AinePay treats any HTTP 2xx response as a successful acknowledgement (redirects are not followed).
  • If the endpoint returns a non-2xx status or the request fails, AinePay retries with exponential backoff: base delay 5s, the nth retry waits 5s × 2^(n-1), up to 5 attempts, after which it stops retrying.
  • When the merchant is rate-limited, the delivery is rescheduled with a 30s backoff.

Request Fields

fieldtypedescriptionrequiredexample
merchantIdstringMerchant ID.yes20001
orderIdstringMerchant order ID.yesORDER_10001
userIdstringMerchant-defined user ID.yesU_90001
coinstringToken symbol.yesUSDT
chainstringChain code.yesETH
qtystringOrder amount as a formatted string.yes88.00
statusstring(enum)Order status. In the current implementation, the webhook status is PAID, EXPIRED, CANCEL, or REFUND.yesPAID
expiredintegerOrder expiration timestamp in milliseconds.yes1760000600000
createdintegerOrder creation timestamp in milliseconds.yes1760000000000
updatedintegerOrder last update timestamp in milliseconds.yes1760000300000

Signature Verification

  • AinePay signs "the URL-encoded webhook body + &timestamp=<ts>&recvWindow=<rw>" with the merchant's webhook verification key.
  • The signature is sent in x-api-signature as lowercase hex; the timestamp and window are sent in x-api-timestamp and x-api-recv-window respectively, and both participate in the HMAC.
  • The body portion of the signing input is the canonical URL-encoded form string sorted by field name. The trailing timestamp/recvWindow values are appended as raw digits and are not URL-encoded.
  • Merchant verification steps:
    1. Read x-api-timestamp and x-api-recv-window; reject the request when |now - timestamp| > min(recvWindow, MAX_ACCEPTABLE_RECV_WINDOW) (a MAX_ACCEPTABLE_RECV_WINDOW of 300000 ms is recommended).
    2. Re-parse and re-encode the body in alphabetical order to obtain the canonical body.
    3. Append &timestamp=<ts>&recvWindow=<rw>, compute HMAC-SHA256, and compare against x-api-signature with constant-time comparison.
  • Because both timestamp and recvWindow are signed, an attacker cannot tamper with either header without invalidating the signature.
  • For Java / TypeScript webhook verification example code, see Authentication.

Example Request

POST /ainepay/notify HTTP/1.1
Host: merchant.example.com
Content-Type: application/x-www-form-urlencoded
x-api-signature: <hex(HMAC-SHA256(notifySecret, body + "&timestamp=" + ts + "&recvWindow=" + rw))>
x-api-timestamp: 1760000300000
x-api-recv-window: 5000

chain=ETH&coin=USDT&created=1760000000000&expired=1760000600000&merchantId=20001&orderId=ORDER_10001&qty=88.00&status=PAID&updated=1760000300000&userId=U_90001

Expected Response

fieldtypedescription
HTTP statusintegerReturn any 2xx status to acknowledge successful receipt.
HTTP/1.1 200 OK
Content-Type: text/plain

ok

Notes

  • Handle the webhook idempotently by orderId.
  • Treat the webhook as a status update signal, then use Query Orders to confirm the latest order status before executing your final business action.
  • If the signature is invalid, do not trust the payload.