Vanilla JS Integration

The integration follows the same two-step flow as every SDK integration: your backend creates the payment, your frontend mounts the card form with the returned paymentId.

Step 1: Create a Payment (Backend)

From your backend, call POST /api/server/payment with your private API key (X-Inflow-Api-Key), using the default base URL and no card field — the SDK collects the card in the browser.

// Your own backend route — name it whatever you like.
// Its job: call the Inflow API server-side and return the paymentId.
app.post('/api/create-payment', async (req, res) => {
  const response = await fetch('https://api.inflowpay.xyz/api/server/payment', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-Inflow-Api-Key': process.env.INFLOW_PRIVATE_KEY
    },
    body: JSON.stringify({
      products: [{ name: 'Pro Plan', price: 4999, quantity: 1 }],
      currency: 'EUR',
      customerEmail: req.body.email,
      billingCountry: req.body.country,
      purchasingAsBusiness: false,
      firstName: 'John',
      lastName: 'Doe',
    })
  });

  const data = await response.json();
  res.json({ paymentId: data.id });
});

Required fields:

FieldTypeDescription
productsarrayList of products: name, price (in cents, tax-excluded), quantity
currencystringEUR or USD
customerEmailstringCustomer's email address
billingCountrystringISO 3166-1 alpha-2 country code (e.g. FR, DE, US)
purchasingAsBusinessbooleantrue requires businessName and taxId; US customers also need postalCode

firstName / lastName are optional but strongly recommended — they improve 3D Secure success rates. Full list of optional fields in the endpoint reference and the Server-to-Server guide.

Never expose your private API key in the browser. The payment must be created server-side; only the resulting paymentId is sent to the frontend.

Step 2: Mount the Card Form (Frontend)

Load the SDK from the CDN, fetch the paymentId from your backend route (the one from Step 1), and mount the card element:

<!DOCTYPE html>
<html>
<head>
  <title>Checkout</title>
</head>
<body>
  <h2>Complete Your Payment</h2>
  <div id="card-container"></div>

  <script src="https://cdn.jsdelivr.net/npm/@inflow_pay/sdk/dist/sdk.umd.js"></script>
  <script>
    const provider = new InflowPaySDK.InflowPayProvider({
      config: { publicKey: 'inflow_pub_xxx' }
    });

    // Calls YOUR backend route from Step 1 — not the Inflow API directly.
    fetch('/api/create-payment', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ email: '[email protected]', country: 'FR' })
    })
      .then(res => res.json())
      .then(data => {
        const cardElement = provider.createCardElement({
          paymentId: data.paymentId,
          container: '#card-container',
          onComplete: (result) => {
            if (result.status === InflowPaySDK.PaymentResultStatus.SUCCESS) {
              setTimeout(() => window.location.href = '/confirmation', 2000);
            }
          },
          onError: (error) => {
            console.error('SDK error:', error);
          },
          appearance: {
            variables: {
              fontFamily: 'Inter, system-ui, sans-serif',
              primaryColor: '#0070F3',
              buttonBackgroundColor: '#0070F3',
              buttonTextColor: '#FFFFFF',
            },
          },
          options: {
            paymentMethodLayout: 'horizontalSelector',
            paymentMethodOrder: ['apple_pay', 'google_pay', 'card'],
            cardFieldLayout: 'compact',
            buttonText: 'Pay €49.99',
            wallets: {
              buttonText: {
                applePay: 'Pay with Apple Pay',
                googlePay: 'Pay with Google Pay',
              },
            },
          },
        });
        cardElement.mount();
      });
  </script>
</body>
</html>

The SDK handles card collection, 3D Secure and payment confirmation automatically — you don't call any confirm endpoint yourself.

See Styling & Customization for the full appearance and options reference.

Using npm Instead of CDN

import { InflowPayProvider, PaymentResultStatus } from '@inflow_pay/sdk';

const provider = new InflowPayProvider({
  config: { publicKey: 'inflow_pub_xxx' }
});

const cardElement = provider.createCardElement({
  paymentId: 'pay_xxx',
  container: '#card-container',
  onComplete: (result) => {
    if (result.status === PaymentResultStatus.SUCCESS) {
      // Payment successful
    }
  }
});

cardElement.mount();

API

InflowPayProvider

const provider = new InflowPayProvider({
  config: {
    publicKey: 'inflow_pub_xxx',  // Required
    locale: 'en',                  // Optional (defaults to browser language)
  }
});

createCardElement Options

OptionTypeRequiredDescription
paymentIdstringYes*Payment ID from your backend
setupIdstringYes*Setup request ID for saving a card without charging (free trials, save-card) — see Free Trial & Card Setup
containerstring or HTMLElementYesCSS selector or DOM element
onCompletefunctionNoCalled when payment completes
onReadyfunctionNoCalled when form is ready
onChangefunctionNoCalled on validation state change
onErrorfunctionNoCalled on SDK errors
appearanceobjectNoTheme tokens and fonts (see Styling & Customization)
optionsobjectNoLayout, width, card / wallet button labels, placeholders, success UI (see Styling & Customization)
styleobjectNoDeprecated. Legacy styling — use appearance. Still applied when appearance is omitted
buttonTextstringNoDeprecated. Use options.buttonText
placeholdersobjectNoDeprecated. Use options.cardForm.placeholders
showDefaultSuccessUIbooleanNoDeprecated. Use options.showDefaultSuccessUI (default: true)

* Provide exactly one of paymentId (charge now) or setupId (save card, no charge) — they are mutually exclusive.

Legacy style / top-level copy props remain fully supported for compatibility.

CardElement Methods

MethodDescription
mount()Mount the card form to the DOM
destroy()Cleanup and unmount

Custom Success UI

By default, the SDK shows a built-in success screen. To use your own:

const cardElement = provider.createCardElement({
  paymentId: 'pay_xxx',
  container: '#card-container',
  options: { showDefaultSuccessUI: false },
  onComplete: (result) => {
    if (result.status === PaymentResultStatus.SUCCESS) {
      document.getElementById('card-container').innerHTML = `
        <div class="custom-success">
          <h2>Thank you!</h2>
          <p>Order confirmed.</p>
        </div>
      `;
    }
  }
});

Top-level showDefaultSuccessUI: false still works; prefer options.showDefaultSuccessUI.

The SDK API may evolve. Refer to the npm package for the latest options and types.


Did this page help you?