# Accept an OXXO payment Learn how to accept OXXO, a common payment method in Mexico. # Direct API > The content of this section refers to a *Legacy* (Technology that's no longer recommended) product. You should use the [Accept a payment](https://docs.stripe.com/payments/accept-a-payment.md) 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* (Customer objects represent customers of your business. They let you reuse payment methods and give you the ability to track multiple payments) 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. ## Set up Stripe [Server-side] First, you need a Stripe account. [Register now](https://dashboard.stripe.com/test/register). Use our official libraries for access to the Stripe API from your application: #### Ruby ```bash # Available as a gem sudo gem install stripe ``` ```ruby # If you use bundler, you can add this line to your Gemfile gem 'stripe' ``` ## Create a PaymentIntent [Server-side] Stripe uses a [PaymentIntent](https://docs.stripe.com/api/payment_intents.md) 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 doesn’t support other currencies). If you already have an integration using the [Payment Intents API](https://docs.stripe.com/payments/payment-intents.md), add `oxxo` to the list of [payment method types](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-payment_method_types) for your `PaymentIntent`. #### curl ```bash curl https://api.stripe.com/v1/payment_intents \ -u <>: \ -d "amount"=1099 \ -d "currency"="mxn" \ -d "payment_method_types[]"="oxxo" ``` ### Retrieve the client secret The PaymentIntent includes a *client secret* (The client secret is a unique key returned from Stripe as part of a PaymentIntent. This key lets the client access important fields from the PaymentIntent (status, amount, currency) while hiding sensitive ones (metadata, customer)) 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. #### Single-page application 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: #### Ruby ```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: ```javascript (async () => { const response = await fetch('/secret'); const {client_secret: clientSecret} = await response.json(); // Render the form using the clientSecret })(); ``` #### Server-side rendering Pass the client secret to the client from your server. This approach works best if your application generates static content on the server before sending it to the browser. Add the [client_secret](https://docs.stripe.com/api/payment_intents/object.md#payment_intent_object-client_secret) in your checkout form. In your server-side code, retrieve the client secret from the PaymentIntent: #### Ruby ```erb
``` ```ruby get '/checkout' do @intent = # ... Fetch or create the PaymentIntent erb :checkout end ``` ### Additional payment method options You can specify an optional `expires_after_days` parameter in the [payment method options](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-payment_method_options-oxxo-expires_after_days) 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: | Field | Value | | ------- | ---------------------------------------------------------------------------------------------------------------------- | | `name` | The full name (first and last) of the customer. The first name and last name must each be a minimum of two characters. | | `email` | The full email address of the customer. | ```html
``` ## 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](https://docs.stripe.com/payments/elements.md) 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. ```html Checkout ``` Create an instance of Stripe.js with the following JavaScript on your checkout page. ```javascript // 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('<>'); ``` Use [stripe.confirmOxxoPayment](https://docs.stripe.com/js/payment_intents/confirm_oxxo_payment) and the [client secret](https://docs.stripe.com/api/payment_intents/object.md#payment_intent_object-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. ```javascript 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; } }); ``` > `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](https://dashboard.stripe.com/test/payments) or by inspecting the status property on the object. If the OXXO voucher wasn’t 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](https://docs.stripe.com/api/events/types.md#event_types-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](https://docs.stripe.com/api/payment_intents/retrieve.md) to get the link upon receiving the event. The `hosted_voucher_url` field in [payment_intent.next_action.oxxo_display_details](https://docs.stripe.com/api/payment_intents/object.md#payment_intent_object-next_action-oxxo_display_details-hosted_voucher_url) contains the link to the voucher. ### Optional: Customize your voucher Stripe allows customization of customer-facing UIs on the [Branding Settings](https://dashboard.stripe.com/account/branding) 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](https://docs.stripe.com/payments/payment-methods.md#payment-notification) payment method, so funds aren’t immediately available. *Customers* (Customer objects represent customers of your business. They let you reuse payment methods and give you the ability to track multiple payments) might not pay for the OXXO voucher at an OXXO convenience store immediately after checking out. Stripe sends a [payment_intent.succeeded](https://docs.stripe.com/api/events/types.md#event_types-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* (A webhook is a real-time push notification sent to your application as a JSON payload through HTTPS requests) 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 wasn’t paid for before 23:59 America/Mexico_City (UTC-6) on the expiry date, Stripe sends a [payment_intent.payment_failed](https://docs.stripe.com/api/events/types.md#event_types-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. | Event | Description | Next steps | | -------------------------------- | --------------------------------------------------------------- | ------------------------------------------------------------------------------------------- | | `payment_intent.requires_action` | The OXXO voucher is created successfully. | Wait for the customer to pay for the OXXO voucher. | | `payment_intent.processing` | The customer can no longer pay for the OXXO voucher. | Wait for the payment to succeed or fail. | | `payment_intent.succeeded` | The customer paid for the OXXO voucher before expiration. | Fulfill the goods or services that the customer purchased. | | `payment_intent.payment_failed` | The customer didn’t 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](https://dashboard.stripe.com/test/payments) #### 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](https://docs.stripe.com/webhooks/handling-payment-events.md#build-your-own-webhook) ## Test the integration In a sandbox, set `payment_method.billing_details.email` to the following values when you call [stripe.confirmOxxoPayment](https://docs.stripe.com/js/payment_intents/confirm_oxxo_payment) to test different scenarios. | Email | Description | | ---------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `{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](https://docs.stripe.com/api/payment_intents/object.md#payment_intent_object-next_action-oxxo_display_details-expires_after) is set to the current time regardless of what the `expires_after_days` parameter in [payment method options](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-payment_method_options-oxxo-expires_after_days) 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](https://docs.stripe.com/api/payment_intents/object.md#payment_intent_object-next_action-oxxo_display_details-expires_after) is set to 3 minutes in the future regardless of what the `expires_after_days` parameter in [payment method options](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-payment_method_options-oxxo-expires_after_days) 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 | ## Optional: Display the OXXO details to your customer [Client-side] We recommend relying on Stripe.js to handle displaying the OXXO voucher with `confirmOxxoPayment`. However, you can also manually display the voucher to your customers. You can specify `handleActions: false` when calling `stripe.confirmOxxoPayment` in step 4 to indicate that you’ll handle the next action to display the OXXO details to your customer. ```javascript 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, }, }, }, {handleActions: false}, ); if (result.error) { // Display error to your customer const errorMsg = document.getElementById('error-message'); errorMsg.innerText = result.error.message; } else { // An OXXO voucher was successfully created const amount = result.paymentIntent.amount; const currency = result.paymentIntent.currency; const details = result.paymentIntent.next_action.oxxo_display_details; const number = details.number; const expires_after = details.expires_after; // Handle the next action by displaying the OXXO details to your customer // You can also use the generated hosted voucher const hosted_voucher_url = result.paymentIntent.next_action.oxxo_display_details.hosted_voucher_url; } }); ``` Include, at minimum, the following: | Detail | Description | | -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | OXXO logo | [Download](https://stripe.com/img/docs/payments/oxxo.png) and display the OXXO logo on the voucher. oxxo | | Number | Locate the number on the `PaymentIntent` object at [next_action.oxxo_display_details.number](https://docs.stripe.com/api/payment_intents/object.md#payment_intent_object-next_action-oxxo_display_details-number). | | Expiry date | Locate the UNIX timestamp after which the OXXO voucher expires on the `PaymentIntent` at [next_action.oxxo_display_details.expires_after](https://docs.stripe.com/api/payment_intents/object.md#payment_intent_object-next_action-oxxo_display_details-expires_after). | | Amount | The amount to be collected. | | Currency | OXXO vouchers are always in Mexican pesos. | | Barcode | Generate the barcode from the number using [Code 128](https://en.wikipedia.org/wiki/Code_128). The barcode should be approximately 7.5 cm wide when printed. For mobile displays, make sure the barcode can be zoomed in. This will help the OXXO convenience store cashier scan the barcode. You can use an external library such as [JSBarcode](https://lindell.me/JsBarcode/). | | Payment instructions | The payment instructions for the customer. See the English and Spanish translations below. | ### OXXO payment instructions #### English OXXO payment instructions: 1. Give the voucher to the cashier to scan the barcode. 1. Provide cash payment to the cashier. 1. After the payment is complete, keep the receipt of your payment for your records. 1. For any questions or clarification, please contact the business. #### HTML ```html
MX
Expires
Instructions to pay your OXXO:
  1. Give the voucher to the cashier to scan the barcode.

  2. Provide cash payment to the cashier.

  3. After the payment is complete, keep the receipt of your payment for your records.

  4. For any questions or clarification, please contact the merchant.

``` #### Spanish Instrucciones de pago en OXXO: 1. Entregue el código al cajero en cualquier OXXO para que lo escanee. 1. Proporcione el pago en efectivo al cajero. 1. Una vez completado el pago, guarde el recibo de su pago para sus archivos. 1. Para cualquier duda o aclaración, por favor contacte al comerciante. #### HTML ```html
MX
Expira el
Instrucciones de pago en OXXO:
  1. Entregue el código al cajero en cualquier OXXO para que lo escanee.

  2. Proporcione el pago en efectivo al cajero.

  3. Una vez completado el pago, guarde el recibo de su pago para sus archivos.

  4. Para cualquier duda o aclaración, por favor contacte al comerciante.

``` *Customers* (Customer objects represent customers of your business. They let you reuse payment methods and give you the ability to track multiple payments) will typically print out the OXXO voucher to bring to an OXXO convenience store. You can provide an easy print button and/or email the OXXO voucher to the customer. Try printing the OXXO voucher yourself to check the barcode size (which should be approximately 7.5 cm wide). ## Optional: Send payment instruction emails You can enable OXXO payment instruction emails on the [Email Settings](https://dashboard.stripe.com/settings/emails) page in the Dashboard. Once enabled, Stripe sends payment instruction emails upon PaymentIntent confirmation. The emails contain the OXXO number and a link to the Stripe hosted voucher page. > In testing environments, instruction emails are only sent to email addresses linked to the Stripe account. ## 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.