Skip to content

Collect a payment method

If hosted checkout doesn’t fit (you want fully embedded UX), you’ll collect the payment method client-side using the provider’s tokenisation SDK and attach the resulting token via Paylera.

The pattern

1. Your frontend asks Paylera for a SetupIntent client secret.
2. The frontend uses the provider's SDK to collect the card and tokenise it.
3. The frontend posts the resulting token to your backend.
4. Your backend attaches the token via Paylera.

PCI scope: as long as the card never touches your servers (and the provider’s SDK is loaded from their CDN), you remain SAQ-A.

1. Create a setup intent

POST /v1/setup-intents
Idempotency-Key: <uuid>
{
"customer_id": "cus_…",
"provider": "stripe",
"payment_method_types": ["card"]
}

The response includes:

{
"id": "si_…",
"provider": "stripe",
"client_secret": "seti_…_secret_…",
"expires_at": "2026-05-06T12:35:00Z"
}

client_secret is what your frontend uses with the provider SDK. It’s short-lived (10 minutes) and single-use.

2. Collect the card client-side

Using Stripe Elements as an example:

<form id="payment-form">
<div id="payment-element"></div>
<button type="submit">Save card</button>
</form>
<script>
const stripe = Stripe(STRIPE_PUBLISHABLE_KEY);
const elements = stripe.elements({ clientSecret: CLIENT_SECRET_FROM_PAYLERA });
const paymentElement = elements.create("payment");
paymentElement.mount("#payment-element");
document.getElementById("payment-form").addEventListener("submit", async (e) => {
e.preventDefault();
const { error, setupIntent } = await stripe.confirmSetup({
elements,
confirmParams: { return_url: "https://yourapp.example/billing/method-saved" }
});
if (error) {
// show error.message
} else {
// setupIntent.payment_method is the token; POST it to your backend
}
});
</script>

PayPal, Adyen, and Braintree have analogous SDKs. The shape is the same: client secret in, tokenised method out.

3. Attach to the customer

Your backend, on receiving the token from your frontend:

POST /v1/customers/{id}/payment-methods
Idempotency-Key: <uuid>
{
"type": "card",
"token": "pm_…",
"set_as_default": true
}

The response includes the Paylera-assigned payment_method_id, the last 4 digits, brand, and expiry. Persist the ID against your user record.

3DS during setup

If the customer’s card requires 3DS for setup (some banks require it even on $0 auths), the provider’s SDK handles the redirect / challenge in step 2. The token returned to your backend is already verified — no extra step on Paylera’s side.

Bank accounts (ACH / SEPA)

For US bank accounts, the SetupIntent flow uses Plaid (instant verify) or micro-deposits (3-day verify). Your frontend integrates Plaid Link and posts the resulting Plaid token to your backend; the rest of the flow is identical.

For SEPA, the customer’s IBAN is collected directly and verified synchronously.

Apple Pay / Google Pay

Both are tokenised by the provider’s wallet SDK. The token you get is a provider-specific payment method ID; pass it to Paylera with type: "wallet":

{ "type": "wallet", "wallet": "apple_pay", "token": "pm_…" }

Verifying the method

For added safety on B2B / high-ticket flows:

POST /v1/payment-methods/{id}/verify

For cards: a $0 auth (where supported) or $1 auth-and-reverse. For bank accounts: micro-deposits if not already verified by Plaid.

What about declines on attach?

Card tokens that the provider returned can still fail attachment if the provider rejects them (revoked, network error, fraud rule). The response is a 422 with the structured payment_method.attach_failed problem and the provider’s reason code. Show it to the user; let them try a different card.

Webhook events

EventWhen
setup_intent.createdAfter step 1.
setup_intent.succeededThe provider tokenised successfully.
setup_intent.failedThe customer abandoned or the provider failed.
customer.payment_method_attachedAfter step 3.
payment_method.verifiedAfter explicit verify.