OVO One-Time
OVO One-Time is a digital wallet payment method in Indonesia that allows customers to bind their OVO account to a merchant once and then pay by authorizing each charge with their OVO PIN.
By integrating OVO One-Time, merchants can keep the customer's OVO account on file, removing the account selection and login friction from every checkout while the customer still approves each individual payment. This method relies on an account binding (enrollment) created through the /ws/userenrollment endpoint and referenced by every subsequent charge sent to /ws/direct.
This guide outlines the key concepts and implementation details required to integrate OVO One-Time via API. It covers essential steps such as binding the customer account, creating payments linked to the binding, and unbinding the account.
Key features
| Feature | Purpose | Benefit |
|---|---|---|
| Account binding | Links the customer OVO account to the merchant once. | Removes repeated account setup from checkout. |
| Per-payment authorization | Every charge is confirmed by the customer with the OVO PIN. | Keeps explicit customer consent for each transaction. |
| Binding lifecycle management | Allows querying and canceling the account binding. | Gives merchants operational control over stored accounts. |
| Payment notifications | Sends asynchronous updates whenever a payment status changes. | Ensures real-time updates for reconciliation and order fulfillment. |
How it works
- Account binding - Merchant creates the enrollment and redirects the customer to OVO to authorize the binding.
- Payment request - Merchant creates a payment referencing the accepted binding.
- Customer authorization - Customer confirms the payment with the OVO PIN.
- Payment confirmation - EBANX sends a payment status notification to the merchant.
- Minimum amount: IDR 1
- Maximum amount: IDR 20,000,000
Customer e-wallet limits, such as KYC level and balance, may further restrict the transaction amount.
Requirements
- API authentication - All API requests must be authenticated using JWS (JSON Web Signature). The legacy
integration_keyis being deprecated — if you still use it, follow the Migration Guide to transition to JWS. If you don't have credentials yet, complete the Merchant Signup Form.
Instructions
Follow the steps below.
- Select your environment
Select the appropriate environment for your integration. Use the sandbox environment for testing or the production environment for live transactions.
Select the appropriate environment for your integration. Use the sandbox environment for testing, or the production environment for live transactions. Use the URL for your HTTP requests based on your selection.
Check the method's availability for your country and tenant in Payment Methods by Country. Questions about tenant and environment? Check our API Operability and API Endpoints.
Cross-border sandboxhttps://sandbox.ebanxpay.com/ws/userenrollment - Define account binding parameters
To bind the customer OVO account to your store, call the
/ws/userenrollmentendpoint with the customer details below.Basic parameters
Parameter Requirement Description integration_keyRequired Your EBANX integration key. operationRequired Must be enrollment.payment_type_codeRequired Must be ovo.Enrollment parameters
Parameter Requirement Description enrollment.merchant_enrollment_codeRequired Unique identifier for the account binding. enrollment.emailRequired Customer email. enrollment.phone_numberRequired Customer phone number registered with OVO. enrollment.countryRequired Set to IDfor Indonesia.enrollment.back_urls.successRequired URL to redirect the customer to on success. enrollment.back_urls.failureRequired URL to redirect the customer to on failure. - Create the account binding request
Sample request
Shellcurl -X POST \
--location 'https://sandbox.ebanxpay.com/ws/userenrollment' \
--header 'Content-Type: application/json' \
--data '{
"integration_key": "{{integration_key}}",
"operation": "enrollment",
"payment_type_code": "ovo",
"enrollment": {
"merchant_enrollment_code": "{{unique_enrollment_code}}",
"email": "john.doe@example.com",
"phone_number": "5555555555",
"country": "ID",
"back_urls": {
"success": "https://merchant.example.com/callback",
"failure": "https://merchant.example.com/callback"
}
}
}' - Account binding response
A successful request returns a JSON response like the one below. Redirect the customer to the
redirect_urlso they can authorize the binding in the OVO app.JSON{
"status": "SUCCESS",
"redirect_url": "{{redirect_url}}",
"enrollment": {
"merchant_enrollment_code": "{{unique_enrollment_code}}",
"country": "ID",
"email": "john.doe@example.com",
"phone_number": "5555555555",
"status": "pending",
"back_urls": {
"success": "https://merchant.example.com/callback",
"failure": "https://merchant.example.com/callback"
}
}
}Binding expirationCustomers have 5 minutes to accept the account binding, after which the enrollment expires and a new one must be created.
Subsequent paymentsStore the
merchant_enrollment_code. It is required in every payment request created for this customer. - Confirm the account binding
As soon as the customer confirms the binding in the OVO app, the enrollment status changes from
pendingtoaccepted, and a Status Update Notification is sent to the URL defined in your merchant configuration.Notification example:
Shell# Sent by EBANX to the merchant notification URL
curl -X POST \
--location 'https://merchant.example.com/callback' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'operation=enrollment_status_change' \
--data-urlencode 'notification_type=update' \
--data-urlencode 'merchant_enrollment_code={{unique_enrollment_code}}' - Query the account binding status
Before requesting any payment, call the
/ws/userenrollments/queryendpoint with themerchant_enrollment_codeto get the latest status of the binding.Sample request
Shellcurl -X POST \
--location 'https://sandbox.ebanxpay.com/ws/userenrollments/query' \
--header 'Content-Type: application/json' \
--data '{
"integration_key": "{{integration_key}}",
"payment_type_code": "ovo",
"enrollment": {
"country": "id",
"merchant_enrollment_code": "{{unique_enrollment_code}}"
}
}'Sample response
JSON{
"status": "SUCCESS",
"enrollment": {
"status": "accepted"
}
}The response indicates one of the following values for the binding status:
accepted- Binding approved by the customer.not accepted- Binding rejected by the customer.pending- Binding awaiting customer action.expired- Binding expired.not_found- Binding not found.revoked- Binding canceled.
Only bindings with the status
acceptedcan be used to create payments. - Define payment parameters
After the binding is accepted, create payments through
/ws/direct. The payment follows the usual standards of/ws/directrequests, with the addition of the enrollment reference.Parameter Requirement Description integration_keyRequired Your EBANX integration key. operationRequired Set to request.payment.nameRequired Full name of the payer. payment.emailRequired Customer email. payment.countryRequired Set to IDfor Indonesia.payment.payment_type_codeRequired Set to ovo.payment.merchant_payment_codeRequired Unique code for the payment. payment.currency_codeRequired Set to IDR.payment.amount_totalRequired Payment amount. IDR amounts are processed as whole units. payment.redirect_urlRequired URL to redirect the customer to after the payment authorization. payment.enrollment.merchant_enrollment_codeRequired Unique identifier associated with the accepted account binding. - Create payment request
Sample request
Shellcurl -X POST \
--location 'https://sandbox.ebanxpay.com/ws/direct' \
--header 'Content-Type: application/json' \
--data '{
"integration_key": "{{integration_key}}",
"operation": "request",
"payment": {
"name": "John Doe",
"email": "john.doe@example.com",
"country": "ID",
"payment_type_code": "ovo",
"merchant_payment_code": "{{unique_merchant_code}}",
"currency_code": "IDR",
"amount_total": 100,
"redirect_url": "https://merchant.example.com/callback",
"enrollment": {
"merchant_enrollment_code": "{{unique_enrollment_code}}",
"email": "john.doe@example.com"
}
}
}'ImportantThe
merchant_enrollment_codeof an accepted binding is required for every charge. - Payment response
A successful request returns a JSON response like the one below. The payment stays with a pending (
PE) status until the customer authorizes it.JSON{
"status": "SUCCESS",
"payment": {
"hash": "59acc5f00945fa382ab051651440826da7701533249b3a475",
"country": "id",
"merchant_payment_code": "{{unique_merchant_code}}",
"order_number": null,
"status": "PE",
"status_date": "{{YYYY-MM-DD HH:mm:ss}}",
"open_date": "{{YYYY-MM-DD HH:mm:ss}}",
"confirm_date": null,
"transfer_date": null,
"amount_br": "100.00",
"amount_ext": "100.00",
"amount_iof": "1",
"currency_rate": "1",
"currency_ext": "IDR",
"due_date": "{{YYYY-MM-DD}}",
"instalments": "1",
"payment_type_code": "ovo",
"pre_approved": false,
"capture_available": null,
"customer": {
"email": "john.doe@example.com",
"name": "John Doe"
},
"currency_ext_base": "IDR"
},
"redirect_url": "{{redirect_url}}"
} - Redirect the customer to authorize the payment
Redirect the customer to the
redirect_urlreturned in the payment response. The customer confirms the charge with their OVO PIN.Payment expirationCustomers have 5 minutes to authorize the payment, after which the payment expires.
- Monitor payment for status changes
Notifications
-
EBANX will send a notification whenever a payment status changes.
-
Make sure your system is set up to receive notifications from EBANX for any changes in payment status.
Status
-
After receiving a notification that the status has changed, retrieve the payment status.
-
When a payment is confirmed, the status will change from pending (PE) to confirmed (CO).
-
- Congratulations!
You have successfully integrated OVO One-Time.
For more information, refer to theDirect API reference guidechevron_right
Error scenarios
This section outlines potential errors that may occur when creating account bindings and payments. These are synchronous error scenarios triggered by EBANX, in accordance with the product business rules and required fields.
| EBANX Code | EBANX Message | Details |
|---|---|---|
BP-DR-210 | The field enrollment.merchant_enrollment_code is required. | Payment request sent without the enrollment reference. |
BP-DR-180 | Enrollment not found. | The merchant_enrollment_code does not match any binding. |
BP-DR-182 | Enrollment not accepted. | The binding is not in the accepted status. |
BP-UE-03 | Field enrollment.email or enrollment.phone_number is required | Mandatory field is missing. |
OVO rejects a new binding when the same customer account is already bound to the same merchant. Query the existing binding and reuse it, or cancel it before creating a new one.
Cancel the account binding
Merchants can unbind the customer OVO account by sending a cancel request with the merchant enrollment code.
Ensure the binding status is pending or accepted
You can only cancel a binding if its status is pending or accepted. You can check the status using the /ws/userenrollments/query endpoint referencing its merchant_enrollment_code.
Sample request
curl -X POST \
--location 'https://sandbox.ebanxpay.com/ws/userenrollments/query' \
--header 'Content-Type: application/json' \
--data '{
"integration_key": "{{integration_key}}",
"payment_type_code": "ovo",
"enrollment": {
"country": "id",
"merchant_enrollment_code": "{{unique_enrollment_code}}"
}
}'
Sample response
{
"status": "SUCCESS",
"payment_type": "ovo",
"enrollment": {
"status": "accepted",
"email": ""
}
}
Cancel the binding
To cancel a binding, call the /ws/userenrollment endpoint (from your server) with the following required fields:
| Parameter | Requirement | Description |
|---|---|---|
integration_key | Required | Your unique and secret integration key. |
operation | Required | Must be cancel. |
payment_type_code | Required | Must be ovo. |
enrollment.country | Required | Must be ID. |
enrollment.merchant_enrollment_code | Required | The unique binding identifier. |
Sample request
curl -X POST \
--location 'https://sandbox.ebanxpay.com/ws/userenrollment' \
--header 'Content-Type: application/json' \
--data '{
"integration_key": "{{integration_key}}",
"operation": "cancel",
"payment_type_code": "ovo",
"enrollment": {
"merchant_enrollment_code": "{{unique_enrollment_code}}",
"email": "john.doe@example.com",
"country": "ID"
}
}'
Sample response
A successful request will return a JSON response with a status of revoked.
{
"status": "SUCCESS",
"payment_type": "ovo",
"enrollment": {
"status": "revoked"
}
}
Resources
Use the following resources when testing in your sandbox environment.
API Reference
Click here to access detailed API documentation to integrate efficiently.
Mock Customer Data
Click here to view mock customer data for testing and validating user flows.
Error Codes
Click here to review common error codes to troubleshoot and resolve issues quickly.
Still need help?
We hope this article was helpful. If you still have questions, you can explore the following options:
- Merchant support: Contact our support team at sales.engineering@ebanx.com for assistance.
- Not a partner yet? Please complete the Merchant Signup Form, and our commercial team will reach out to you.