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-intentsIdempotency-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-methodsIdempotency-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}/verifyFor 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
| Event | When |
|---|---|
setup_intent.created | After step 1. |
setup_intent.succeeded | The provider tokenised successfully. |
setup_intent.failed | The customer abandoned or the provider failed. |
customer.payment_method_attached | After step 3. |
payment_method.verified | After explicit verify. |