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:

  1. Save the payment method during the initial purchase
  2. Verify that the customer has a saved payment method
  3. Create the one-click payment
  4. Confirm the payment

How It Works

Two parameters of POST /api/server/payment control this flow:

ParameterWhen to use itEffect
savePaymentMethod: trueOn the initial paymentThe card used for the payment is saved and linked to the customer's email (customerEmail).
useCustomerPaymentMethod: trueOn subsequent paymentsThe 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:

  1. The customer completes an initial purchase and their card is saved.
  2. Your application presents an additional offer (upsell).
  3. If the customer accepts, your backend charges the saved card in a single API call — no card form is displayed.
  4. 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:

IntegrationHow to save the cardDocumentation
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-serverAdd 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.com/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.com/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 customerEmail as when the card was saved.
  • Same currency as the saved card — a card saved for EUR cannot be charged in USD.
  • No card data and no save flag — do not send card, tokenIntentId, or savePaymentMethod. Combining savePaymentMethod or tokenIntentId with useCustomerPaymentMethod returns a 400 error; a card object is ignored.

Product prices are expressed in cents (1999 = €19.99), as everywhere in the API.

curl -X POST https://api.inflowpay.com/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.com. The api-card.inflowpay.com base 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": "succeeded",
  "threeDsSessionUrl": null,
  "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 submitted to the card network during creation. When the create call returns, the charge has already been attempted: depositStatus is "succeeded" (or "requires_capture" with manual capture) and the payment moves to CHECKOUT_SUCCESS a moment later, once the processor's notification is received.


Step 4: Do Not Call Confirm — Track the Result

Do not call POST /api/server/payment/{paymentId}/confirm after a saved-card charge. The payment is confirmed during creation, whatever the value of autoConfirm. A confirm call on an already-confirmed payment does nothing useful and adds a concurrent write on the payment while the processor's notification is being handled.

Track the final 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.

If the bank requires 3D Secure

Occasionally the customer's bank requires authentication even for a saved card. The create response then carries a non-null threeDsSessionUrl and depositStatus: "requires_confirmation":

  1. Redirect the customer to the threeDsSessionUrl (pass threeDsSuccessUrl and threeDsFailureUrl on creation).
  2. After a successful challenge:

We recommend sending autoConfirm: true on one-click payments so that the 3DS case needs no extra call either. The rule of thumb for every server payment: call confirm only when depositStatus is "requires_confirmation" and no 3DS redirect is pending.


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 with different products (for example with "funnelStep": "downsell_1" in metadatas), then track the result as in Step 4.

You can chain as many one-click offers as your funnel requires — each one is an independent payment.


Rules and Limitations

RuleDetail
Mutually exclusive parameterssavePaymentMethod 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 customerEmailThe 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 matchEach saved card is linked to a currency. A useCustomerPaymentMethod payment in EUR requires a card saved for EUR.
Last saved card is chargedIf 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 allowedSending tokenIntentId together with useCustomerPaymentMethod: true returns a 400 error; a card object is ignored.
No confirm callThe saved card is charged during creation, whatever the value of autoConfirm. Call POST /api/server/payment/{paymentId}/confirm only in the 3DS case described in Step 4, when depositStatus is "requires_confirmation".
Charge limit per customerA customer's saved card can be charged at most 5 times per rolling 30 days through your account, successful and declined attempts included. The 6th attempt is refused with a 400 (see Error Handling: Charge Limit Reached) and the card must be collected again through the SDK or Checkout. Subscription payments (first payment, upgrades, renewals) are not blocked by this limit.

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.

Error Handling: Charge Limit Reached

Card networks cap how often a stored card may be charged or retried, and repeated attempts on the same card, whether they succeed or get declined, expose your account to excessive-retry fees and to network blocks. Inflow therefore refuses a useCustomerPaymentMethod payment once the customer's saved card has already been charged 5 times in the last 30 days with your account, counting every attempt, successful or declined:

{
  "statusCode": 400,
  "timestamp": "2026-10-01T09:12:44.000Z",
  "path": "/api/server/payment",
  "correlationId": "req_abc123",
  "message": {
    "errorCode": "SAVED_PAYMENT_METHOD_ATTEMPT_LIMIT_EXCEEDED",
    "message": "This customer's saved payment method has already been charged 5 times in the last 30 days (successful and failed attempts included). Card network rules cap charges on a stored card; collect the card again through the SDK or Checkout for this payment.",
    "details": {
      "attempts": 5,
      "limit": 5,
      "windowDays": 30,
      "nextAllowedAt": "2026-10-18T14:03:10.000Z"
    }
  }
}

The payment is not created: nothing is charged and the refused call does not count towards the limit. nextAllowedAt is when the oldest counted attempt leaves the 30-day window.

What to do:

  • Do not retry the one-click call. Fall back to collecting the card: display the SDK card form or send the customer through Checkout for this purchase. A payment where the customer enters the card is never subject to this limit.
  • Never replay a decline in a loop. A saved-card charge declined by the bank (payment.failed webhook) should not be retried within the day, and a hard decline (do_not_honor, lost_card, stolen_card, invalid_account, pickup_card) must not be retried at all. Each retry counts as an attempt.
  • Plan your funnel within the limit. An initial purchase followed by a few upsells fits comfortably; per-item micro-charges or automatic retries do not. Batch several items into one payment rather than charging the card once per item.

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.
  • Keep the number of saved-card charges per customer low and spaced out: the charge limit is 5 attempts per rolling 30 days, declines included, and going beyond it on a card network's side gets merchants fined or blocked. Never retry a declined saved-card charge automatically.
  • One-click charges also work for subscriptions: useCustomerPaymentMethod is available on subscription initiation.

Did this page help you?