# 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]

Important: Don’t pass `payment_method_types` when creating PaymentIntents. Instead, enable payment methods in the Dashboard and use dynamic payment methods. For API versions before 2023-08-16, pass `automatic_payment_methods[enabled]=true`. See https://docs.stripe.com/payments/payment-methods/dynamic-payment-methods for details.

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 with the amount and currency. [Enable the payment method](https://dashboard.stripe.com/settings/payment_methods) in your Dashboard. With [dynamic payment methods](https://docs.stripe.com/payments/payment-methods/dynamic-payment-methods.md), Stripe displays it to eligible customers automatically. If you currently pass `payment_method_types`, see the [migration guide](https://docs.stripe.com/payments/payment-methods/dynamic-payment-methods.md#migrate-to-dynamic-payment-methods).

```curl
curl https://api.stripe.com/v1/payment_intents \
  -u "<<YOUR_SECRET_KEY>>:" \
  -d amount=1000 \
  -d currency=brl \
  -d "automatic_payment_methods[enabled]=true"
```

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 front-end 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
<form id="payment-form" data-secret="<%= @intent.client_secret %>">
  <button id="submit">Submit</button>
</form>
```

```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 customers provide a tax identifier (CPF or CNPJ) when completing cross-border transactions. |

```html
<form id="payment-form">
  <div class="form-row">
    <label for="name">Full 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>
  <div class="form-row">
    <label for="tax-id">CPF or CNPJ:</label>
    <input id="tax-id" name="tax-id" required>
  </div>

  <!-- Used to display form errors. -->
  <div id="error-message" role="alert"></div>

  <button id="submit-button">Pay with Pix</button>
</form>
```

#### 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
<!-- Stripe.js -->
<script src="https://js.stripe.com/endive/stripe.js"></script>

<!-- React Stripe.js development build -->
<script src="https://unpkg.com/@stripe/react-stripe-js@latest/dist/react-stripe.umd.js"></script>

<!-- When you are ready to deploy your site to production, remove the
      above development script, and include the following production build. -->
<script src="https://unpkg.com/@stripe/react-stripe-js@latest/dist/react-stripe.umd.min.js"></script>
```

> 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?ui=elements#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('<<YOUR_PUBLISHABLE_KEY>>');

function App() {
  return (
    <Elements stripe={stripePromise}>
      <CheckoutForm />
    </Elements>
  );
};

ReactDOM.render(<App />, 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
<head>
  <title>Checkout</title>
  <script src="https://js.stripe.com/endive/stripe.js"></script>
</head>
```

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

```javascript
var stripe = Stripe('<<YOUR_PUBLISHABLE_KEY>>');
```

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. |
| Expiration 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.
2. Enter the customer’s details and tap **Pay**. In a [sandbox](https://docs.stripe.com/sandboxes.md), use `000.000.000-00` as a test tax identifier (CPF or CNPJ).
3. 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 configurable endpoint that executes when it receives certain event notifications from an event destination) 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 after `PaymentIntent` confirmation when a pending Pix payment expires. Valid values are from 60 to 1209600 seconds (14 days), inclusive. | 14400 seconds (4 hours), only when neither expiration option is supplied. | No | If you create a Pix payment on Monday at 13:50, confirm the `PaymentIntent` at 14:00, and set `expires_after_seconds` to 600, the Pix expires on Monday at 14:10. |
| `expires_at` | An absolute Unix timestamp when the pending Pix payment expires. It must be between 60 and 1209600 seconds (14 days) from the current time when validated, inclusive. | None. If neither expiration option is supplied, the Pix expires 14400 seconds (4 hours) after `PaymentIntent` confirmation. | No | If you set `expires_at` on Monday at 14:00 to the Unix timestamp for Wednesday at 14:00, the Pix expires at that timestamp. |

You can’t set both `expires_after_seconds` and `expires_at`. Attempting to do so returns an error. If you don’t set either one, the Pix expires 14400 seconds (4 hours) after 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.
