npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

inflowpay-js

v0.6.0

Published

InflowPay JavaScript SDK with React components

Readme

InflowPay React SDK

Accept card payments with a pre-built, secure payment form for React applications.

Installation

npm install inflowpay-js react react-dom

Quick Start

import { InflowPayProvider, CardElement } from 'inflowpay-js/react';

function App() {
  const paymentId = 'pay_xxx'; // From your backend

  return (
    <InflowPayProvider config={{ apiKey: 'inflow_pub_xxx' }}>
      <CardElement
        paymentId={paymentId}
        onComplete={(result) => {
          if (result.status === 'CHECKOUT_SUCCESS') {
            window.location.href = '/success';
          }
        }}
      />
    </InflowPayProvider>
  );
}

That's it! The form handles card validation, tokenization, 3DS, and payment completion.

Features

  • 🔒 PCI Compliant - Card data never touches your server
  • 🎨 Customizable - Match your brand with custom styling
  • 🔐 3DS Built-in - Automatic 3D Secure authentication
  • 📱 Mobile Ready - Responsive on all devices
  • 💪 TypeScript - Full type definitions included

How It Works

  1. Backend: Create a payment with billing info (your private key)
  2. Frontend: Wrap your app with InflowPayProvider
  3. Frontend: Add CardElement component with the paymentId
  4. User: Enters card details and submits
  5. SDK: Handles tokenization, 3DS, and payment completion
  6. Your app: Receives success/error via callback

Payment Status

Payments progress through these statuses:

| Status | Description | Success? | |--------|-------------|----------| | CHECKOUT_SUCCESS | Payment confirmed | ✅ Yes | | PAYMENT_RECEIVED | Funds received | ✅ Yes | | PAYMENT_SUCCESS | Complete | ✅ Yes | | PAYMENT_FAILED | Failed | ❌ No |

Check for success:

if (result.status === 'CHECKOUT_SUCCESS' ||
    result.status === 'PAYMENT_RECEIVED' ||
    result.status === 'PAYMENT_SUCCESS') {
  // Payment successful
}

Customization

Styling

The CardElement supports comprehensive styling through the style prop. All styles are scoped to the card element using Shadow DOM, so they won't conflict with your page styles.

Basic Styling Example

<CardElement
  paymentId={paymentId}
  style={{
    // Input container (the box wrapping all inputs)
    inputContainer: {
      backgroundColor: '#F5F5F5',
      border: '1px solid #E0E0E0',
      borderRadius: '8px',
    },
    // Individual input fields
    input: {
      border: '1px solid #CECECE',
      borderRadius: '5px',
      color: '#333333',
      fontWeight: '400',
      placeholder: {
        color: '#999999',
      },
    },
    // Global font family
    fontFamily: "'Inter', sans-serif",
  }}
/>

Complete Styling Reference

style prop - CSSProperties
interface CSSProperties {
  // Input container styles (the box wrapping all 3 input fields)
  inputContainer?: {
    backgroundColor?: string;  // Background color of the container
    border?: string;            // Border (e.g., '1px solid #E0E0E0')
    borderRadius?: string;       // Border radius (e.g., '8px')
  };

  // Individual input field styles
  input?: {
    border?: string;            // Input border (e.g., '1px solid #CECECE')
    borderRadius?: string;       // Input border radius (e.g., '5px')
    color?: string;             // Text color
    fontWeight?: string;        // Font weight (e.g., '400', '500', 'bold')
    placeholder?: {
      color?: string;           // Placeholder text color
    };
  };

  // Global font family (applies to entire card element)
  fontFamily?: string;         // Font family (e.g., "'Inter', sans-serif")

  // Theme-specific overrides (optional)
  // These automatically follow system preference via CSS media queries
  light?: {
    inputContainer?: { backgroundColor?: string; border?: string; borderRadius?: string; };
    input?: { border?: string; borderRadius?: string; color?: string; fontWeight?: string; placeholder?: { color?: string; }; };
  };
  dark?: {
    inputContainer?: { backgroundColor?: string; border?: string; borderRadius?: string; };
    input?: { border?: string; borderRadius?: string; color?: string; fontWeight?: string; placeholder?: { color?: string; }; };
  };
}

Example with theme-specific styling:

<CardElement
  paymentId={paymentId}
  style={{
    inputContainer: {
      backgroundColor: '#F5F5F5',
      border: '1px solid #E0E0E0',
      borderRadius: '8px',
    },
    input: {
      border: '1px solid #CECECE',
      borderRadius: '5px',
      color: '#333333',
      fontWeight: '400',
      placeholder: {
        color: '#999999',
      },
    },
    fontFamily: "'Inter', sans-serif",
    // Light mode overrides
    light: {
      inputContainer: {
        backgroundColor: '#FFFFFF',
        border: '1px solid #E0E0E0',
      },
      input: {
        color: '#000000',
        placeholder: {
          color: '#999999',
        },
      },
    },
    // Dark mode overrides
    dark: {
      inputContainer: {
        backgroundColor: '#2d2d2d',
        border: '1px solid #7A7A7A',
      },
      input: {
        color: '#FFFFFF',
        border: '1px solid #7A7A7A',
        placeholder: {
          color: '#959499',
        },
      },
    },
  }}
/>

Note: Theme styles (light and dark) automatically follow system preference using CSS media queries. If only light is provided, dark mode uses defaults. If only dark is provided, light mode uses defaults.

buttonStyle prop - ButtonStyle | Partial

Two ways to style the button:

Option 1: Flat structure (simple CSS properties)

<CardElement
  paymentId={paymentId}
  buttonStyle={{
    backgroundColor: '#007bff',
    color: '#ffffff',
    borderRadius: '6px',
    padding: '12px 24px',
    fontSize: '16px',
    fontWeight: '500',
  }}
/>

Option 2: Structured with states

<CardElement
  paymentId={paymentId}
  buttonStyle={{
    base: {
      backgroundColor: '#007bff',
      color: '#ffffff',
      borderRadius: '6px',
      padding: '12px 24px',
      fontSize: '16px',
      fontWeight: '500',
    },
    hover: {
      backgroundColor: '#0056b3',
      transform: 'translateY(-1px)',
    },
    disabled: {
      backgroundColor: '#cccccc',
      cursor: 'not-allowed',
    },
    loaderColor: '#ffffff',  // Color of the loading spinner
  }}
/>

ButtonStyle interface:

interface ButtonStyle {
  base?: Partial<CSSStyleDeclaration>;      // Base button styles
  hover?: Partial<CSSStyleDeclaration>;     // Hover state styles
  disabled?: Partial<CSSStyleDeclaration>;  // Disabled state styles
  loaderColor?: string;                     // Loading spinner color
}
buttonText prop

Customize the button text:

<CardElement
  buttonText="Complete Payment"  // Default: "Complete Payment"
/>
placeholders prop

Customize placeholder text for each input:

<CardElement
  placeholders={{
    cardNumber: '1234 5678 9012 3456',  // Default: '4242 4242 4242 4242'
    expiry: 'MM/YY',                     // Default: 'MM/YY'
    cvc: '123',                          // Default: 'CVC'
  }}
/>

Callbacks

<CardElement
  paymentId={paymentId}
  onReady={() => {
    // Card element is mounted and ready
    console.log('Card form is ready');
  }}
  onChange={(state) => {
    // Track validation state
    console.log('Valid:', state.complete);
  }}
  onComplete={(result) => {
    // Payment complete (success or failure)
    if (result.error) {
      alert(result.error.message);
    }
  }}
  onError={(error) => {
    // SDK-level errors (network, validation)
    console.error(error);
  }}
/>

3D Secure

3DS is handled automatically. When required:

  1. SDK opens a modal with the authentication page
  2. User completes authentication
  3. Modal closes automatically
  4. Your onComplete callback receives the final result

Setup

Copy 3ds-callback.html to your public directory:

cp node_modules/inflowpay-js/templates/3ds-callback.html public/

The file must be accessible at https://yourdomain.com/3ds-callback.html for the 3DS authentication flow to work.

That's it! The SDK automatically uses this URL for 3DS redirects.

What This File Does

  • Receives the redirect from the 3DS authentication provider
  • Shows a success/failure message to the user
  • Sends the result back to the parent modal via postMessage
  • Auto-closes after completion

Important notes:

  • The file must be on the same origin as your application (same domain, protocol, and port)
  • Do not modify the file unless you need custom branding
  • The file is small (~4KB) and has no external dependencies

Local Development with 3DS

Important: To test 3DS locally, you MUST serve your application through a public HTTPS tunnel. Modern browsers block 3DS providers from redirecting to localhost due to Private Network Access security restrictions.

Setup:

  1. Serve your application on a local port (e.g., port 5173 for Vite):
npm run dev  # Vite runs on port 5173
  1. Tunnel that port to a public HTTPS URL:
# Option 1: localtunnel (simple, no signup)
npx localtunnel --port 5173
# Returns: https://random-subdomain.loca.lt

# Option 2: ngrok (more reliable, requires free account)
npx ngrok http 5173
# Returns: https://abc123.ngrok.io
  1. Access your app via the tunnel URL (not localhost):
  • ✅ Use: https://random-subdomain.loca.lt
  • ❌ Don't use: http://localhost:5173

The SDK will automatically use the tunnel URL for the 3DS callback (https://random-subdomain.loca.lt/3ds-callback.html), and the 3DS provider will be able to redirect to it successfully.

Why this is required:

  • 3DS providers (Basis Theory, Stripe) run on public servers
  • After authentication, they redirect to your callback URL
  • Browsers block redirects from public → private (localhost) networks
  • A tunnel creates a public HTTPS URL that forwards to your localhost

Error Handling

Errors are automatically mapped to user-friendly messages:

<CardElement
  paymentId={paymentId}
  onComplete={(result) => {
    if (result.error) {
      console.log(result.error.message); // User-friendly
      console.log(result.error.retryable); // Can retry?
    }
  }}
/>

Common errors:

  • card_declined - Card was declined
  • insufficient_funds - Not enough funds
  • invalid_card_number - Invalid card
  • THREE_DS_FAILED - 3DS authentication failed

API Reference

InflowPayProvider

<InflowPayProvider config={SDKConfig}>
  {children}
</InflowPayProvider>

Config:

  • apiKey (required) - Your public key (inflow_pub_xxx)
  • timeout (optional) - Request timeout in ms (default: 30000)
  • debug (optional) - Enable logging (default: false)

CardElement

<CardElement
  paymentId={string}
  config?: SDKConfig  // Optional if using InflowPayProvider
  buttonText?: string
  buttonStyle?: ButtonStyle | Partial<CSSStyleDeclaration>
  style?: CSSProperties
  placeholders?: {
    cardNumber?: string
    expiry?: string
    cvc?: string
  }
  onReady?: () => void
  onChange?: (state: CardElementState) => void
  onComplete?: (result: PaymentResult) => void
  onError?: (error: PaymentError) => void
/>

useInflowPay Hook

Access the InflowPay instance from context:

import { useInflowPay } from 'inflowpay-js/react';

function CustomComponent() {
  const inflow = useInflowPay();
  
  // Use inflow methods if needed
  const checkStatus = async () => {
    const status = await inflow.getPaymentStatus('pay_xxx');
    console.log(status);
  };
  
  return <button onClick={checkStatus}>Check Status</button>;
}

TypeScript

Full type definitions included:

import { InflowPayProvider, CardElement } from 'inflowpay-js/react';
import type { PaymentResult, PaymentError } from 'inflowpay-js';

function App() {
  return (
    <InflowPayProvider config={{ apiKey: 'inflow_pub_xxx' }}>
      <CardElement
        paymentId="pay_xxx"
        onComplete={(result: PaymentResult) => {
          if (result.status === 'CHECKOUT_SUCCESS') {
            // Success
          }
        }}
        onError={(error: PaymentError) => {
          console.error(error);
        }}
      />
    </InflowPayProvider>
  );
}

Backend Integration

Create payments on your backend with your private key:

// Your backend (Node.js example)
const response = await fetch('https://api.inflowpay.xyz/api/server/payment', {
  method: 'POST',
  headers: {
    'Accept': 'application/json',
    'Content-Type': 'application/json',
    'X-Inflow-Api-Key': 'inflow_priv_xxx' // Private key
  },
  body: JSON.stringify({
    products: [{ name: 'Product', price: 4999, quantity: 1 }],
    currency: 'EUR',
    customerEmail: '[email protected]',
    billingCountry: 'FR',
    postalCode: '75001',
    firstName: 'John',
    lastName: 'Doe',
    purchasingAsBusiness: false,
    successUrl: 'https://yourdomain.com/success',
    cancelUrl: 'https://yourdomain.com/cancel'
  })
});

const { payment } = await response.json();
// Send payment.id to your frontend

See API documentation for full backend API reference.

Security

  • ✅ Card data is tokenized before reaching your server (PCI compliant)
  • ✅ Public keys can be safely exposed in browser
  • ✅ Private keys should never be in frontend code
  • ✅ All requests over HTTPS

Support

  • Documentation: https://docs.inflowpay.com/v0/reference/createpayment
  • Email: [email protected]
  • GitHub: https://github.com/inflowpay/sdk

License

MIT - See LICENSE