Skip to content
Create account
or
Sign in
The Stripe Docs logo
/
Ask AI
Create account
Sign in
Get started
Payments
Revenue
Platforms and marketplaces
Money management
Developer resources
Overview
About Stripe payments
Upgrade your integration
Payments analytics
Online payments
OverviewFind your use caseManaged Payments
Use Payment Links
Build a checkout page
Build an advanced integration
Build an in-app integration
Payment methods
Add payment methods
    Overview
    Payment method integration options
    Manage default payment methods in the Dashboard
    Payment method types
    Cards
    Pay with Stripe balance
    Crypto
    Bank debits
    Bank redirects
    Bank transfers
    Credit transfers (Sources)
    Buy now, pay later
    Real-time payments
    Vouchers
      Boleto
      Konbini
      Multibanco
      OXXO
        Accept a payment
    Wallets
    Enable local payment methods by country
    Custom payment methods
Manage payment methods
Faster checkout with Link
Payment interfaces
Payment Links
Checkout
Web Elements
In-app Elements
Payment scenarios
Handle multiple currencies
Custom payment flows
Flexible acquiring
Orchestration
In-person payments
Terminal
Beyond payments
Incorporate your company
Crypto
Financial Connections
Climate
HomePaymentsAdd payment methodsVouchersOXXO

Accept an OXXO payment

Learn how to accept OXXO, a common payment method in Mexico.

Copy page

Caution

The content of this section refers to a Legacy product. You should use the Accept a payment guide for the most recent integration path instead. While Stripe still supports this product, this support might end if the product is deprecated.

Stripe users in Mexico can accept OXXO payments from customers in Mexico by using the Payment Intents and Payment Methods APIs. Customers pay by providing an OXXO voucher with a generated number and cash payment at an OXXO convenience store. Stripe notifies you when the payment is completed.

What you're building

Set up Stripe
Server-side

First, you need a Stripe account. Register now.

Use our official libraries for access to the Stripe API from your application:

Command Line
Ruby
# Available as a gem sudo gem install stripe
Gemfile
Ruby
# If you use bundler, you can add this line to your Gemfile gem 'stripe'

Create a PaymentIntent
Server-side

Stripe uses a PaymentIntent object to represent your intent to collect payment from a customer, tracking state changes from OXXO voucher creation to payment completion.

Create a PaymentIntent on your server with an amount and the mxn currency (OXXO does not support other currencies). If you already have an integration using the Payment Intents API, add oxxo to the list of payment method types for your PaymentIntent.

Command Line
curl
curl https://api.stripe.com/v1/payment_intents \ -u
sk_test_BQokikJOvBiI2HlWgH4olfQ2
:
\ -d "amount"=1099 \ -d "currency"="mxn" \ -d "payment_method_types[]"="oxxo"

Retrieve the client secret

The PaymentIntent includes a client secret that the client side uses to securely complete the payment process. You can use different approaches to pass the client secret to the client side.

Retrieve the client secret from an endpoint on your server, using the browser’s fetch function. This approach is best if your client side is a single-page application, particularly one built with a modern frontend framework like React. Create the server endpoint that serves the client secret:

main.rb
Ruby
get '/secret' do intent = # ... Create or retrieve the PaymentIntent {client_secret: intent.client_secret}.to_json end

And then fetch the client secret with JavaScript on the client side:

(async () => { const response = await fetch('/secret'); const {client_secret: clientSecret} = await response.json(); // Render the form using the clientSecret })();

Additional payment method options

You can specify an optional expires_after_days parameter in the payment method options for your PaymentIntent that sets the number of calendar days before an OXXO voucher expires. For example, if you create an OXXO voucher on Monday and you set expires_after_days to 2, the OXXO voucher will expire on Wednesday at 23:59 America/Mexico_City (UTC-6) time. The expires_after_days parameter can be set from 1 to 7 days. The default is 3 days.

Collect payment method details
Client-side

Create a payment form on your client to collect the required billing details from the customer:

FieldValue
nameThe full name (first and last) of the customer. The first name and last name must each be a minimum of two characters.
emailThe full email address of the customer.
checkout.html
<form id="payment-form"> <div class="form-row"> <label for="name"> Name </label> <input id="name" name="name" required> </div> <div class="form-row"> <label for="email"> Email </label> <input id="email" name="email" required> </div> <!-- Used to display form errors. --> <div id="error-message" role="alert"></div> <button id="submit-button">Pay with OXXO</button> </form>

Submit the payment to Stripe
Client-side

When a customer clicks to pay with OXXO, use Stripe.js to submit the payment to Stripe. Stripe.js is our foundational JavaScript library for building payment flows.

Include the Stripe.js script on your checkout page by adding it to the head of your HTML file.

checkout.html
<head> <title>Checkout</title> <script src="https://js.stripe.com/basil/stripe.js"></script> </head>

Create an instance of Stripe.js with the following JavaScript on your checkout page.

client.js
// Set your publishable key. Remember to switch to your live publishable key in production! // See your keys here: https://dashboard.stripe.com/apikeys const stripe = Stripe(
'pk_test_TYooMQauvdEDq54NiTphI7jx'
);

Use stripe.confirmOxxoPayment and the client secret of the PaymentIntent object that you created in Step 2 to submit the customer’s billing details.

Upon confirmation, Stripe will automatically open a modal to display the OXXO voucher to your customer.

client.js
const form = document.getElementById('payment-form'); form.addEventListener('submit', async (event) => { event.preventDefault(); const result = await stripe.confirmOxxoPayment( '{{PAYMENT_INTENT_CLIENT_SECRET}}', { payment_method: { billing_details: { name: document.getElementById('name').value, email: document.getElementById('email').value, }, }, }); // Stripe.js will open a modal to display the OXXO voucher to your customer // This async function finishes when the customer closes the modal if (result.error) { // Display error to your customer const errorMsg = document.getElementById('error-message'); errorMsg.innerText = result.error.message; } });

Note

stripe.confirmOxxoPayment may take several seconds to complete. During that time, disable your form from being resubmitted and show a waiting indicator like a spinner. If you receive an error, show it to the customer, re-enable the form, and hide the waiting indicator.

When an OXXO voucher is created successfully, the value of the returned PaymentIntent’s status property is requires_action. Check the status of a PaymentIntent in the Dashboard or by inspecting the status property on the object. If the OXXO voucher was not created successfully, inspect the returned error to determine the cause (for example, invalid email format).

Optional: Email voucher link to your customer

Stripe sends a payment_intent.requires_action event when an OXXO voucher is created successfully. If you need to email your customers the voucher link, you can retrieve the PaymentIntent to get the link upon receiving the event. The hosted_voucher_url field in payment_intent.next_action.oxxo_display_details contains the link to the voucher.

Optional: Customize your voucher

Stripe allows customization of customer-facing UIs on the Branding Settings page.

The following brand settings can be applied to the voucher:

  • Icon—your brand image and public business name
  • Accent color—used as the color of the Copy Number button
  • Brand color—used as the background color

Handle post-payment events
Server-side

OXXO is a delayed notification payment method, so funds are not immediately available. Customers might not pay for the OXXO voucher at an OXXO convenience store immediately after checking out.

Stripe sends a payment_intent.succeeded event on the next business day (Monday through Friday excluding Mexican holidays) for each OXXO voucher that was paid. Use the Dashboard or build a webhook handler to receive these events and run actions (for example, sending an order confirmation email to your customer, logging the sale in a database, or starting a shipping workflow).

After the expiry date, the PaymentIntent’s status transitions to processing and the customer can no longer pay for the expired OXXO voucher. If the OXXO voucher was not paid for before 23:59 America/Mexico_City (UTC-6) on the expiry date, Stripe sends a payment_intent.payment_failed event within 10 calendar days after the expiry date (in most cases, this event is sent within 7 calendar days). For example, if the OXXO voucher expires on September 1, this event is sent by September 10 at the latest.

EventDescriptionNext steps
payment_intent.requires_actionThe OXXO voucher is created successfully.Wait for the customer to pay for the OXXO voucher.
payment_intent.processingThe customer can no longer pay for the OXXO voucher.Wait for the payment to succeed or fail.
payment_intent.succeededThe customer paid for the OXXO voucher before expiration.Fulfill the goods or services that the customer purchased.
payment_intent.payment_failedThe customer did not pay for the OXXO voucher before expiration.Contact the customer through email or push notification and request another payment method.

Receive events and run business actions

Manually

Use the Stripe Dashboard to view all your Stripe payments, send email receipts, handle payouts, or retry failed payments.

  • View your test payments in the Dashboard

Custom Code

Build a webhook handler to listen for events and build custom asynchronous payment flows. Test and debug your webhook integration locally with the Stripe CLI.

  • Build a custom webhook

Test the integration

In a sandbox, set payment_method.billing_details.email to the following values when you call stripe.confirmOxxoPayment to test different scenarios.

EmailDescription

{any_prefix}@{any_domain}

Simulates an OXXO voucher which a customer pays after 3 minutes and the payment_intent.succeeded webhook arrives after about 3 minutes. In production, this webhook arrives after 1 business day.

Example: fulano@test.com

{any_prefix}succeed_immediately@{any_domain}

Simulates an OXXO voucher which a customer pays immediately and the payment_intent.succeeded webhook arrives within several seconds. In production, this webhook arrives after 1 business day.

Example: succeed_immediately@test.com

{any_prefix}expire_immediately@{any_domain}

Simulates an OXXO voucher which expires before a customer pays and the payment_intent.payment_failed webhook arrives within several seconds.

The expires_after field in next_action.oxxo_display_details is set to the current time regardless of what the expires_after_days parameter in payment method options is set to.

Example: expire_immediately@test.com

{any_prefix}expire_with_delay@{any_domain}

Simulates an OXXO voucher which expires before a customer pays and the payment_intent.payment_failed webhook arrives after about 3 minutes.

The expires_after field in next_action.oxxo_display_details is set to 3 minutes in the future regardless of what the expires_after_days parameter in payment method options is set to.

Example: expire_with_delay@test.com

{any_prefix}fill_never@{any_domain}

Simulates an OXXO voucher which expires before a customer pays and the payment_intent.payment_failed webhook arrives after 1 business day and 2 calendar days. In production, this webhook arrives at the same time as in testmode.

Example: fill_never@test.com

OptionalDisplay the OXXO details to your customer
Client-side

OptionalSend payment instruction emails

Expiration and cancellation

OXXO vouchers expire after the expires_after UNIX timestamp and a customer can’t pay an OXXO voucher once it has expired. OXXO vouchers can’t be canceled before expiration.

After an OXXO voucher expires, the PaymentIntent’s status changes to requires_payment_method. At this point, you can confirm the PaymentIntent with another payment method or cancel.

Was this page helpful?
YesNo
Need help? Contact Support.
Join our early access program.
Check out our changelog.
Questions? Contact Sales.
LLM? Read llms.txt.
Powered by Markdoc