Bybit Pay - Payments
Payment Method NameSet
payments[].paymentInstrument.paymentMethodNametoByBitPaywhen creating anIntent:"paymentMethodName": "ByBitPay"
Bybit Pay lets your customers pay by scanning a QR code with Bybit. In the H2H flow, you create an
Intentwith aPayment, receive the QR image in the synchronous response, and display it on your checkout page. The payer scans the QR, reviews the transaction details, and confirms the payment in Bybit. Configure a webhook URL in the request or in your settlement account settings to receive status updates.
Payment Method Features
| Payment Method | VietQR |
|---|---|
| Country | -- |
| Processing Currencies | USDT |
| Payments | Yes |
| Disbursements | Yes |
| Refunds | Yes |
| Chargebacks | No |
Workflow: Payment
In step 1.7, display the QR from
payments[].additionalData.details.qrBase64on your checkout page.
incomingDetails.redirectUrlandincomingDetails.failureUrlare the payer's return destinations after the payment attempt. Payment status notifications use the webhook URL configured for the payment or its settlement account.
Possible Payment Scenarios
| Scenario | Steps |
|---|---|
| QR creation | [1.1] The payer chooses Bybit Pay on your checkout page. [1.2] You send POST /processing/api/v1/intents with a Bybit Pay payment.[1.3] Our platform creates the Intent and Payment.[1.4–1.5] Our platform creates the Bybit order and obtains its QR. [1.6] The synchronous response contains the created objects, payment status, and QR image. [1.7] You display the QR to the payer. The payment awaits confirmation in AUTHORIZATION_REQUESTED. |
| Successful payment | [2.1] The payer scans the QR in Bybit. [2.2–2.4] Bybit displays the transaction details, the payer confirms the payment, and Bybit displays the result. [2.5–2.6] Our platform receives the successful payment result and changes the payment status to CAPTURED.[2.7] Our platform sends a webhook to the configured URL. |
| QR expiration | [3.1] The payment session expires before the payer completes the payment. [3.2] Our platform changes the payment status to CANCELLED.[3.3] Our platform sends a webhook to the configured URL with the updated status. |
Use the payment status received from our platform to confirm the result. Returning the payer to
redirectUrlorfailureUrlis part of the browser flow and does not replace a payment status notification.Stop displaying the QR when the payment session expires or the payment reaches a final status. If a result notification is unavailable, retrieve the current payment status.
List of Used API Requests
| Request | Endpoint | Description |
|---|---|---|
| Intent creation | POST /processing/api/v1/intents | Creates an Intent with a Bybit Pay Payment and returns the QR when creation succeeds. |
| Refund creation | POST /processing/api/v1/refunds | Creates a refund for an existing payment in CAPTURED status. |
| Payment status | GET /processing/api/v1/payments/{id} | Retrieves the payment and its current status. Use the platform payment ID. |
| Refund status | GET /processing/api/v1/refunds/{id} | Retrieves the refund and its current status. Use the platform refund ID. |
Use the standard platform authentication and settlement-account-id header described in the API reference. The examples below show JSON request and response bodies.
Intent Creation Request/Response
Request specifics
- Endpoint:
POST /processing/api/v1/intents.- Payment method:
ByBitPay.- Method-specific data:
payments[].paymentInstrument.incomingDetails.- Required incoming details:
redirectUrl,failureUrl,terminalType,device,browserVersion, andip.- Optional incoming detail:
sessionTimeoutMin.
Intent Request: Template
CURRENCY_CODE is a placeholder: replace it with the currency enabled for your integration. This example uses the same submitted and authorisation currency. The sample amount is illustrative.
{
"clientReferenceId": "order-bybit-1001",
"useCheckoutForm": false,
"payments": [
{
"paymentInstrument": {
"paymentMethodName": "ByBitPay",
"incomingDetails": {
"sessionTimeoutMin": 20,
"redirectUrl": "https://merchant.example.com/payments/success",
"failureUrl": "https://merchant.example.com/payments/failure",
"terminalType": "WEB",
"device": "checkout-device-01",
"browserVersion": "Chrome/133.0.0.0",
"ip": "203.0.113.10"
}
},
"submittedAmount": {
"value": 100.50,
"currency": "USDT"
},
"authCurrencyCode": "USDT",
"webhookUrl": "https://merchant.example.com/webhooks/payments"
}
]
}Intent Request: Fields Description
Top-Level Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
clientReferenceId | String | Required | Your reference for the intent, such as an order number. |
payments | Array of Objects | Required | Payments to create within the intent. Include the Bybit Pay payment in this array. |
useCheckoutForm | Boolean | Optional | Set to false for this H2H flow. Defaults to false. |
description | String | Optional | Intent description. In the sandbox, use this top-level field to select an emulator scenario. See Testing in Sandbox. |
payment — payments[]
| Parameter | Type | Required | Description |
|---|---|---|---|
paymentInstrument | Object | Required | Payment method and its incoming details. |
submittedAmount | Object | Required | Payment amount and currency submitted to the platform. |
authCurrencyCode | String | Required | Currency used to authorise the payment. Use a currency enabled for your integration. If no currency conversion is needed, use the same value as submittedAmount.currency. |
webhookUrl | String | Optional | HTTPS endpoint for this payment's status updates. Overrides the settlement account's default webhook URL. If omitted, the account default is used. Configure either URL to receive notifications. |
payer | Object | Optional | Payer details, if supplied. See the common Intent API reference for the object schema. |
description | String | Optional | Description of this payment. The sandbox scenario keyword belongs in the top-level intent description. |
billingAddress | Object | Optional | Payer billing address, using the common API schema. |
totalTaxAmount | Object | Optional | Tax amount included in the payment, using the common monetary amount schema. |
paymentInstrument
| Parameter | Type | Required | Description |
|---|---|---|---|
paymentMethodName | String | Required | Set to ByBitPay. |
incomingDetails | Object | Required | Bybit Pay details listed below. |
incomingDetails
Full path: payments[].paymentInstrument.incomingDetails.
| Parameter | Type | Required | Description |
|---|---|---|---|
sessionTimeoutMin | Integer | Optional | Payment session lifetime in minutes. |
redirectUrl | String | Required | URL to return the payer to after a successful payment. |
failureUrl | String | Required | URL to return the payer to after a failed payment. |
terminalType | String | Required | Transaction source: APP, WEB, WAP, MINIAPP, or OTHERS. |
device | String | Required | Identifier of the payer's device. |
browserVersion | String | Required | Browser or application version information for the payer's environment. |
ip | String | Required | Payer IP address. |
submittedAmount
| Parameter | Type | Required | Description |
|---|---|---|---|
value | Number | Required | Payment amount as a JSON number, for example 100.50. Use a decimal point as the separator. |
currency | String | Required | Submitted currency code. Use a currency enabled for your integration. |
Response specifics
- The response contains the created
Intentand its payments.- When QR creation succeeds, the payment is in
AUTHORIZATION_REQUESTEDand awaits the payer's action.- The QR image is returned in
payments[].additionalData.details.qrBase64. Display this image on your checkout page.- Save
payments[].id: use this platform payment identifier when requesting a refund.- Check
paymentCreationErrorsand each returned payment'sstatusbefore displaying a QR. HTTP200alone does not confirm that every requested payment was created. An entry inpaymentCreationErrorsand a created payment withstatus = ERRORare separate response cases.
Intent Response: Example
The example shows the fields relevant to this flow. CURRENCY_CODE and BASE64_ENCODED_QR_IMAGE are placeholders; the latter represents the returned QR image data.
{
"intentId": "344115072451633152",
"payments": [
{
"id": "344115072812343296",
"submittedAmount": {
"value": 100.50,
"currency": "USDT"
},
"authAmount": {
"value": 100.50,
"currency": "USDT"
},
"paymentInstrument": {
"paymentMethodName": "ByBitPay"
},
"status": "AUTHORIZATION_REQUESTED",
"additionalData": {
"details": {
"qrBase64": "BASE64_ENCODED_QR_IMAGE",
"url": "bybit-payment-url"
}
}
}
],
"disbursements": [],
"clientReferenceIntentId": "order-bybit-1001",
"paymentCreationErrors": [],
"disbursementCreationErrors": [],
"intentStatus": "IN_PROGRESS"
}Intent Response: Fields Description
Top-Level Response Parameters
| Parameter | Type | Description |
|---|---|---|
intentId | String | Intent identifier in our platform. |
payments | Array of Objects | Created payments and their current statuses. |
disbursements | Array | Empty in this payment-only example. |
clientReferenceIntentId | String | Your intent reference, supplied as top-level clientReferenceId in the request. |
paymentCreationErrors | Array | Payment creation errors, if any. Empty when there are no such errors. |
disbursementCreationErrors | Array | Disbursement creation errors. Empty in this payment-only example. |
intentStatus | String | Overall intent status. The example is IN_PROGRESS while the payment awaits completion. |
payment — payments[]
| Parameter | Type | Description |
|---|---|---|
id | String | Platform payment identifier. Save this value for subsequent operations, including refunds. |
submittedAmount | Object | Submitted payment amount and currency. |
authAmount | Object | Authorisation amount and currency used for the payment. |
paymentInstrument | Object | Payment method information. |
status | String | Current payment status. AUTHORIZATION_REQUESTED means the payment awaits completion. |
additionalData | Object | Method-specific data needed to continue the payment. Returned when applicable. |
submittedAmount / authAmount
| Parameter | Type | Description |
|---|---|---|
value | Number | Amount in the corresponding currency. |
currency | String | Currency of this amount. Submitted and authorisation currencies can differ if currency conversion applies. |
paymentInstrument
| Parameter | Type | Description |
|---|---|---|
paymentMethodName | String | ByBitPay for this flow. |
id | String | Payment instrument identifier, when returned. |
paymentMethodId | String | Payment method identifier, when returned. |
storedDetails | Object | Stored payment instrument details, when returned. |
additionalData.details
| Parameter | Type | Description |
|---|---|---|
qrBase64 | String | Payment QR image encoded as plain Base64, without a data URI prefix. Available when QR creation succeeds. Decode it as image data before displaying it; the string itself is not a payment URL. In the sandbox, it is a PNG whose QR encodes the emulator link in url. |
url | String | In the sandbox response, the emulator URL for completing the test payment. Open the complete returned URL without modifying it. See Testing in Sandbox for the response example and trigger behaviour. |
Payment Statuses
| Status | Meaning |
|---|---|
AUTHORIZATION_REQUESTED | The payment has been initiated and awaits completion. In the successful QR creation flow, the QR is available for display. |
CAPTURED | Payment completed successfully. Final payment status. |
DECLINED | Payment was declined. Final payment status. See the sandbox scenario for a simulated decline. |
CANCELLED | The payment session expired before completion. Final payment status for the expiration scenario. |
ERROR | Payment processing failed. In the LinkCreationError sandbox scenario, this status is returned synchronously and no usable QR is created. |
Webhooks
Our platform sends transaction status updates using the following URL selection:
- The
webhookUrlsupplied for the payment or refund. - If that field is omitted, the default webhook URL configured for the settlement account.
If neither URL is configured, no webhook is sent. A refund's URL is resolved independently of the original payment's URL.
Webhooks use HTTPS POST requests with a JSON body. Acknowledge receipt with an HTTP 2xx response. See Webhooks: Overview for delivery rules and Webhooks: Example for payloads.
| Transaction | Optional URL override | Outcomes described on this page |
|---|---|---|
| Payment | payments[].webhookUrl in the intent creation request | CAPTURED, DECLINED, CANCELLED, or a processing ERROR. |
| Refund | webhookUrl in the refund creation request | REFUNDED, DECLINED, or a technical ERROR. |
Use the transaction identifier and status in the webhook to update your records. Payment and refund statuses belong to their respective transactions. The table lists the outcomes covered here; it is not an exhaustive list of notifications. Handle intermediate updates according to the common webhook contract.
If you do not receive a result notification, retrieve the current payment or refund by its platform ID. A missing webhook does not establish whether an operation succeeded or failed.
Workflow: Refunds
Create a refund for an existing Bybit Pay payment in CAPTURED status. The request references the original platform payment ID returned as payments[].id.
The diagram shows a request accepted for processing. Request validation failures are returned synchronously; see Refund Creation Errors below.
How It Works
| Step | What happens |
|---|---|
| 1 | The payer asks you for a refund. |
| 2 | You send a refund creation request with the original paymentId. |
| 3 | Our platform validates the request and creates a separate Refund transaction. |
| 4 | A successful HTTP 200 response with status = ACCEPTED confirms acceptance for processing. |
| 5 | Our platform submits the refund request to Bybit. |
| 6 | Bybit returns the refund result. |
| 7 | Our platform records the refund result: REFUNDED or DECLINED. Technical processing failures can result in ERROR. |
| 8 | Our platform sends the latest refund status to the configured webhook URL, if available. |
Refund Request/Response
Request specifics
- Endpoint:
POST /processing/api/v1/refunds.- Original transaction: a payment in
CAPTUREDstatus.- Example below: a full refund. Omit
paymentSubmittedAmountandpaymentAuthAmountto request the original payment amounts.- Existing successful or in-progress refunds reduce the amount available for refund. Omitting the amount fields requests the original amount, not just the remaining balance; the request must pass the available-amount checks.
Full Refund Request: Example
{
"paymentId": 344115072812343296,
"reason": "Customer requested a refund",
"clientReferenceId": "order-bybit-1001-refund-1",
"webhookUrl": "https://merchant.example.com/webhooks/refunds"
}Full Refund Request: Fields Description
| Parameter | Type | Required | Description |
|---|---|---|---|
paymentId | Integer (int64) | Required | Original platform payment identifier. Use the exact value of payments[].id from the intent creation response. |
partial | Boolean | Optional | Defaults to false. Omit it or set it to false for the full refund example. |
reason | String | Optional | Reason for the refund. |
clientReferenceId | String | Optional | Your reference for one logical refund, unique within the settlement account. If supplied, it must be non-empty and no longer than 2048 characters. Preserve the same value when retrying that refund. |
webhookUrl | String | Optional | HTTPS endpoint for this refund's status updates. Overrides the settlement account's default webhook URL. If omitted, the account default is used. Configure either URL to receive notifications. |
paymentId is a JSON integer in the refund request, while it is returned as a string in responses. Preserve the identifier exactly: these int64 values can exceed the exact integer range of JavaScript Number.
Response specificsA successful HTTP
200response withstatus = ACCEPTEDconfirms acceptance for processing. Treat the refund as completed only when its status isREFUNDED. Status updates are sent when a webhook URL is configured. You can also retrieve the current refund status.
Refund Response: Example
This example shows a full refund of the payment illustrated above. CURRENCY_CODE represents that payment's currency. Fields relevant to this flow are shown.
{
"id": "344115098234117632",
"intentId": "344115072451633152",
"paymentId": "344115072812343296",
"refundSubmittedAmount": {
"value": 100.50,
"currency": "CURRENCY_CODE"
},
"refundAuthAmount": {
"value": 100.50,
"currency": "CURRENCY_CODE"
},
"reason": "Customer requested a refund",
"status": "ACCEPTED",
"createdAt": "2026-09-15T10:15:12.318Z",
"clientReferenceTransactionId": "order-bybit-1001-refund-1"
}Refund Response: Fields Description
| Parameter | Type | Description |
|---|---|---|
id | String | Platform refund identifier. Save it to identify this refund. |
intentId | String | Identifier of the intent containing the original payment. |
paymentId | String | Identifier of the original payment. |
refundSubmittedAmount | Object | Refund amount and currency in submitted terms. Contains numeric value and string currency. |
refundAuthAmount | Object | Refund amount and currency in authorisation terms. Contains numeric value and string currency. |
reason | String | Reason supplied in the request, when present. |
status | String | Current refund status. The acceptance response is ACCEPTED. |
createdAt | String | Refund creation time in ISO 8601 format. |
clientReferenceTransactionId | String | Your refund reference supplied as clientReferenceId, when present. |
Refund Statuses
| Status | Meaning |
|---|---|
ACCEPTED | The platform accepted the refund request for processing. |
REFUND_REQUESTED | Refund processing has been requested; a final result is pending. |
REFUNDED | The refund completed successfully. Final refund status. |
DECLINED | The refund was declined. Final refund status. |
ERROR | A technical error occurred while processing the refund. Final refund status in the platform. |
Refund Creation Errors
A failed creation request does not have the successful response shown above. The common error response uses errorCode, failureMessage, and timestamp; see the Refund API reference for the schema.
| HTTP status | Meaning |
|---|---|
400 | Invalid refund request or refund creation failure. |
404 | The original payment was not found or is unavailable to the caller. |
409 | A duplicate refund reference or a conflict with another refund creation operation. |
Retrying a Refund Request
Use one clientReferenceId per logical refund. If the HTTP response is lost or times out, preserve that reference when retrying the same refund. A refund already created with the same reference is rejected with HTTP 409; the original successful response is not replayed.
Check the existing refund before submitting a new logical refund. When its platform ID is known, use GET /processing/api/v1/refunds/{id}. A timeout or 409 alone does not establish the final refund result.
Testing in Sandbox
Use the Bybit Pay emulator to test your handling of QR creation, successful payments, declines, and expiration.
Set payments[].paymentInstrument.paymentMethodName to ByBitPay and provide the required incoming details as in the production request.
For the webhook-based tests below, provide payments[].webhookUrl or configure a default webhook URL for the settlement account.
Select a scenario through the top-level description in the intent creation request, referred to below as intent.description. For a scenario without a keyword, omit this field or send an empty string.
Bybit Pay Payment Test Flow
The sandbox response returns the test payment link in
payments[].additionalData.details.url. The QR inpayments[].additionalData.details.qrBase64encodes the same link.Opening the URL triggers the scenario result. Open it directly, or scan the QR with a QR reader and open the decoded link. The sandbox QR opens the platform's emulator. Displaying the QR image alone does not complete the payment.
For the expiration scenario, leave the test link unopened and wait for the payment session to expire.
Sandbox Response: Relevant Fields
The following is the additionalData fragment of a sandbox payment returned with status = AUTHORIZATION_REQUESTED. SANDBOX_PAYMENT_URL and BASE64_ENCODED_PNG_QR_IMAGE are placeholders for values returned by the API.
{
"details": {
"url": "SANDBOX_PAYMENT_URL",
"qrBase64": "BASE64_ENCODED_PNG_QR_IMAGE"
}
}| Field | Format | How to use |
|---|---|---|
payments[].additionalData.details.url | String containing the complete emulator URL. | Open the returned value as-is to trigger the selected scenario. Do not construct the URL or change its query parameters. |
payments[].additionalData.details.qrBase64 | PNG image encoded as plain Base64, without a data URI prefix. | Display the image. Scanning it yields the same URL as details.url. Opening that URL triggers the selected scenario. |
Displaying the Sandbox QR in a Browser
For a returned payment object named payment and an HTML image element named qrImage:
qrImage.src =
"data:image/png;base64," + payment.additionalData.details.qrBase64;This displays the image only. Open payment.additionalData.details.url separately when you are ready to simulate the successful or declined payment.
List of Supported Cases
| Case | Scenario keyword (intent.description) | Action | Expected payment status |
|---|---|---|---|
| Successful payment | No keyword | Open payments[].additionalData.details.url before expiration. | AUTHORIZATION_REQUESTED → CAPTURED |
| QR expired | No keyword | Leave the link unopened and wait for expiration. | AUTHORIZATION_REQUESTED → CANCELLED |
| Payment declined | DeclinedPayment | Open payments[].additionalData.details.url before expiration. | AUTHORIZATION_REQUESTED → DECLINED |
| QR creation error | LinkCreationError | Send the intent creation request. | ERROR in the synchronous response. |
Successful Payment
- Create an intent using the request template above, with no scenario keyword in the top-level
description. - Check that the response contains a payment in
AUTHORIZATION_REQUESTEDand a QR inpayments[].additionalData.details.qrBase64. - Display the QR on your checkout page and open
payments[].additionalData.details.urlbefore the session expires. - Receive the payment status webhook and check that the payment status is
CAPTURED.
QR Expiration
- Create an intent with no scenario keyword. You can set the session lifetime through
payments[].paymentInstrument.incomingDetails.sessionTimeoutMin. - Check that the payment is in
AUTHORIZATION_REQUESTEDand a QR has been returned. - Leave
payments[].additionalData.details.urlunopened and wait for the payment session to expire. - Receive a webhook with payment status
CANCELLEDand stop displaying the expired QR.
Declined Payment
- Create an intent with the top-level
descriptionset toDeclinedPayment. - Check that the payment is in
AUTHORIZATION_REQUESTEDand a QR has been returned. - Open
payments[].additionalData.details.urlbefore the session expires. - Receive a webhook with payment status
DECLINED.
This scenario simulates a decline caused by a modified payment amount. Select it using the keyword in description.
Updated about 2 hours ago
