Server-to-Server Payments

Available via: API only (private key)

Overview

Server-to-server (S2S) payments let you collect card details on your own frontend and process the payment entirely through the API. This gives you full control over the checkout experience.

Important: S2S card payments require sending card data to https://api-card.inflowpay.com (not the standard API URL). This endpoint handles PCI-compliant card tokenization.

Prerequisites

Server-to-server payments must be enabled on your account before you can use them. If S2S is not enabled, you will receive the error:

"Not authorized to create payment, server to server payment not enabled, please contact support"

Contact [email protected] to request activation.

When to Use S2S

ApproachBest For
Hosted Checkout (payment links)Simple integration, no frontend work needed
SDK (iframe card form)Custom checkout with PCI compliance handled for you
Server-to-ServerFull control, custom UI, PCI-compliant infrastructure

If your server-to-server flow is for a marketplace, creator platform, or SaaS product with multiple sellers, use Connect alongside the payment API. Connect shows how to create sub-merchants and send merchant API calls with X-On-Behalf-Of.

Payment Flow

Step 1: Create the Payment

curl -X POST https://api-card.inflowpay.com/api/server/payment \
  -H "X-Inflow-Api-Key: inflow_prod_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "products": [
      { "name": "Pro Plan", "price": 4999, "quantity": 1 }
    ],
    "currency": "EUR",
    "customerEmail": "[email protected]",
    "billingCountry": "FR",
    "postalCode": "75001",
    "purchasingAsBusiness": false,
    "firstName": "John",
    "lastName": "Doe",
    "autoConfirm": true,
    "pricingMode": "TAX_EXCLUSIVE",
    "threeDsSuccessUrl": "https://yoursite.com/3ds-success",
    "threeDsFailureUrl": "https://yoursite.com/3ds-failure",
    "metadatas": {
      "orderId": "order_12345"
    }
  }'

Request Body

FieldTypeRequiredDescription
productsarrayYesList of products (name, price in cents, quantity)
currencystringYesEUR or USD
customerEmailstringYesCustomer's email address
billingCountrystringYesISO 3166-1 alpha-2 country code (e.g., FR, DE, US)
purchasingAsBusinessbooleanYesWhether the customer is purchasing as a business
postalCodestringIf USBilling postal code (required for US customers)
businessNamestringIf businessBusiness name (required if purchasingAsBusiness is true)
taxIdstringIf businessTax ID (required if purchasingAsBusiness is true)
firstNamestringNoCustomer first name (recommended for 3DS)
lastNamestringNoCustomer last name (recommended for 3DS)
savePaymentMethodbooleanNoSave the card for future use (default: false). See Saving and reusing payment methods below.
useCustomerPaymentMethodbooleanNoUse the customer's last saved payment method (default: false). See Saving and reusing payment methods below.
autoConfirmbooleanNoAuto-confirm a new-card payment if no 3DS is required, and complete the charge automatically after a successful 3DS challenge (default: false). Not needed for a frictionless saved-card charge, which is always confirmed during creation.
captureModestringNoAUTOMATIC (default) or MANUAL — authorize now, capture later. See Manual Capture
pricingModestringNoTAX_EXCLUSIVE (default) or TAX_INCLUSIVE
statementDescriptorstringNoBank statement descriptor (max 11 characters)
threeDsSuccessUrlstringNoRedirect URL after successful 3DS
threeDsFailureUrlstringNoRedirect URL after failed 3DS
metadatasobjectNoCustom metadata

Response

{
  "id": "pay_abc123",
  "amountInCents": 4999,
  "currency": "EUR",
  "status": "INITIATION",
  "customerEmail": "[email protected]",
  "threeDsSessionUrl": null
}

Step 2: Handle 3D Secure (If Required)

If threeDsSessionUrl is not null, the customer's bank requires 3DS authentication:

  1. Redirect the customer to the threeDsSessionUrl.
  2. The customer completes authentication with their bank.
  3. The customer is redirected to your threeDsSuccessUrl or threeDsFailureUrl.

Step 3: Confirm the Payment (Only When Required)

Confirmation is needed only when the payment object shows depositStatus: "requires_confirmation" and no 3DS redirect is pending. Always read depositStatus on the create response (or on GET /api/payment/{paymentId}) before calling confirm:

SituationdepositStatus after creationWhat to do
New card, autoConfirm: false (default), no 3DSrequires_confirmationCall confirm
New card, autoConfirm: true, no 3DSsucceeded / requires_captureNothing — already confirmed
New card, 3DS required (threeDsSessionUrl set)requires_confirmationRedirect to 3DS; after success, confirm automatically if autoConfirm: true, otherwise call confirm
Saved card (useCustomerPaymentMethod: true), no 3DSsucceeded / requires_captureNothing — the card is charged during creation, whatever autoConfirm is
Saved card, 3DS requiredrequires_confirmationRedirect to 3DS; then same rule as a new card
curl -X POST https://api.inflowpay.com/api/server/payment/{paymentId}/confirm \
  -H "X-Inflow-Api-Key: inflow_prod_your_key"

Do not call confirm on a payment that is already confirmed. It is not needed, and it adds a concurrent write on the payment at the very moment the card processor's notification is being processed. Confirm is not a "make sure it went through" call — use webhooks or GET /api/payment/{paymentId} for that.

SDK users: If you use the Inflow SDK (iframe card form), the SDK handles confirmation automatically — you do not need to call this endpoint.

Step 4: Verify Payment Status

curl https://api.inflowpay.com/api/payment/{paymentId} \
  -H "X-Inflow-Api-Key: inflow_prod_your_key"

Or set up a webhook to receive automatic notifications when the payment status changes.

Saving and Reusing Payment Methods

You can save a customer's card for future purchases and reuse it without asking them to enter their card details again.

This mechanism is the building block for one-click upsells, downsells, and instant purchases — see One-Click Upsells, Downsells & Instant Purchases for the full funnel guide.

How it works

  • savePaymentMethod: true — When the customer pays with a card, that card is saved and linked to their email (customerEmail). Use this on the first payment when you want to allow one-click or faster checkout later.
  • useCustomerPaymentMethod: true — For a returning customer, use their last saved card instead of collecting new card details. The customer must use the same customerEmail as when the card was saved.

You cannot use both parameters in the same request: either you save a new card, or you use an existing one.

Typical flow

  1. First purchase — Customer enters their card. Send savePaymentMethod: true to store it.
  2. Later purchases — Customer returns (same email). Send useCustomerPaymentMethod: true and omit card details. The saved card is charged during the create call itself: the response comes back with depositStatus: "succeeded" and the payment reaches CHECKOUT_SUCCESS as soon as the processor's notification arrives. Do not call confirm — it is only needed if the bank requested 3DS (threeDsSessionUrl set) and autoConfirm is false. See One-Click Upsells for the full flow.

Important: last saved card is used

When useCustomerPaymentMethod: true is set, the system charges the last saved card for that customer. If a customer saves multiple cards over time, the most recently saved card becomes the active one.

Selecting a specific card among multiple saved payment methods is not currently supported but is planned. Contact [email protected] for updates.

Customer identification

The saved payment method is tied to customerEmail. For reuse to work, the customer must use the same email address across payments. If they have no saved card yet, useCustomerPaymentMethod: true will fail.

Charge limit: 5 attempts per customer per 30 days

A saved card may be charged at most 5 times per rolling 30 days for a given customer with your account, successful and declined attempts included. Beyond that, useCustomerPaymentMethod: true is refused with a 400 carrying the error code SAVED_PAYMENT_METHOD_ATTEMPT_LIMIT_EXCEEDED, nothing is charged, and the card has to be collected again (SDK card form or Checkout). This mirrors the card networks' rules on stored credentials: repeated charges or retries on the same card, even when they succeed, expose the merchant to excessive-retry fees and network blocks. Never retry a declined saved-card charge in a loop. Subscription payments are not blocked by this limit. See Charge Limit Reached for the response body and the fallback to apply.

Update a Payment Before Confirmation

You can update a server payment before it's finalized:

curl -X POST https://api.inflowpay.com/api/server/payment/{paymentId} \
  -H "X-Inflow-Api-Key: inflow_prod_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "products": [
      { "name": "Updated Product", "price": 5999, "quantity": 1 }
    ]
  }'

Error Handling

If the payment fails, check the lastDepositAttempt field in the payment object:

{
  "lastDepositAttempt": {
    "status": "failed",
    "amount": 4999,
    "paymentMethod": "CARD",
    "error": "card_declined",
    "attemptedAt": "2025-01-15T10:31:00.000Z"
  }
}

A 400 on payment creation with message.errorCode set to SAVED_PAYMENT_METHOD_ATTEMPT_LIMIT_EXCEEDED means the customer's saved card has reached its charge limit: collect the card again instead of retrying.

See Error Handling for a complete list of error codes.

Minimum Payment Amounts

Each currency has a minimum payment amount. Payments below these thresholds will be rejected:

CurrencyMinimum Amount
EUR€1.50 (150 cents)
USD$2.00 (200 cents)

Tips

  • Always provide firstName and lastName — this significantly improves 3DS authentication success rates.
  • Use autoConfirm: true for a smoother flow when 3DS is not required.
  • Set up webhooks instead of polling for payment status updates.
  • Tax is calculated automatically based on billingCountry.

Did this page help you?