Skip to content

QR Scan-to-Pay

Tip

Scan-to-Pay lets users scan an offline recipient QR code in the merchant app. The merchant server parses the QR code and submits a payout using the returned resolveToken, providing an in-app QR payment experience similar to a wallet app.

The product flow is:

Scan -> Parse -> Confirm -> Pay

  1. Scan: The user scans the recipient's QR code in the merchant app.
  2. Parse: The merchant server calls the QR Code Parsing API to retrieve the recipient details and a resolveToken.
  3. Confirm: The app displays the recipient name, amount, and other payment details for the user to confirm.
  4. Pay: The merchant server calls the QR Payout API with the resolveToken to submit the payout.

1. QR Code Parsing

Call this API to parse the QR code before submitting a Scan-to-Pay transaction.

Request URL
  • /v1/vexora/qr/parse
Request Method
  • Method: POST
  • Content-Type: application/json
Header Required Type Description
merchantNo Yes String Merchant number
Request Body Parameters
Parameter Name Required Type Description
sign Yes String Sort all body fields except sign alphabetically by field name and concatenate them as key1=value1key2=value2. Use the app secret as the salt for MD5 hashing. The resulting signature must be lowercase.
timestamp Yes String Request timestamp in milliseconds, for example 1715941383720.
qrType Yes String QR code type. Use KHQR.
qrContent Yes String QR code content.
scanSource No String Scan source: CAMERA or ALBUM. This field is for source identification only.
Request Parameter Example
{
  "sign": "363b4674a1c1772e8a50295dc19d6727",
  "timestamp": "1732988442913",
  "qrType": "KHQR",
  "qrContent": "000201010212...",
  "scanSource": "CAMERA"
}
Response Result
Parameter Name Required Type Description
code Yes String Response code. 0000 indicates that the QR code parsing request succeeded.
msg Yes String Request result message.
success Yes Boolean Indicates whether the API request was processed successfully.
timestamp Yes Number Response timestamp in milliseconds.
data Yes Object Parsed QR code details.
data.payable Yes Boolean Indicates whether the QR code can be paid.
data.qrType Yes String QR code type, for example KHQR.
data.qrMode Yes String QR code mode: STATIC or DYNAMIC.
data.merchantType Yes String Recipient type: INDIVIDUAL for a personal QR code or MERCHANT for a merchant QR code.
data.resolveToken Yes String Identifier for the parsed QR payment details. Use this token when submitting the QR Payout request.
data.accountName Yes String Recipient name displayed to the user.
data.accountBank Yes String Recipient bank or payment provider name.
data.amount Conditional String Returned when the QR code contains an amount. If present, the payout amount must match it.
data.currency Yes String Account currency.
data.qrExpireAt Yes String QR code expiration time, in Cambodia local time or the format returned by the API.
data.expireAt Yes String Expiration time of the resolveToken, in Cambodia local time.
data.message Yes String Result message or explanation.

Note

For QR code security, every resolveToken expires after 600 seconds. Parsing the same QR code again generates a new token; an earlier token remains usable until it expires, unless it has already been used to create a payout order. If the QR code expires sooner than 600 seconds, the token expires with the QR code.

Successful Response Example: Dynamic QR
{
  "code": "0000",
  "data": {
    "accountBank": "LantonPay",
    "accountName": "LAN TON",
    "amount": "0.1",
    "currency": "USD",
    "expireAt": "2026-11-03 12:31:14",
    "merchantType": "INDIVIDUAL",
    "message": "Success.",
    "payable": true,
    "qrExpireAt": "1793680274716",
    "qrMode": "DYNAMIC",
    "qrType": "KHQR",
    "resolveToken": "SCAN5EDBA8542BB34A538E8D8C4D6FAE5B7B"
  },
  "msg": "success",
  "success": true,
  "timestamp": 1793679696292
}
Successful Response Example: Static QR
{
  "code": "0000",
  "data": {
    "sign": "363b4674a1c1772e8a50295dc19d6727",
    "timestamp": "1732988442913",
    "resolveToken": "SCAN202609250001",
    "accountBank": "Lanton Pay",
    "qrType": "KHQR",
    "qrMode": "STATIC",
    "merchantType": "MERCHANT",
    "accountName": "ABC Merchant",
    "currency": "KHR",
    "payable": true,
    "expireAt": "2026-09-26 20:10:00",
    "qrExpireAt": "2026-09-26 20:10:00"
  },
  "msg": "success",
  "success": true,
  "timestamp": 1793679696292
}
Failed Response Example
{
  "msg": "success",
  "code": "0000",
  "timestamp": 1793680461168,
  "success": true,
  "data": {
    "payable": false,
    "qrType": "KHQR",
    "resolveToken": "SCAN55D6D7B3CD92434A8211D968EC230984",
    "expireAt": "2026-11-03 12:44:19",
    "message": "QR code is not valid"
  }
}

In this example, the parsing request succeeded, but the QR code is not payable. Check data.payable before allowing the user to continue.

2. QR Payout

After retrieving and confirming the QR code details, submit a payout using the resolveToken returned by the QR Code Parsing API.

Note

A resolveToken can be used to successfully create only one payout order.

Request URL
  • /v1/vexora/qr/payOut
Request Method
  • Method: POST
  • Content-Type: application/json
Header
Header Required Type Description
merchantNo Yes String Merchant number
Request Body Parameters
Parameter Name Required Type Description
sign Yes String Sort all body fields except sign alphabetically by field name and concatenate them as key1=value1key2=value2. Use the app secret as the salt for MD5 hashing. The resulting signature must be lowercase.
timestamp Yes String Request timestamp in milliseconds, for example 1715941383720.
tradeNo Yes String Unique merchant order identifier. A date/time string followed by random digits is recommended.
userId Yes String Unique identifier for the user in the merchant system.
resolveToken Yes String Token returned by the QR Code Parsing API. It links the payout to the parsed QR payment details.
amount Yes Float Payout amount. Required for STATIC QR codes. For DYNAMIC QR codes with a parsed amount, this value must match the parsed amount.
notifyUrl Yes String Asynchronous notification URL.
remark Yes String Remark returned unchanged in the response.
Request Parameter Example
{
  "sign": "363b4674a1c1772e8a50295dc19d6727",
  "timestamp": "1732988442913",
  "resolveToken": "SCAN202609250001",
  "tradeNo": "39fa354542824ee789faf526f27c024f",
  "userId": "6bdsjk435n67iuw7633",
  "amount": 500,
  "notifyUrl": "https://merchant.example/callback",
  "remark": "test"
}
Response Result
Parameter Name Required Type Description
msg Yes String Request result. A value of success only indicates that the API request succeeded; it is not the final payout result.
code Yes String API response code. 0000 only indicates that the request succeeded. Refer to the business error code documentation for other codes.
timestamp Yes Number Response timestamp in milliseconds.
success Yes Boolean Indicates whether the API request was processed successfully.
data Yes Object Payout response object.
data.tradeNo Yes String Merchant order identifier.
data.platFormTradeNo Yes String Unique platform transaction number.
data.status Yes String Payout status: 0000 successful, 0015 processing, or 00029 failed.
data.message Yes String Status description or error message.
data.remark Yes String Remark returned from the request.
Successful Response Example
{
  "msg": "success",
  "code": "0000",
  "timestamp": 1728611805322,
  "success": true,
  "data": {
    "tradeNo": "39fa354542824ee789faf526f27c024f",
    "platFormTradeNo": "2tjba3vz6wm6hx3l",
    "status": "0015",
    "message": "Paying",
    "remark": "test"
  }
}