> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.recurrente.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.recurrente.com/_mcp/server.

# Save and reuse payment methods

> Tokenize a card with a secure checkout and charge it later with variable amounts

Use this flow when you need to save a customer's card and charge it later without asking them to enter card details again. It works well for variable monthly billing, usage-based charges, add-ons to a subscription, or renewals controlled by your system.

The customer always enters their card on a Recurrente-hosted checkout. Your system stores only the `payment_method_id` returned by Recurrente and uses it to create future charges.

> **Note**
>
> Before saving a card, make sure the customer accepts clear terms for future charges: frequency, how the amount is calculated, when charges happen, and how they can cancel or update their payment method.

## Flow overview

1. Create or identify the customer in Recurrente.
2. Create a checkout with `mode: "setup"` to tokenize the card without charging it.
3. Send the `checkout_url` to the customer.
4. When the customer completes the checkout, store the `payment_method.id`.
5. Use `POST /api/one_time_payments` to charge that saved method when you know the final amount.

## Step 1: Create or identify the customer

If you already have a Recurrente customer, pass their `customer_id` when creating the tokenization checkout. If you only have the `user_id`, you can pass that instead.

```bash
curl -X POST https://app.recurrente.com/api/customers \
  -H "X-SECRET-KEY: your_secret_key" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "customer@example.com",
    "full_name": "Ana Lopez"
  }'
```

The response includes the customer `id`:

```json
{
  "id": "cus_abc123",
  "user_id": "us_123456",
  "email": "customer@example.com",
  "name": "Ana Lopez"
}
```

## Step 2: Create a tokenization checkout

Create a checkout with `mode: "setup"`. This checkout does not charge anything; it only saves the card for future use.

When saving a new card, Recurrente verifies the account with a zero-amount authorization, without capturing funds. An approved authorization with a permanent token generates `setup_intent.succeeded`; a decline or failed verification generates `setup_intent.cancelled`, and the card is not enabled for charges. Saving a card does not guarantee that future charges will succeed or replace authentication that a later payment may require.

```bash
curl -X POST https://app.recurrente.com/api/checkouts \
  -H "X-SECRET-KEY: your_secret_key" \
  -H "Content-Type: application/json" \
  -d '{
    "mode": "setup",
    "customer_id": "cus_abc123",
    "success_url": "https://yoursite.com/payment-method-saved",
    "cancel_url": "https://yoursite.com/payment-method-cancelled",
    "metadata": {
      "billing_agreement_id": "ba_789"
    }
  }'
```

Recurrente returns a hosted URL:

```json
{
  "id": "ch_eegw9j5zgqoae3ms",
  "checkout_url": "https://app.recurrente.com/checkout-session/ch_eegw9j5zgqoae3ms"
}
```

Redirect the customer to `checkout_url`, or send it by email, WhatsApp, or inside your app.

## Step 3: Store the payment\_method\_id

When the customer completes the checkout, Recurrente emits `setup_intent.succeeded`. The saved method is inside `data.checkout.payment_method`.

```json
{
  "eventType": "setup_intent.succeeded",
  "eventId": "act_123",
  "data": {
    "event_type": "setup_intent.succeeded",
    "status": "succeeded",
    "checkout": {
      "id": "ch_eegw9j5zgqoae3ms",
      "status": "paid",
      "payment_method": {
        "id": "pay_m_7v5ie3pw",
        "type": "card",
        "card": {
          "last4": "4242",
          "network": "visa"
        }
      }
    },
    "customer": {
      "id": "cus_abc123",
      "email": "customer@example.com"
    }
  }
}
```

Store `data.checkout.payment_method.id`. This is the token you will use for future charges.

> **Note**
>
> If you do not receive webhooks, retrieve the checkout after the customer returns to your `success_url`: `GET /api/checkouts/{id}`. Once the checkout is complete, the response includes `payment_method.id`.

```bash
curl https://app.recurrente.com/api/checkouts/ch_eegw9j5zgqoae3ms \
  -H "X-SECRET-KEY: your_secret_key"
```

```json
{
  "id": "ch_eegw9j5zgqoae3ms",
  "status": "paid",
  "payment_method": {
    "id": "pay_m_7v5ie3pw",
    "type": "card",
    "card": {
      "last4": "4242",
      "network": "visa"
    }
  }
}
```

## Step 4: Charge later with a variable amount

When your system knows the amount to charge, create a one-time payment with the saved `payment_method_id`. Use a unique idempotency key per charge attempt to avoid duplicates if you need to retry the request.

```bash
curl -X POST https://app.recurrente.com/api/one_time_payments \
  -H "X-SECRET-KEY: your_secret_key" \
  -H "Idempotency-Key: invoice-2026-06-cus-abc123" \
  -H "Content-Type: application/json" \
  -d '{
    "payment_method_id": "pay_m_7v5ie3pw",
    "items": [
      {
        "name": "June usage",
        "amount_in_cents": 87500,
        "currency": "GTQ",
        "quantity": 1
      }
    ]
  }'
```

Successful response:

```json
{
  "object": "one_time_payment",
  "id": "on_123456789",
  "status": "paid"
}
```

If the bank declines the charge, Recurrente returns an error and you should not reuse the same `Idempotency-Key` for a new charge attempt. Create a new attempt with a different key when the customer confirms they want to retry or updates their payment method.

## Variable charges tied to a subscription

If the customer already has a subscription and you want to add variable charges, you have two options.

To immediately charge the subscription's saved payment method:

```bash
curl -X POST https://app.recurrente.com/api/one_time_payments \
  -H "X-SECRET-KEY: your_secret_key" \
  -H "Idempotency-Key: su-nehndm7j-extra-june" \
  -H "Content-Type: application/json" \
  -d '{
    "subscription_id": "su_nehndm7j",
    "amount_in_cents": 5000,
    "description": "Additional usage charge",
    "mode": "now"
  }'
```

To add the charge to the next subscription cycle:

```bash
curl -X POST https://app.recurrente.com/api/one_time_payments \
  -H "X-SECRET-KEY: your_secret_key" \
  -H "Content-Type: application/json" \
  -d '{
    "subscription_id": "su_nehndm7j",
    "amount_in_cents": 5000,
    "description": "Additional usage charge",
    "mode": "next_cycle"
  }'
```

`mode: "next_cycle"` creates a pending `invoice_item`. It also supports negative amounts for credits.

## Update the saved card

When the customer needs to change cards, create another `mode: "setup"` checkout. After completion, store the new `payment_method.id`.

If the method belongs to a subscription, update it with:

```bash
curl -X PUT https://app.recurrente.com/api/subscriptions/su_nehndm7j \
  -H "X-SECRET-KEY: your_secret_key" \
  -H "Content-Type: application/json" \
  -d '{
    "payment_method_id": "pay_m_new123"
  }'
```

If you only use variable charges with `POST /api/one_time_payments`, store the new `payment_method_id` in your system and use it for future charges.

## Considerations

* `POST /api/one_time_payments` requires one-time payments to be enabled for your account.
* The `payment_method_id` must belong to a customer in your account.
* Future charges can fail even if tokenization succeeded. Handle bank errors and let the customer update their card.
* Real webhooks are only sent for production events. To validate the complete tokenization flow, use a LIVE key; the `setup` checkout does not charge an amount.
* Never collect or store raw card numbers directly. Always use the Recurrente-hosted checkout.