Save a customer's payment method for future payments

Use Fintoc's Checkout Session API to save a customer's payment method so their next payments take fewer steps.

Build a flow that saves a customer's payment method during a payment or through a standalone setup session.

For payment flows where customers pay often, like ride sharing or delivery apps, you can let your customers save a payment method for future payments. A saved payment method can be a card or a bank account, so their next payments take fewer steps.

You can save a customer's payment method in two ways:

  1. Save during a payment: create a Checkout Session with flow set to payment and save_payment_method set to enabled. Your customer gets the option to save the payment method for future payments.
  2. Save without a current payment: create a Checkout Session with flow set to setup to enroll a payment method without processing a payment.

Option 1: Save during a payment (recommended)

The customer pays and their payment method is saved in a single step.

Create or choose a Customer

Every saved payment method must belong to a Customer. If you already have one, reuse its id. Otherwise, you can either create it first with the Customers API or send customer_data in the Checkout Session to create the Customer inline.

Server

curl --request POST "https://api.fintoc.com/v2/customers" \
  --header "Authorization: YOUR_SECRET_API_KEY" \
  --header "Content-Type: application/json" \
  --data-raw '{
    "name": "Felipe Castro",
    "email": "[email protected]"
  }'
const { Fintoc } = require('fintoc');

const fintoc = new Fintoc('YOUR_SECRET_API_KEY');

const customer = await fintoc.customers.create({
  name: 'Felipe Castro',
  email: '[email protected]'
});
from fintoc import Fintoc

client = Fintoc('YOUR_SECRET_API_KEY')

customer = client.customers.create(
    name='Felipe Castro',
    email='[email protected]'
)

Fintoc responds with the created Customer:

{
  "id": "cus_3B2bODrQFje7ZVkT69xyaTSDwXQ",
  "object": "customer",
  "address": null,
  "created_at": "2026-03-16T19:41:17Z",
  "email": "[email protected]",
  "metadata": {},
  "mode": "live",
  "name": "Felipe Castro",
  "phone": null,
  "tax_id": null,
  "updated_at": "2026-03-16T19:41:17Z"
}

Save the returned id (e.g. cus_3B2bODrQFje7ZVkT69xyaTSDwXQ).

If a Customer with the same tax_id already exists, the request returns a 409 Conflict error. To reuse an existing Customer, send its id in customer instead of customer_data.

Create a Checkout Session with save_payment_method

Create a Checkout Session with the customer ID, flow set to payment, and save_payment_method set to enabled. Fintoc processes the payment and, if your customer chooses to save the payment method during the flow, stores their credentials for future use.

Server

curl --request POST "https://api.fintoc.com/v2/checkout_sessions" \
  --header "Authorization: YOUR_SECRET_API_KEY" \
  --header "Content-Type: application/json" \
  --data-raw '{
    "flow": "payment",
    "customer": "cus_3B2bODrQFje7ZVkT69xyaTSDwXQ",
    "amount": 5000,
    "currency": "CLP",
    "success_url": "https://merchant.com/success",
    "cancel_url": "https://merchant.com/cancel",
    "save_payment_method": "enabled",
    "payment_method_types": ["bank_transfer"]
  }'
const { Fintoc } = require('fintoc');

const fintoc = new Fintoc('YOUR_SECRET_API_KEY');

const checkoutSession = await fintoc.checkoutSessions.create({
  flow: 'payment',
  customer: 'cus_3B2bODrQFje7ZVkT69xyaTSDwXQ',
  amount: 5000,
  currency: 'CLP',
  success_url: 'https://merchant.com/success',
  cancel_url: 'https://merchant.com/cancel',
  save_payment_method: 'enabled',
  payment_method_types: ['bank_transfer']
});
from fintoc import Fintoc

client = Fintoc('YOUR_SECRET_API_KEY')

checkout_session = client.checkout_sessions.create(
    flow='payment',
    customer='cus_3B2bODrQFje7ZVkT69xyaTSDwXQ',
    amount=5000,
    currency='CLP',
    success_url='https://merchant.com/success',
    cancel_url='https://merchant.com/cancel',
    save_payment_method='enabled',
    payment_method_types=['bank_transfer']
)

Fintoc responds with the created CheckoutSession, including its redirect_url:

{
  "id": "cs_VvO7kpUMWlq2N10B",
  "object": "checkout_session",
  "flow": "payment",
  "status": "created",
  "amount": 5000,
  "currency": "CLP",
  "customer": {
    "id": "cus_3B2bODrQFje7ZVkT69xyaTSDwXQ",
    "object": "customer",
    "email": "[email protected]",
    "metadata": {},
    "name": "Felipe Castro",
    "tax_id": null
  },
  "save_payment_method": "enabled",
  "payment_method_types": ["bank_transfer"],
  "success_url": "https://merchant.com/success",
  "cancel_url": "https://merchant.com/cancel",
  "redirect_url": "https://pay.fintoc.com/checkout/cs_VvO7kpUMWlq2N10B",
  "metadata": {}
}
ParameterExampleDescription
flowpaymentRequired. Flow the session runs. One of payment, setup, or subscription.
amount5000Required. A positive integer representing the amount to charge in the smallest currency unit. CLP has no minor unit, so 5000 charges $5000 CLP.
currencyCLPRequired. Three-letter ISO 4217 currency code. Only CLP supports saving a payment method.
success_urlhttps://merchant.com/successRequired. URL Fintoc redirects your customer to after a successful payment.
cancel_urlhttps://merchant.com/cancelRequired. URL Fintoc redirects your customer to if they cancel the payment and return to your website.
customer"cus_3B2bODrQFje7ZVkT69xyaTSDwXQ"Required if no customer_data. ID of an existing Customer.
customer_data{"email": "[email protected]"}

Required if no customer. Data for inline customer creation. Send at least one of email or tax_id.

If a Customer with the same tax_id already exists, the request returns a 409 Conflict error. To reuse an existing Customer, send its id in customer instead of customer_data.

metadata{"order": "987654321"}Set of key-value pairs you can attach to an object. Useful for storing additional information about the object in a structured format.
payment_method_types["bank_transfer"]

Required. Payment method types available for the session. For the payment flow, one or more of bank_transfer or card.

Only bank_transfer supports saving a payment method while collecting a payment.

payment_method_optionsbank_transfer object with sender_account informationOptions for a specific payment method type, like preselecting a specific bank. See the complete example in the API Reference.
save_payment_methodenabledWhether your customer gets the option to save the payment method during the flow. One of enabled or disabled. Defaults to disabled.

Redirect the customer

Use the redirect_url returned in the response to redirect your customer to the Fintoc-hosted page, where they complete the payment and can choose to save their credentials for future payments.

Client

window.location.assign(REDIRECT_URL_FROM_YOUR_BACKEND);

Handle post-payment events

Always use webhooks to determine the final outcome. Your customer may close the tab, lose connection, or never reach your success_url.

Checkout Session events

  • checkout_session.finished: Sent when a Checkout Session reaches a final successful state. The event includes the payment outcome and, if your customer opted in, the saved payment_method.

    Example data object:

    {
        "id": "cs_VvO7kpUMWlq2N10B",
        "flow": "payment",
        "mode": "test",
        "amount": 5000,
        "object": "checkout_session",
        "status": "finished",
        "currency": "CLP",
        "customer": {
            "id": "cus_3B2bODrQFje7ZVkT69xyaTSDwXQ",
            "mode": "test",
            "name": null,
            "email": "[email protected]",
            "phone": null,
            "object": "customer",
            "tax_id": {
                "type": "cl_rut",
                "value": "11.111.111-1"
            },
            "address": null,
            "metadata": {},
            "created_at": "2026-03-16T19:41:17Z"
        },
        "metadata": {},
        "cancel_url": "https://merchant.com/cancel",
        "created_at": "2026-03-30T20:47:41Z",
        "expires_at": "2026-03-31T20:47:41Z",
        "line_items": null,
        "success_url": "https://merchant.com/success",
        "redirect_url": "https://pay.fintoc.com/checkout/cs_VvO7kpUMWlq2N10B",
        "setup_intent": null,
        "subscription": null,
        "session_token": "cs_VvO7kpUMWlq2N10B_sec_UuSuPYRuEQTcWKXT6ba9LDTh",
        "customer_email": null,
        "payment_method": "pm_3BgHP7aSqsqLiEcotFQfyx7Of8u",
        "business_profile": null,
        "payment_resource": {
            "payment_intent": {
                "id": "pi_3BgHFvey4Qj4QZvTTZwyLL9OfSA",
                "mode": "test",
                "amount": 5000,
                "object": "payment_intent",
                "status": "succeeded",
                "currency": "CLP",
                "metadata": {},
                "created_at": "2026-03-30T20:48:12Z",
                "expires_at": null,
                "next_action": null,
                "error_reason": null,
                "payment_type": "bank_transfer",
                "reference_id": "575930",
                "widget_token": null,
                "customer_email": null,
                "sender_account": {
                    "type": "checking_account",
                    "number": "19831940978",
                    "holder_id": "11.111.111-1",
                    "institution_id": "cl_banco_falabella"
                },
                "business_profile": null,
                "transaction_date": "2026-03-30T20:49:00Z",
                "recipient_account": {
                    "type": "checking_account",
                    "number": "922358017",
                    "holder_id": "11.111.111-1",
                    "institution_id": "cl_banco_security"
                },
                "payment_type_options": {}
            }
        },
        "save_payment_method": "enabled",
        "payment_method_types": [
            "bank_transfer"
        ],
        "payment_method_options": {}
    }

Payment Method events

If your customer chose to save the method, Fintoc also sends a payment_method.activated event. The event's data field contains a PaymentMethod like this:

{
  "id": "pm_3BgHP7aSqsqLiEcotFQfyx7Of8u",
  "object": "payment_method",
  "card": null, 
  "created_at": "2026-03-30T20:49:12Z",
  "customer": "cus_3B2bODrQFje7ZVkT69xyaTSDwXQ",
  "mode": "test",
  "metadata": {},
  "bank_transfer": {
    "account_holder_id": "11.111.111-1",
    "account_number": "19831940978",
    "account_type": "checking_account",
    "institution_id": "cl_banco_falabella",
    "status": "active"
  },
  "type": "bank_transfer"
}

Subscribe to the following post-session events:

EventDescriptionRecommended action
checkout_session.finishedSession successfully completed.Store customer + payment_method. Update your records based on the final status.
checkout_session.expiredSession expired before completion.Allow your customer to retry.
payment_method.activatedMethod becomes available for future charges.Enable "pay with saved method" experiences for that customer.
payment_intent.succeededPayment succeeded.Fulfill the order.
payment_intent.failedPayment failed.Ask your customer to retry or use another payment method.

Option 2: Save a method without a current payment

Create a Setup session

The Checkout Session object represents your intent to save a payment method without processing a payment.

Using your Secret Key, create a Checkout Session from your backend with flow set to setup:

Server

curl --request POST "https://api.fintoc.com/v2/checkout_sessions" \
  --header "Authorization: YOUR_SECRET_API_KEY" \
  --header "Content-Type: application/json" \
  --data-raw '{
    "flow": "setup",
    "currency": "CLP",
    "success_url": "https://merchant.com/success",
    "cancel_url": "https://merchant.com/cancel",
    "customer_data": {
      "email": "[email protected]"
    },
    "metadata": {}
  }'
const { Fintoc } = require('fintoc');

const fintoc = new Fintoc('YOUR_SECRET_API_KEY');

const checkoutSession = await fintoc.checkoutSessions.create({
  flow: 'setup',
  currency: 'CLP',
  success_url: 'https://merchant.com/success',
  cancel_url: 'https://merchant.com/cancel',
  customer_data: {
    email: '[email protected]'
  },
  metadata: {}
});
from fintoc import Fintoc

client = Fintoc('YOUR_SECRET_API_KEY')

checkout_session = client.checkout_sessions.create(
    flow='setup',
    currency='CLP',
    success_url='https://merchant.com/success',
    cancel_url='https://merchant.com/cancel',
    customer_data={
        'email': '[email protected]'
    },
    metadata={}
)

Fintoc responds with the created CheckoutSession, including its redirect_url:

{
  "id": "cs_VvO7kpUMWlq2N10B",
  "object": "checkout_session",
  "flow": "setup",
  "status": "created",
  "currency": "CLP",
  "customer": {
    "id": "cus_3B2bODrQFje7ZVkT69xyaTSDwXQ",
    "object": "customer",
    "email": "[email protected]",
    "metadata": {},
    "name": null,
    "tax_id": null
  },
  "success_url": "https://merchant.com/success",
  "cancel_url": "https://merchant.com/cancel",
  "redirect_url": "https://pay.fintoc.com/checkout/cs_VvO7kpUMWlq2N10B",
  "metadata": {}
}
ParameterExampleDescription
flowsetupRequired. Flow the session runs. One of payment, setup, or subscription.
currencyCLPRequired. Three-letter ISO 4217 currency code. Only CLP supports the setup flow.
success_urlhttps://merchant.com/successRequired. URL Fintoc redirects your customer to after a successful setup.
cancel_urlhttps://merchant.com/cancelRequired. URL Fintoc redirects your customer to if they cancel the setup and return to your website.
customer"cus_3B2bODrQFje7ZVkT69xyaTSDwXQ"Required if no customer_data. ID of an existing Customer.
customer_data{"email": "[email protected]"}

Required if no customer. Data for inline customer creation. Send at least one of email or tax_id.

If a Customer with the same tax_id already exists, the request returns a 409 Conflict error. To reuse an existing Customer, send its id in customer instead of customer_data.

metadata{"order": "987654321"}Set of key-value pairs you can attach to an object. Useful for storing additional information about the object in a structured format.
payment_method_types["pac", "direct_debit", "card"]Payment method types available for the session. For the setup flow, one or more of pac, direct_debit, or card. Send this parameter if your customer already selected the method on your website.
payment_method_optionspac object with sender_account informationOptions for a specific payment method type, like preselecting a specific bank. See the complete example in the API Reference.
📘

Send payment_method_options to preselect a bank for your customer

You can create the Checkout Session with a specific bank by sending its institution_id inside the sender_account of payment_method_options. Your customer can then only save a bank account of that institution. You can also send the customer's Chilean tax ID (RUT) as the holder_id, so the flow pre-fills it.

We recommend this option when your customer selects their bank in your flow before you create the session, so they do not have to select a bank twice.

Redirect the customer

After creating a session, you receive a redirect_url that you use to redirect your customer so they can enroll their bank account as a saved payment method.

Client

window.location.assign(REDIRECT_URL_FROM_YOUR_BACKEND);

Handle post-session events

Subscribe to checkout_session.finished, checkout_session.expired, and payment_method.activated for the standalone setup flow.


Create a payment session with a saved payment method

If your customer has a saved bank_transfer payment method, you can create a payment Checkout Session that uses the saved method. Fintoc redirects your customer directly to the bank approval step, without asking them to log in with their bank credentials.

Server

curl --request POST "https://api.fintoc.com/v2/checkout_sessions" \
  --header "Authorization: YOUR_SECRET_API_KEY" \
  --header "Content-Type: application/json" \
  --data-raw '{
    "amount": 100000,
    "currency": "CLP",
    "flow": "payment",
    "success_url": "https://merchant.com/success",
    "cancel_url": "https://merchant.com/cancel",
    "customer": "cus_3B2bODrQFje7ZVkT69xyaTSDwXQ",
    "payment_method": "pm_1NkL7QKs215JZ1LyW4c1m9Ut",
    "payment_method_types": [
      "bank_transfer"
    ]
  }'
const { Fintoc } = require('fintoc');

const fintoc = new Fintoc('YOUR_SECRET_API_KEY');

const checkoutSession = await fintoc.checkoutSessions.create({
  amount: 100000,
  currency: 'CLP',
  flow: 'payment',
  success_url: 'https://merchant.com/success',
  cancel_url: 'https://merchant.com/cancel',
  customer: 'cus_3B2bODrQFje7ZVkT69xyaTSDwXQ',
  payment_method: 'pm_1NkL7QKs215JZ1LyW4c1m9Ut',
  payment_method_types: ['bank_transfer']
});
from fintoc import Fintoc

client = Fintoc('YOUR_SECRET_API_KEY')

checkout_session = client.checkout_sessions.create(
    amount=100000,
    currency='CLP',
    flow='payment',
    success_url='https://merchant.com/success',
    cancel_url='https://merchant.com/cancel',
    customer='cus_3B2bODrQFje7ZVkT69xyaTSDwXQ',
    payment_method='pm_1NkL7QKs215JZ1LyW4c1m9Ut',
    payment_method_types=['bank_transfer']
)

Fintoc responds with the created CheckoutSession, including its redirect_url:

{
  "id": "cs_VvO7kpUMWlq2N10B",
  "object": "checkout_session",
  "flow": "payment",
  "status": "created",
  "amount": 100000,
  "currency": "CLP",
  "customer": {
    "id": "cus_3B2bODrQFje7ZVkT69xyaTSDwXQ",
    "object": "customer",
    "email": "[email protected]",
    "metadata": {},
    "name": "Felipe Castro",
    "tax_id": null
  },
  "payment_method": "pm_1NkL7QKs215JZ1LyW4c1m9Ut",
  "payment_method_types": ["bank_transfer"],
  "success_url": "https://merchant.com/success",
  "cancel_url": "https://merchant.com/cancel",
  "redirect_url": "https://pay.fintoc.com/checkout/cs_VvO7kpUMWlq2N10B",
  "metadata": {}
}

Redirect the customer

Use the redirect_url to redirect your customer to the Fintoc-hosted page, where they go directly to the bank approval step without logging in with their bank credentials.

Client

window.location.assign(REDIRECT_URL_FROM_YOUR_BACKEND);

Handle post-payment events

After the payment, Fintoc sends checkout_session.finished, checkout_session.expired, payment_intent.succeeded, or payment_intent.failed, depending on the outcome:

EventDescriptionRecommended action
checkout_session.finishedSession successfully completed.Store customer + payment_method. Update your records based on the final status.
checkout_session.expiredSession expired before completion.Allow your customer to retry.
payment_intent.succeededPayment succeeded.Fulfill the order.
payment_intent.failedPayment failed.Ask your customer to retry or use another payment method.

Test your integration

Using your test mode API Secret Key, you can create Checkout Sessions that simulate successful and failed outcomes without moving any money.

This lets you validate your full setup and payment flow end-to-end:

  • Your backend API requests (creating sessions and handling responses)
  • The redirect flow from your frontend to the redirect_url and back to the success_url or cancel_url after the setup or payment
  • The webhooks for post-session events that save the customer, the payment_method, and the payment result
    • After a successful setup or payment that saves a payment_method, test creating a payment session with the customer's saved method

To learn how to trigger specific scenarios, use the test credentials and special test values described in our testing guide.

For a bank_transfer flow in test mode, open the redirect_url, select the test bank, and log in with the test credentials user_good and pass_good. This combination simulates a successful enrollment and payment.

After a successful run, Fintoc sends a checkout_session.finished event with status set to finished:

{
  "id": "cs_VvO7kpUMWlq2N10B",
  "object": "checkout_session",
  "flow": "payment",
  "mode": "test",
  "status": "finished",
  "customer": {
    "id": "cus_3B2bODrQFje7ZVkT69xyaTSDwXQ",
    "object": "customer",
    "email": "[email protected]",
    "metadata": {},
    "name": "Felipe Castro",
    "tax_id": null
  },
  "payment_method": "pm_3BgHP7aSqsqLiEcotFQfyx7Of8u"
}

If your customer opted in, Fintoc also sends a payment_method.activated event with status set to active:

{
  "id": "pm_3BgHP7aSqsqLiEcotFQfyx7Of8u",
  "object": "payment_method",
  "customer": "cus_3B2bODrQFje7ZVkT69xyaTSDwXQ",
  "mode": "test",
  "type": "bank_transfer",
  "bank_transfer": {
    "account_holder_id": "11.111.111-1",
    "account_number": "19831940978",
    "account_type": "checking_account",
    "institution_id": "cl_banco_falabella",
    "status": "active"
  }
}