Accept a payment
Learn how to use Fintoc's Payment Initiation API
There are three steps to accept payments using Fintoc:
- On your backend, create a
PaymentIntent
using your Secret Key - Open the widget on your frontend using your Public Key
- Handle post-payments events
Create a payment
The Payment Intent object represents your intent to collect a payment from a customer and tracks state changes throughout the payment process.
Before creating a payment, you must decide how to operate with Fintoc. The recommended way βand the defaultβ is that Fintoc collects your payments and then makes payouts to your bank account based on a payout schedule. If you want the money to be sent directly to a specific bank account, see the setup direct payments guide.
Using your Secret Key, create a PaymentIntent
on your server with an amount
and currency
.
curl --request POST "https://api.fintoc.com/v1/payment_intents" \
-- header 'Authorization: sk_live_0000000000000000' \
-- header 'Content-Type: application/json' \
--data-raw '{
"amount": 2476,
"currency": "CLP",
"customer_email":"[email protected]"
}'
const fetch = require('node-fetch');
const payment_intent = {
amount: 1000,
currency: 'clp'
}
fetch('https://api.fintoc.com/v1/payment_intents', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'sk_live_000000000000'
},
body: JSON.stringify(payment_intent),
},
)
import requests
payment_intent = {
'amount': 1000,
'currency': 'clp'
headers = {
'Accept': 'application/json', 'Authorization': 'sk_live_000000000000'
}
r = requests.post(
'https://api.fintoc.com/v1/payment_intents',
json=payment_intent,
headers=headers
)
require 'net/http'
require 'uri'
require 'json'
payment_intent = {
amount: 1000,
currency: 'clp'
}
uri = URI("https://api.fintoc.com/v1/payment_intents")
header = {
Accept: 'application/json', Authorization: 'sk_live_000000000000'
}
http = Net::HTTP.new(uri.host, uri.port)
request = Net::HTTP::Post.new(uri.request_uri, header)
request.body = payment_intent.to_json
response = http.request(request)
If you want to create a Payment Intent for Mexico, change the currency to MXN
.
Currencies are represented as integers
The Fintoc API represents currencies in its smallest possible units with no decimals (as an integer). That means that an amount of MXN 10.29 gets represented by Fintoc as 1029. You can read more about currencies here.
Parameter | Example | Explanation |
---|---|---|
amount | 2476 | Amount of money that needs to be paid. It's represented as integer with no decimals in the smallest possible unit of the currency you are using. If your payment uses Chilean peso, an amount of CLP 2476 is represented as 2476. If your payment uses Mexican peso, an amount of MXN 24.76 is represented as 2476. Read here to learn more. |
currency | CLP | Currency that is being used for the payment. We currently support CLP and MXN. |
Always create the PaymentIntent from your server
Always create the
PaymentIntent
on the server side, a trusted environment, as opposed to the client. This prevents malicious customers from being able to choose their own prices.
Response when creating a Payment Intent
After making the request to create the Payment Intent, Fintoc should respond with something like this:
{
"id": "pi_BO381oEATXonG6bj",
"object": "payment_intent",
"amount": 1000,
"currency": "CLP",
"widget_token": "pi_BO381oEATXonG6bj_sec_a4xK32BanKWYn",
"status": "created",
"metadata": {},
"reference_id": null,
"recipient_account": {
"holder_id": "183917137",
"number": "123456",
"type": "checking_account",
"institution_id": "cl_banco_de_chile"
},
"sender_account": null,
"created_at": "2021-10-15T15:23:11.474Z",
}
In the response, you should receive the widget_token
attribute. This attribute is returned only when the Payment Intent is created. After that, every request to get an existing Payment Intent from Fintoc will return the widget_token
attribute empty. In the following step, you'll use this attribute to set up the Fintoc widget.
The values of the attributes reference_id
and sender_account
will be null
. These fields will populate once the payment starts to be processed.
Once the transfer is completed successfully, the reference_id
field will correspond to the bank's operation number assigned to the transfer.
Widget token expiration
A Payment Intent expires after 10 minutes in Chile and 15 minutes in Mexico. After the mentioned time of the Payment Intent being created, the widget won't accept the
widget_token
of the expired Payment Intent, and you will have to create a new one.
Open the widget
Once you create the Payment Intent, you need to use the widget_token
to setup the widget for a payment.
The Fintoc Widget is the client-side component that your customers will interact with to pay using Fintoc. The Fintoc Widget will handle credential validation, multi-factor authentication, and error handling for each institution that we support.
Use your Public Key and the Payment Intent widget token to configure the widget.
<!DOCTYPE html>
<html lang="en">
<head>
<title>Fintoc Demo</title>
<script src="https://js.fintoc.com/v1/"></script>
</head>
<body>
<script>
window.onload() => {
const widget = Fintoc.create({
holderType: 'individual',
widgetToken: 'pi_XXXXXXXX_sec_YYYYYYYY',
product: 'payments',
country: 'cl',
publicKey: 'pk_live_0000000000',
onSuccess: () => {},
onExit: () => {},
});
widget.open();
};
</script>
</body>
</html>
If you want a Mexican payment, change the country parameter to mx
.
You can read more about the widget and its configurations in the Widget guide.
Use our Widget Webview if you are building a mobile app
If you are integrating Fintoc into an iOS or Android application, you can use our WebView integration.
Handle post-payments events
Once a Payment Intent finishes, you handle the payment result in your frontend and complete the payment in your backend. For your frontend you will use the widget callback, and for your backend you will use the events sent by webhooks.
Use webhooks events to complete payments
Your customer could close the browser window or quit the app before the
onSuccess
widget callback executes. For this reason, you should always use the payment intent successful event to handle post-payments actions like sending an order confirmation email to your customer, logging the sale in a database, or starting a shipping workflow.
Handle the payment result on your frontend
Once a Payment Intent finishes successfully, the widget executes the onSuccess
callback. You need to pass this function to the widget upon creation.
With this callback, you can decide what to do with your user's frontend once the payment is complete, for example:
- Redirect the user to a post-sale or post-payment view
- Show the user a success screen.
Don't use this callback as a payment confirmation
You shouldn't trust on the
onSuccess
callback as a confirmation for a successful payment, as the frontend is an insecure realm and a malicious third party may execute a JavaScript function that simulates that the transfer was executed successfully.For a more comprehensive validation mechanism, we strongly encourage integrating webhooks and subscribing to the payment intent successful event. By implementing webhooks, you can ensure timely and accurate updates on payment statuses, enhancing the overall security and reliability of your payment confirmation process. You can also validate the payment's authenticity by leveraging the Retrieve Payment Intent endpoint, but this can be subject to rate limits and fail.
Handle errors
You don't only need to handle succeeded payments because payments can also fail. For example, your customer doesn't have funds in their bank account to complete the payment.
When a payment fails or is rejected by your customer, the widget executes the onExit
callback. With this callback, you can handle errors on your frontend. For example, you can invite your customer to use another payment method.
Complete the payment on your backend
Fintoc sends a payment_intent.succeeded
event when the payment completes. Use the follow the webhook guide to receive these events and run actions, such as sending an order confirmation email to your customer, logging the sale in a database, or starting a shipping workflow.
The payment_intent.succeeded
event looks like this:
{
"id": "evt_eNkjd5zjCpM5PaGy",
"type": "payment_intent.succeeded",
"mode": "live",
"created_at": "2024-10-17T18:13:26.750Z",
"data": {
"id": "pi_7WG4XwODSJndlDg6",
"mode": "live",
"amount": 1000,
"object": "payment_intent",
"status": "succeeded",
"currency": "CLP",
"metadata": {'order_id':"7n4dFs3z"},
"created_at": "2024-10-17T18:11:39Z",
"error_reason": null,
"reference_id": "173021729",
"widget_token": null,
"customer_email": null,
"sender_account": {
"type": "checking_account",
"number": "12345678",
"holder_id": "123456789",
"institution_id": "cl_banco_bice"
},
"transaction_date": null,
"recipient_account": {
"type": "checking_account",
"number": "1111111",
"holder_id": "498574628",
"institution_id": "cl_banco_de_chile"
}
},
"object": "event"
}
You should handle the following events when using our Payment Initiation product:
Event | Description | Action |
---|---|---|
checkout_session.finished | Sent when a customer has successfully completed a payment. | Complete the order based on the status of the related payment |
checkout_session.expired | Sent when a session expires due to the customer not attempting a payment. | Offer the customer another attempt to pay. |
Handle empty fields
Some payments may have
null
fields if the user exits mid-process. For example, the sender account may benull
if the user quits before selecting a bank. Account fornulls
appropriately when processing payments and saving payment objects in your database.
Updated 30 days ago