---
title: "Accept a payment"
description: "One call for card, Pix and Boleto; only the currency and the payment method fields change."
url: https://docs.fluveo.com/payments/accept-a-payment/
---

> Index of every page: https://docs.fluveo.com/llms.txt. Integrating Fluveo? Read https://docs.fluveo.com/agents/integrate.md first.

> Too long? This page with code in one language only: https://docs.fluveo.com/payments/accept-a-payment.curl.md (cURL), https://docs.fluveo.com/payments/accept-a-payment.node.md (Node.js), https://docs.fluveo.com/payments/accept-a-payment.python.md (Python), https://docs.fluveo.com/payments/accept-a-payment.php.md (PHP).

# Accept a payment

One call for card, Pix and Boleto; only the currency and the payment method fields change.

Each payment you take is one PaymentIntent (`pi_...`): it holds the amount, the method and a status that moves from your first call to the end. Every payment method goes through the same call, `POST /v1/payment_intents`: you send the amount, your store's currency and the payment method, with `confirm=true`. A card is usually decided in the answer; a Pix or a Boleto waits for the buyer, and a webhook tells you when it is paid. This page shows the one-call flow, the two-call flow, 3D Secure and manual capture.

## What changes by payment method

Everything else in the request stays the same: the key, the `Idempotency-Key`, `amount`, `confirm=true`, `metadata`.

|  | Card | Pix | Boleto |
| --- | --- | --- | --- |
| `currency` | `usd` or `brl` | `brl` | `brl` |
| `payment_method_types[]` | `card` | `pix` | `boleto` |
| How you name the method | `payment_method=pm_...` (in tests, `pm_card_visa`) | `payment_method_data[type]=pix` and the payer in `payment_method_data[billing_details]` | `payment_method_data[type]=boleto`, the tax id in `payment_method_data[boleto][tax_id]`, and the payer's name, email and address |
| Options | `capture_method`, `return_url` | `payment_method_options[pix][expires_after_seconds]` | `payment_method_options[boleto][expires_after_days]` |
| The answer | `succeeded` (or `requires_action` for 3D Secure) | `requires_action` with a Pix code | `requires_action` with a Boleto line and PDF |
| Details | [Cards](https://docs.fluveo.com/payments/cards.md) | [Pix](https://docs.fluveo.com/payments/pix.md) | [Boleto](https://docs.fluveo.com/payments/boleto.md) |

> **Card numbers:** A card number never goes through your server or a plain `/v1` request. Confirm with a PaymentMethod id, or let [the hosted checkout page](https://docs.fluveo.com/payments/checkout.md) take the card. See [Cards](https://docs.fluveo.com/payments/cards.md).

## Take a payment in one call

### Step 1: Create and confirm the PaymentIntent

Send the amount in the currency's smallest unit, the currency, the payment method and `confirm=true`. Use one `Idempotency-Key` per order, and send that same key again if you retry.

For a card, name the PaymentMethod in `payment_method`; in tests, `pm_card_visa` is always approved. `2000` is $20.00.

**Card: $20.00**

cURL:

```bash
curl https://api.fluveo.com/v1/payment_intents \
  -H "Authorization: Bearer sk_test_..." \
  -H "Idempotency-Key: order-1042-pay" \
  -d amount=2000 \
  -d currency=usd \
  -d "payment_method_types[]=card" \
  -d payment_method=pm_card_visa \
  -d confirm=true \
  -d "metadata[order_id]=1042"
```

Node.js:

```javascript
const response = await fetch("https://api.fluveo.com/v1/payment_intents", {
  method: "POST",
  headers: {
    Authorization: "Bearer sk_test_...",
    "Idempotency-Key": "order-1042-pay",
  },
  body: new URLSearchParams({
    amount: "2000",
    currency: "usd",
    "payment_method_types[]": "card",
    payment_method: "pm_card_visa",
    confirm: "true",
    "metadata[order_id]": "1042",
  }),
});
const paymentIntent = await response.json();
console.log(response.status, paymentIntent.status);
```

Python:

```python
import requests

response = requests.post(
    "https://api.fluveo.com/v1/payment_intents",
    headers={
        "Authorization": "Bearer sk_test_...",
        "Idempotency-Key": "order-1042-pay",
    },
    data={
        "amount": 2000,
        "currency": "usd",
        "payment_method_types[]": "card",
        "payment_method": "pm_card_visa",
        "confirm": "true",
        "metadata[order_id]": "1042",
    },
)
payment_intent = response.json()
print(response.status_code, payment_intent["status"])
```

PHP:

```php
<?php
$ch = curl_init("https://api.fluveo.com/v1/payment_intents");
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST => true,
    CURLOPT_HTTPHEADER => [
        "Authorization: Bearer sk_test_...",
        "Idempotency-Key: order-1042-pay",
    ],
    CURLOPT_POSTFIELDS => http_build_query([
        "amount" => 2000,
        "currency" => "usd",
        "payment_method_types" => ["card"],
        "payment_method" => "pm_card_visa",
        "confirm" => "true",
        "metadata" => ["order_id" => "1042"],
    ]),
]);
$paymentIntent = json_decode(curl_exec($ch), true);
echo $paymentIntent["status"];
```

For a Pix, describe the method and its payer in `payment_method_data`. `1000` is R$ 10,00.

**Pix in BRL: R$ 10,00**

cURL:

```bash
curl https://api.fluveo.com/v1/payment_intents \
  -H "Authorization: Bearer sk_test_..." \
  -H "Idempotency-Key: order-1042-pay" \
  -d amount=1000 \
  -d currency=brl \
  -d "payment_method_types[]=pix" \
  -d "payment_method_data[type]=pix" \
  -d "payment_method_data[billing_details][name]=Ada Pagadora" \
  -d "payment_method_data[billing_details][email]=ada@example.com" \
  --data-urlencode "payment_method_data[billing_details][phone]=+5511999999999" \
  -d "payment_method_data[billing_details][tax_id]=12345678909" \
  -d confirm=true \
  -d "metadata[order_id]=1042"
```

Node.js:

```javascript
const response = await fetch("https://api.fluveo.com/v1/payment_intents", {
  method: "POST",
  headers: {
    Authorization: "Bearer sk_test_...",
    "Idempotency-Key": "order-1042-pay",
  },
  body: new URLSearchParams({
    amount: "1000",
    currency: "brl",
    "payment_method_types[]": "pix",
    "payment_method_data[type]": "pix",
    "payment_method_data[billing_details][name]": "Ada Pagadora",
    "payment_method_data[billing_details][email]": "ada@example.com",
    "payment_method_data[billing_details][phone]": "+5511999999999",
    "payment_method_data[billing_details][tax_id]": "12345678909",
    confirm: "true",
    "metadata[order_id]": "1042",
  }),
});
const paymentIntent = await response.json();
console.log(response.status, paymentIntent.status, paymentIntent.next_action);
```

Python:

```python
import requests

response = requests.post(
    "https://api.fluveo.com/v1/payment_intents",
    headers={
        "Authorization": "Bearer sk_test_...",
        "Idempotency-Key": "order-1042-pay",
    },
    data={
        "amount": 1000,
        "currency": "brl",
        "payment_method_types[]": "pix",
        "payment_method_data[type]": "pix",
        "payment_method_data[billing_details][name]": "Ada Pagadora",
        "payment_method_data[billing_details][email]": "ada@example.com",
        "payment_method_data[billing_details][phone]": "+5511999999999",
        "payment_method_data[billing_details][tax_id]": "12345678909",
        "confirm": "true",
        "metadata[order_id]": "1042",
    },
)
payment_intent = response.json()
print(response.status_code, payment_intent["status"], payment_intent["next_action"])
```

PHP:

```php
<?php
$ch = curl_init("https://api.fluveo.com/v1/payment_intents");
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST => true,
    CURLOPT_HTTPHEADER => [
        "Authorization: Bearer sk_test_...",
        "Idempotency-Key: order-1042-pay",
    ],
    CURLOPT_POSTFIELDS => http_build_query([
        "amount" => 1000,
        "currency" => "brl",
        "payment_method_types" => ["pix"],
        "payment_method_data" => [
            "type" => "pix",
            "billing_details" => [
                "name" => "Ada Pagadora",
                "email" => "ada@example.com",
                "phone" => "+5511999999999",
                "tax_id" => "12345678909",
            ],
        ],
        "confirm" => "true",
        "metadata" => ["order_id" => "1042"],
    ]),
]);
$paymentIntent = json_decode(curl_exec($ch), true);
echo $paymentIntent["status"];
```

A Boleto is the same call with the fields of [Boleto](https://docs.fluveo.com/payments/boleto.md#create).

### Step 2: Read the result

A card that is approved answers `200` with `status: "succeeded"`. The PaymentIntent's `latest_charge` is the `ch_` of the charge, and `amount_received` is what the buyer paid.

*Card: response (trimmed)*

```json
{
  "id": "pi_...",
  "object": "payment_intent",
  "amount": 2000,
  "amount_received": 2000,
  "currency": "usd",
  "status": "succeeded",
  "latest_charge": "ch_...",
  "payment_method": "pm_...",
  "capture_method": "automatic_async",
  "metadata": { "order_id": "1042" }
}
```

A Pix or a Boleto answers `status: "requires_action"`, and `next_action` holds what you show the buyer: a `pix_display_qr_code` (see [Pix](https://docs.fluveo.com/payments/pix.md)) or `boleto_display_details` (see [Boleto](https://docs.fluveo.com/payments/boleto.md)). It is never paid at create time. No Charge exists yet: `latest_charge` stays `null` until the buyer pays or the code expires, and the Charge shows up with `charge.succeeded` or `charge.failed`.

*Pix: response (trimmed)*

```json
{
  "id": "pi_...",
  "object": "payment_intent",
  "amount": 1000,
  "currency": "brl",
  "status": "requires_action",
  "latest_charge": null,
  "next_action": {
    "type": "pix_display_qr_code",
    "pix_display_qr_code": { "data": "00020101021226...", "expires_at": 1767225600 }
  }
}
```

`capture_method` is `automatic_async` when you send none. It captures exactly like `automatic`: the money is taken as soon as the card is approved, so a `succeeded` PaymentIntent is already captured and needs no capture call. You may send `capture_method=automatic` instead and the payment behaves the same; the field then shows `automatic`. Only `manual` holds the money until you capture (see [Authorize now, capture later](https://docs.fluveo.com/payments/accept-a-payment.md#manual-capture)).

### Step 3: Fulfil the order from the webhook

Fluveo sends `payment_intent.succeeded` (with `charge.succeeded`) to your webhook endpoint when the money is in. Ship the order when that event arrives and its `Fluveo-Signature` checks out, once per PaymentIntent id. The answer to your POST is only a hint: a payment that needs 3D Secure, a Pix, a Boleto, or a card you capture later, finishes after the call returns.

In test mode, Fluveo's test bank pays a Pix or a Boleto for you, through the same path as a real payment: you get the same `charge.succeeded` and `payment_intent.succeeded`, then `charge.updated` once the sale's balance transaction is booked, and the sale shows in your balance and your balance transactions as a real one does. The payer email in `payment_method_data[billing_details][email]` decides when:

- an address whose part before the `@` ends in `succeed_immediately`, such as `succeed_immediately@example.com`, is paid within seconds of the confirm;
- an address ending in `expire_immediately`, `expire_with_delay` or `fill_never` before the `@` is never paid: it waits and expires on its own deadline (a Pix's expiry, a Boleto's clearing period), so give a Pix a short `expires_after_seconds` to watch it expire;
- any other address, such as `ada@example.com`, is paid about 3 minutes after the confirm, unless the Pix code expires first;
- a Pix with no email is never paid on its own.

You can also pay a waiting one yourself: open the Pix's `hosted_instructions_url` or the Boleto's `hosted_voucher_url` in a browser, follow **simular o pagamento** and press **Autorizar pagamento de teste**. A Pix or Boleto that is already paid, canceled or expired is never paid again. With a live key nothing is simulated: the email is just the payer's email, and the pages have no test link.

## Take a payment in two calls

Create the PaymentIntent first when you want its id before the buyer picks a card, for example to store it with the order. Then confirm it.

*1. Create*

```bash
curl https://api.fluveo.com/v1/payment_intents \
  -H "Authorization: Bearer sk_test_..." \
  -H "Idempotency-Key: order-1043-create" \
  -d amount=4500 \
  -d currency=usd \
  -d "payment_method_types[]=card"
```

The PaymentIntent starts in `requires_payment_method`. Confirm it with the card:

*2. Confirm*

```bash
curl https://api.fluveo.com/v1/payment_intents/pi_.../confirm \
  -H "Authorization: Bearer sk_test_..." \
  -H "Idempotency-Key: order-1043-confirm" \
  -d payment_method=pm_card_visa
```

**Parameters you can send on create**

- `amount` (integer, required): The amount in the smallest unit of the currency: `2000` is $20.00 in `usd`, `1000` is R$ 10,00 in `brl`. A Boleto takes at least `200`.
- `currency` (string, required): A currency your account accepts, in lower case, such as the `default_currency` of `GET /v1/account`. Any other is a 400 on `currency`.
- `payment_method_types` (array, optional): `card`, `pix` or `boleto`. A method in `payment_method_data[type]` must also be in this list. The currency each method charges in is on [Payments](https://docs.fluveo.com/payments.md#payment-methods).
- `payment_method` (string, optional): The card to charge: a `pm_` id, or a test card name like `pm_card_visa`.
- `payment_method_data` (object, optional): A Pix or a Boleto and its payer, used once for this payment. Do not send it together with `payment_method`, and never put card numbers in it. See [Pix](https://docs.fluveo.com/payments/pix.md#payer) and [Boleto](https://docs.fluveo.com/payments/boleto.md#payer).
- `payment_method_options` (object, optional): Options of one method: `payment_method_options[pix]` or `payment_method_options[boleto]`.
- `confirm` (boolean, optional): `true` confirms in the same call. Default `false`.
- `capture_method` (string, optional): For a card: `automatic_async` (the default), `automatic` or `manual`. `automatic_async` and `automatic` both capture as soon as the card is approved; `manual` holds the money until you capture. See [Authorize now, capture later](https://docs.fluveo.com/payments/accept-a-payment.md#manual-capture).
- `confirmation_method` (string, optional): `automatic` (the default) or `manual`. With `manual`, a card that passes 3D Secure does not charge on its own: the PaymentIntent stops in `requires_confirmation` and charges only when you call `/confirm` again.
- `return_url` (string, optional): Where the buyer comes back after 3D Secure. Only with `confirm=true`. Required when you leave out `payment_method_types`, unless you send `automatic_payment_methods[allow_redirects]=never` or `off_session=true`. See [3D Secure](https://docs.fluveo.com/payments/accept-a-payment.md#3d-secure).
- `customer` (string, optional): A `cus_` of your store. For a Pix, the Customer's name and tax id (`br_cpf` or `br_cnpj`) name the payer when the request carries none. A Boleto never takes its payer from the Customer: send it in the request (see [Boleto](https://docs.fluveo.com/payments/boleto.md#payer)).
- `description` (string, optional): Your own text about the payment.
- `receipt_email` (string, optional): The buyer's email for the receipt.
- `statement_descriptor_suffix` (string, optional): Text added to what the buyer sees on their card statement.
- `metadata` (object, optional): Your own key-value pairs, sent as `metadata[key]=value`. Keys that start with `fluveo_` are refused.

The [API reference](https://docs.fluveo.com/api/payment_intents.md) lists every route of the PaymentIntent. A parameter Fluveo does not take answers a 400 that names it, so an unknown field never passes in silence.

## Statuses

| Status | What it means | What you do |
| --- | --- | --- |
| `requires_payment_method` | No payment method yet, the last card was declined, or a Pix or Boleto expired unpaid. | Confirm with a payment method, or create a new Pix or Boleto. |
| `requires_confirmation` | A card is set but the payment is not confirmed. An update that sets another `payment_method` on a waiting Pix drops its code and lands here. With `confirmation_method=manual`, a card that passed 3D Secure waits here too, with no `next_action`, no Charge and no event. | Call `/confirm`. |
| `requires_action` | The buyer must act: authenticate the card (3D Secure), or pay the Pix or the Boleto. | Send the buyer to `next_action.redirect_to_url.url`, or show the Pix code or the Boleto. A confirm with another card replaces a 3D Secure or Pix wait; a waiting Boleto refuses it with `400`. |
| `processing` | The card network has not answered yet, or a Boleto's line is not ready yet. | Wait for the webhook. |
| `requires_capture` | The card is authorized; money is held, not taken. | Capture or cancel. |
| `succeeded` | The money is taken. | Fulfil the order. |
| `canceled` | Closed for good; nothing will be charged on it. | Nothing; create a new one to try again. |

## Events

| Event | When |
| --- | --- |
| `payment_intent.created` | The PaymentIntent was created. |
| `payment_intent.requires_action` | The buyer must authenticate (3D Secure), or the Pix code or Boleto is ready to show. |
| `payment_intent.processing` | A card payment is waiting on the card network. A Boleto never sends it. |
| `charge.succeeded` + `payment_intent.succeeded` | The buyer paid and the money is in. |
| `charge.succeeded` + `payment_intent.amount_capturable_updated` | A manual-capture card payment is authorized. |
| `charge.captured` + `payment_intent.succeeded` | You captured an authorized payment. |
| `charge.failed` + `payment_intent.payment_failed` | The card was declined, or a Pix or Boleto expired unpaid. The PaymentIntent goes back to `requires_payment_method`. |
| `payment_intent.canceled` | The PaymentIntent moved to `canceled`. |

A Pix or a Boleto creates its events in this order: `payment_intent.created`, `payment_intent.requires_action`, then the success or failure events. Its charge appears only with them: while the buyer has not paid, `latest_charge` is `null`. Events can arrive more than once and out of order. Keep the `evt_` ids you handled and skip a repeat; read the PaymentIntent again when the order matters. The full list is on [Event types](https://docs.fluveo.com/webhooks/events.md).

## When a card is declined

A declined card answers `402` with an error of type `card_error`. `error.code` says why (for example `card_declined`, `expired_card`, `incorrect_cvc`), `error.decline_code` gives the bank's reason when there is one, and `error.payment_intent` holds the PaymentIntent, now back in `requires_payment_method`.

*402 Payment Required (trimmed)*

```json
{
  "error": {
    "type": "card_error",
    "code": "card_declined",
    "decline_code": "generic_decline",
    "message": "Your card was declined.",
    "payment_intent": { "id": "pi_...", "status": "requires_payment_method" }
  }
}
```

Ask the buyer for another card and confirm the same PaymentIntent again, with a new `Idempotency-Key` because it is a new attempt. See [Errors](https://docs.fluveo.com/errors.md) for every code.

## 3D Secure

Some banks ask the buyer to prove they own the card. Fluveo handles this with a redirect:

1. Confirm with `return_url`, the page on your site where the buyer should land afterwards.
2. If the bank asks for authentication, the answer has `status: "requires_action"` and a `next_action` of type `redirect_to_url`.
3. Send the buyer's browser to `next_action.redirect_to_url.url`. It is a Fluveo page; the bank's check opens inside it, so the address bar stays on Fluveo. In test mode it opens a test page with Complete and Fail instead (see [Testing](https://docs.fluveo.com/start/testing.md#3d-secure)).
4. The buyer comes back to your `return_url`. Do not trust that visit alone: read the PaymentIntent, or wait for `payment_intent.succeeded` or `payment_intent.payment_failed`.

*next_action*

```json
{
  "status": "requires_action",
  "next_action": {
    "type": "redirect_to_url",
    "redirect_to_url": {
      "url": "https://.../v1/card_actions/...",
      "return_url": "https://shop.example.com/orders/1042/return"
    }
  }
}
```

> **Always send return_url:** Fluveo has no in-page authentication. With `payment_method_types` sent on create, a card that needs 3D Secure and was confirmed without `return_url` answers `requires_action` with a `next_action` of type `use_fluveo_sdk`, which only an in-page check could complete: that PaymentIntent cannot finish. To fail at once instead of waiting for the buyer, send `error_on_requires_action=true`: such a card then answers `402` with code `authentication_required`. See [3D Secure on Cards](https://docs.fluveo.com/payments/cards.md#3d-secure).
>
> A PaymentIntent created without `payment_method_types` refuses a confirm without `return_url`, a create with `confirm=true` included, with `400` and charges nothing, unless it was created with `automatic_payment_methods[allow_redirects]=never` or the confirm sends `off_session=true`. On such a PaymentIntent, a `return_url` from an earlier confirm does not count: send it on every confirm.

## Authorize now, capture later

Use `capture_method=manual` when you only charge once you ship. The card is authorized and the money is held; nothing is taken until you capture. This works for every card payment.

*Authorize*

```bash
curl https://api.fluveo.com/v1/payment_intents \
  -H "Authorization: Bearer sk_test_..." \
  -H "Idempotency-Key: order-1044-pay" \
  -d amount=5000 \
  -d currency=usd \
  -d "payment_method_types[]=card" \
  -d payment_method=pm_card_visa \
  -d capture_method=manual \
  -d confirm=true
```

The PaymentIntent is now `requires_capture` and `amount_capturable` is `5000`. Capture all of it with no body, or a smaller amount with `amount_to_capture`; the rest is released to the buyer.

**Capture $30.00 of $50.00**

cURL:

```bash
curl https://api.fluveo.com/v1/payment_intents/pi_.../capture \
  -H "Authorization: Bearer sk_test_..." \
  -H "Idempotency-Key: order-1044-capture" \
  -d amount_to_capture=3000
```

Node.js:

```javascript
const response = await fetch("https://api.fluveo.com/v1/payment_intents/pi_.../capture", {
  method: "POST",
  headers: {
    Authorization: "Bearer sk_test_...",
    "Idempotency-Key": "order-1044-capture",
  },
  body: new URLSearchParams({ amount_to_capture: "3000" }),
});
console.log((await response.json()).status);
```

Python:

```python
import requests

response = requests.post(
    "https://api.fluveo.com/v1/payment_intents/pi_.../capture",
    headers={
        "Authorization": "Bearer sk_test_...",
        "Idempotency-Key": "order-1044-capture",
    },
    data={"amount_to_capture": 3000},
)
print(response.json()["status"])
```

PHP:

```php
<?php
$ch = curl_init("https://api.fluveo.com/v1/payment_intents/pi_.../capture");
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST => true,
    CURLOPT_HTTPHEADER => [
        "Authorization: Bearer sk_test_...",
        "Idempotency-Key: order-1044-capture",
    ],
    CURLOPT_POSTFIELDS => http_build_query(["amount_to_capture" => 3000]),
]);
echo json_decode(curl_exec($ch), true)["status"];
```

A capture makes the PaymentIntent `succeeded` and sends `charge.captured` and `payment_intent.succeeded`. One capture per PaymentIntent: `final_capture` and multiple captures are not available.

To drop an authorization instead, cancel it. `cancellation_reason` is optional and takes `requested_by_customer`, `duplicate`, `fraudulent` or `abandoned`.

*Cancel*

```bash
curl https://api.fluveo.com/v1/payment_intents/pi_.../cancel \
  -H "Authorization: Bearer sk_test_..." \
  -H "Idempotency-Key: order-1044-cancel" \
  -d cancellation_reason=requested_by_customer
```

Capturing or canceling a PaymentIntent in the wrong status answers 400 `payment_intent_unexpected_state`.

## Test it

Use these test card names with a `sk_test_` key. Each use makes a new `pm_` for your store. Every test value is on [Testing](https://docs.fluveo.com/start/testing.md).

| PaymentMethod | Result |
| --- | --- |
| `pm_card_visa` | Approved (Visa ending 4242). |
| `pm_card_mastercard`, `pm_card_amex`, `pm_card_visa_debit` | Approved. |
| `pm_card_visa_chargeDeclined` | 402 `card_declined`, `decline_code` `generic_decline`. |
| `pm_card_visa_chargeDeclinedInsufficientFunds` | 402 `card_declined`, `decline_code` `insufficient_funds`. |
| `pm_card_chargeDeclinedExpiredCard` | 402 `expired_card`. |
| `pm_card_chargeDeclinedIncorrectCvc` | 402 `incorrect_cvc`. |
| `pm_card_chargeDeclinedProcessingError` | 402 `processing_error`. |
| `pm_card_authenticationRequired` | Asks for 3D Secure: confirm it with a `return_url`. |

1. Create and confirm with `pm_card_visa`: the answer is `succeeded`, and your endpoint gets `payment_intent.succeeded`.
2. Do it again with `pm_card_visa_chargeDeclined`: the answer is `402`, then confirm the same PaymentIntent with `pm_card_visa` and a new key.
3. Authorize with `capture_method=manual`, then capture `amount_to_capture=1500`: `amount_received` is `1500`.
4. Confirm `pm_card_authenticationRequired` with a `return_url`: the answer is `requires_action`. Open `next_action.redirect_to_url.url` in a browser and complete the test page.
5. Send the first request again with the same `Idempotency-Key`: you get the same PaymentIntent back, with the header `Idempotent-Replayed: true`, and no second charge.
6. Create a Pix with the payer email `succeed_immediately@example.com`: the answer is `requires_action` with a `pix_display_qr_code`, and a few seconds later the PaymentIntent is `succeeded` and your endpoint gets `payment_intent.succeeded`. See [Pix](https://docs.fluveo.com/payments/pix.md#test-it).

> **For AI agents:**
>
> One call for every method: POST /v1/payment_intents with amount (smallest unit), currency (one your account accepts; any other is a 400), payment_method_types[] and the method, confirm=true and an Idempotency-Key. Card: payment_method=pm_card_visa (test), expect 200 and status "succeeded". Pix: payment_method_data[type]=pix plus payment_method_data[billing_details] name, email, phone and tax_id; expect status "requires_action" with next_action.pix_display_qr_code. Boleto: see /payments/boleto. The currency of each method is in the table on /payments. Always send return_url when you confirm a card: a 3D Secure card confirmed without it waits in requires_action with next_action type use_fluveo_sdk, which no integration can complete. Without payment_method_types a confirm without return_url is a 400 unless the create sent automatic_payment_methods[allow_redirects]=never; send return_url on every confirm, an earlier one does not count. A 402 card_error is a decline, not a bug: the PaymentIntent is reusable. Never send raw card numbers or routing fields. With confirmation_method=manual, a card that passed 3D Secure waits in requires_confirmation (no next_action, no Charge, no event) until you call /confirm again. Fulfil only on a signed payment_intent.succeeded webhook or a fresh GET that shows succeeded; never on the browser's return and never on the create answer of a Pix or Boleto. In test mode the payer email decides when a Pix or Boleto is paid: a local part ending in succeed_immediately is paid within seconds, one ending in expire_immediately, expire_with_delay or fill_never is never paid, any other email is paid about 3 minutes after the confirm, and a Pix with no email is never paid; wait for payment_intent.succeeded and never call a /__mock/ or /v1/test_helpers/ path.

## Next

- [Use the hosted checkout page](https://docs.fluveo.com/payments/checkout.md) to let Fluveo take the card for you.
- [Refund a payment](https://docs.fluveo.com/payments/refunds.md)
- [Check webhook signatures](https://docs.fluveo.com/webhooks/signatures.md)
- [Idempotency](https://docs.fluveo.com/start/idempotency.md)
