Payments

Payment Orders

Check payment-order status and trigger the built-in payment webhook simulation.

Flow Summary

The current payment-order flow is:

Step 1. Read a payment order
GET /api/v1/payment-orders/{merchant_order_sn}

Step 2. Simulate a paid webhook
POST /api/v1/payment-orders/paid

Both routes should be treated as protected and require Authorization.

Important Scope Note

  • these routes are mounted after the global AuthMiddleware()
  • the current repository lookup uses only merchant_order_sn
  • there is no extra ownership check in the handler or repository

That means any authenticated token that knows a valid merchant_order_sn can currently query that order.

1. Get Payment Order By Merchant Order SN

GET /api/v1/payment-orders/{merchant_order_sn}

Returns the payment-order snapshot for one merchant order serial number.

Path parameter

  • merchant_order_sn is required

Response shape

The response includes fields such as:

  • id
  • order_sn
  • merchant_order_sn
  • amount_usdt
  • actual_usdt
  • merchant_usdt
  • chain_type
  • payment_address
  • payment_url
  • status
  • transaction_hash
  • confirmations
  • webhook_url
  • callback_url
  • qr_code
  • event_type
  • exchange_rate
  • net_amount
  • expires_at
  • expires_in
  • card_type

Important backend behavior

  • if expires_at has already passed, the service updates the order status to EXPIRED before returning it
  • when that happens, the repository also updates the related card request payment_status to EXPIRED
  • expires_in is calculated at response time in seconds; once expired, it becomes 0
  • exchange_rate is truncated through utils.TruncateExchangeRate(...) before response
  • card_type is populated by a left join from card_requests using the same merchant_order_sn

Error behavior

  • missing path values return a logical 400
  • unknown order numbers return a logical 404

2. Simulate Paid Webhook

POST /api/v1/payment-orders/paid

Triggers the built-in test webhook flow for a payment order.

Request body

{
  "merchant_order_sn": "PO-20260228-0001"
}

Success response shape

The response returns a generated payload with fields such as:

  • order_sn
  • merchant_order_sn
  • amount_usdt
  • actual_usdt
  • merchant_usdt
  • status
  • chain_type
  • transaction_hash
  • paid_at
  • timestamp
  • payment_address

Important backend behavior

  • this is a simulation/testing endpoint, not a real payment callback receiver
  • the service loads the order, builds a synthetic PAID payload, and sends it asynchronously in a goroutine
  • the webhook target URL is currently hardcoded to https://htp-webhook.vai247.pro/api/v1/webhooks/tokenb/payment
  • the webhook secret is also hardcoded in the service
  • merchant_usdt is currently simulated as amount_usdt * 0.9
  • the handler returns success immediately; it does not wait for the external webhook request to finish
  • a successful API response here does not guarantee that the remote webhook accepted the request

Current implementation caveat

  • the response payload adds payment_address before returning to the client
  • but the external webhook body is marshaled before that field is added
  • in the current code, the client sees payment_address, while the remote webhook body does not include it

Encryption note

  • this route passes through DecryptField()
  • you can send plain JSON or the encrypted wrapper format described in the overview

Integration Notes

  1. Use GET /api/v1/payment-orders/{merchant_order_sn} for status polling after card-request payment creation.
  2. Do not treat POST /api/v1/payment-orders/paid as a production settlement API; it is a simulation helper.
  3. Because lookup is not user-scoped yet, avoid exposing arbitrary order lookup in an untrusted client UI without extra server-side controls.
Copyright © 2026