# Accept a one-time payment Learn how to accept a Pix one-time payment, a common payment method in Brazil. # Direct API Stripe users can accept Pix payments from customers in Brazil. Customers pay by copying and pasting a Pix string or scanning a QR code directly in their bank apps. ## Set up Stripe [Server-side] To get started, [create a Stripe account](https://dashboard.stripe.com/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] A [PaymentIntent](https://docs.stripe.com/api/payment_intents/object.md) is an object that represents your intent to collect payment from your customer and tracks the lifecycle of the payment process. Create a `PaymentIntent` on your server and specify the amount to collect and a supported currency. If you have an existing [Payment Intents](https://docs.stripe.com/payments/payment-intents.md) integration, add `pix` to the list of [payment method types](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-payment_method_types). ```curl curl https://api.stripe.com/v1/payment_intents \ -u "<>:" \ -d "payment_method_types[]=pix" \ -d amount=1000 \ -d currency=brl ``` 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)). Send the client secret to your client to securely complete the payment process instead of passing the entire `PaymentIntent` object. ### 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 ``` ## Collect payment method details [Client-side] #### HTML + JS Create a payment form on your client to collect the required billing details from the customer: | Field | Value | | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `name` | The customer’s full name. | | `email` | The customer’s email address. | | `tax_id` | The customer’s tax identifier: a CPF if the customer is an individual, or a CNPJ if the customer is a business. The Brazilian government requires that buyers provide a tax identifier (CPF or CNPJ) when completing cross-border transactions. | ```html
``` #### React #### npm Install [React Stripe.js](https://www.npmjs.com/package/@stripe/react-stripe-js) and the [Stripe.js loader](https://www.npmjs.com/package/@stripe/stripe-js) from the npm public registry. ```bash npm install --save @stripe/react-stripe-js @stripe/stripe-js ``` #### umd We also provide a UMD build for sites that don’t use npm or modules. Include the Stripe.js script, which exports a global `Stripe` function, and the UMD build of React Stripe.js, which exports a global `ReactStripe` object. Always load the Stripe.js script directly from **js.stripe.com** to remain PCI compliant. Don’t include the script in a bundle or host a copy of it yourself. ```html ``` > The [demo in CodeSandbox](https://codesandbox.io/s/react-stripe-official-q1loc?fontsize=14&hidenavigation=1&theme=dark) lets you try out React Stripe.js without having to create a new project. ### Add Stripe.js and Elements to your page To use Element components, wrap your checkout page component in an [Elements provider](https://docs.stripe.com/sdks/stripejs-react.md#elements-provider). Call `loadStripe` with your publishable key and pass the returned `Promise` to the `Elements` provider. ```jsx import React from 'react'; import ReactDOM from 'react-dom'; import {Elements} from '@stripe/react-stripe-js'; import {loadStripe} from '@stripe/stripe-js'; import CheckoutForm from './CheckoutForm'; // Make sure to call `loadStripe` outside of a component's render to avoid // recreating the `Stripe` object on every render. const stripePromise = loadStripe('<>'); function App() { return ( ); }; ReactDOM.render(, document.getElementById('root')); ``` ## Display the Pix QR Code with Stripe [Client-side] 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 var stripe = Stripe('<>'); ``` Call [stripe.confirmPixPayment](https://docs.stripe.com/js/payment_intents/confirm_pix_payment) to render the QR code that allows your customer to complete their payment. Include a `return_url` to redirect your customer after they complete the payment. ```javascript const form = document.getElementById('payment-form'); form.addEventListener('submit', async (event) => { event.preventDefault(); const {error} = await stripe.confirmPixPayment( '{{PAYMENT_INTENT_CLIENT_SECRET}}', { payment_method: { billing_details: { name: document.getElementById('name').value, email: document.getElementById('email').value, tax_id: document.getElementById('tax-id').value, } }, return_url: 'https://example.com/checkout/complete', } ); if (error) { // Inform the customer that there was an error. } }); ``` ### Handling the redirect The following URL query parameters are provided when Stripe redirects the customer to the `return_url`. | Parameter | Description | | ------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------- | | `payment_intent` | The unique identifier for the `PaymentIntent`. | | `payment_intent_client_secret` | The [client secret](https://docs.stripe.com/api/payment_intents/object.md#payment_intent_object-client_secret) of the `PaymentIntent` object. | You can also append your own query parameters when providing the `return_url`. They persist throughout the redirect process. The `return_url` should correspond to a page on your website that provides the status of the payment. You should verify the status of the `PaymentIntent` when rendering the return page. You can do so by using the `retrievePaymentIntent` function from Stripe.js and passing in the `payment_intent_client_secret`. ```javascript (async () => { const url = new URL(window.location); const clientSecret = url.searchParams.get('payment_intent_client_secret'); const {paymentIntent, error} = await stripe.retrievePaymentIntent(clientSecret); if (error) { // Handle error } else if (paymentIntent && paymentIntent.status === 'succeeded') { // Handle successful payment } })(); ``` ## Optional: Display the Pix QR Code yourself [Client-side] We recommend using Stripe.js to display the Pix details with `confirmPixPayment`. However, you can also manually display the Pix string or QR code to your customers. Specify `handleActions: false` when calling `stripe.confirmPixPayment` to manually handle the next action to display the Pix details to your customer. ```javascript var form = document.getElementById('payment-form'); form.addEventListener('submit', async (event) => { event.preventDefault(); const result = await stripe.confirmPixPayment( '{{PAYMENT_INTENT_CLIENT_SECRET}}', { // payment method details omitted for brevity }, { handleActions: false } ); if (result.error) { // Display error to your customer const errorMsg = document.getElementById('error-message'); errorMsg.innerText = result.error.message; } else { // Pix details were successfully created const amount = result.paymentIntent.amount; const currency = result.paymentIntent.currency; const details = result.paymentIntent.next_action.pix_display_qr_code; // Pix string, also known as Pix "copy and paste" const emvString = details.data; // SVG image Pix QR code const imageUrlSvg = details.image_url_svg; // PNG image Pix QR code const imageUrlPng = details.image_url_png; // Pix expiration date as a unix timestamp const expires_at = details.expires_at; // Handle the next action by displaying the Pix details to your customer // You can also use the generated hosted instructions const hosted_instructions_url = details.hosted_instructions_url; } }); ``` We suggest you display the following: | Detail | Description | | -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Pix string | Display the Pix string for your customers to copy it to their clipboard using the PaymentIntent’s [payment_intent.next_action.pix_display_qr_code.data](https://docs.stripe.com/api/payment_intents/object.md#payment_intent_object-next_action-pix_display_qr_code-data) string. | | QR code as SVG image | Display the Pix QR code for your customers to scan with their phone using the PaymentIntent’s [payment_intent.next_action.pix_display_qr_code.image_url_svg](https://docs.stripe.com/api/payment_intents/object.md#payment_intent_object-next_action-pix_display_qr_code-image_url_svg) string. | | QR code as PNG image | Display the Pix QR code for your customers to scan with their phone using the PaymentIntent’s [payment_intent.next_action.pix_display_qr_code.image_url_png](https://docs.stripe.com/api/payment_intents/object.md#payment_intent_object-next_action-pix_display_qr_code-image_url_png) string. | | Expiry date | Display the Pix expiration date. Use [payment_intent.next_action.pix_display_qr_code.expires_at](https://docs.stripe.com/api/payment_intents/object.md#payment_intent_object-next_action-pix_display_qr_code-expires_at) to see the expiration date. | You can also redirect customers to the Stripe-hosted instructions page, which is similar to the modal opened by `confirmPixPayment`. Use [payment_intent.next_action.pix_display_qr_code.hosted_instructions_url](https://docs.stripe.com/api/payment_intents/object.md#payment_intent_object-next_action-pix_display_qr_code-hosted_instructions_url) to find the Stripe-hosted instructions page URL on the PaymentIntent object. ## Test your integration To test your integration: 1. Select Pix. 1. Enter the buyer’s details and tap **Pay**. In a testing environment, you can use `000.000.000-00` as a test tax identifier (CPF or CNPJ). 1. Click **Simulate scan** to open a Stripe-hosted Pix test payment page. From this page, you can either authorize or expire the test payment. In live mode, the **Pay** button displays a Pix QR code. You need a Brazilian bank account with Pix enabled to complete or cancel this payment flow. You can also set [payment_method.billing_details.email](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-payment_method_data-billing_details-email) to the following values to test different scenarios. | Email | Description | | ---------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `{any_prefix}@{any_domain}` | Simulates a Pix that a customer pays after 3 minutes. The [payment_intent.succeeded](https://docs.stripe.com/api/events/types.md#event_types-payment_intent.succeeded) *webhook* (A webhook is a real-time push notification sent to your application as a JSON payload through HTTPS requests) arrives after approximately 3 minutes. In production, this webhook arrives immediately after the Pix is paid. Stripe ignores the [payment_method_options.pix.expires_at](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-payment_method_options-pix-expires_at) and [payment_method_options.pix.expires_after_seconds](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-payment_method_options-pix-expires_after_seconds) parameters in this case. Example: `anything@example.com` | | `{any_prefix}succeed_immediately@{any_domain}` | Simulates a Pix that your customer pays immediately. The [payment_intent.succeeded](https://docs.stripe.com/api/events/types.md#event_types-payment_intent.succeeded) webhook arrives within several seconds. In production, this webhook arrives immediately after the Pix is paid. Stripe ignores the [payment_method_options.pix.expires_at](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-payment_method_options-pix-expires_at) and [payment_method_options.pix.expires_after_seconds](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-payment_method_options-pix-expires_after_seconds) parameters in this case. Example: `succeed_immediately@example.com` | | `{any_prefix}expire_immediately@{any_domain}` | Simulates a Pix that expires before your customer pays. The [payment_intent.payment_failed](https://docs.stripe.com/api/events/types.md#event_types-payment_intent.payment_failed) webhook arrives within several seconds. Stripe ignores the [payment_method_options.pix.expires_at](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-payment_method_options-pix-expires_at) and [payment_method_options.pix.expires_after_seconds](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-payment_method_options-pix-expires_after_seconds) parameters in this case. Example: `expire_immediately@example.com` | | `{any_prefix}expire_with_delay@{any_domain}` | Simulates a Pix that expires before your customer pays. The [payment_intent.payment_failed](https://docs.stripe.com/api/events/types.md#event_types-payment_intent.payment_failed) webhook arrives after approximately 3 minutes. Stripe ignores the [payment_method_options.pix.expires_at](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-payment_method_options-pix-expires_at) and [payment_method_options.pix.expires_after_seconds](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-payment_method_options-pix-expires_after_seconds) parameters in this case. Example: `expire_with_delay@example.com` | | `{any_prefix}fill_never@{any_domain}` | Simulates a Pix that never succeeds. It expires according to the [payment_method_options.pix.expires_at](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-payment_method_options-pix-expires_at) or [payment_method_options.pix.expires_after_seconds](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-payment_method_options-pix-expires_after_seconds) parameter. The [payment_intent.payment_failed](https://docs.stripe.com/api/events/types.md#event_types-payment_intent.payment_failed) webhook arrives after the simulation completes. Example: `fill_never@example.com` | ## Expiration Pix codes expire after the specified `expires_at` UNIX timestamp. After the Pix expires, confirm the PaymentIntent with another payment method or cancel it. Set expiration parameters in the [payment method options](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-payment_method_options-pix) under the `pix` key. A customer can’t pay a Pix after it expires. | Field | Value | Default value | Required | Example | | ----------------------- | -------------------------------------------------------------------------------------------------------------------------- | ------------------------------------ | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `expires_after_seconds` | The number of seconds before a pending Pix payment expires. Valid values are from 10 seconds to 259200 seconds (3 days) | 14400 seconds (4 hours) | No | If you confirm a Pix on Monday at 14:00 and you set `expires_after_seconds` to 600, the Pix expires on Monday at 14:10. The start time isn’t when the `PaymentIntent` is created, but when it’s confirmed. | | `expires_at` | A Unix timestamp that shows when the pending Pix payment expires. Valid values are from 10 seconds to 3 days in the future | The default is 4 hours in the future | No | If you create a Pix on Monday at 14:00 and you set `expires_at` to 2 days in the future, the Pix expires on Wednesday at 14:00 | `expires_after_seconds` and `expires_at` are mutually exclusive. An error occurs if both are set. Both are also optional, and if neither is set, the expiration defaults to 4 hours from when the `PaymentIntent` is confirmed. > The value returned on `expires_at` from the `next_action` response is the same as the input set on [payment_method_options.expires_at](https://docs.stripe.com/api/payment_intents/create.md#create_payment_intent-payment_method_options-pix-expires_at). Although, in real scenarios, the actual expiration timestamp might differ. This doesn’t impact your customer’s experience paying with Pix, but it’s recommended to rely on the [payment_intent.payment_failed](https://docs.stripe.com/api/events/types.md?event_types-payment_intent.payment_failed) event to consider if the payment intent has expired. ## Cancellation You can cancel Pix payments before they expire by [canceling the PaymentIntent](https://docs.stripe.com/api/payment_intents/cancel.md) associated with the Pix payment.