Skip to main content

OPay Recurring enables merchants in Nigeria to debit a customer's OPay wallet after the customer approves a recurring payment agreement in the OPay app. Merchants can create an enrollment only, or combine the enrollment with the first charge, depending on the billing use case.

OPay Recurring is available in Nigeria (ng) for payments in Nigerian naira (NGN). The payment method may also be referred to as OPay Debit.

Key Features

  • Enrollment (one-time consent): customers approve the recurring agreement once in the OPay app.
  • Two enrollment modes: enroll and charge (enrollment plus the first payment in a single request) or enrollment only (agreement set up now, charge later).
  • Enroll and charge supports two use cases: subscription, for a fixed recurring amount, and variable on-file payments, for unfixed amounts on an unfixed cycle.
  • Pre-debit notification: for the enroll and charge (subscription) flow only, EBANX sends OPay the amount and the debit date before each subsequent charge.
  • Secure authentication: customers approve the agreement in the OPay mobile app with their OPay PIN or a phone OTP.
  • Wallet-based debits: recurring charges are debited directly from the customer's OPay wallet balance.
  • Minimum amount per transaction: 50 NGN.
  • Maximum amount per transaction: 5,000,000 NGN.
  • Refund date range: refunds can be processed within 180 days.
  • Enrollment approval window: the customer has 30 minutes to approve the request in the OPay app. An enrollment that is still pending after 70 minutes expires and a new enrollment is required.

This guide explains how to create OPay Recurring enrollments, process recurring payments, monitor status changes, and cancel agreements through the EBANX API.

Note

OPay Recurring must be displayed as a standalone payment method on the merchant checkout page. It should not be nested under another payment method, such as bank transfer. Contact our integration specialists for the official logo and branding guidelines.


Merchant onboarding and agreement template

Before integrating OPay Recurring, merchants must complete an onboarding process with OPay. This includes setting up an agreement template that defines the terms under which recurring debits are executed against customer OPay wallets. The agreement template must be created and approved by OPay before any enrollment or payment integration can begin.

The template is used to display the debit agreement to OPay wallet users when they are asked to authorize recurring payments, and it is identified by an agreement template ID assigned by OPay upon approval. There are two types of agreement templates:

  • Enroll and charge - use it when the merchant needs to create the agreement and collect the first payment in one request. It supports fixed subscriptions and on-demand variable on-file payments.
  • Enrollment only - use it when the merchant needs to create the agreement first and submit payment requests separately, including on-demand charges.
Note
  • For enroll and charge (subscription), a separate agreement template is required for each product offering. If you offer different products or plans (for example, a weekly, monthly or yearly package), an agreement template must be completed for each one.
  • For enroll and charge (variable on-file payments), a single agreement template ID is required.
  • For enrollment only, a single agreement template ID is enough. If required, separate agreement template IDs can be created for each product frequency (for example, daily, weekly, monthly or yearly).

Enrollment modes and use cases

Enroll and charge (subscription)

Use this flow for subscriptions with a fixed recurring amount defined in the agreement template — for example, a monthly streaming or membership plan. The customer authorizes the agreement and completes the first payment in the same journey: the first payment amount is sent in the request, and the fixed recurring amount is defined in the agreement template.

Enroll and charge (variable on-file payments)

Use this flow for enterprise merchants, such as retail merchants, that want to enable on-file style payments. The amount is not fixed: the merchant enrolls the customer once and can then charge unfixed amounts on an unfixed cycle. It suits retail and marketplace scenarios where the basket value and the billing frequency differ on each purchase, such as pay-as-you-go orders, top-ups or usage-based billing. For this use case, send frequency as ondemand.

Enrollment only

Use enrollment only when future charge amounts may change, or when you do not need to collect a payment during enrollment — typically for on-demand services. The integration requires two separate requests:

  1. Send an enrollment request without a payment amount, passing frequency as ondemand.
  2. Send a separate charge request to /ws/direct when the payment is due, which can be immediately after the enrollment is accepted.
Warning

Fixed amount and price changes (subscription use case): for enroll and charge (subscription), the amount set in the agreement is fixed and price changes are a manual process — they cannot be done through the API. To change a subscription price, contact your EBANX account manager or the sales engineering team with the new price, and EBANX coordinates the template update with OPay. If you anticipate frequent price changes, the enrollment only mode is more suitable, because it allows a different amount on each charge sent to /ws/direct.


Requirements

  • API authentication - All API requests must be authenticated using JWS (JSON Web Signature). The legacy integration_key is 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.
  • Familiarity with EBANX Direct - This setup follows the same general structure as other payment methods, with a few unique steps for OPay Recurring, such as the pre-debit notification and the agreement management.
  • OPay onboarding - The agreement template must be approved by OPay before you start the integration.

How it works

Customers authorize recurring payments through a cashier redirect flow. On enroll and charge, the merchant submits the enrollment and the first payment in a single request; on enrollment only, no payment is taken at the enrollment stage. In both modes, the customer is redirected to the OPay cashier page to authorize the agreement in the OPay app.

StepActionEndpointDescription
1Create enrollment/ws/userenrollmentRegister the customer and define the recurring agreement terms
2Customer approval(via OPay app)The customer is redirected to OPay to authorize the agreement in the OPay app
3Query enrollment status/ws/userenrollments/queryPoll this endpoint to confirm whether the enrollment was accepted
4Pre-debit notification (enroll and charge subscription)Handled by EBANXMandatory step: OPay is notified of the upcoming debit amount and date before each charge
5Recurring payments/ws/directExecute the recurring payment using the enrollment code linked to the OPay agreement
6Enrollment cancellation/ws/userenrollmentWhere applicable, merchants can cancel the enrollment to terminate the agreement

  • Return URLs - the URL the customer returns to after the OPay approval flow is defined in the back_urls object of the enrollment (back_urls.success). It applies to every OPay Recurring scenario and can also be sent in the recurring charge request. When it is not provided, EBANX uses the default url_response configured for your integration.
  • Enrollment status update - EBANX notifies you of enrollment status updates through webhooks sent to your registered URL.
  • Pre-debit notification - for enroll and charge (subscription) only, EBANX sends OPay the upcoming amount and debit date before each subsequent charge.
  • Subsequent payments - you submit /ws/direct requests using the merchant_enrollment_code, which EBANX maps to the OPay agreement.
  • Payment status update - EBANX sends webhook notifications when the payment status changes.
Note
  • Pre-debit notification applies only to enroll and charge (subscription). It does not apply to enrollment only or to enroll and charge (variable on-file payments).
  • Enrollment agreements can be canceled by the merchant through the /ws/userenrollment endpoint, or by the customer directly in the OPay app.

Instructions

To complete an OPay Recurring enrollment and process recurring payments, 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. 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/direct
  2. Define your enrollment parameters

    To start the recurring payment process, enroll the customer using the /ws/userenrollment endpoint for enrollment only, or the /ws/direct endpoint for enroll and charge. This step involves collecting the customer details and configuring the agreement.

    Basic parameters

    These apply to all enrollment options.

    ParameterRequirementDescription
    integration_keyRequiredYour EBANX integration key
    payment_type_codeRequiredMust be opay-recurring
    operationRequiredMust be enrollment on /ws/userenrollment requests

    Enrollment data

    The phone_number linked to the customer's OPay wallet is required to initiate the transaction. Where each field goes depends on the endpoint you call:

    • /ws/userenrollment (enrollment only) - send every field below inside the enrollment object, with the subscription fields inside enrollment.subscription.
    • /ws/direct (enroll and charge) - send the customer fields (country, name, email and phone_number) inside the payment object, alongside the payment fields. Only merchant_enrollment_code, subscription and back_urls go inside payment.enrollment.
    ParameterRequirementDescription
    countryRequiredMust be ng
    emailRequiredCustomer's email address
    nameRequiredCustomer's full name
    phone_numberRequiredCustomer's phone number linked to the OPay wallet
    merchant_enrollment_codeRequiredYour unique identifier for this enrollment
    subscription_nameRequiredDescriptor of the service being provided, or a short description that helps the customer understand this enrollment
    frequencyRequiredBilling cycle of the agreement. Supported values: daily, weekly, monthly, yearly and ondemand
    start_dateOptionalScheduled date for the first transaction that starts the enrollment (YYYY-MM-DD)
    expiration_dateOptionalDefines when the enrollment is no longer valid, after which no further payments are charged (YYYY-MM-DD)

    Note

    Pass the frequency that corresponds to the agreement template created during onboarding, so the request is routed to the correct template in OPay. For enrollment only and for enroll and charge (variable on-file payments), pass ondemand.


    Return URLs (back_urls)

    back_urls defines where the customer is sent back to after the OPay approval flow. It applies to every OPay Recurring scenario and can also be sent in the recurring charge request. Send it inside the enrollment object.

    ParameterRequirementDescription
    back_urlsConditionalReturn URL configuration for the OPay approval flow. Required unless a default url_response is already configured for your EBANX integration
    back_urls.successRequired with back_urlsHTTPS URL where the customer is redirected after completing the OPay approval flow. This is the only URL used by OPay Recurring
    back_urls.failureRequired with back_urlsMust be included when back_urls is provided, although it is not used by this flow. It can be an empty string

    Note

    For OPay Recurring, EBANX uses only back_urls.success. If you provide the back_urls object, you must include both success and failure, and failure can be empty. You can omit the entire back_urls object when a default response URL (url_response) is configured for your EBANX integration. If you are unsure whether url_response is configured, contact your EBANX integration representative.

  3. Enrollment option one: Enroll and charge

    This option creates the enrollment and processes the first payment in a single /ws/direct request: the customer signs the agreement and the first charge is executed in the same journey. Use it for a fixed subscription or for an unfixed-cycle, unfixed-amount variable on-file payment.

    In addition to the basic and enrollment fields, include the following payment fields:

    ParameterRequirementDescription
    operationRequiredMust be request
    merchant_payment_codeRequiredYour unique identifier for this payment
    currency_codeRequiredMust be NGN
    amount_totalRequiredAmount of the first payment, in naira

    Check a request example below:


    Shell
    curl -X POST '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",
    "phone_number": "5555555555",
    "country": "ng",
    "payment_type_code": "opay-recurring",
    "merchant_payment_code": "{{unique_merchant_code}}",
    "currency_code": "NGN",
    "amount_total": 99.85,
    "enrollment": {
    "merchant_enrollment_code": "{{unique_enrollment_code}}",
    "subscription": {
    "subscription_name": "{{subscription_name}}",
    "frequency": "monthly"
    },
    "back_urls": {
    "success": "https://merchant.example.com/callback/success",
    "failure": ""
    }
    }
    }
    }'

    A successful response returns status: SUCCESS, the payment data, and a redirect_url. Redirect the customer to this URL to authorize the agreement in OPay.


    JSON
    {
    "payment": {
    "hash": "69b7212caaacfdfcfe743224f7c40d4bbec37d53233b186c",
    "country": "ng",
    "merchant_payment_code": "{{unique_merchant_code}}",
    "order_number": null,
    "status": "PE",
    "status_date": "2026-03-15 21:14:20",
    "open_date": "2026-03-15 21:14:20",
    "confirm_date": null,
    "transfer_date": null,
    "amount_br": "99.85",
    "amount_ext": "99.85",
    "amount_iof": "1",
    "currency_rate": "1",
    "currency_ext": "NGN",
    "due_date": "2026-03-16",
    "instalments": "1",
    "payment_type_code": "opay-recurring",
    "redirect_url": "https://sandbox.ebanxpay.com/ws/redirect?hash=69b7212caaacfdfcfe743224f7c40d4bbec37d53233b186c",
    "pre_approved": false,
    "capture_available": null,
    "customer": {
    "document": "",
    "email": "john.doe@example.com",
    "name": "John Doe",
    "birth_date": null
    }
    },
    "status": "SUCCESS",
    "redirect_url": "https://sandbox.ebanxpay.com/ws/redirect?hash=69b7212caaacfdfcfe743224f7c40d4bbec37d53233b186c"
    }
  4. Enrollment option two: Enrollment only

    This option creates the enrollment without processing a payment: the customer signs the agreement only, and charges are sent later with the enrollment code. No parameters beyond the basic and enrollment parameters are needed, and no amount is sent during the enrollment.


    Check a request example below:


    Shell
    curl -X POST 'https://sandbox.ebanxpay.com/ws/userenrollment' \
    --header 'Content-Type: application/json' \
    --data '{
    "integration_key": "{{integration_key}}",
    "payment_type_code": "opay-recurring",
    "operation": "enrollment",
    "enrollment": {
    "country": "ng",
    "name": "John Doe",
    "email": "john.doe@example.com",
    "phone_number": "5555555555",
    "merchant_enrollment_code": "{{unique_enrollment_code}}",
    "subscription": {
    "subscription_name": "{{subscription_name}}",
    "frequency": "ondemand"
    },
    "back_urls": {
    "success": "https://merchant.example.com/callback/success",
    "failure": ""
    }
    }
    }'

    Check a response example below:


    JSON
    {
    "status": "SUCCESS",
    "redirect_url": "https://sandbox.ebanxpay.com/ws/redirect?hash=a4f9c1d7e83b6a5f2c9e0d14b7a62f3c8d1e9b4a7f6c2d10",
    "enrollment": {
    "merchant_enrollment_code": "{{unique_enrollment_code}}",
    "country": "ng",
    "name": "John Doe",
    "email": "john.doe@example.com",
    "phone_number": "5555555555",
    "back_urls": {
    "success": "https://merchant.example.com/callback/success",
    "failure": ""
    },
    "subscription": {
    "frequency": "ondemand",
    "subscription_name": "{{subscription_name}}"
    }
    }
    }
  5. Redirect the customer and check the enrollment status

    After a successful enrollment request, redirect the customer to the returned redirect_url to authorize the recurring agreement in OPay. EBANX sends an asynchronous notification once OPay reports the agreement status:

    Text
    operation=enrollment_status_change&notification_type=update&merchant_enrollment_code={{unique_enrollment_code}}

    You can also retrieve the latest enrollment status before submitting a payment. Query /ws/userenrollments/query with the merchant_enrollment_code to confirm that the enrollment was accepted.

    ParameterRequirementDescription
    integration_keyRequiredYour EBANX integration key
    enrollment.merchant_enrollment_codeRequiredUnique identifier defined by the merchant for the enrollment

    Check a request example below:


    Shell
    curl -X POST 'https://sandbox.ebanxpay.com/ws/userenrollments/query' \
    --header 'Content-Type: application/json' \
    --data '{
    "integration_key": "{{integration_key}}",
    "enrollment": {
    "merchant_enrollment_code": "{{unique_enrollment_code}}"
    }
    }'

    A successful query returns status: SUCCESS. An agreement authorized by the customer has an enrollment status of accepted:


    JSON
    {
    "status": "SUCCESS",
    "payment_type": "opay-recurring",
    "enrollment": {
    "status": "accepted",
    "email": "john.doe@example.com"
    }
    }

    OPay agreement statuses map to EBANX enrollment statuses as follows:

    OPay statusMeaningEBANX enrollment status
    YSigned / activeaccepted
    NTerminatedrevoked
    YDSigning in progresspending
    NDTerminating in progresspending
  6. Pre-debit notification (mandatory for subscriptions)

    For the enroll and charge (subscription) use case, a pre-debit notification must be sent to the customer before each recurring charge. This is a mandatory step required by OPay's recurring payment infrastructure, so that customers are informed of upcoming debits from their wallet.

    From the merchant perspective, only one request is required:

    1. Submit the recurring payment through /ws/direct using the merchant_enrollment_code of the accepted agreement.
    2. EBANX sends the pre-debit notification to OPay. You do not need to call a separate endpoint.
    3. EBANX holds the payment during the mandatory 24-hour notification window.
    4. After the waiting period, EBANX submits the debit request to OPay.
    5. The payment stays pending (PE) during the window. After OPay confirms the debit it changes to confirmed (CO), and EBANX sends a status notification.
    Note
    • Pre-debit notification applies only to enroll and charge (subscription). It does not apply to enrollment only or to the variable on-file payments flow.
    • Each subsequent subscription charge requires a pre-debit notification.
    • Allow at least 24 hours between submitting the payment request and the charge execution. This is a mandatory customer notification window.
    • Configure payment notifications and use /ws/query when you need to retrieve the latest payment status.
  7. Process recurring payments

    After the enrollment is accepted, submit the charge through /ws/direct. For the subscription flow, EBANX applies the required pre-debit notification window before executing the debit. The charge is debited from the customer's OPay wallet balance.

    ParameterRequirementDescription
    integration_keyRequiredYour EBANX integration key
    operationRequiredMust be request
    payment_type_codeRequiredMust be opay-recurring
    countryRequiredMust be ng
    currency_codeRequiredMust be NGN
    amount_totalRequiredTotal amount to be charged, in naira
    merchant_payment_codeRequiredYour unique identifier for this payment
    nameRequiredCustomer's full name
    emailRequiredCustomer's email address
    phone_numberRequiredCustomer's phone number linked to the OPay wallet
    merchant_enrollment_codeRequiredEnrollment code created in the previous steps, sent inside the payment.enrollment object. The enrollment must have an accepted status to be charged
    back_urlsConditionalReturn URL configuration for the OPay approval flow. See Return URLs

    Check a request example below:


    Shell
    curl -X POST '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",
    "phone_number": "5555555555",
    "country": "ng",
    "payment_type_code": "opay-recurring",
    "merchant_payment_code": "{{unique_merchant_code}}",
    "currency_code": "NGN",
    "amount_total": 99.85,
    "enrollment": {
    "merchant_enrollment_code": "{{unique_enrollment_code}}"
    }
    }
    }'

    A successful request returns status: SUCCESS, and the payment stays pending (PE) until OPay confirms the debit.


    JSON
    {
    "payment": {
    "hash": "69b72acadc65a48bd0d7c3728705b4afa18289d4e61d58a7",
    "country": "ng",
    "merchant_payment_code": "{{unique_merchant_code}}",
    "order_number": null,
    "status": "PE",
    "status_date": "2026-03-15 21:14:20",
    "open_date": "2026-03-15 21:14:20",
    "confirm_date": null,
    "transfer_date": null,
    "amount_br": "99.85",
    "amount_ext": "99.85",
    "amount_iof": "1",
    "currency_rate": "1",
    "currency_ext": "NGN",
    "due_date": "2026-03-16",
    "instalments": "1",
    "payment_type_code": "opay-recurring",
    "pre_approved": false,
    "capture_available": null,
    "customer": {
    "document": "",
    "email": "john.doe@example.com",
    "name": "John Doe",
    "birth_date": null
    }
    },
    "status": "SUCCESS"
    }
    Note

    Before retrying a failed or timed-out payment, query the original payment status to avoid creating a duplicate debit. Retry only after the original request is confirmed as unsuccessful.


    Confirming the payment

    As soon as the payment is confirmed, the payment status changes from PE to CO and a status update notification is sent to your configured webhook URL:

    JSON
    {
    "operation": "payment_status_change",
    "notification_type": "update",
    "merchant_payment_code": "{{unique_merchant_code}}"
    }

    Using the hash returned in the payment response, call the /ws/query endpoint to get the latest status of the payment:

    Shell
    curl -X POST 'https://sandbox.ebanxpay.com/ws/query' \
    --header 'Content-Type: application/json' \
    --data '{
    "integration_key": "{{integration_key}}",
    "hash": "69b72acadc65a48bd0d7c3728705b4afa18289d4e61d58a7"
    }'

    Now, repeat this step for each billing cycle. For enroll and charge (subscription), EBANX also repeats the pre-debit notification process.

  8. Enrollment cancellation

    Customers can request the enrollment cancellation at any time. To comply with the request, cancel the enrollment by sending operation: cancel to /ws/userenrollment. Once an enrollment is revoked it can no longer be used for future transactions, and a new enrollment is required to resume recurring debits.


    Check a request example below:


    Shell
    curl -X POST 'https://sandbox.ebanxpay.com/ws/userenrollment' \
    --header 'Content-Type: application/json' \
    --data '{
    "integration_key": "{{integration_key}}",
    "operation": "cancel",
    "payment_type_code": "opay-recurring",
    "enrollment": {
    "merchant_enrollment_code": "{{unique_enrollment_code}}"
    }
    }'

    Check a response example below:


    JSON
    {
    "status": "SUCCESS",
    "enrollment": {
    "status": "revoked",
    "email": "john.doe@example.com"
    }
    }

    Notifications


    Status

    • After receiving a notification that the status has changed, retrieve the payment status.

    • A successful payment changes from pending (PE) to confirmed (CO). An unsuccessful payment moves to canceled (CA) or to another failure status returned by the API.

  9. Congratulations!

    You have successfully integrated OPay Recurring.

    For more information, refer to the
    Direct API reference guidechevron_right

Error codes

When an enrollment or a charge request fails, EBANX returns the error in the status_code field of the response. The tables below list the errors you can expect when integrating OPay Recurring. For the full reference, see API Error Codes, APM Response Codes and Enrollment Response Codes.

Enrollment creation errors

CodeError messageDescription
BP-UE-42Provider rejected enrollment creationOPay declined the enrollment. It happens when authentication fails on the provider side, the payment method is not supported, the request reference was already used, the merchant is not available, the agreement template expired, or the template only allows automatic renewal. The response does not indicate which cause applies. If the enrollment code was already used, generate a new, unique merchant_enrollment_code and retry. Otherwise, check your OPay configuration — payment method availability, merchant status and agreement template validity — and contact your EBANX integration representative if the rejection persists
BP-UE-34Request with invalid paramsOne or more parameters were rejected by OPay: invalid request params, an agreement expiration date outside the allowed window, or an amount that does not match the agreed subscription amount
BP-UE-12Failed to communicate with providerCredential or agreement mismatch: the agreement template ID is incorrect, or the merchant ID does not match the agreement. Contact your EBANX integration representative
BP-UE-32Provider internal server errorOPay is facing downtime, or the request was sent outside the time window in which it accepts the operation. Retry later

Payment processing errors

When a charge is declined, the payment moves to canceled (CA) and the reason is returned in the transaction_status node of the payment query response, in both description and description_code:

JSON
"transaction_status": {
"acquirer": "EBANX",
"code": "NOK",
"description": "Not accepted",
"description_code": "NOT_ACCEPTED"
}
description_codePayment stepDetails
NOT_ACCEPTEDCharge rejected by the providerOPay declined the charge: do not honor, restricted or suspected fraud, transaction not permitted, or limits and attempts exceeded. Do not retry automatically
INVALID_PARAMETERSCharge rejected by the providerThe parameters sent in the charge request were rejected. Review the payload against the required fields and the amount limits of the agreement
PAYMENT_NOT_VALIDCharge rejected by the providerThe payment method sent is not supported for the recurring charge. Confirm that payment_type_code is opay-recurring and that the enrollment is active
DUPLICATE_TRANSACTIONCharge rejected by the providerThe merchant_payment_code was already used. Generate a new, unique code before retrying
TIMEOUTCharge not answered by the providerOPay did not respond within the expected time. Query the payment status before retrying to avoid a double charge
PROVIDER_RESPONSE_ERRORCharge failed at the providerOPay returned an internal or unexpected response. Retry later; if it persists, contact EBANX Support

Enrollment cancellation reasons

When an enrollment status is not_accepted, expired or revoked, the query response includes the reason that caused its termination. Check the enrollment status with the /ws/userenrollments/query endpoint, referencing the merchant_enrollment_code. The response returns the latest status in status, the timestamp of the last change in status_date, the reason code in reason_code, and a description with more details:

JSON
"enrollment": {
"status": "not_accepted",
"status_date": "2026-08-03 10:16:43",
"reason_code": "NOT_ACCEPTED",
"description": "Not accepted"
}
reason_codeDescriptionDetails
NOT_ACCEPTEDNot acceptedOPay declined the request: do not honor, restricted or suspected fraud, transaction not permitted, invalid request, or limits and attempts exceeded. Do not retry automatically
INVALID_PARAMETERSRequest with invalid paramsOPay rejected the parameters sent in the request. Review the payload against the required fields and the amount limits of the agreement
PAYMENT_NOT_VALIDPayment method not supportedThe payment method sent is not supported for the recurring charge. Confirm that payment_type_code is opay-recurring and that the enrollment is active
DUPLICATE_TRANSACTIONDuplicated request referenceThe merchant_enrollment_code was already used. Generate a new, unique code and retry
TIMEOUTExternal service not available, try againOPay did not respond within the expected time. Query the payment status before retrying to avoid a double charge
PROVIDER_RESPONSE_ERRORProvider response errorOPay returned an internal or unexpected response. Retry later; if it persists, contact EBANX Support

OPay Recurring UX flow

The screens below illustrate the customer journey for each supported use case.

Enrollment only

Enable background charging on your platform without requiring customer re-authentication at every transaction: the customer authorizes the auto-debit once, and you trigger charges automatically within the agreed transaction limits.

Merchant checkout

The customer selects OPay as a payment method, reviews the service terms, debit priority, transaction limit and linked OPay account, and then activates the agreement. No payment is collected.

Payment authentication (PIN or OTP)

The customer confirms the agreement using the OPay payment PIN or a phone OTP.

Enrollment status (successful or failed)

Display the enrollment result, showing whether the agreement was activated or failed, and provide a clear return path to the merchant.

Enroll and charge (subscription)

Offer customers a recurring billing experience with automatic fixed-amount deductions from the OPay wallet. The customer authenticates once to authorize the recurring auto-debit, the subscription is activated, and you are notified of the successful payment.

Merchant checkout

The customer selects the subscription plan, chooses OPay, reviews the fixed recurring amount, and verifies the phone number linked to the wallet.

Auto-debit agreement

OPay displays the first payment amount, the fixed renewal amount, the next debit date and the wallet details before the customer selects Pay and Activate.

Payment authentication (PIN or OTP)

The customer selects the wallet balance and confirms the combined request with a PIN or a phone OTP.

Enroll and charge (variable on-file payments)

Accept OPay wallet payments with an express payment setup: the customer authenticates via PIN or OTP to complete the purchase, the order is confirmed, and the wallet is linked for faster future checkouts.

Merchant checkout

The customer selects OPay at checkout, reviews the current order amount and the variable payment agreement, and then chooses Pay and Activate.

Payment authentication (PIN or OTP)

The customer confirms the combined enrollment and first charge using the OPay PIN or a phone OTP.

Payment status and linked wallet

Show the activation and payment result. After a successful payment, display the linked OPay wallet as an available payment method for later purchases.

Customer notifications and agreement management

Successful auto-debit notification

After a successful debit, OPay can inform the customer through in-app messages, SMS, the transaction page and email.

Auto-debit management in the OPay app

Customers can review active agreements, inspect the debit terms, disable an agreement, and authenticate the change with their OPay PIN.

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: