Accept recurring payments
Enroll a customer into a fixed-amount recurring subscription with Fintoc's Checkout Session API.
Build a subscription flow that enrolls a payment method and charges the customer automatically on a fixed schedule.
To accept recurring payments with Fintoc, you complete three steps:
- On your backend, create a
Checkout Sessionwithflow: subscription. - Redirect the customer to complete the enrollment at the Fintoc-hosted checkout page.
- Handle post-enrollment and recurring payment events (webhooks).
The following diagram shows how Fintoc interacts with both your backend and your frontend:

Create a Checkout Session
The Checkout Session object represents your intent to enroll a payment method for recurring charges, and to create a subscription with a fixed amount and periodicity.
Using your Secret Key, create a Checkout Session on your backend with flow set to subscription.
Server
curl --request POST "https://api.fintoc.com/v2/checkout_sessions" \
--header "Authorization: YOUR_TEST_SECRET_API_KEY" \
--header "Content-Type: application/json" \
--data-raw '{
"flow": "subscription",
"amount": 350000,
"currency": "CLP",
"success_url": "https://merchant.com/success",
"cancel_url": "https://merchant.com/cancel",
"payment_method_types": [
"pac"
],
"customer_data": {
"tax_id": {
"type": "cl_rut",
"value": "11.111.111-1"
},
"name": "Felipe Castro",
"email": "[email protected]",
"metadata": {}
},
"line_items": [
{
"price_data": {
"currency": "CLP",
"unit_amount": 350000,
"product_data": {
"name": "Plan 1"
},
"recurring": {
"interval": "month",
"interval_count": 1
}
},
"quantity": 1
}
],
"metadata": {
"subscription_external_id": "sub_987654321"
}
}'const { Fintoc } = require('fintoc');
const fintoc = new Fintoc('YOUR_TEST_SECRET_API_KEY');
const checkoutSession = await fintoc.checkoutSessions.create({
flow: 'subscription',
amount: 350000,
currency: 'CLP',
success_url: 'https://merchant.com/success',
cancel_url: 'https://merchant.com/cancel',
payment_method_types: ['pac'],
customer_data: {
tax_id: {
type: 'cl_rut',
value: '11.111.111-1'
},
name: 'Felipe Castro',
email: '[email protected]',
metadata: {}
},
line_items: [
{
price_data: {
currency: 'CLP',
unit_amount: 350000,
product_data: {
name: 'Plan 1'
},
recurring: {
interval: 'month',
interval_count: 1
}
},
quantity: 1
}
],
metadata: {
subscription_external_id: 'sub_987654321'
}
});from fintoc import Fintoc
client = Fintoc('YOUR_TEST_SECRET_API_KEY')
checkout_session = client.checkout_sessions.create(
flow='subscription',
amount=350000,
currency='CLP',
success_url='https://merchant.com/success',
cancel_url='https://merchant.com/cancel',
payment_method_types=['pac'],
customer_data={
'tax_id': {
'type': 'cl_rut',
'value': '11.111.111-1',
},
'name': 'Felipe Castro',
'email': '[email protected]',
'metadata': {},
},
line_items=[
{
'price_data': {
'currency': 'CLP',
'unit_amount': 350000,
'product_data': {
'name': 'Plan 1',
},
'recurring': {
'interval': 'month',
'interval_count': 1,
},
},
'quantity': 1,
}
],
metadata={
'subscription_external_id': 'sub_987654321',
},
)Fintoc responds with the Checkout Session object. Store its id and redirect_url to continue the flow:
{
"id": "cs_li5531onlFDi235",
"object": "checkout_session",
"mode": "test",
"flow": "subscription",
"status": "created",
"amount": 350000,
"currency": "CLP",
"payment_method_types": ["pac"],
"customer": {
"id": "cus_NffrFeUfNV2Hib",
"object": "customer",
"name": "Felipe Castro",
"email": "[email protected]",
"metadata": {},
"tax_id": {
"type": "cl_rut",
"value": "11.111.111-1"
}
},
"line_items": [
{
"price": {
"product": {
"name": "Plan 1",
"description": "Fixed-amount monthly plan"
},
"currency": "CLP",
"unit_amount": 350000,
"recurring": {
"interval": "month",
"interval_count": 1
}
},
"quantity": 1
}
],
"metadata": {
"subscription_external_id": "sub_987654321"
},
"success_url": "https://merchant.com/success",
"cancel_url": "https://merchant.com/cancel",
"redirect_url": "https://pay.fintoc.com/checkout/cs_li5531onlFDi235"
}The response includes a redirect_url attribute. In the next step, you redirect the customer to this location to complete the subscription.
The following table describes the parameters you send when creating a Checkout Session:
| Parameter | Example | Description |
|---|---|---|
amount | 350000 | Required. A positive integer representing the value to charge, in the smallest currency unit (for example, 1000 for $1000 CLP, since CLP has no minor unit, or 2476 for $24.76 MXN, since MXN has a minor unit). See the currencies page for details. |
currency | CLP | Required. Three-letter ISO 4217 currency code for the recurring payments. One of CLP or MXN. |
flow | subscription | Required. Flow type for the session. One of payment, setup, or subscription. |
success_url | https://merchant.com/success | Required. URL to redirect the customer to after a successful enrollment. |
cancel_url | https://merchant.com/cancel | Required. URL to redirect the customer to if they cancel the enrollment and return to your website. |
customer | cus_3B2bODrQFje7ZVkT69xyaTSDwXQ | Required if no customer_data. ID of an existing Customer. |
customer_data | (object) | Required if no If a |
payment_method_types | ["pac"] | List of allowed payment methods during enrollment. One or more of pac (charges on bank accounts in Chile), direct_debit (charges on bank accounts in Mexico), and card. |
line_items | (array) | Required. Array of items the customer subscribes to. Each item contains quantity and either price or price_data. |
metadata | {"subscription_external_id": "sub_987654321"} | Set of key-value pairs you can attach to an object. Useful for storing additional information about the object in a structured format. |
Include customer data (required for subscriptions)
When creating a Checkout Session with flow: subscription, you must include customer information. You can do this either by referencing an existing customer ID (customer) or by sending customer_data to create one inline:
| Attribute | Type | Description |
|---|---|---|
tax_id | object | Required if no If a |
name | string | Full name of the customer. |
email | string | Required if no tax_id. Email address of the customer. |
metadata | object | Custom data that stores additional information about the customer, for example internal IDs, CRM references, or tags. |
Include an items list (required for subscriptions)
When creating a Checkout Session with flow: subscription, you must include the items the customer subscribes to. This information lets Fintoc display the items on the checkout page and show only the payment methods available for specific products.
| Attribute | Type | Description |
|---|---|---|
quantity | integer | Required. Number of units of this item being purchased. |
price_data | object | Required. Data used to generate a new recurring price inline. |
Each item in line_items must include price_data.
price_data object
price_data object| Attribute | Type | Description |
|---|---|---|
product_data | object | Required. Data used to generate a new Product object inline. |
currency | string | Three-letter ISO 4217 currency code for the subscription. One of CLP or MXN. |
unit_amount | integer | Required. A positive integer representing the price per unit, in the smallest currency unit (for example, 1000 for $1000 CLP, since CLP has no minor unit, or 2476 for $24.76 MXN, since MXN has a minor unit). |
recurring | object | Required. Recurring configuration. interval is one of day, week, month, or year, and interval_count is the number of intervals between charges (for example, 1 for monthly when interval is month). |
product_data object
product_data object| Attribute | Type | Description |
|---|---|---|
name | string | Required. Name of the product or service being purchased. |
image_url | string | Image URL for the product. Must be an HTTPS URL. Recommended aspect ratio: 9:4. |
Redirect the customer to complete the enrollment
Next, redirect the customer to the Fintoc-hosted checkout page using the redirect_url. After the customer completes the enrollment, Fintoc redirects the customer back to your site: to the success_url on success, or to the cancel_url if they cancel.
Client
window.location.assign(REDIRECT_URL_FROM_YOUR_BACKEND);Handle post-session events
Once a Checkout Session finishes, you handle the result in your frontend and complete the subscription in your backend. For your backend, you use the events that Fintoc sends through webhooks.
Complete the subscription on your backend
Fintoc sends a checkout_session.finished event when the session completes.
In a subscription flow, this event includes information about the session and references to the subscription and payment_method created during enrollment.
{
"id": "evt_a4xK32BanKWYn",
"object": "event",
"type": "checkout_session.finished",
"data": {
"id": "cs_li5531onlFDi235",
"flow": "subscription",
"customer": {
"id": "cus_NffrFeUfNV2Hib",
"object": "customer",
"name": "Felipe Castro",
"email": "[email protected]",
"metadata": {},
"tax_id": {
"type": "cl_rut",
"value": "11.111.111-1"
}
},
"payment_method_types": ["pac"],
"status": "finished",
"payment_status": "succeeded",
"subscription": "sub_NffrFeUfNV2Hib",
"payment_method": "pm_NffrFeUfNV2Hib"
}
}You should handle the following post-session events:
| Event | Description | Action |
|---|---|---|
checkout_session.finished | Sent when a subscription Checkout Session reaches a final state. | Activate the subscription on your side based on the final status, and store the created ids (subscription, payment_method, customer). |
checkout_session.expired | Sent when a session expires. | Offer the customer another attempt to subscribe. |
payment_intent.succeeded | Sent when a payment intent succeeds, like a charge on a bank account or card. | Confirm to your customer that the subscription charge succeeded. |
payment_intent.failed | Sent when a payment intent fails. | Offer the customer another attempt to pay the subscription. |
Test your integration
To confirm that your integration works correctly, you can simulate subscriptions and scheduled recurring payments without moving any money.
1) Create a subscription Checkout Session using test user credentials
Using your test mode Secret Key, create a Checkout Session with flow: subscription on your backend. Then complete the enrollment on the Fintoc-hosted checkout page with the following credentials:
Test credentials
- Username (RUT):
11.111.111-1 - Password:
jonsnow
2) Handle simulated scheduled payments of the subscription
In test mode, the subscription's scheduled payments are simulated so you can verify how your integration handles success and failure without moving any money. You handle them through the same invoice.* and payment_intent.* events described in the Manage invoices section below: a successful charge emits invoice.payment_succeeded and payment_intent.succeeded, and a failed one emits invoice.payment_failed and payment_intent.failed.
Test mode is not yet available for recurring payments in Mexico.
Manage invoices
When a subscription is created after a successful checkout enrollment, Fintoc automatically generates an Invoice for each billing cycle. An invoice represents the amount the customer owes for a given period. Fintoc attempts to collect payment for the invoice using the enrolled payment method.
For full details on invoices, see the Invoice Object.
Invoices in the subscription flow
After the checkout_session.finished event, the subscription becomes active and Fintoc creates the first invoice. From that point on, you should handle the following invoice-related events alongside the post-session events described above:
| Event | Description | Action |
|---|---|---|
invoice.created | Sent when Fintoc generates a new invoice for a billing cycle. | Log the invoice and update your records. |
invoice.finalized | Sent when Fintoc finalizes the invoice and it becomes ready for payment. | Update your records accordingly. |
invoice.payment_succeeded | Sent when Fintoc collects the invoice payment. | Confirm the payment to your customer and extend access. |
invoice.payment_failed | Sent when a payment attempt for the invoice fails. | Notify your customer and offer an alternative payment method. |
Month 1: Right after the subscription is created, Fintoc generates the first invoice and attempts payment immediately. You'll receive invoice.created, followed by invoice.finalized, then invoice.payment_succeeded and payment_intent.succeeded on success.
Month 2 onwards: At each billing cycle renewal (based on the subscription's billing_cycle_anchor), Fintoc creates a new invoice in draft status. After 1 hour, Fintoc attempts payment automatically. On success you receive invoice.payment_succeeded. On failure, invoice.payment_failed.
Test invoice creation with status draft
draftTo test an invoice that is created in draft status, create a subscription with a line item using the product name sandbox_draft:
Server
curl --request POST "https://api.fintoc.com/v2/checkout_sessions" \
--header "Authorization: YOUR_TEST_SECRET_API_KEY" \
--header "Content-Type: application/json" \
--data-raw '{
"flow": "subscription",
"amount": 350000,
"currency": "CLP",
"success_url": "https://merchant.com/success",
"cancel_url": "https://merchant.com/cancel",
"payment_method_types": ["pac"],
"customer_data": {
"tax_id": {
"type": "cl_rut",
"value": "11.111.111-1"
},
"name": "Felipe Castro",
"email": "[email protected]"
},
"line_items": [
{
"price_data": {
"currency": "CLP",
"unit_amount": 350000,
"product_data": {
"name": "sandbox_draft"
},
"recurring": {
"interval": "month",
"interval_count": 1
}
},
"quantity": 1
}
]
}'const { Fintoc } = require('fintoc');
const fintoc = new Fintoc('YOUR_TEST_SECRET_API_KEY');
const checkoutSession = await fintoc.checkoutSessions.create({
flow: 'subscription',
amount: 350000,
currency: 'CLP',
success_url: 'https://merchant.com/success',
cancel_url: 'https://merchant.com/cancel',
payment_method_types: ['pac'],
customer_data: {
tax_id: {
type: 'cl_rut',
value: '11.111.111-1'
},
name: 'Felipe Castro',
email: '[email protected]'
},
line_items: [
{
price_data: {
currency: 'CLP',
unit_amount: 350000,
product_data: {
name: 'sandbox_draft'
},
recurring: {
interval: 'month',
interval_count: 1
}
},
quantity: 1
}
]
});from fintoc import Fintoc
client = Fintoc('YOUR_TEST_SECRET_API_KEY')
checkout_session = client.checkout_sessions.create(
flow='subscription',
amount=350000,
currency='CLP',
success_url='https://merchant.com/success',
cancel_url='https://merchant.com/cancel',
payment_method_types=['pac'],
customer_data={
'tax_id': {
'type': 'cl_rut',
'value': '11.111.111-1',
},
'name': 'Felipe Castro',
'email': '[email protected]',
},
line_items=[
{
'price_data': {
'currency': 'CLP',
'unit_amount': 350000,
'product_data': {
'name': 'sandbox_draft',
},
'recurring': {
'interval': 'month',
'interval_count': 1,
},
},
'quantity': 1,
}
],
)Fintoc creates the Checkout Session:
{
"id": "cs_li5531onlFDi235",
"object": "checkout_session",
"mode": "test",
"flow": "subscription",
"status": "created",
"amount": 350000,
"currency": "CLP",
"payment_method_types": ["pac"],
"customer": {
"id": "cus_NffrFeUfNV2Hib",
"object": "customer",
"name": "Felipe Castro",
"email": "[email protected]",
"metadata": {},
"tax_id": {
"type": "cl_rut",
"value": "11.111.111-1"
}
},
"line_items": [
{
"price": {
"product": {
"name": "sandbox_draft",
"description": "Fixed-amount monthly plan"
},
"currency": "CLP",
"unit_amount": 350000,
"recurring": {
"interval": "month",
"interval_count": 1
}
},
"quantity": 1
}
],
"success_url": "https://merchant.com/success",
"cancel_url": "https://merchant.com/cancel",
"redirect_url": "https://pay.fintoc.com/checkout/cs_li5531onlFDi235"
}Fintoc creates the invoice in draft status, so you can edit its items with the Add Lines endpoint before the invoice transitions to the next status.