Skip to main content

OVO Recurring enables merchants to charge customers automatically for subscription-based services using the OVO wallet, one of the top e-wallets in Indonesia. Customers authorize a subscription once, allowing merchants to perform future debits without manual approval for every transaction.

This capability is commonly used for SaaS and digital subscriptions, gaming memberships and in-app subscriptions, streaming and entertainment services, e-learning platforms, and utilities and telecom services.

Key Features

FeaturePurposeBenefit
Enrollment AuthorizationCustomer enrollment approval.Enables secure customer consent while removing the need for repeated authorization for each payment.
Merchant Initiated ChargesCreate new charges linked to a previously accepted enrollment.Simplifies billing automation and reduces friction for subscription-based services.
Subscription Lifecycle ManagementProvides the ability to query, cancel, or manage subscription enrollments.Gives merchants operational control over subscriptions and improves customer account management.
Payment NotificationsSends asynchronous updates whenever payment status changes.Ensures merchants receive real-time transaction updates for reconciliation and order fulfillment.

How it works

  1. Subscription Enrollment - Merchant registers the subscription and redirects the user to authorize it in OVO.
  2. Recurring Payment Request - Merchant triggers a recurring charge linked to the enrollment when required.
  3. Payment Confirmation - EBANX sends payment status notifications to the merchant.
Transaction Limits
  • Minimum Amount: IDR 1,000 (approx. $0.06 USD)
  • Maximum Amount (Regular / Non-KYC User): IDR 2,000,000 (approx. $123 USD)
  • Maximum Amount (Premium / KYC User): IDR 20,000,000 (approx. $1,227 USD)

Requirements

  • API credentials - Ensure you have your EBANX integration key. If not, complete the Merchant Signup Form.

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.

    https://sandbox.ebanx.com/ws/userenrollment
  2. Define enrollment parameters

    To start the recurring payment process, enroll the subscription using the /ws/userenrollment endpoint. This step involves collecting essential customer details and subscription parameters to establish the recurring billing agreement.

    Users will be required to complete 2FA to accept this enrollment in their app. Once the process is complete, the customer will be enrolled for the recurring payment cycle.

    Basic parameters

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

    Enrollment parameters

    ParameterRequirementDescription
    enrollment.merchant_enrollment_codeRequiredUnique identifier for the enrollment.
    enrollment.emailRequiredCustomer email.
    enrollment.phone_numberRequiredCustomer phone number.
    enrollment.countryRequiredSet to ID for Indonesia.
    enrollment.back_urls.successRequiredURL to redirect on success.
    enrollment.back_urls.failureRequiredURL to redirect on failure.
    enrollment.subscription.subscription_nameRequiredSubscription description (1-35 characters).
    enrollment.subscription.expiration_dateRequiredExpiration date in YYYY-MM-DD format (must be a future date).
    enrollment.subscription.frequencyRequiredSet to ondemand.
    enrollment.subscription.min_amountRequiredMinimum charge amount.
    enrollment.subscription.max_amountRequiredMaximum charge amount.
  3. Enrollment request

    Enroll the customer in OVO Recurring using the /ws/userenrollment endpoint.


    Sample request

    curl -X POST \
    --location 'https://sandbox.ebanx.com/ws/userenrollment' \
    --header 'Content-Type: application/json' \
    --data '{
    "integration_key": "{{integration_key}}",
    "operation": "enrollment",
    "payment_type_code": "ovo-recurring",
    "enrollment": {
    "merchant_enrollment_code": "{{unique_enrollment_code}}",
    "email": "john.doe@example.com",
    "phone_number": "6281299221630",
    "country": "ID",
    "back_urls": {
    "success": "https://your-success-url.com",
    "failure": "https://your-failure-url.com"
    },
    "subscription": {
    "subscription_name": "{{description}}",
    "expiration_date": "{{YYYY-MM-DD}}",
    "frequency": "ondemand",
    "min_amount": "{{min_amount}}",
    "max_amount": "{{max_amount}}"
    }
    }
    }'
  4. Enrollment response

    A successful request will return a JSON response like the one below:

    {
    "status": "SUCCESS",
    "redirect_url": "https://sandbox.ebanx.com/ws/redirect/execute?hash=<{hash_here}>",
    "enrollment": {
    "merchant_enrollment_code": "{{unique_enrollment_code}}",
    "country": "ID",
    "email": "john.doe@example.com",
    "phone_number": "6281299221630",
    "back_urls": {
    "success": "https://your-success-url.com",
    "failure": "https://your-failure-url.com"
    },
    "subscription": {
    "subscription_name": "{{description}}",
    "expiration_date": "{{YYYY-MM-DD}}",
    "frequency": "{{frequency}}",
    "min_amount": "{{min_amount}}",
    "max_amount": "{{max_amount}}"
    }
    }
    }
    Important notes about enrollment
    • On-demand frequency provides merchants with the flexibility to initiate charges at any time within the subscription validity period, without being restricted to a fixed billing schedule.
    • The enrollment request does not trigger a debit from the customer's bank account. It only serves as a one-time authorization, allowing merchants to initiate future debits according to the subscription parameters.
    • Users have 5 minutes to confirm enrollments, after which the enrollment will expire.
    Subsequent payments

    Save the merchant_enrollment_code for subsequent payment requests.

  5. Confirm the enrollment

    As soon as the enrollment is confirmed by the customer in their banking 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:

    operation=enrollment_status_change&notification_type=update&merchant_enrollment_code=99911110104
  6. Query enrollment status

    Before requesting any payments, call the /ws/userenrollments/query endpoint using the merchant_enrollment_code to get the latest status of the enrollment.


    Sample request

    curl -X POST \
    --location 'https://sandbox.ebanx.com/ws/userenrollments/query' \
    --header 'Content-Type: application/json' \
    --data '{
    "integration_key": "{{integration_key}}",
    "payment_type_code": "ovo-recurring",
    "enrollment": {
    "country": "id",
    "merchant_enrollment_code": "{{unique_enrollment_code}}"
    }
    }'

    Sample response

    {
    "status": "SUCCESS",
    "enrollment": {
    "status": "accepted"
    }
    }

    The response will indicate one of the following values for enrollment status:

    • accepted - Enrollment approved by the customer.
    • not accepted - Enrollment rejected by the customer.
    • paused - Enrollment temporarily paused.
    • pending - Enrollment awaiting customer action.
    • expired - Enrollment expired.
    • not_found - Enrollment not found.
    • revoked - Enrollment cancelled.

    If your enrollment has the status accepted, you can start requesting recurring payments.

  7. Define payment parameters

    After the enrollment is accepted, you can create recurring payment requests. The payment follows the usual standards of /ws/direct payment requests, with the addition of specific parameters related to enrollment.

    ParameterRequirementDescription
    integration_keyRequiredYour EBANX integration key.
    operationRequiredSet to request.
    payment.nameRequiredFull name of the payer.
    payment.emailRequiredCustomer email.
    payment.phone_numberRequiredCustomer contact number.
    payment.countryRequiredSet to ID for Indonesia.
    payment.payment_type_codeRequiredSet to ovo-recurring.
    payment.merchant_payment_codeRequiredUnique code for the payment.
    payment.currency_codeRequiredSet to IDR.
    payment.amount_totalRequiredPayment amount.
    payment.enrollment.merchant_enrollment_codeRequiredUnique identifier associated with the enrollment.
  8. Create payment request

    Sample request

    curl -X POST \
    --location 'https://sandbox.ebanx.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",
    "phone_number": "6281299221630",
    "payment_type_code": "ovo-recurring",
    "merchant_payment_code": "{{merchant_payment_code}}",
    "currency_code": "IDR",
    "amount_total": 200,
    "enrollment": {
    "merchant_enrollment_code": "{{unique_enrollment_code}}",
    "email": "john.doe@example.com"
    }
    }
    }'
    Important

    The merchant_enrollment_code associated with the enrollment is required for every subsequent charge.

  9. Payment response

    A successful request will return a JSON response like the one below. The OVO Recurring payment will have a pending (PE) status until confirmed.

    {
    "payment": {
    "hash": "<{hash_here}>",
    "country": "id",
    "merchant_payment_code": "{{merchant_payment_code}}",
    "order_number": null,
    "status": "PE",
    "status_date": "{{YYYY-MM-DD HH:mm:ss}}",
    "open_date": "{{YYYY-MM-DD HH:mm:ss}}",
    "confirm_date": "{{YYYY-MM-DD HH:mm:ss}}",
    "transfer_date": "{{YYYY-MM-DD HH:mm:ss}}",
    "amount_br": "200.00",
    "amount_ext": "200.00",
    "amount_iof": "0.00",
    "currency_rate": "1.0000",
    "currency_ext": "IDR",
    "due_date": "{{YYYY-MM-DD}}",
    "instalments": "1",
    "payment_type_code": "ovo-recurring",
    "pre_approved": false,
    "capture_available": null,
    "customer": {
    "email": "john.doe@example.com",
    "name": "John Doe",
    "birth_date": "{{YYYY-MM-DD}}"
    },
    "single_transaction": {
    "amount_local": "0.00",
    "amount_crossborder": "100.00"
    },
    "currency_ext_base": "IDR"
    },
    "status": "SUCCESS"
    }
    Billing engine

    EBANX does not offer a recurrent billing engine. Merchants are required to maintain their own charge schedules.

  10. Monitor payment for status changes

    Notifications

    Status

  11. Congratulations!

    You have successfully integrated OVO Recurring.

    For more information, refer to the
    Direct API reference guidechevron_right

Error Scenarios

This section outlines potential errors that may occur during enrollment creation and recurring payment requests in OVO Recurring. These are synchronous error scenarios triggered by EBANX, in accordance with the product business rules and required fields.

EBANX CodeEBANX MessageDetails
BP-UE-18Field enrollment.subscription.expiration_date must be a future dateIncorrect expiration date.
BP-UE-03Field enrollment.email or enrollment.phone_number is requiredMandatory field is missing.
BP-UE-17Field enrollment.subscription.min/max amount is required.Min/max amount is blank.
BP-UE-30The field subscription_name size must be between 1 and 35 characters.Subscription name character limit exceeded.
BP-UE-23Field enrollment.subscription.frequency is requiredFrequency field is blank.

Cancel Enrollment

Merchants can revoke an active subscription by sending a cancel request with the merchant enrollment code.

Ensure enrollment status is Pending or Accepted

You can only cancel an enrollment 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.ebanx.com/ws/userenrollments/query' \
--header 'Content-Type: application/json' \
--data '{
"integration_key": "{{integration_key}}",
"payment_type_code": "ovo-recurring",
"enrollment": {
"country": "id",
"merchant_enrollment_code": "{{unique_enrollment_code}}"
}
}'

Sample response

{
"status": "SUCCESS",
"payment_type": "ovo-recurring",
"enrollment": {
"status": "accepted",
"email": ""
}
}

Enrollments with a status of accepted or pending are eligible for cancellation. Other possible enrollment statuses include expired, not_found, and revoked, none of which can be cancelled.

Cancel the enrollment

To cancel an enrollment, 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-recurring.
enrollment.countryRequiredMust be ID.
enrollment.merchant_enrollment_codeRequiredThe unique enrollment identifier.

Sample request

curl -X POST \
--location 'https://sandbox.ebanx.com/ws/userenrollment' \
--header 'Content-Type: application/json' \
--data '{
"integration_key": "{{integration_key}}",
"operation": "cancel",
"payment_type_code": "ovo-recurring",
"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-recurring",
"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: