Skip to main content

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​

FeaturePurposeBenefit
Account bindingLinks the customer OVO account to the merchant once.Removes repeated account setup from checkout.
Per-payment authorizationEvery charge is confirmed by the customer with the OVO PIN.Keeps explicit customer consent for each transaction.
Binding lifecycle managementAllows querying and canceling the account binding.Gives merchants operational control over stored accounts.
Payment notificationsSends asynchronous updates whenever a payment status changes.Ensures real-time updates for reconciliation and order fulfillment.

How it works​

  1. Account binding - Merchant creates the enrollment and redirects the customer to OVO to authorize the binding.
  2. Payment request - Merchant creates a payment referencing the accepted binding.
  3. Customer authorization - Customer confirms the payment with the OVO PIN.
  4. Payment confirmation - EBANX sends a payment status notification to the merchant.
Transaction limits
  • 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​

Instructions​

Follow the steps below.

  1. 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 sandbox
    https://sandbox.ebanxpay.com/ws/userenrollment
  2. Define account binding parameters
    ​

    To bind the customer OVO account to your store, call the /ws/userenrollment endpoint with the customer details below.

    Basic parameters​

    ParameterRequirementDescription
    integration_keyRequiredYour EBANX integration key.
    operationRequiredMust be enrollment.
    payment_type_codeRequiredMust be ovo.

    Enrollment parameters​

    ParameterRequirementDescription
    enrollment.merchant_enrollment_codeRequiredUnique identifier for the account binding.
    enrollment.emailRequiredCustomer email.
    enrollment.phone_numberRequiredCustomer phone number registered with OVO.
    enrollment.countryRequiredSet to ID for Indonesia.
    enrollment.back_urls.successRequiredURL to redirect the customer to on success.
    enrollment.back_urls.failureRequiredURL to redirect the customer to on failure.
  3. Create the account binding request
    ​

    Sample request

    Shell
    curl -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"
    }
    }
    }'
  4. Account binding response
    ​

    A successful request returns a JSON response like the one below. Redirect the customer to the redirect_url so 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 expiration

    Customers have 5 minutes to accept the account binding, after which the enrollment expires and a new one must be created.

    Subsequent payments

    Store the merchant_enrollment_code. It is required in every payment request created for this customer.

  5. Confirm the account binding
    ​

    As soon as the customer confirms the binding in the OVO app, the enrollment status changes from pending to accepted, 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}}'
  6. Query the account binding status
    ​

    Before requesting any payment, call the /ws/userenrollments/query endpoint with the merchant_enrollment_code to get the latest status of the binding.


    Sample request

    Shell
    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

    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 accepted can be used to create payments.

  7. Define payment parameters
    ​

    After the binding is accepted, create payments through /ws/direct. The payment follows the usual standards of /ws/direct requests, with the addition of the enrollment reference.

    ParameterRequirementDescription
    integration_keyRequiredYour EBANX integration key.
    operationRequiredSet to request.
    payment.nameRequiredFull name of the payer.
    payment.emailRequiredCustomer email.
    payment.countryRequiredSet to ID for Indonesia.
    payment.payment_type_codeRequiredSet to ovo.
    payment.merchant_payment_codeRequiredUnique code for the payment.
    payment.currency_codeRequiredSet to IDR.
    payment.amount_totalRequiredPayment amount. IDR amounts are processed as whole units.
    payment.redirect_urlRequiredURL to redirect the customer to after the payment authorization.
    payment.enrollment.merchant_enrollment_codeRequiredUnique identifier associated with the accepted account binding.
  8. Create payment request
    ​

    Sample request

    Shell
    curl -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"
    }
    }
    }'
    Important

    The merchant_enrollment_code of an accepted binding is required for every charge.

  9. 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}}"
    }
  10. Redirect the customer to authorize the payment
    ​

    Redirect the customer to the redirect_url returned in the payment response. The customer confirms the charge with their OVO PIN.

    Payment expiration

    Customers have 5 minutes to authorize the payment, after which the payment expires.

  11. Monitor payment for status changes
    ​

    Notifications

    Status

  12. Congratulations!
    ​

    You have successfully integrated OVO One-Time.

    For more information, refer to the
    Direct 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 CodeEBANX MessageDetails
BP-DR-210The field enrollment.merchant_enrollment_code is required.Payment request sent without the enrollment reference.
BP-DR-180Enrollment not found.The merchant_enrollment_code does not match any binding.
BP-DR-182Enrollment not accepted.The binding is not in the accepted status.
BP-UE-03Field enrollment.email or enrollment.phone_number is requiredMandatory field is missing.
Duplicate account binding

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

Shell
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

JSON
{
"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:

ParameterRequirementDescription
integration_keyRequiredYour unique and secret integration key.
operationRequiredMust be cancel.
payment_type_codeRequiredMust be ovo.
enrollment.countryRequiredMust be ID.
enrollment.merchant_enrollment_codeRequiredThe unique binding identifier.

Sample request

Shell
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.

JSON
{
"status": "SUCCESS",
"payment_type": "ovo",
"enrollment": {
"status": "revoked"
}
}

Resources​

Use the following resources when testing in your sandbox environment.

Still need help?

Help Image

We hope this article was helpful. If you still have questions, you can explore the following options: