Access Gateway Outbound Host API
QR Payments

QR Payments

QR payments use their own resource, /qr-payments. A QR-only host does not need to implement the card endpoints.

Two modes are supported:

  • MPQR (merchant-presented): your host returns a QR code, the device shows it, the customer scans it with a wallet.
  • CPQR (consumer-presented): the merchant scans the code in the customer's wallet, your host charges it.

Endpoints

DirectionEndpointPurposeRequired
MineSec → youPOST {baseUrl}/qr-paymentsCreate: return a QR (MPQR) or charge a code (CPQR)Yes
MineSec → youPOST {baseUrl}/qr-payments/inquiriesCurrent status of a payment or refundYes
MineSec → youPOST {baseUrl}/qr-payments/cancellationsClose an unpaid order or reverse a paid oneYes
MineSec → youPOST {baseUrl}/qr-payments/refundsFull or partial refundOnly if qrSupportsRefund is on
You → MineSecPOST {notifyUrl}Push the final resultOnly if notifyProfile and notifyKey are set

Refunds, notify and the optional uses of cancellation are switched on in the extraParams of your acceptance; see Configuration & Go-live. Cancellation itself is always required: MineSec cancels a QR payment whenever the device reverses it, whatever these settings are. See Cancellation.

Status

Only status decides the outcome.

StatusMeaning
PENDINGOrder created, waiting for the customer (MPQR only)
SUCCESSPaid. paymentMethod must be the concrete wallet
FAILEDNot paid. Include failureCode and respMessage
CLOSEDCancelled or expired before any money moved
REVERSEDCharged, then returned by a cancellation

respCode is kept for diagnostics and must be 00 when status is PENDING or SUCCESS.

MPQR: merchant-presented

  • Answer the create at once with status: PENDING and the qrCode to display.
  • qrCode.format tells the device how to render it: EMVCO_MPM, URL or RAW.
  • qrExpirySeconds in the request is the expiry MineSec asks for. Your expiresAt is shown on the device, but MineSec stops polling and closes the order at whichever comes first: your expiresAt or the requested expiry. Do not return an expiresAt later than the requested expiry.
  • A PENDING answer without qrCode is treated as a decline.
  • If the create times out or fails, MineSec declines the payment and does not cancel it. Let the order expire on your side.

CPQR: consumer-presented

  • Answer a final SUCCESS or FAILED within 20 seconds. Do not answer PENDING. If the create does not settle, MineSec sends /qr-payments/inquiries every 2 seconds inside the same 20-second window.
  • After a timeout cancellation, only CLOSED or REVERSED settles the payment. Any other answer leaves it in processing and flags it for reconciliation.
  • consumerCode.format is BARCODE_NUMERIC, QR_TEXT or EMV_CPM_BASE64.
  • MineSec may cancel a payment whose create never reached you. Answer CLOSED, without rrn.

Cancellation

MineSec sends a cancellation in these cases:

CasereasonSent
The device reverses the sale: the cashier closes the QR, or the device stops waitingUSER_CANCELLED (MPQR), TIMEOUT (CPQR)Always
CPQR without a final answer within 20 sTIMEOUTAlways
MPQR still unpaid at expiryTIMEOUTOnly if qrSupportsCancel is on
The merchant voids a paid paymentMERCHANT_VOIDOnly if qrSupportsCancel is on

Without qrSupportsCancel, an expired MPQR order is not closed by MineSec and can only be settled by a notify, and QR payments cannot be voided.

A cancellation for a device reversal must be answered CLOSED or REVERSED. Any other answer, or no answer, leaves the reversal pending: MineSec sends it again before the device's next card or CPQR payment, refund, void or settlement on your host, and those requests fail until it is cleared. If you never received the payment, answer CLOSED, without rrn.

Answer with what actually happened to the money:

AnswerWhen
CLOSEDNo money moved; the order is closed
REVERSEDThe customer was charged and the money was returned
FAILEDThe payment could not be cancelled

reason is USER_CANCELLED, TIMEOUT or MERCHANT_VOID. A void of a QR payment is a MERCHANT_VOID cancellation; CLOSED and REVERSED both count as voided.

MineSec may retry a cancellation. Each retry carries the same enbrActionId, so use it to recognise a repeated cancellation. A different cancellation of the same payment gets a new enbrActionId.

Refund

A refund is a transaction of its own, with its own enbrTranId. linkedEnbrTranId and orgRrn identify the original payment. Partial and multiple refunds are supported; MineSec rejects a refund above the remaining amount before it reaches you, and a payment without rrn cannot be refunded.

Answer a final SUCCESS or FAILED within 20 seconds. A refund without a final answer is not sent again and is not polled. It stays unresolved until a refund notify arrives or it is reconciled with you. Refunds do not change the status of the original payment.

Inquiry

MineSec polls /qr-payments/inquiries whether or not you send notifies:

PaymentPolling
MPQREvery 3 seconds, from 3 seconds after the create until expiry
CPQREvery 2 seconds, inside the 20-second window
RefundNot polled

inquiryType is PAYMENT or REFUND. Return the same result body as the create or refund.

Notify (optional)

A notify pushes the final result to MineSec without waiting for the next inquiry.

  • Send it only when the request carried notifyUrl. Call the URL unchanged; never build it yourself.
  • Send it once per final status, never for PENDING. The body is the result plus eventId, eventType and eventTime.
  • Sign it with the notify key MineSec issues at onboarding (separate from the request HMAC key). The path is not signed:
x-request-time: 2025-02-07T02:35:10Z
x-signature: HmacSHA256 hex( HMAC-SHA256( notifyKey, "POST.." + x-request-time + ".." + body ) )
  • amountValue and currency are the order amount of the request, unchanged.
  • If no 200 arrives within 10 seconds, retry, except after a 400 or 404. Retry 5 s, 10 s, 15 s, 1 min, 5 min, 30 min, 2 h and 6 h after the first attempt, then stop; MineSec's inquiries still settle the transaction. Keep the same eventId and body; use a new x-request-time and signature for each retry. A notify can be delivered more than once.
AnswerMeaning
200{"respCode":"00"}: received. Also the answer when the notify reports another amount or a final status other than the one MineSec holds (MineSec reconciles these, and sending them again changes nothing), and when the transaction is already final (nothing changes)
400The notify does not match the transaction: enbrTranId in the body differs from the URL, eventType does not match a payment or a refund, the body is not a notify, or the notify profile in the URL is not the one set up for your host. Nothing changed. Do not retry the same content
401Missing or wrong signature, or x-request-time more than 5 minutes off. Nothing changed
404Unknown transaction. Nothing changed. Do not retry

Unified QR

With paymentMethod: UNIFIED_QR, one code accepts several wallets and your host identifies the wallet.

  • On SUCCESS, return the concrete paymentMethod, never UNIFIED_QR.
  • CPQR: if the merchant does not accept the scanned wallet, answer FAILED with failureCode: UNSUPPORTED_WALLET.

Rules

  • Idempotency: enbrTranId is the key on every QR endpoint. A repeated create returns the first result and does not create a second order.
  • References: return your rrn from the create onward, in every response including PENDING, with the same value each time. A payment that succeeded without rrn cannot be traced or refunded. Put the wallet's own transaction ID, if different, in extra.walletTransactionId.
  • Amounts: echo amountValue and currency from the request. A SUCCESS with a different amount is not approved; it is flagged for reconciliation.
  • rrn length: at most 64 characters.
  • Failures: use a listed failureCode where one fits: CODE_EXPIRED, CODE_INVALID, INSUFFICIENT_FUNDS, USER_CANCELLED, RISK_REJECTED, LIMIT_EXCEEDED, UNSUPPORTED_WALLET, DUPLICATE, SYSTEM_ERROR. Other codes are accepted as a generic failure.
  • Settlement: QR is captured by your host. batchId and batchNo are for reference only.

Example: MPQR create

POST /api/v1/qr-payments
{
  "enbrTranId": "tran_01JQ8V2M5N3K7P9R1T3V5X7Z9B",
  "srsTranId": "M1866239185331064832",
  "trace": "000187",
  "pboId": "<your-pbo-id>",
  "profileId": "prof_01JDK7CS51N7DM47JK9M4R9XPP",
  "acptId": "acpt_01JDK7CS6QKRN5K9BP053H0WBP",
  "primaryMid": "111",
  "primaryTid": "222",
  "merchantDevice": "SOFTPOS",
  "qrMode": "MPQR",
  "tranType": "SALE",
  "amountValue": "25.00",
  "currency": "HKD",
  "paymentMethod": "UNIFIED_QR",
  "qrExpirySeconds": 120,
  "notifyUrl": "https://<minesec-notify-url-issued-per-transaction>",
  "merchantName": "ACME",
  "merchantAddress": "No. 7, Park Avenue, Sunset Beach",
  "merchantCity": "Hong Kong",
  "merchantCountry": "HK",
  "mcc": "5999",
  "timestamp": "2025-02-07T02:34:33.732Z"
}
Response (200)
{
  "respCode": "00",
  "status": "PENDING",
  "enbrTranId": "tran_01JQ8V2M5N3K7P9R1T3V5X7Z9B",
  "rrn": "QR20250207000187",
  "amountValue": "25.00",
  "currency": "HKD",
  "qrCode": { "content": "00020101021226...6304ABCD", "format": "EMVCO_MPM" },
  "expiresAt": "2025-02-07T02:36:33Z"
}

Example: MPQR inquiry

MineSec polls the MPQR order above. trace is the payment's own.

POST /api/v1/qr-payments/inquiries
{
  "enbrTranId": "tran_01JQ8V2M5N3K7P9R1T3V5X7Z9B",
  "srsTranId": "M1866239185331064832",
  "trace": "000187",
  "pboId": "<your-pbo-id>",
  "profileId": "prof_01JDK7CS51N7DM47JK9M4R9XPP",
  "acptId": "acpt_01JDK7CS6QKRN5K9BP053H0WBP",
  "primaryMid": "111",
  "primaryTid": "222",
  "inquiryType": "PAYMENT",
  "rrn": "QR20250207000187",
  "batchId": "batch_01JKF2QRZJFZ5BHF0SC0X9ZR5M",
  "batchNo": "029290",
  "timestamp": "2025-02-07T02:35:06.020Z"
}
Response (200): paid
{
  "respCode": "00",
  "status": "SUCCESS",
  "enbrTranId": "tran_01JQ8V2M5N3K7P9R1T3V5X7Z9B",
  "rrn": "QR20250207000187",
  "networkMerchantId": "2088621900012345",
  "primaryMid": "111",
  "primaryTid": "222",
  "paymentMethod": "ALIPAY",
  "amountValue": "25.00",
  "currency": "HKD",
  "paidAt": "2025-02-07T02:35:01Z",
  "payer": { "id": "2088102112345678", "fundingSource": "BALANCE" },
  "extra": { "walletTransactionId": "2025020722001412345678901234" }
}
Response (200): still waiting
{
  "respCode": "00",
  "respMessage": "Waiting for payer",
  "status": "PENDING",
  "enbrTranId": "tran_01JQ8V2M5N3K7P9R1T3V5X7Z9B",
  "rrn": "QR20250207000187",
  "paymentMethod": "UNIFIED_QR",
  "amountValue": "25.00",
  "currency": "HKD"
}

Example: CPQR create

POST /api/v1/qr-payments
{
  "enbrTranId": "tran_01JQ8V4C7E9G1J3L5N7Q9S1U3W",
  "srsTranId": "M1866239402170769408",
  "trace": "000188",
  "pboId": "<your-pbo-id>",
  "profileId": "prof_01JDK7CS51N7DM47JK9M4R9XPP",
  "acptId": "acpt_01JDK7CS6QKRN5K9BP053H0WBP",
  "primaryMid": "111",
  "primaryTid": "222",
  "merchantDevice": "SOFTPOS",
  "qrMode": "CPQR",
  "tranType": "SALE",
  "amountValue": "128.00",
  "currency": "HKD",
  "paymentMethod": "WECHAT",
  "consumerCode": { "value": "134567890123456789", "format": "BARCODE_NUMERIC", "scanMethod": "CAMERA" },
  "notifyUrl": "https://<minesec-notify-url-issued-per-transaction>",
  "merchantName": "ACME",
  "merchantAddress": "No. 7, Park Avenue, Sunset Beach",
  "merchantCity": "Hong Kong",
  "merchantCountry": "HK",
  "mcc": "5999",
  "batchId": "batch_01JKF2QRZJFZ5BHF0SC0X9ZR5M",
  "batchNo": "029290",
  "timestamp": "2025-02-07T02:40:38.104Z"
}
Response (200): paid
{
  "respCode": "00",
  "respMessage": "Approved",
  "status": "SUCCESS",
  "enbrTranId": "tran_01JQ8V4C7E9G1J3L5N7Q9S1U3W",
  "rrn": "QR20250207000188",
  "networkMerchantId": "1900012345",
  "paymentMethod": "WECHAT",
  "amountValue": "128.00",
  "currency": "HKD",
  "paidAt": "2025-02-07T02:40:40Z",
  "payer": { "id": "oUpF8uMuAJO_M2pxb1Q9zNjWeS6o", "fundingSource": "BALANCE" },
  "extra": { "walletTransactionId": "4200002891202502075566778899" }
}
Response (200): failed
{
  "respCode": "51",
  "respMessage": "Insufficient balance",
  "status": "FAILED",
  "failureCode": "INSUFFICIENT_FUNDS",
  "enbrTranId": "tran_01JQ8V4C7E9G1J3L5N7Q9S1U3W",
  "rrn": "QR20250207000188",
  "paymentMethod": "WECHAT",
  "amountValue": "128.00",
  "currency": "HKD"
}

Example: cancellation

The CPQR payment above had no final answer within 20 seconds.

POST /api/v1/qr-payments/cancellations
{
  "enbrTranId": "tran_01JQ8V4C7E9G1J3L5N7Q9S1U3W",
  "enbrActionId": "act_01JQ8V5D2F4H6K8M0P2R4T6V8X",
  "srsTranId": "M1866239402170769408",
  "trace": "000188",
  "pboId": "<your-pbo-id>",
  "profileId": "prof_01JDK7CS51N7DM47JK9M4R9XPP",
  "acptId": "acpt_01JDK7CS6QKRN5K9BP053H0WBP",
  "primaryMid": "111",
  "primaryTid": "222",
  "reason": "TIMEOUT",
  "rrn": "QR20250207000188",
  "batchId": "batch_01JKF2QRZJFZ5BHF0SC0X9ZR5M",
  "batchNo": "029290",
  "timestamp": "2025-02-07T02:40:58.512Z"
}
Response (200): closed, no money moved
{
  "respCode": "00",
  "respMessage": "Order closed",
  "status": "CLOSED",
  "enbrTranId": "tran_01JQ8V4C7E9G1J3L5N7Q9S1U3W",
  "rrn": "QR20250207000188",
  "paymentMethod": "WECHAT",
  "amountValue": "128.00",
  "currency": "HKD"
}
Response (200): order never reached you
{
  "respCode": "00",
  "respMessage": "Order not found, nothing to cancel",
  "status": "CLOSED",
  "enbrTranId": "tran_01JQ8V4C7E9G1J3L5N7Q9S1U3W",
  "amountValue": "128.00",
  "currency": "HKD"
}

Example: refund

A partial refund of the MPQR payment above. The refund has its own enbrTranId and trace.

POST /api/v1/qr-payments/refunds
{
  "enbrTranId": "tran_01JQ8VR1N3P5R7T9V1X3Z5B7D9",
  "srsTranId": "M1866251377260482560",
  "trace": "000192",
  "pboId": "<your-pbo-id>",
  "profileId": "prof_01JDK7CS51N7DM47JK9M4R9XPP",
  "acptId": "acpt_01JDK7CS6QKRN5K9BP053H0WBP",
  "primaryMid": "111",
  "primaryTid": "222",
  "amountValue": "5.00",
  "currency": "HKD",
  "notifyUrl": "https://<minesec-notify-url-issued-per-transaction>",
  "linkedEnbrTranId": "tran_01JQ8V2M5N3K7P9R1T3V5X7Z9B",
  "orgRrn": "QR20250207000187",
  "batchId": "batch_01JKF2QRZJFZ5BHF0SC0X9ZR5M",
  "batchNo": "029290",
  "timestamp": "2025-02-07T03:10:08.310Z"
}
Response (200)
{
  "respCode": "00",
  "status": "SUCCESS",
  "enbrTranId": "tran_01JQ8VR1N3P5R7T9V1X3Z5B7D9",
  "linkedEnbrTranId": "tran_01JQ8V2M5N3K7P9R1T3V5X7Z9B",
  "rrn": "RF20250207000031",
  "orgRrn": "QR20250207000187",
  "paymentMethod": "ALIPAY",
  "amountValue": "5.00",
  "currency": "HKD",
  "refundedAt": "2025-02-07T03:10:11Z",
  "extra": { "walletTransactionId": "2025020722001412345678909999" }
}

Example: notify

Your host reports the MPQR payment above to the notifyUrl from the create request.

POST {notifyUrl}
Content-Type: application/json
x-request-time: 2025-02-07T02:35:02Z
x-signature: HmacSHA256 <hex HMAC-SHA256 of "POST.." + x-request-time + ".." + body, under the notify key>
 
{
  "eventId": "evt_8842117",
  "eventType": "PAYMENT",
  "eventTime": "2025-02-07T02:35:02.902Z",
  "respCode": "00",
  "status": "SUCCESS",
  "enbrTranId": "tran_01JQ8V2M5N3K7P9R1T3V5X7Z9B",
  "rrn": "QR20250207000187",
  "networkMerchantId": "2088621900012345",
  "primaryMid": "111",
  "primaryTid": "222",
  "paymentMethod": "ALIPAY",
  "amountValue": "25.00",
  "currency": "HKD",
  "paidAt": "2025-02-07T02:35:01Z",
  "payer": { "id": "2088102112345678", "fundingSource": "BALANCE" },
  "extra": { "walletTransactionId": "2025020722001412345678901234" }
}
Response (200)
{ "respCode": "00" }

Full field lists: Data Model › QR.

Implementation checklist: answer MPQR create at once with PENDING and a qrCode; answer CPQR create and refunds with a final status within 20 seconds; implement inquiries and cancellations; return a stable rrn; de-duplicate cancellations on enbrActionId; answer CLOSED to a cancellation for an order you never received.