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:

  1. Get a client_secret — call your server endpoint from step 1
  2. 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.
  3. 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

StateDescription
REQUIRES_PAYMENT_METHODNo payment method attached yet
REQUIRES_CONFIRMATIONHas a payment method, awaiting confirmation
PROCESSINGPayment is being processed
SUCCEEDEDPayment completed successfully
CANCELEDPayment was canceled

Key fields

FieldDescription
amountTotal amount in smallest currency unit (e.g. 10000 = 100.00 GBP)
currencyThree-letter ISO currency code
customerResource name of the customer being charged (optional)
payment_methodResource name of a saved payment method (optional — omit for client-side confirmation)
confirmSet to true to immediately attempt the charge (requires payment_method)
client_secretToken for the frontend to confirm the payment via the JS SDK
metadataKey-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.

FieldDescription
statePENDING, SUCCEEDED, or FAILED
amountAmount in smallest currency unit
currencyThree-letter ISO currency code
payment_methodThe payment method that was charged
payment_intentThe payment intent that created this charge
amount_refundedAmount refunded against this charge (in smallest currency unit)
refundedtrue if the charge has been fully refunded