Taking Payments
Taking Payments
Payments are collected using payment intents, which represent a payment from creation through to completion. There are two ways to collect a payment:
- Client-side confirmation — your server creates the intent, your frontend collects card details via the JS SDK and confirms. Use this when the customer needs to enter their card.
- Server-side confirmation — your server creates and confirms the intent in one step using a saved payment method. Use this to charge a card you've already saved.
Client-side confirmation
This is the most common flow. Your server creates the payment intent, passes the client_secret to the frontend, and the JS SDK handles card collection and confirmation.
1. Add a server endpoint to create payment intents (server)
Add an endpoint to your server that creates a payment intent and returns the client_secret to the frontend. The client_secret authorizes the customer to view and confirm the payment — do not log it or expose it beyond the customer's browser session.
Here's the Synapto API call your endpoint needs to make. The Idempotency-Key header lets you safely retry the request if it fails — see Idempotency.
curl -X POST https://api.synaptopay.com/v1/accounts/acct_YOUR_ACCOUNT/payment-intents \
-H "Authorization: Api-Key $API_KEY" \
-H "Idempotency-Key: YOUR_UNIQUE_KEY" \
-H "Content-Type: application/json" \
-d '{
"amount": "10000",
"currency": "GBP"
}'{
"name": "accounts/acct_YOUR_ACCOUNT/payment-intents/pi_PAYMENT_INTENT_ID",
"amount": "10000",
"currency": "GBP",
"state": "REQUIRES_PAYMENT_METHOD",
"client_secret": "pi_PAYMENT_INTENT_ID_secret_SECRET",
"create_time": "2025-11-17T16:49:39Z",
"update_time": "2025-11-17T16:49:39Z"
}Your endpoint should return the client_secret from the response to the frontend. In the example below, we assume this endpoint is POST /api/create-payment-intent and returns { "clientSecret": "pi_..._secret_..." }.
2. Collect card details and confirm (frontend)
The frontend side has three steps:
- Get a
client_secret— call your server endpoint from step 1 - Create and mount a payment element — this renders a secure card input form (an iframe) inside an empty element on your page. The customer's raw card data never touches your servers.
- Confirm the payment — when the customer clicks your submit button, call
confirmPayment()to tokenize the card and process the payment
<!-- Any empty element — the SDK renders the card input form here -->
<div id="payment-container"></div>
<!-- Your own submit button — the SDK does not provide one -->
<button id="pay-btn">Pay</button>
<div id="result"></div>
<script src="https://js.synaptopay.com/synapto.js"></script>
<script>
const syn = Synapto("pk_YOUR_PUBLISHABLE_KEY");
// 1. Get a client_secret from your server
fetch("/api/create-payment-intent", { method: "POST" })
.then((res) => res.json())
.then(({ clientSecret }) => {
// 2. Create and mount the payment element
const paymentElement = syn.paymentElement(clientSecret);
paymentElement.mount("#payment-container");
// 3. Confirm when the customer clicks pay
document.getElementById("pay-btn").addEventListener("click", async () => {
const { paymentIntent, error } = await paymentElement.confirmPayment();
if (error) {
// Display the error — the customer can correct their details and try again
document.getElementById("result").textContent = error;
} else {
// Redirect to your confirmation page — use webhooks to confirm payment status (step 3)
window.location.href = "/order-confirmation";
}
});
});
</script>When confirmPayment() is called, the SDK tokenizes the card details, sends them to Synapto, and returns the result. If 3D Secure is required, the SDK handles it automatically — see JS SDK for customization options.
The result always includes the paymentIntent object with its current state, and an error string if something went wrong (e.g. invalid card details, card declined, or 3D Secure failure). When there is an error, the payment intent returns to REQUIRES_PAYMENT_METHOD — the customer can correct their card details and click pay again without creating a new payment intent.
3. Get notified of payment (server)
Listen for the payment_intent.succeeded webhook event to confirm the payment on your server. See Webhooks for setup instructions.
Server-side confirmation
If you've already saved a customer's card (see Saving Cards), you can charge it server-side with no frontend involved. Set confirm: true and pass the saved payment_method:
curl -X POST https://api.synaptopay.com/v1/accounts/acct_YOUR_ACCOUNT/payment-intents \
-H "Authorization: Api-Key $API_KEY" \
-H "Idempotency-Key: YOUR_UNIQUE_KEY" \
-H "Content-Type: application/json" \
-d '{
"amount": "10000",
"currency": "GBP",
"customer": "accounts/acct_YOUR_ACCOUNT/customers/cus_CUSTOMER_ID",
"payment_method": "accounts/acct_YOUR_ACCOUNT/payment-methods/pm_PAYMENT_METHOD_ID",
"confirm": true
}'{
"name": "accounts/acct_YOUR_ACCOUNT/payment-intents/pi_PAYMENT_INTENT_ID",
"amount": "10000",
"currency": "GBP",
"state": "SUCCEEDED",
"client_secret": "pi_PAYMENT_INTENT_ID_secret_SECRET",
"customer": "accounts/acct_YOUR_ACCOUNT/customers/cus_CUSTOMER_ID",
"payment_method": "accounts/acct_YOUR_ACCOUNT/payment-methods/pm_PAYMENT_METHOD_ID",
"latest_charge": "accounts/acct_YOUR_ACCOUNT/charges/ch_CHARGE_ID",
"create_time": "2025-11-17T16:49:39Z",
"update_time": "2025-11-17T16:49:42Z"
}Payment intent states
| State | Description |
|---|---|
REQUIRES_PAYMENT_METHOD | No payment method attached yet |
REQUIRES_CONFIRMATION | Has a payment method, awaiting confirmation |
PROCESSING | Payment is being processed |
SUCCEEDED | Payment completed successfully |
CANCELED | Payment was canceled |
Key fields
| Field | Description |
|---|---|
amount | Total amount in smallest currency unit (e.g. 10000 = 100.00 GBP) |
currency | Three-letter ISO currency code |
customer | Resource name of the customer being charged (optional) |
payment_method | Resource name of a saved payment method (optional — omit for client-side confirmation) |
confirm | Set to true to immediately attempt the charge (requires payment_method) |
client_secret | Token for the frontend to confirm the payment via the JS SDK |
metadata | Key-value pairs for your own use (max 50 keys, key max 40 chars, value max 500 chars) |
Settlement
When a charge succeeds, the funds are settled and automatically transferred to the bank account provided during onboarding. There is no separate payout step — settlement happens automatically. Note that settlement is not instantaneous; there will be a delay between a successful charge and the funds arriving in the bank account.
Charges
A charge represents a single attempt to move money. When a payment intent is confirmed, a charge is created automatically — the payment intent's latest_charge field contains the charge resource name. You don't create charges directly.
You need the charge resource name to issue a refund. You can also use charges for reconciliation — each charge records the amount, the payment method used, and how much has been refunded.
If a payment intent is retried (for example, after a failed attempt), a new charge is created for each attempt. Use ListCharges filtered by payment_intent to retrieve all charges for a given payment intent.
| Field | Description |
|---|---|
state | PENDING, SUCCEEDED, or FAILED |
amount | Amount in smallest currency unit |
currency | Three-letter ISO currency code |
payment_method | The payment method that was charged |
payment_intent | The payment intent that created this charge |
amount_refunded | Amount refunded against this charge (in smallest currency unit) |
refunded | true if the charge has been fully refunded |
Updated about 1 month ago