@inflow_pay/sdk
v1.8.1
Published
InflowPay SDK - payment SDK with React and vanilla JS support
Downloads
3,983
Maintainers
Readme
InflowPay SDK v2
Accept card payments with a secure, iframe-based payment form. This version delivers enhanced security and automatic 3D Secure handling - all with just a few lines of code.
What's Best in This Version
- 🔒 Iframe Architecture: Card data never touches your server (PCI compliant)
- ✨ Built-in Success UI: Beautiful success screen automatically shown after payment - fully customizable or replaceable
- 🎯 Auto 3D Secure: SDK handles authentication modals automatically
- 🚀 Zero Configuration: Works out of the box with sensible defaults
- 🎨 Fully Customizable: Match your brand with comprehensive styling options
- 💳 Save card (setup): Collect and tokenize a card without charging, using a
CustomerPaymentMethodRequestID (setupId) - 🎨 Appearance API: Theme the form with design tokens (
appearance.variables) - 📐 Layout options: Payment-method and card-field layouts via
options
Installation
npm install @inflow_pay/sdkReact apps — import from @inflow_pay/sdk/react and ensure peer dependencies are installed:
npm install @inflow_pay/sdk react react-domCDN Option (vanilla HTML pages only):
<script src="https://cdn.jsdelivr.net/npm/@inflow_pay/sdk@1/dist/sdk.umd.js"></script>Complete Example
Backend: Create Payment
// backend/api/create-payment.js
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: "Product", price: 4999, quantity: 1 }],
currency: "EUR",
customerEmail: "[email protected]",
firstName: "John",
lastName: "Doe",
billingCountry: "FR",
postalCode: "75001",
}),
});
const data = await response.json();
res.json({ paymentId: data.payment.id });
});Frontend: React
import {
InflowPayProvider,
CardElement,
PaymentResultStatus,
} from "@inflow_pay/sdk/react";
export default function CheckoutPage() {
const [paymentId, setPaymentId] = useState<string | null>(null);
useEffect(() => {
fetch("/api/create-payment", { method: "POST" })
.then((res) => res.json())
.then((data) => setPaymentId(data.paymentId));
}, []);
if (!paymentId) return <div>Loading...</div>;
return (
<InflowPayProvider config={{ publicKey: "inflow_pub_xxx" }}>
<CardElement
paymentId={paymentId}
onComplete={(result) => {
if (result.status === PaymentResultStatus.SUCCESS) {
setTimeout(() => (window.location.href = "/confirmation"), 2000);
}
}}
appearance={{
variables: {
primaryColor: "#0070F3",
buttonBackgroundColor: "#0070F3",
buttonTextColor: "#FFFFFF",
fontFamily: "Inter, system-ui, sans-serif",
},
}}
options={{
paymentMethodLayout: "horizontalSelector",
paymentMethodOrder: ["apple_pay", "google_pay", "card"],
cardFieldLayout: "compact",
buttonText: "Pay now",
}}
/>
</InflowPayProvider>
);
}Save card (setup) — React
Use setupId (ID returned when you create a CustomerPaymentMethodRequest on your server) instead of paymentId. The two are mutually exclusive. On success, onComplete receives a SaveCardResult with status and setupId.
import {
InflowPayProvider,
CardElement,
PaymentResultStatus,
type SaveCardResult,
} from "@inflow_pay/sdk/react";
export default function AddCardPage({ setupId }: { setupId: string }) {
return (
<InflowPayProvider config={{ publicKey: "inflow_pub_xxx" }}>
<CardElement
setupId={setupId}
onComplete={(result) => {
if (result.status === PaymentResultStatus.SUCCESS) {
const save = result as SaveCardResult;
console.log("Card saved for request:", save.setupId);
}
}}
/>
</InflowPayProvider>
);
}Your backend should create the customer payment method request with your private API key and expose only the returned request ID to the browser as setupId.
Frontend: Vanilla JavaScript
<div id="card-container"></div>
<script src="https://cdn.jsdelivr.net/npm/@inflow_pay/sdk@1/dist/sdk.umd.js"></script>
<script>
const provider = new InflowPaySDK.InflowPayProvider({
config: { publicKey: "inflow_pub_xxx" },
});
fetch("/api/create-payment", { method: "POST" })
.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);
}
},
appearance: {
variables: {
primaryColor: "#0070F3",
buttonBackgroundColor: "#0070F3",
buttonTextColor: "#FFFFFF",
},
},
options: {
paymentMethodLayout: "horizontalSelector",
paymentMethodOrder: ["apple_pay", "google_pay", "card"],
cardFieldLayout: "compact",
},
});
cardElement.mount();
});
</script>Save card (setup) — Vanilla JavaScript
const provider = new InflowPaySDK.InflowPayProvider({
config: { publicKey: "inflow_pub_xxx" },
});
fetch("/api/create-card-setup", { method: "POST" })
.then((res) => res.json())
.then((data) => {
const cardElement = provider.createCardElement({
setupId: data.setupId,
container: "#card-container",
onComplete: (result) => {
if (result.status === InflowPaySDK.PaymentResultStatus.SUCCESS) {
console.log("setupId:", result.setupId);
}
},
});
cardElement.mount();
});Built-in Success UI Explained
The SDK includes a polished success screen that appears automatically after successful payment:
What it shows:
- ✅ Success icon and "Payment successful!" message
- 💳 Payment amount (when available from your payment data)
Default vs custom:
- Default UI (default): Built-in success screen shows automatically. You don't need to do anything — quick integration, looks great out of the box.
- Custom UI: Set
options.showDefaultSuccessUI: false(or the deprecated top-levelshowDefaultSuccessUI: false) and render your own UI inonComplete.
Vanilla JS:
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 for your purchase!</h2>
<p>Order ID: ${result.paymentId}</p>
</div>
`;
}
},
});React:
const [result, setResult] = useState<PaymentResult | null>(null);
return (
<>
{result ? (
<div className="custom-success">
<h2>Thank you for your purchase!</h2>
<p>Your payment was successful.</p>
</div>
) : (
<InflowPayProvider config={{ publicKey: "inflow_pub_xxx" }}>
<CardElement
paymentId="pay_xxx"
options={{ showDefaultSuccessUI: false }}
onComplete={(res) => {
if (res.status === PaymentResultStatus.SUCCESS) {
setResult(res);
}
}}
/>
</InflowPayProvider>
)}
</>
);Payment flow
Charge (payment)
- Backend creates a payment → returns
paymentId - Frontend mounts
CardElementwithpaymentIdand your public key - User enters card details in the secure iframe
- SDK tokenizes the card and processes the payment
- 3D Secure (if required) — handled automatically
onCompletefires with aPaymentResult→ redirect or update UI
Save card (setup)
- Backend creates a CustomerPaymentMethodRequest → returns its ID as
setupId - Frontend mounts
CardElementwithsetupId(notpaymentId) - User completes the card form; 3DS may still run when the issuer requires it
onCompletefires with aSaveCardResult(setupId+status, anderroron failure)
API Reference
InflowPayProvider
const provider = new InflowPayProvider({
config: {
publicKey: "inflow_pub_xxx", // Required
locale: "en", // Optional - defaults to browser language
},
});Supported locales: en, de, es, fr, it, nl, pl, pt
CardElement
Pass exactly one of paymentId (checkout) or setupId (save card). Passing both or neither throws.
const cardElement = provider.createCardElement({
paymentId: "pay_xxx", // XOR setupId — from your backend
// setupId: "cus_pm_req_xxx", // save-card flow (CustomerPaymentMethodRequest ID)
container: "#card-container", // Required (vanilla) — CSS selector or HTMLElement
// Callbacks
onComplete: (result) => {
if (result.status === PaymentResultStatus.SUCCESS) {
// Payment successful
} else if (result.status === PaymentResultStatus.FAILED) {
// Payment failed — result.error contains details
}
},
onReady: () => {
// Element mounted and ready
},
onChange: (state) => {
// state.complete — whether the form is filled
},
onError: (error) => {
// SDK-level errors
},
// Theme (recommended)
appearance: {
// Only needed for fonts outside the SDK auto-load list (e.g. Lato).
// Built-in fonts like Inter load from `fontFamily` alone.
fonts: [
{
cssSrc:
"https://fonts.googleapis.com/css2?family=Lato:wght@400;700&display=swap",
},
],
variables: {
fontFamily: "Lato, system-ui, sans-serif",
primaryColor: "#111111",
buttonBackgroundColor: "#111111",
buttonTextColor: "#FFFFFF",
},
},
// Layout and copy (recommended)
options: {
paymentMethodLayout: "horizontalSelector", // or "stacked" | "verticalSelector"
paymentMethodOrder: ["apple_pay", "google_pay", "card"],
cardFieldLayout: "compact", // or "split"
fillParent: false,
buttonText: "Pay now",
cardForm: {
placeholders: {
cardNumber: "Card number",
expiry: "MM / YY",
cvc: "CVC",
},
},
showDefaultSuccessUI: true,
},
});
cardElement.mount();CardElement properties
| Property | Status | Description |
|---|---|---|
| paymentId | Required* | Payment id from your backend |
| setupId | Required* | Save-card request id (mutually exclusive with paymentId) |
| container | Required (vanilla) | Mount target (selector or element) |
| onComplete / onError / onClose / onReady / onChange | Active | Lifecycle callbacks |
| appearance | Active | Theme tokens and fonts |
| options | Active | Layout, width, labels, placeholders, success UI |
| style | Deprecated | Legacy styling object — use appearance |
| buttonText | Deprecated | Use options.buttonText |
| placeholders | Deprecated | Use options.cardForm.placeholders |
| showDefaultSuccessUI | Deprecated | Use options.showDefaultSuccessUI |
* Provide exactly one of paymentId or setupId.
Legacy properties remain fully supported. Prefer appearance and options for new integrations.
Precedence
- Theme: if
appearanceis set, it is used; otherwisestyleis applied. - Width:
options.fillParentif set; otherwisestyle.fillParent. - Copy / success UI: values under
optionswin over the deprecated top-level fields.
Payment and save-card results
interface PaymentResult {
status: "SUCCESS" | "FAILED";
paymentId: string;
error?: {
code: string; // Error code (e.g., 'THREE_DS_FAILED')
message: string; // User-friendly message
retryable: boolean; // Can retry?
};
}
interface SaveCardResult {
status: "SUCCESS" | "FAILED";
setupId: string;
error?: {
code: string;
message: string;
retryable: boolean;
};
}In React, onComplete is typed as (result: PaymentResult | SaveCardResult) => void. Use result.status and narrow with 'paymentId' in result vs 'setupId' in result, or cast when you know which mode you mounted.
CardElement Methods
cardElement.mount(); // Mount to DOM
cardElement.destroy(); // Cleanup and unmountAppearance
Use appearance to theme the checkout form. Variables map to CSS custom properties inside the iframe.
appearance: {
fonts: [
{
cssSrc:
"https://fonts.googleapis.com/css2?family=Lato:wght@400;700&display=swap",
},
],
variables: {
fontFamily: "Lato, system-ui, sans-serif",
formBackgroundColor: "transparent",
backgroundColor: "#ffffff",
textColor: "#111111",
textSecondaryColor: "#6b7280",
placeholderColor: "#9ca3af",
primaryColor: "#111111",
primaryTextColor: "#ffffff",
layoutBorderColor: "#d8d8d8",
inputBorderColor: "#e5e5e5",
inputFocusBorderColor: "#111111",
layoutBorderRadius: "12px",
inputBorderRadius: "8px",
buttonBorderRadius: "8px",
dangerColor: "#ef4444",
dangerBackgroundColor: "#fee2e2",
successColor: "#ffffff",
successBackgroundColor: "#22c55e",
buttonBackgroundColor: "#111111",
buttonTextColor: "#ffffff",
// Special-case only — see table below:
// iframeBackgroundColor: "#ffffff",
},
}appearance.variables
| Variable | Purpose |
|---|---|
| fontFamily | CSS font stack (known Google Fonts in the primary slot are auto-loaded) |
| formBackgroundColor | Checkout form box background. Default is transparent so the parent page shows through; set this when you need an explicit form surface color. |
| iframeBackgroundColor | Special case. Overrides the iframe document (html / body) background. Omit by default. Use only when the iframe background does not match the page (e.g. unexpected white/colored bleed) and formBackgroundColor alone is not enough. |
| backgroundColor | Input / panel surface background |
| textColor | Primary text |
| textSecondaryColor | Secondary / muted text |
| placeholderColor | Input placeholders |
| primaryColor | Brand / selection accent |
| primaryTextColor | Text/icons on primary-filled surfaces |
| layoutBorderColor | Payment-method layout and divider borders |
| inputBorderColor | Card input borders |
| inputFocusBorderColor | Focused input borders |
| layoutBorderRadius | Layout chrome radius |
| inputBorderRadius | Input radius |
| buttonBorderRadius | Card pay button and wallet button radius |
| dangerColor | Error text / field errors |
| dangerBackgroundColor | Error banner background |
| successColor | Success accent |
| successBackgroundColor | Success fill surfaces |
| buttonBackgroundColor | Card pay button background (falls back to primaryColor when omitted). Does not apply to wallet buttons. |
| buttonTextColor | Card pay button label color. Does not apply to wallet buttons. |
| loaderColor | Skeleton loader shimmer base color (parent page). Default light grey; highlight stop is lightened from this. |
appearance.fonts
Optional Google Fonts stylesheets for fonts outside the SDK auto-load list.
These are auto-loaded from fontFamily alone (no fonts entry needed):DM Sans, Inter, Poppins, Nunito, Work Sans, Manrope, Rubik, Karla, Figtree, Outfit, Space Grotesk, Urbanist.
For any other Google Font, pass a stylesheet URL. Only https://fonts.googleapis.com and https://fonts.google.com URLs are accepted.
fonts: [
{
cssSrc:
"https://fonts.googleapis.com/css2?family=Lato:wght@400;700&display=swap",
},
],
variables: {
fontFamily: "Lato, system-ui, sans-serif",
},Options
Use options for layout and non-theme configuration.
options: {
paymentMethodLayout: "stacked", // "stacked" | "verticalSelector" | "horizontalSelector"
paymentMethodOrder: ["apple_pay", "google_pay", "card"],
cardFieldLayout: "compact", // "compact" | "split"
fillParent: true, // stretch to parent width (default is fixed width)
buttonText: "Pay now",
wallets: {
buttonText: {
applePay: "Pay",
googlePay: "Pay",
},
buttonTheme: "auto", // "auto" | "black" | "white"
},
cardForm: {
placeholders: {
cardNumber: "Card number",
expiry: "MM / YY",
cvc: "CVC",
},
},
showDefaultSuccessUI: true,
}| Option | Default | Description |
|---|---|---|
| paymentMethodLayout | stacked | stacked, verticalSelector, or horizontalSelector |
| paymentMethodOrder | card, then wallets | Display order. First available method is shown first and selected. Unavailable entries are skipped (e.g. Apple Pay on unsupported browsers). Values: card, apple_pay, google_pay. |
| paymentMethods | — (dashboard state) | Per-session enable/disable overrides. See Per-session payment methods. |
| cardFieldLayout | compact | compact (grouped fields) or split (number alone; expiry + CVC row) |
| fillParent | false | When true, host iframe and form use 100% width |
| buttonText | "Complete Payment" | Card pay button label (does not apply to wallet buttons) |
| wallets.buttonText.applePay | locale "Pay" | Apple Pay button label (translated when omitted; overrides are used as-is) |
| wallets.buttonText.googlePay | locale "Pay" | Google Pay button label (translated when omitted; overrides are used as-is) |
| wallets.buttonTheme | "auto" | Wallet button color — set "black" or "white" to match your checkout design ("auto" renders the default light appearance); see Wallet buttons |
| cardForm.placeholders | — | Card number / expiry / CVC placeholders |
| showDefaultSuccessUI | true | Show built-in success screen after payment |
Wallet buttons
Configure Apple Pay and Google Pay button labels and theme under options.wallets.
Labels — set wallets.buttonText.applePay / googlePay to any string. If omitted, the iframe uses the localized default for "Pay". Merchant-provided strings are not translated.
Colors — Appearance color variables do not theme wallet buttons. That includes buttonBackgroundColor, buttonTextColor, primaryColor, and related color tokens. Wallet button color is controlled entirely by wallets.buttonTheme, so set it to match your checkout design. Only black and white are available:
| buttonTheme | Background | Text |
|---|---|---|
| auto (default) | Black (light appearance) | White |
| black | Black | White |
| white | White | Black |
Set buttonTheme to "black" or "white" to match the surface your buttons sit on (e.g. "white" for dark backgrounds). "auto" renders the default light appearance (black).
Radius — the only Appearance variable applied to wallet buttons is appearance.variables.buttonBorderRadius (shared with the card pay button).
options: {
buttonText: "Start Trial", // card CTA only
wallets: {
buttonText: {
applePay: "Pay $0.50 & Start Trial",
googlePay: "Pay",
},
buttonTheme: "black", // or "white" | "auto"
},
},
appearance: {
variables: {
buttonBackgroundColor: "#adffd2", // card pay button only
buttonTextColor: "#000000", // card pay button only
buttonBorderRadius: "8px", // card + wallet buttons
},
},Payment method order example (prefer Apple Pay, then Google Pay, then Card):
options: {
paymentMethodLayout: "horizontalSelector",
paymentMethodOrder: ["apple_pay", "google_pay", "card"],
}If Apple Pay is unavailable, Google Pay is selected when available; otherwise Card.
Per-session payment methods
options.paymentMethods enables/disables specific payment methods for a single checkout session, layered on top of whatever the merchant already has enabled in Settings. It's an object keyed by method (card, apple_pay, google_pay), each with an enabled boolean:
options: {
paymentMethods: {
apple_pay: { enabled: false }, // hide Apple Pay for this session only
},
}- Methods not listed keep their normal dashboard-enabled state — this is a partial override, not an allowlist.
- It can only narrow down what's already enabled for your account — it can never force-enable a method that isn't authorized (e.g. Apple Pay before domain verification, or a method disabled in Settings).
paymentMethodOrderis unaffected — it's still order-only and works alongsidepaymentMethods.
Wallet-only sessions. You can disable Card to show only wallet buttons for a session:
options: {
paymentMethods: {
card: { enabled: false },
apple_pay: { enabled: true },
},
}Since wallet availability is only known in the buyer's browser (device support, domain verification, isReadyToPay, etc.), the iframe automatically falls back to showing Card if none of the requested wallets end up usable for that buyer — so a wallet-only session never leaves someone with zero payment options. If at least one requested wallet is usable, Card stays hidden as intended. The same fallback applies whether you request Apple Pay only, Google Pay only, or both together.
Legacy styling (style)
The style object is deprecated but still fully supported when appearance is not provided.
style: {
fontFamily: "Inter",
fillParent: true,
formBackgroundColor: "#211e19",
inputContainer: {
backgroundColor: "#F5F5F5",
borderRadius: "8px",
borderEnabled: true,
borderColor: "#E0E0E0",
},
input: {
textColor: "#1A1A1A",
placeholderColor: "#999999",
backgroundColor: "transparent",
},
button: {
backgroundColor: "#0070F3",
textColor: "#FFFFFF",
borderRadius: "8px",
fontSize: "16px",
fontWeight: 600,
hover: { backgroundColor: "#0051CC" },
disabled: { opacity: 0.5 },
},
successUI: {
backgroundColor: "#F5F5F5",
primaryTextColor: "#1A1A1A",
secondaryTextColor: "#999999",
},
dark: {
input: {
textColor: "#FFFFFF",
placeholderColor: "#959499",
},
button: {
backgroundColor: "#0066CC",
},
},
}Supported legacy style fields: fontFamily, fillParent, formBackgroundColor, inputContainer, input, button (including hover / disabled / loaderColor), successUI, generalError, disclaimerColor, fieldErrorColor, dark.
| Legacy field | Recommended replacement |
|---|---|
| style | appearance.variables |
| style.fillParent | options.fillParent |
| style.fontFamily | appearance.variables.fontFamily (+ optional appearance.fonts) |
| top-level buttonText | options.buttonText |
| top-level placeholders | options.cardForm.placeholders |
| top-level showDefaultSuccessUI | options.showDefaultSuccessUI |
See package TypeScript types for the full shape of each object.
Error Handling
import { PaymentResultStatus, PaymentResultErrorCode } from "@inflow_pay/sdk";
onComplete: (result) => {
if (result.status === PaymentResultStatus.FAILED && result.error) {
console.log(result.error.code); // e.g., 'THREE_DS_FAILED'
console.log(result.error.message); // User-friendly message
console.log(result.error.retryable); // Can user retry?
}
};Common error codes: THREE_DS_FAILED, PAYMENT_PROCESSING_ERROR
3D Secure
3DS is handled automatically by the SDK. When required, a modal opens for bank authentication, then closes automatically. No setup needed.
Migration from React SDK
For React apps: Import from @inflow_pay/sdk/react instead. Prefer appearance and options for new theming and layout configuration. Legacy style continues to work when appearance is omitted.
For non-React apps: Use the vanilla JavaScript API shown in the examples above.
TypeScript
Full type definitions are included for both entry points:
// Vanilla (InflowPayProvider, PaymentSDK, CardElement)
import {
InflowPayProvider,
PaymentResultStatus,
} from "@inflow_pay/sdk";
import type {
AppearanceVariables,
PaymentElementAppearance,
PaymentElementOptions,
PaymentError,
PaymentResult,
SaveCardResult,
} from "@inflow_pay/sdk";
// React components and hooks
import {
InflowPayProvider,
CardElement,
useInflowPay,
PaymentResultStatus,
} from "@inflow_pay/sdk/react";
import type {
AppearanceVariables,
PaymentElementAppearance,
PaymentElementOptions,
PaymentResult,
SaveCardResult,
} from "@inflow_pay/sdk/react";useInflowPay() returns the shared PaymentSDK instance from InflowPayProvider (e.g. for advanced use).
Features
- ✅ Dynamic height adjustment
- ✅ Skeleton loader with shimmer effect
- ✅ Appearance theme tokens (light by default; full merchant control)
- ✅ Payment-method and card-field layouts
- ✅ Responsive design
- ✅ WCAG compliant
- ✅ Multi-language support
Support
- Docs: https://docs.inflowpay.com/v0/reference/createpayment
- Email: [email protected]
- GitHub: https://github.com/inflowpay/sdk
- Changelog: CHANGELOG.md
License
MIT - See LICENSE
