One-Click Upsells, Downsells & Instant Purchases
Available via: API (private key). The initial card collection can be done with the SDK or server-to-server.
Introduction
One-click upsells, downsells, post-purchase offers, and instant purchases all rely on the same capability: charging a payment method that the customer has previously saved, without asking them to enter their card details again.
On Inflow, this is achieved by creating a server payment with the useCustomerPaymentMethod parameter set to true. This guide covers the complete integration:
- Save the payment method during the initial purchase
- Verify that the customer has a saved payment method
- Create the one-click payment
- Confirm the payment
How It Works
Two parameters of POST /api/server/payment control this flow:
| Parameter | When to use it | Effect |
|---|---|---|
savePaymentMethod: true | On the initial payment | The card used for the payment is saved and linked to the customer's email (customerEmail). |
useCustomerPaymentMethod: true | On subsequent payments | The payment is charged to the customer's saved card. No card details are collected. |
The saved payment method is identified by the customer's email address. For the one-click charge to succeed, the subsequent payment must use the same customerEmail and the same currency as the saved card.
A typical upsell funnel therefore looks like this:
- The customer completes an initial purchase and their card is saved.
- Your application presents an additional offer (upsell).
- If the customer accepts, your backend charges the saved card in a single API call — no card form is displayed.
- If the customer declines, you may present a cheaper alternative (downsell) and charge it the same way.
Step 1: Save the Payment Method During the Initial Purchase
A one-click charge requires an existing saved payment method. There are three ways to obtain one, depending on how you collect card details:
| Integration | How to save the card | Documentation |
|---|---|---|
| SDK card form (recommended for web funnels) | Create the initial payment with savePaymentMethod: true, then mount the SDK CardElement as usual. The SDK handles tokenization, 3D Secure, and confirmation. | React, Vanilla JS |
| Full server-to-server | Add savePaymentMethod: true to your standard S2S payment creation call on https://api-card.inflowpay.com. | Server-to-Server Payments |
| Card setup without a charge (signup, free trial) | Create a no-charge PaymentSetup and complete it with the SDK. | Free Trial & Card Setup |
Customer consent (SDK integrations)
When savePaymentMethod: true is set, the SDK card form displays a "save my card" checkbox to the customer. The card is only saved if the customer checks this box.
If your funnel depends on the card always being saved — for example, when the upsell step is a core part of your flow — contact [email protected] to enable card saving without the consent checkbox on your account.
Step 2: Verify That the Customer Has a Saved Payment Method
Before presenting a one-click offer, confirm that the customer actually has a saved payment method. Attempting a useCustomerPaymentMethod payment for a customer without one results in an error (see Error Handling).
You have two options:
Option A — Webhooks (recommended). Listen for payment.* success events (card saved during a payment) or payment_setup.completed (card saved without a charge), and record in your database that the customer has a saved payment method. See Webhooks.
Option B — On-demand lookup. Query the API at the time of the offer. Note that the list endpoint takes the customer id (cus_...), not the email, so resolve the id first:
# 1. Resolve the customer id from the email
curl "https://api.inflowpay.xyz/api/customer/email/{customerEmail}" \
-H "X-Inflow-Api-Key: your_private_key"
# 2. List the saved payment methods for the relevant currency
curl "https://api.inflowpay.xyz/api/customer/{customerId}/payment-methods?currency=EUR" \
-H "X-Inflow-Api-Key: your_private_key"API reference: GET /api/customer/email/{email}, GET /api/customer/{customerId}/payment-methods.
Step 3: Create the One-Click Payment
When the customer accepts the offer, create a payment with useCustomerPaymentMethod: true. The request must respect three rules:
- Same
customerEmailas when the card was saved. - Same currency as the saved card — a card saved for
EURcannot be charged inUSD. - No card data and no save flag — do not send
card,tokenIntentId, orsavePaymentMethod. CombiningsavePaymentMethodortokenIntentIdwithuseCustomerPaymentMethodreturns a400error; acardobject is ignored.
Product prices are expressed in cents (1999 = €19.99), as everywhere in the API.
curl -X POST https://api.inflowpay.xyz/api/server/payment \
-H "X-Inflow-Api-Key: your_private_key" \
-H "Content-Type: application/json" \
-d '{
"products": [{ "name": "Premium Add-on", "price": 1999, "quantity": 1 }],
"currency": "EUR",
"customerEmail": "[email protected]",
"billingCountry": "FR",
"purchasingAsBusiness": false,
"useCustomerPaymentMethod": true,
"metadatas": { "funnelStep": "upsell_1" }
}'This call uses the standard base URL
https://api.inflowpay.xyz. Theapi-card.inflowpay.combase URL is only required when transmitting raw card data, which is not the case here.
Response
{
"id": "pay_def456",
"amountInCents": 1999,
"currency": "EUR",
"status": "CHECKOUT_PENDING",
"depositStatus": "requires_confirmation",
"customerEmail": "[email protected]",
"customerPaymentMethod": {
"id": "cpm_abc123",
"type": "card",
"cardBrand": "visa",
"cardLast4": "4242"
}
}Unlike a new-card payment (returned with status INITIATION), a saved-card charge is prepared during creation. The payment is therefore returned with status CHECKOUT_PENDING and depositStatus: "requires_confirmation" — it is waiting for your confirmation call.
Step 4: Confirm the Payment
autoConfirmdoes not apply to this flow. A payment created withuseCustomerPaymentMethod: trueis never confirmed automatically. You must always call the confirm endpoint — if this step is skipped, the customer is never charged.
curl -X POST https://api.inflowpay.xyz/api/server/payment/{paymentId}/confirm \
-H "X-Inflow-Api-Key: your_private_key"API reference: POST /api/server/payment/{paymentId}/confirm.
After confirmation, track the final payment status through webhooks (payment.* events) rather than polling. A charge on a saved card can still be declined by the customer's bank — handle payment.failed events and offer a card re-entry fallback in that case.
Implementing a Downsell
A downsell is technically identical to an upsell: only your funnel logic differs. If the customer declines the initial offer, present a cheaper alternative and repeat Step 3 and Step 4 with different products (for example with "funnelStep": "downsell_1" in metadatas).
You can chain as many one-click offers as your funnel requires — each one is an independent payment.
Rules and Limitations
| Rule | Detail |
|---|---|
| Mutually exclusive parameters | savePaymentMethod and useCustomerPaymentMethod cannot both be true in the same request (400 error). Either you save a new card, or you use an existing one. |
Same customerEmail | The saved card is tied to the customer's email for your account. The one-click payment must use the exact same customerEmail as when the card was saved. |
| Currency must match | Each saved card is linked to a currency. A useCustomerPaymentMethod payment in EUR requires a card saved for EUR. |
| Last saved card is charged | If a customer saves multiple cards over time, the most recently saved one becomes the default and is charged. Selecting a card per payment is not supported, but the default card can be changed with set-default. |
| No card fields allowed | Sending tokenIntentId together with useCustomerPaymentMethod: true returns a 400 error; a card object is ignored. |
| Confirmation is a separate call | autoConfirm is ignored — always call POST /api/server/payment/{paymentId}/confirm. |
Error Handling: No Saved Payment Method
If the customer has no saved payment method for the requested currency, the payment creation fails with:
"couldn't process payment in the useCustomerPaymentMethod context, the customer [email protected] has no payment method for currency EUR"In this case, fall back to collecting the card normally: display the SDK card form with savePaymentMethod: true (so that the next offer can be one-click), or send the customer through a PaymentSetup.
Best Practices
- Store your funnel context (offer id, step, campaign) in
metadatas— it is returned on the payment object and in webhook payloads. - Check for a saved payment method before rendering the one-click button, so that customers without a saved card never trigger the error above.
- One-click charges also work for subscriptions:
useCustomerPaymentMethodis available on subscription initiation.