Payments
Vouchers
OXXO
Accept a payment

OXXO payments

Use the Payment Intents and Payment Methods APIs to accept OXXO, a common payment method in Mexico.

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

1 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:

# Available as a gem gem install stripe
# If you use bundler, you can add this line to your Gemfile gem 'stripe'
# Install through pip pip install --upgrade stripe
# Or find the Stripe package on http://pypi.python.org/pypi/stripe/
# Find the version you want to pin: # https://github.com/stripe/stripe-python/blob/master/CHANGELOG.md # Specify that version in your requirements.txt file stripe>=2.48.0,<3.0
# Install the PHP library via Composer composer require stripe/stripe-php
# Or download the source directly: https://github.com/stripe/stripe-php/releases
/* For Gradle, add the following dependency to your build.gradle and replace {VERSION} with the version number you want to use from - https://mvnrepository.com/artifact/com.stripe/stripe-java or - https://github.com/stripe/stripe-java/releases/latest */ implementation "com.stripe:stripe-java:{VERSION}"
<!-- For Maven, add the following dependency to your POM and replace {VERSION} with the version number you want to use from - https://mvnrepository.com/artifact/com.stripe/stripe-java or - https://github.com/stripe/stripe-java/releases/latest --> <dependency> <groupId>com.stripe</groupId> <artifactId>stripe-java</artifactId> <version>{VERSION}</version> </dependency>
# For other environments, manually install the following JARs: # - The Stripe JAR from https://github.com/stripe/stripe-java/releases/latest # - Google Gson from https://github.com/google/gson
# Install via npm npm install --save stripe
# Make sure your project is using Go Modules go mod init # Install stripe-go go get -u github.com/stripe/stripe-go/v71
// Then import the package import ( "github.com/stripe/stripe-go/v71" )
# Install via dotnet dotnet add package Stripe.net dotnet restore
# Or install via NuGet PM> Install-Package Stripe.net

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

curl https://api.stripe.com/v1/payment_intents \ -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \ -d amount=1099 \ -d currency=mxn \ -d "payment_method_types[]"=oxxo
# Set your secret key. Remember to switch to your live secret key in production! # See your keys here: https://dashboard.stripe.com/account/apikeys Stripe.api_key = 'sk_test_4eC39HqLyjWDarjtT1zdp7dc' intent = Stripe::PaymentIntent.create( amount: 1099, currency: 'mxn', payment_method_types: ['oxxo'] ) # Send the intent.client_secret back to the client so you can use it in later steps.
# Set your secret key. Remember to switch to your live secret key in production! # See your keys here: https://dashboard.stripe.com/account/apikeys stripe.api_key = 'sk_test_4eC39HqLyjWDarjtT1zdp7dc' intent = stripe.PaymentIntent.create( amount=1099, currency='mxn', payment_method_types=['oxxo'] ) # Send the intent.client_secret back to the client so you can use it in later steps.
// Set your secret key. Remember to switch to your live secret key in production! // See your keys here: https://dashboard.stripe.com/account/apikeys \Stripe\Stripe::setApiKey('sk_test_4eC39HqLyjWDarjtT1zdp7dc'); $intent = \Stripe\PaymentIntent::create([ "amount" => 1099, "currency" => "mxn", "payment_method_types" => ["oxxo"] ]); // Send the intent.client_secret back to the client so you can use it in later steps.
// Set your secret key. Remember to switch to your live secret key in production! // See your keys here: https://dashboard.stripe.com/account/apikeys Stripe.apiKey = "sk_test_4eC39HqLyjWDarjtT1zdp7dc"; PaymentIntentCreateParams params = PaymentIntentCreateParams.builder() .setAmount(1099L) .setCurrency("mxn") .addPaymentMethodType("oxxo") .build(); PaymentIntent paymentIntent = PaymentIntent.create(params); // Send the paymentIntent.client_secret back to the client so you can use it in later steps.
// Set your secret key. Remember to switch to your live secret key in production! // See your keys here: https://dashboard.stripe.com/account/apikeys const stripe = require('stripe')('sk_test_4eC39HqLyjWDarjtT1zdp7dc'); const paymentIntent = await stripe.paymentIntents.create({ amount: 1099, currency: 'mxn', payment_method_types: ['oxxo'], }); // Send the paymentIntent.client_secret back to the client so you can use it in later steps.
// Set your secret key. Remember to switch to your live secret key in production! // See your keys here: https://dashboard.stripe.com/account/apikeys stripe.Key = "sk_test_4eC39HqLyjWDarjtT1zdp7dc" params := &stripe.PaymentIntentParams{ Amount: stripe.Int64(1099), Currency: stripe.String(string(stripe.CurrencyMXN)), PaymentMethodTypes: stripe.StringSlice([]string{ "oxxo", }), } pi, _ := paymentintent.New(params) // Send the pi.client_secret back to the client so you can use it in later steps.
// Set your secret key. Remember to switch to your live secret key in production! // See your keys here: https://dashboard.stripe.com/account/apikeys StripeConfiguration.ApiKey = "sk_test_4eC39HqLyjWDarjtT1zdp7dc"; var options = new PaymentIntentCreateOptions { Amount = 1099, Currency = "mxn", PaymentMethodTypes = new List<string> { "oxxo", }, }; var service = new PaymentIntentService(); var intent = service.Create(options); // Send the intent.client_secret back to the client so you can use it in later steps.

Included in the returned PaymentIntent is a client secret, which is used on the client side to securely complete the payment process instead of passing the entire PaymentIntent object. There are different approaches that you can use to pass the client secret to the client side.

You can retrieve the client secret from an endpoint on your server using the browser’s fetch function on the client side. This approach is generally most suitable when your client side is a single-page application, particularly one built with a modern frontend framework such as React. This example shows how to create the server endpoint that serves the client secret:

get '/secret' do intent = # ... Create or retrieve the PaymentIntent {client_secret: intent.client_secret}.to_json end
from flask import Flask, jsonify app = Flask(__name__) @app.route('/secret') def secret(): intent = # ... Create or retrieve the PaymentIntent return jsonify(client_secret=intent.client_secret)
<?php $intent = # ... Create or retrieve the PaymentIntent echo json_encode(array('client_secret' => $intent->client_secret)); ?>
import java.util.HashMap; import java.util.Map; import com.stripe.model.PaymentIntent; import com.google.gson.Gson; import static spark.Spark.get; public class StripeJavaQuickStart { public static void main(String[] args) { Gson gson = new Gson(); get("/secret", (request, response) -> { PaymentIntent intent = // ... Fetch or create the PaymentIntent Map<String, String> map = new HashMap(); map.put("client_secret", intent.getClientSecret()); return map; }, gson::toJson); } }
const express = require('express'); const app = express(); app.get('/secret', async (req, res) => { const intent = // ... Fetch or create the PaymentIntent res.json({client_secret: intent.client_secret}); }); app.listen(3000, () => { console.log('Running on port 3000'); });
package main import ( "encoding/json" "net/http" stripe "github.com/stripe/stripe-go/v71" ) type CheckoutData struct { ClientSecret string `json:"client_secret"` } func main() { http.HandleFunc("/secret", func(w http.ResponseWriter, r *http.Request) { intent := // ... Fetch or create the PaymentIntent data := CheckoutData{ ClientSecret: intent.ClientSecret, } w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) json.NewEncoder(w).Encode(data) }) http.ListenAndServe(":3000", nil) }
using System; using Microsoft.AspNetCore.Mvc; using Stripe; namespace StripeExampleApi.Controllers { [Route("secret")] [ApiController] public class CheckoutApiController : Controller { [HttpGet] public ActionResult Get() { var intent = // ... Fetch or create the PaymentIntent return Json(new {client_secret = intent.ClientSecret}); } } }

This example demonstrates how to fetch the client secret with JavaScript on the client side:

var response = fetch('/secret').then(function(response) { return response.json(); }).then(function(responseJson) { var clientSecret = responseJson.client_secret; // Call stripe.confirmOxxoPayment() with the client secret. });
(async () => { const response = await fetch('/secret'); const {client_secret: clientSecret} = await response.json(); // Call stripe.confirmOxxoPayment() with the client secret. })();

If your application uses server-side rendering, you may wish to use your template framework to embed the client secret in the HTML output of your checkout page during rendering. You can embed it in a data attribute or hidden HTML element and then extract it with JavaScript in order to use it to complete payment.

<input id="card-name" type="text"> <!-- placeholder for Elements --> <div id="card-element"></div> <button id="card-button" data-secret="<%= @intent.client_secret %>">Submit Payment</button>
get '/checkout' do @intent = # ... Fetch or create the PaymentIntent erb :checkout end
<input id="card-name" type="text"> <!-- placeholder for Elements --> <div id="card-element"></div> <button id="card-button" data-secret="{{ client_secret }}"> Submit Payment </button>
@app.route('/checkout') def checkout(): intent = # ... Fetch or create the PaymentIntent return render_template('checkout.html', client_secret=intent.client_secret)
<?php $intent = # ... Fetch or create the PaymentIntent; ?> ... <input id="card-name" type="text"> <!-- placeholder for Elements --> <div id="card-element"></div> <button id="card-button" data-secret="<?= $intent->client_secret ?>"> Submit Payment </button> ...
<input id="card-name" type="text"> <!-- placeholder for Elements --> <div id="card-element"></div> <button id="card-button" data-secret="{{ client_secret }}"> Submit Payment </button>
import java.util.HashMap; import java.util.Map; import com.stripe.model.PaymentIntent; import spark.ModelAndView; import static spark.Spark.get; public class StripeJavaQuickStart { public static void main(String[] args) { get("/checkout", (request, response) -> { PaymentIntent intent = // ... Fetch or create the PaymentIntent Map map = new HashMap(); map.put("client_secret", intent.getClientSecret()); return new ModelAndView(map, "checkout.hbs"); }, new HandlebarsTemplateEngine()); } }
<input id="card-name" type="text"> <!-- placeholder for Elements --> <div id="card-element"></div> <button id="card-button" data-secret="{{ client_secret }}"> Submit Payment </button>
const express = require('express'); const expressHandlebars = require('express-handlebars'); const app = express(); app.engine('.hbs', expressHandlebars({ extname: '.hbs' })); app.set('view engine', '.hbs'); app.set('views', './views'); app.get('/checkout', async (req, res) => { const intent = // ... Fetch or create the PaymentIntent res.render('checkout', { client_secret: intent.client_secret }); }); app.listen(3000, () => { console.log('Running on port 3000'); });
<input id="card-name" type="text"> <!-- placeholder for Elements --> <div id="card-element"></div> <button id="card-button" data-secret="{{ .ClientSecret }}"> Submit Payment </button>
package main import ( "html/template" "net/http" stripe "github.com/stripe/stripe-go/v71" ) type CheckoutData struct { ClientSecret string } func main() { checkoutTmpl := template.Must(template.ParseFiles("views/checkout.html")) http.HandleFunc("/checkout", func(w http.ResponseWriter, r *http.Request) { intent := // ... Fetch or create the PaymentIntent data := CheckoutData{ ClientSecret: intent.ClientSecret, } checkoutTmpl.Execute(w, data) }) http.ListenAndServe(":3000", nil) }
<input id="card-name" type="text"> <!-- placeholder for Elements --> <div id="card-element"></div> <button id="card-button" data-secret="@ViewData["ClientSecret"]"> Submit Payment </button>
using System; using Microsoft.AspNetCore.Mvc; using Stripe; namespace StripeExampleApi.Controllers { [Route("/[controller]")] public class CheckoutController : Controller { public IActionResult Index() { var intent = // ... Fetch or create the PaymentIntent ViewData["ClientSecret"] = intent.ClientSecret; return View(); } } }

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.

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

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

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

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

// Set your publishable key. Remember to switch to your live publishable key in production! // See your keys here: https://dashboard.stripe.com/account/apikeys var stripe = Stripe('pk_test_TYooMQauvdEDq54NiTphI7jx');
// Set your publishable key. Remember to switch to your live publishable key in production! // See your keys here: https://dashboard.stripe.com/account/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.

var form = document.getElementById('payment-form'); form.addEventListener('submit', function(event) { event.preventDefault(); 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 .then(function(result) { // This promise resolves when the customer closes the modal if (result.error) { // Display error to your customer var errorMsg = document.getElementById('error-message'); errorMsg.innerText = result.error.message; } }); });
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; } });

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 (e.g., invalid email format).

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. You can select which language to use for the voucher on the customer emails settings page.

The following brand settings can be applied to the voucher:

  • Icon—your brand image and public business name
  • Logo—your brand image
  • Accent color—used as the color of the print button
  • Language—used to localize the voucher

5 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 (e.g., 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.

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 did not pay for the OXXO voucher before expiration. Contact the customer via 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.

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.

6 Test the integration

In test mode, set payment_method.billing_details.email to the following values when you call stripe.confirmOxxoPayment 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 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

Expiration and cancellation

OXXO vouchers expire after the expires_after UNIX timestamp and a customer cannot pay an OXXO voucher once it has expired. OXXO vouchers cannot 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.

Accepting OXXO in your app consists of displaying a webview to show the OXXO voucher. 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.

1 Set up Stripe Server-side Client-side

First, you need a Stripe account. Register now.

Server-side

This integration requires endpoints on your server that talk to the Stripe API. Use our official libraries for access to the Stripe API from your server:

# Available as a gem gem install stripe
# If you use bundler, you can add this line to your Gemfile gem 'stripe'
# Install through pip pip install --upgrade stripe
# Or find the Stripe package on http://pypi.python.org/pypi/stripe/
# Find the version you want to pin: # https://github.com/stripe/stripe-python/blob/master/CHANGELOG.md # Specify that version in your requirements.txt file stripe>=2.48.0,<3.0
# Install the PHP library via Composer composer require stripe/stripe-php
# Or download the source directly: https://github.com/stripe/stripe-php/releases
/* For Gradle, add the following dependency to your build.gradle and replace {VERSION} with the version number you want to use from - https://mvnrepository.com/artifact/com.stripe/stripe-java or - https://github.com/stripe/stripe-java/releases/latest */ implementation "com.stripe:stripe-java:{VERSION}"
<!-- For Maven, add the following dependency to your POM and replace {VERSION} with the version number you want to use from - https://mvnrepository.com/artifact/com.stripe/stripe-java or - https://github.com/stripe/stripe-java/releases/latest --> <dependency> <groupId>com.stripe</groupId> <artifactId>stripe-java</artifactId> <version>{VERSION}</version> </dependency>
# For other environments, manually install the following JARs: # - The Stripe JAR from https://github.com/stripe/stripe-java/releases/latest # - Google Gson from https://github.com/google/gson
# Install via npm npm install --save stripe
# Make sure your project is using Go Modules go mod init # Install stripe-go go get -u github.com/stripe/stripe-go/v71
// Then import the package import ( "github.com/stripe/stripe-go/v71" )
# Install via dotnet dotnet add package Stripe.net dotnet restore
# Or install via NuGet PM> Install-Package Stripe.net

Client-side

The iOS SDK is open source, fully documented, and compatible with apps supporting iOS 11 or above.

  1. If you haven't already, install the latest version of CocoaPods.
  2. If you don't have an existing Podfile, run the following command to create one:
    pod init
  3. Add this line to your Podfile:
    pod 'Stripe'
  4. Run the following command:
    pod install
  5. Don't forget to use the .xcworkspace file to open your project in Xcode, instead of the .xcodeproj file, from here on out.
  6. In the future, to update to the latest version of the SDK, just run:
    pod update Stripe
  1. If you haven't already, install the latest version of Carthage.
  2. Add this line to your Cartfile:
    github "stripe/stripe-ios"
  3. Follow the Carthage installation instructions.
  4. In the future, to update to the latest version of the SDK, run the following command:
    carthage update stripe-ios --platform ios
  1. Head to our GitHub releases page and download and unzip Stripe.framework.zip.
  2. Drag Stripe.framework to the "Embedded Binaries" section of your Xcode project's "General" settings. Make sure to select "Copy items if needed".
  3. Head to the "Build Phases" section of your Xcode project settings, and create a new "Run Script Build Phase". Paste the following snippet into the text field:
    bash "${BUILT_PRODUCTS_DIR}/${FRAMEWORKS_FOLDER_PATH}/Stripe.framework/integrate-dynamic-framework.sh"
  4. In the future, to update to the latest version of our SDK, just repeat steps 1 and 2.

When your app starts, configure the SDK with your Stripe publishable key so that it can make requests to the Stripe API.

import UIKit import Stripe @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { StripeAPI.defaultPublishableKey = "pk_test_TYooMQauvdEDq54NiTphI7jx" // do any other necessary launch configuration return true } }
#import "AppDelegate.h" @import Stripe; @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { [StripeAPI setDefaultPublishableKey:@"pk_test_TYooMQauvdEDq54NiTphI7jx"]; // do any other necessary launch configuration return YES; } @end

2 Create a PaymentIntent Server-side Client-side

A PaymentIntent is an object that represents your intent to collect payment from a customer and tracks the lifecycle of the payment process through each stage.

Server-side

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.

curl https://api.stripe.com/v1/payment_intents \ -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \ -d amount=1099 \ -d currency=mxn \ -d "payment_method_types[]"=oxxo
# Set your secret key. Remember to switch to your live secret key in production! # See your keys here: https://dashboard.stripe.com/account/apikeys Stripe.api_key = 'sk_test_4eC39HqLyjWDarjtT1zdp7dc' intent = Stripe::PaymentIntent.create( amount: 1099, currency: 'mxn', payment_method_types: ['oxxo'] ) # Send the intent.client_secret back to the client so you can use it in later steps.
# Set your secret key. Remember to switch to your live secret key in production! # See your keys here: https://dashboard.stripe.com/account/apikeys stripe.api_key = 'sk_test_4eC39HqLyjWDarjtT1zdp7dc' intent = stripe.PaymentIntent.create( amount=1099, currency='mxn', payment_method_types=['oxxo'] ) # Send the intent.client_secret back to the client so you can use it in later steps.
// Set your secret key. Remember to switch to your live secret key in production! // See your keys here: https://dashboard.stripe.com/account/apikeys \Stripe\Stripe::setApiKey('sk_test_4eC39HqLyjWDarjtT1zdp7dc'); $intent = \Stripe\PaymentIntent::create([ "amount" => 1099, "currency" => "mxn", "payment_method_types" => ["oxxo"] ]); // Send the intent.client_secret back to the client so you can use it in later steps.
// Set your secret key. Remember to switch to your live secret key in production! // See your keys here: https://dashboard.stripe.com/account/apikeys Stripe.apiKey = "sk_test_4eC39HqLyjWDarjtT1zdp7dc"; PaymentIntentCreateParams params = PaymentIntentCreateParams.builder() .setAmount(1099L) .setCurrency("mxn") .addPaymentMethodType("oxxo") .build(); PaymentIntent paymentIntent = PaymentIntent.create(params); // Send the paymentIntent.client_secret back to the client so you can use it in later steps.
// Set your secret key. Remember to switch to your live secret key in production! // See your keys here: https://dashboard.stripe.com/account/apikeys const stripe = require('stripe')('sk_test_4eC39HqLyjWDarjtT1zdp7dc'); const paymentIntent = await stripe.paymentIntents.create({ amount: 1099, currency: 'mxn', payment_method_types: ['oxxo'], }); // Send the paymentIntent.client_secret back to the client so you can use it in later steps.
// Set your secret key. Remember to switch to your live secret key in production! // See your keys here: https://dashboard.stripe.com/account/apikeys stripe.Key = "sk_test_4eC39HqLyjWDarjtT1zdp7dc" params := &stripe.PaymentIntentParams{ Amount: stripe.Int64(1099), Currency: stripe.String(string(stripe.CurrencyMXN)), PaymentMethodTypes: stripe.StringSlice([]string{ "oxxo", }), } pi, _ := paymentintent.New(params) // Send the pi.client_secret back to the client so you can use it in later steps.
// Set your secret key. Remember to switch to your live secret key in production! // See your keys here: https://dashboard.stripe.com/account/apikeys StripeConfiguration.ApiKey = "sk_test_4eC39HqLyjWDarjtT1zdp7dc"; var options = new PaymentIntentCreateOptions { Amount = 1099, Currency = "mxn", PaymentMethodTypes = new List<string> { "oxxo", }, }; var service = new PaymentIntentService(); var intent = service.Create(options); // Send the intent.client_secret back to the client so you can use it in later steps.

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.

Client-side

On the client, request a PaymentIntent from your server and store its client secret.

class CheckoutViewController: UIViewController { var paymentIntentClientSecret: String? func startCheckout() { // Request a PaymentIntent from your server and store its client secret } }
@interface CheckoutViewController () @property (strong) NSString *paymentIntentClientSecret; @end @implementation CheckoutViewController - (void)startCheckout { // Request a PaymentIntent from your server and store its client secret } @end

3 Collect payment method details Client-side

In your app, collect the following required billing details from the customer. Create a STPPaymentIntentParams with the billing details.

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.
let billingDetails = STPPaymentMethodBillingDetails() billingDetails.name = "Jane Doe" billingDetails.email = "test@example.com"
STPPaymentMethodBillingDetails *billingDetails = [[STPPaymentMethodBillingDetails alloc] init]; billingDetails.name = @"Jane Doe"; billingDetails.email = @"test@example.com"

4 Submit the payment to Stripe Client-side

Retrieve the client secret from the PaymentIntent you created in step 2 and call STPPaymentHandler confirmPayment. This presents a webview to display the OXXO voucher. Upon completion, the completion block is called with the result of the payment.

let paymentIntentParams = STPPaymentIntentParams(clientSecret: paymentIntentClientSecret) let oxxoParams = STPPaymentMethodOXXOParams(); paymentIntentParams.paymentMethodParams = STPPaymentMethodParams( oxxo: oxxoParams, billingDetails: billingDetails, metadata: nil ) STPPaymentHandler.shared().confirmPayment(withParams: paymentIntentParams, authenticationContext: self) { (handlerStatus, paymentIntent, error) in switch handlerStatus { case .succeeded: // The OXXO voucher was displayed successfully. The customer can now pay the OXXO voucher at the OXXO convenience store. case .canceled: // Payment was cancelled case .failed: // Payment failed @unknown default: fatalError() } }
STPPaymentIntentParams *paymentIntentParams = [[STPPaymentIntentParams alloc] initWithClientSecret:self.paymentIntentClientSecret]; STPPaymentMethodOXXOParams *oxxoParams = [[STPPaymentMethodOXXOParams alloc] init]; paymentIntentParams.paymentMethodParams = [STPPaymentMethodParams paramsWithOXXO:oxxoParams billingDetails:billingDetails metadata:nil]; [[STPPaymentHandler sharedHandler] confirmPayment:paymentIntentParams withAuthenticationContext:self completion:^(STPPaymentHandlerActionStatus handlerStatus, STPPaymentIntent * handledIntent, NSError * _Nullable handlerError) { switch (handlerStatus) { case STPPaymentHandlerActionStatusSucceeded: // The OXXO voucher was displayed successfully. The customer can now pay the OXXO voucher at the OXXO convenience store. break; case STPPaymentHandlerActionStatusCanceled: // Payment was cancelled break; case STPPaymentHandlerActionStatusFailed: // Payment failed break; } }];

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. You can select which language to use for the voucher on the customer emails settings page.

The following brand settings can be applied to the voucher:

  • Icon—your brand image and public business name
  • Logo—your brand image
  • Accent color—used as the color of the print button
  • Language—used to localize the voucher

5 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 (e.g., 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.

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 did not pay for the OXXO voucher before expiration. Contact the customer via 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.

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.

6 Test the integration

In test mode, set STPPaymentMethodBillingDetails email to the following values when you call STPPaymentHandler confirmPayment 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 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

Expiration and cancellation

OXXO vouchers expire after the expires_after UNIX timestamp and a customer cannot pay an OXXO voucher once it has expired. OXXO vouchers cannot 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.

Accepting OXXO in your app consists of displaying a webview to show the OXXO voucher. 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.

1 Set up Stripe Server-side Client-side

First, you need a Stripe account. Register now.

Server-side

This integration requires endpoints on your server that talk to the Stripe API. Use our official libraries for access to the Stripe API from your server:

# Available as a gem gem install stripe
# If you use bundler, you can add this line to your Gemfile gem 'stripe'
# Install through pip pip install --upgrade stripe
# Or find the Stripe package on http://pypi.python.org/pypi/stripe/
# Find the version you want to pin: # https://github.com/stripe/stripe-python/blob/master/CHANGELOG.md # Specify that version in your requirements.txt file stripe>=2.48.0,<3.0
# Install the PHP library via Composer composer require stripe/stripe-php
# Or download the source directly: https://github.com/stripe/stripe-php/releases
/* For Gradle, add the following dependency to your build.gradle and replace {VERSION} with the version number you want to use from - https://mvnrepository.com/artifact/com.stripe/stripe-java or - https://github.com/stripe/stripe-java/releases/latest */ implementation "com.stripe:stripe-java:{VERSION}"
<!-- For Maven, add the following dependency to your POM and replace {VERSION} with the version number you want to use from - https://mvnrepository.com/artifact/com.stripe/stripe-java or - https://github.com/stripe/stripe-java/releases/latest --> <dependency> <groupId>com.stripe</groupId> <artifactId>stripe-java</artifactId> <version>{VERSION}</version> </dependency>
# For other environments, manually install the following JARs: # - The Stripe JAR from https://github.com/stripe/stripe-java/releases/latest # - Google Gson from https://github.com/google/gson
# Install via npm npm install --save stripe
# Make sure your project is using Go Modules go mod init # Install stripe-go go get -u github.com/stripe/stripe-go/v71
// Then import the package import ( "github.com/stripe/stripe-go/v71" )
# Install via dotnet dotnet add package Stripe.net dotnet restore
# Or install via NuGet PM> Install-Package Stripe.net

Client-side

The Android SDK is open source and fully documented.

To install the SDK, add stripe-android to the dependencies block of your app/build.gradle file:

apply plugin: 'com.android.application' android { ... } dependencies { // ... // Stripe Android SDK implementation 'com.stripe:stripe-android:16.1.1' }

Configure the SDK with your Stripe publishable key so that it can make requests to the Stripe API, such as in your Application subclass:

import com.stripe.android.PaymentConfiguration class MyApp : Application() { override fun onCreate() { super.onCreate() PaymentConfiguration.init( applicationContext, "pk_test_TYooMQauvdEDq54NiTphI7jx" ) } }
import com.stripe.android.PaymentConfiguration; public class MyApp extends Application { @Override public void onCreate() { super.onCreate(); PaymentConfiguration.init( getApplicationContext(), "pk_test_TYooMQauvdEDq54NiTphI7jx" ); } }

Our code samples also use OkHttp and GSON to make HTTP requests to a server.

2 Create a PaymentIntent Server-side Client-side

A PaymentIntent is an object that represents your intent to collect payment from a customer and tracks the lifecycle of the payment process through each stage.

Server-side

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.

curl https://api.stripe.com/v1/payment_intents \ -u sk_test_4eC39HqLyjWDarjtT1zdp7dc: \ -d amount=1099 \ -d currency=mxn \ -d "payment_method_types[]"=oxxo
# Set your secret key. Remember to switch to your live secret key in production! # See your keys here: https://dashboard.stripe.com/account/apikeys Stripe.api_key = 'sk_test_4eC39HqLyjWDarjtT1zdp7dc' intent = Stripe::PaymentIntent.create( amount: 1099, currency: 'mxn', payment_method_types: ['oxxo'] ) # Send the intent.client_secret back to the client so you can use it in later steps.
# Set your secret key. Remember to switch to your live secret key in production! # See your keys here: https://dashboard.stripe.com/account/apikeys stripe.api_key = 'sk_test_4eC39HqLyjWDarjtT1zdp7dc' intent = stripe.PaymentIntent.create( amount=1099, currency='mxn', payment_method_types=['oxxo'] ) # Send the intent.client_secret back to the client so you can use it in later steps.
// Set your secret key. Remember to switch to your live secret key in production! // See your keys here: https://dashboard.stripe.com/account/apikeys \Stripe\Stripe::setApiKey('sk_test_4eC39HqLyjWDarjtT1zdp7dc'); $intent = \Stripe\PaymentIntent::create([ "amount" => 1099, "currency" => "mxn", "payment_method_types" => ["oxxo"] ]); // Send the intent.client_secret back to the client so you can use it in later steps.
// Set your secret key. Remember to switch to your live secret key in production! // See your keys here: https://dashboard.stripe.com/account/apikeys Stripe.apiKey = "sk_test_4eC39HqLyjWDarjtT1zdp7dc"; PaymentIntentCreateParams params = PaymentIntentCreateParams.builder() .setAmount(1099L) .setCurrency("mxn") .addPaymentMethodType("oxxo") .build(); PaymentIntent paymentIntent = PaymentIntent.create(params); // Send the paymentIntent.client_secret back to the client so you can use it in later steps.
// Set your secret key. Remember to switch to your live secret key in production! // See your keys here: https://dashboard.stripe.com/account/apikeys const stripe = require('stripe')('sk_test_4eC39HqLyjWDarjtT1zdp7dc'); const paymentIntent = await stripe.paymentIntents.create({ amount: 1099, currency: 'mxn', payment_method_types: ['oxxo'], }); // Send the paymentIntent.client_secret back to the client so you can use it in later steps.
// Set your secret key. Remember to switch to your live secret key in production! // See your keys here: https://dashboard.stripe.com/account/apikeys stripe.Key = "sk_test_4eC39HqLyjWDarjtT1zdp7dc" params := &stripe.PaymentIntentParams{ Amount: stripe.Int64(1099), Currency: stripe.String(string(stripe.CurrencyMXN)), PaymentMethodTypes: stripe.StringSlice([]string{ "oxxo", }), } pi, _ := paymentintent.New(params) // Send the pi.client_secret back to the client so you can use it in later steps.
// Set your secret key. Remember to switch to your live secret key in production! // See your keys here: https://dashboard.stripe.com/account/apikeys StripeConfiguration.ApiKey = "sk_test_4eC39HqLyjWDarjtT1zdp7dc"; var options = new PaymentIntentCreateOptions { Amount = 1099, Currency = "mxn", PaymentMethodTypes = new List<string> { "oxxo", }, }; var service = new PaymentIntentService(); var intent = service.Create(options); // Send the intent.client_secret back to the client so you can use it in later steps.

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.

Client-side

On the client, request a PaymentIntent from your server and store its client secret.

class OXXOActivity: AppCompatActivity() { private lateinit var paymentIntentClientSecret: String private fun startCheckout() { // Request a PaymentIntent from your server and store its client secret } }
public class OXXOActivity extends AppCompatActivity { private String paymentIntentClientSecret; private void startCheckout() { // Request a PaymentIntent from your server and store its client secret } }

3 Collect payment method details Client-side

In your app, collect the following required billing details from the customer. Create a PaymentMethodCreateParams with the billing details.

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.
val billingDetails = PaymentMethod.BillingDetails(email = "email@email.com", name = "Jenny Rosen") val paymentMethodCreateParams = PaymentMethodCreateParams.createOxxo(billingDetails)
PaymentMethod.BillingDetails billingDetails = new PaymentMethod.BillingDetails.Builder() .setEmail("email@email.com") .setName("Jenny Rosen") .build(); PaymentMethodCreateParams paymentMethodCreateParams = PaymentMethodCreateParams.createOxxo(billingDetails);

4 Submit the payment to Stripe Client-side

Retrieve the client secret from the PaymentIntent you created in step 2 and call stripe confirmPayment. This presents a webview to display the OXXO voucher. Afterwards, onActivityResult is called with the result of the payment.

class OXXOActivity : AppCompatActivity() { // ... private lateinit var paymentIntentClientSecret: String private val stripe: Stripe by lazy { Stripe( applicationContext, PaymentConfiguration.getInstance(applicationContext).publishableKey ) } private fun startCheckout() { // ... val confirmParams = ConfirmPaymentIntentParams .createWithPaymentMethodCreateParams( paymentMethodCreateParams = paymentMethodCreateParams, clientSecret = paymentIntentClientSecret ) stripe.confirmPayment(this, confirmParams) } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) // Handle the result of stripe.confirmPayment stripe.onPaymentResult(requestCode, data, object : ApiResultCallback<PaymentIntentResult> { override fun onSuccess(result: PaymentIntentResult) { val paymentIntent = result.intent val status = paymentIntent.status when (status) { StripeIntent.Status.Succeeded -> { // The OXXO voucher was displayed successfully. The customer can now pay the OXXO voucher at the OXXO convenience } else -> { // Payment failed/cancelled } } } override fun onError(e: Exception) { // Payment failed } }) } }
public class OXXOActivity extends AppCompatActivity { // ... private String paymentIntentClientSecret; private Stripe stripe; private void startCheckout() { // ... ConfirmPaymentIntentParams = ConfirmPaymentIntentParams .createWithPaymentMethodCreateParams( paymentMethodCreateParams, paymentIntentClientSecret ); final Context context = getApplicationContext(); stripe = new Stripe( context, PaymentConfiguration.getInstance(context).getPublishableKey() ); stripe.confirmPayment(this, confirmParams); } // ... @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); // Handle the result of stripe.confirmPayment stripe.onPaymentResult(requestCode, data, new PaymentResultCallback()); } // ... private static final class PaymentResultCallback implements ApiResultCallback<PaymentIntentResult> { @Override public void onSuccess(@NonNull PaymentIntentResult result) { PaymentIntent paymentIntent = result.getIntent(); PaymentIntent.Status status = paymentIntent.getStatus(); if (status == PaymentIntent.Status.Succeeded) { // The OXXO voucher was displayed successfully. The customer can now pay the OXXO voucher at the OXXO convenience } else { // Payment failed/cancelled } } @Override public void onError(@NonNull Exception e) { // Payment failed } } }

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. You can select which language to use for the voucher on the customer emails settings page.

The following brand settings can be applied to the voucher:

  • Icon—your brand image and public business name
  • Logo—your brand image
  • Accent color—used as the color of the print button
  • Language—used to localize the voucher

5 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 (e.g., 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.

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 did not pay for the OXXO voucher before expiration. Contact the customer via 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.

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.

6 Test the integration

In test mode, set PaymentMethod.BillingDetails#email to the following values when you call Stripe# confirmPayment() 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 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

Expiration and cancellation

OXXO vouchers expire after the expires_after UNIX timestamp and a customer cannot pay an OXXO voucher once it has expired. OXXO vouchers cannot 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.