JSPM

@paylayer/react

0.1.2
  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 1
  • Score
    100M100P100Q33437F
  • License MIT

React SDK for PayLayer - Build billing once. Switch providers anytime.

Package Exports

  • @paylayer/react

Readme

PayLayer Logo

💳 @paylayer/react

Build billing once. Switch providers anytime.

npm version License: MIT React Version

The official React SDK for PayLayer. Simple, type-safe hooks for integrating payments into your React application.

FeaturesQuick StartHooksAPI ReferenceConfiguration


What is PayLayer React?

PayLayer React provides React hooks and components that make it easy to integrate payments into your React application. Write your billing logic once, switch providers anytime.

Key Benefits:

  • Simple Hooks - useCharge, useSubscription, useBillingPortal for all payment operations
  • Provider Flexibility - Switch between Stripe, Paddle, PayPal, Lemon Squeezy, and Polar without code changes
  • Type Safety - Full TypeScript support with autocomplete
  • SSR Ready - Works with Next.js, Remix, and other React frameworks

💡 Looking for Node.js/Backend support? Check out @paylayer/core - the server-side SDK for PayLayer that powers this React SDK.


📋 Table of Contents


✨ Features

Feature Description
💰 One-time payments useCharge hook for simple payment processing
🔄 Subscriptions useSubscription hook for recurring billing management
🏢 Billing portal useBillingPortal hook for customer self-service
🔀 Provider-agnostic Switch providers without changing your code
📘 TypeScript Full type safety with autocomplete
SSR Ready Works with Next.js, Remix, and other React frameworks
🎣 Simple Hooks Clean, intuitive API for all payment operations

📦 Installation

npm install @paylayer/react

🚀 Quick Start

Step 1: Configure PayLayer

Load your configuration using setConfig() at the top level of your app, in the same place where you use PayLayerProvider:

import {
  PayLayerProvider,
  setConfig,
  type PayLayerConfig,
} from "@paylayer/react";

const config: PayLayerConfig = {
  provider: "stripe",
  environment: "sandbox",
  stripe: {
    secretKey: process.env.STRIPE_SECRET_KEY!,
  },
};

setConfig(config);

function App() {
  return (
    <PayLayerProvider>
      <YourApp />
    </PayLayerProvider>
  );
}

Important: setConfig() must be called at the top level of your app, in the same file where you render PayLayerProvider. It cannot be called inside components or hooks.

Step 2: Use the Hooks

import { useCharge } from "@paylayer/react";

function CheckoutButton() {
  const { charge, loading } = useCharge();

  const handlePayment = async () => {
    const result = await charge({
      amount: 29.99,
      currency: "USD",
      email: "customer@example.com",
    });

    if (result.url) {
      window.location.href = result.url;
    }
  };

  return (
    <button onClick={handlePayment} disabled={loading}>
      {loading ? "Processing..." : "Pay $29.99"}
    </button>
  );
}

📝 Configuration

Using setConfig()

Load your PayLayer configuration by calling setConfig() with a configuration object. Important: setConfig() must be called at the top level of your app, in the same place where you render PayLayerProvider. It cannot be called inside components, hooks, or conditional blocks.

You can structure and load your config however you prefer - from a file, inline, or dynamically.

Basic Example (Inline):

import { setConfig, type PayLayerConfig } from "@paylayer/react";

const config: PayLayerConfig = {
  provider: "stripe",
  environment: "sandbox",
  stripe: {
    secretKey: process.env.STRIPE_SECRET_KEY!,
    webhookSecret: process.env.STRIPE_WEBHOOK_SECRET,
  },
};

setConfig(config);

Loading from a File (Recommended):

You can organize your config in a separate file. We recommend placing it in your project root, but you can put it anywhere you prefer:

import type { PayLayerConfig } from "@paylayer/react";

const config: PayLayerConfig = {
  provider: "stripe",
  environment: "sandbox",
  stripe: {
    secretKey: process.env.STRIPE_SECRET_KEY!,
    webhookSecret: process.env.STRIPE_WEBHOOK_SECRET,
  },
};

export default config;

Then load it at the top level of your app (in the same file where you use PayLayerProvider):

import { PayLayerProvider, setConfig } from "@paylayer/react";
import config from "./paylayer.config.ts";

setConfig(config);

function App() {
  return (
    <PayLayerProvider>
      <YourApp />
    </PayLayerProvider>
  );
}

Complete Example (All Providers):

import { setConfig, type PayLayerConfig } from "@paylayer/react";

const config: PayLayerConfig = {
  provider: "stripe",
  environment: "sandbox",
  stripe: {
    secretKey: process.env.STRIPE_SECRET_KEY!,
    webhookSecret: process.env.STRIPE_WEBHOOK_SECRET,
    checkoutSuccessUrl: "https://yourapp.com/success",
    checkoutCancelUrl: "https://yourapp.com/cancel",
    portalReturnUrl: "https://yourapp.com",
  },
  paddle: {
    apiKey: process.env.PADDLE_API_KEY!,
    webhookSecret: process.env.PADDLE_WEBHOOK_SECRET,
    sandbox: true,
  },
  paypal: {
    clientId: process.env.PAYPAL_CLIENT_ID!,
    clientSecret: process.env.PAYPAL_CLIENT_SECRET!,
    webhookId: process.env.PAYPAL_WEBHOOK_ID,
    sandbox: true,
  },
  lemonsqueezy: {
    apiKey: process.env.LEMONSQUEEZY_API_KEY!,
    storeId: process.env.LEMONSQUEEZY_STORE_ID!,
    webhookSecret: process.env.LEMONSQUEEZY_WEBHOOK_SECRET,
    testMode: true,
  },
  polar: {
    apiKey: process.env.POLAR_API_KEY!,
    webhookSecret: process.env.POLAR_WEBHOOK_SECRET,
    sandbox: true,
  },
};

setConfig(config);

Important: setConfig() must be called at the top level of your app, in the same file where you render PayLayerProvider. It must be called before PayLayerProvider is rendered, and cannot be called inside components, hooks, or conditional blocks.


🎣 Hooks

useCharge

One-time payment hook.

Returns:

Property Type Description
charge(input: ChargeInput) function Execute payment
loading boolean Loading state
error Error | null Error object (if any)
payment ChargeResult | null Last successful payment result

Parameters:

Parameter Type Required Description
amount number ✅* Payment amount (e.g., 29.99) - See provider support below
priceId string ✅* Provider-specific price ID - Recommended for most providers
productId string ✅* Provider-specific product ID - Works with all providers
currency string ISO 4217 currency code (e.g., USD)
email string Customer email address
successUrl string URL to redirect after successful payment
cancelUrl string URL to redirect if payment is cancelled
metadata object Additional metadata to attach to the payment

*Either amount, priceId, or productId must be provided.

Setting the Payment Amount:

You can specify the payment amount in three ways:

  1. amount - Direct amount value (e.g., 29.99)
  2. priceId - Provider-specific price ID (recommended for most providers)
  3. productId - Provider-specific product ID (works with all providers)

Provider Support:

Provider amount priceId productId Recommendation
Stripe Use amount for Stripe
Paddle Use priceId or productId
PayPal Use amount or productId
Lemon Squeezy ✅* Use priceId or productId
Polar Use productId only

*Lemon Squeezy supports amount with custom_price, but requires a variant ID from environment or priceId.

Best Practices:

  • For Stripe: You can use amount directly - it's the simplest option
  • For other providers: Use priceId or productId for better compatibility
  • For maximum compatibility: Use productId - it works with all providers

Examples:

Using amount (Stripe, PayPal, Lemon Squeezy):

import { useCharge } from "@paylayer/react";

function CheckoutButton() {
  const { charge, loading } = useCharge();

  const handlePayment = async () => {
    const result = await charge({
      amount: 29.99,
      currency: "USD",
      email: "customer@example.com",
    });

    if (result.url) {
      window.location.href = result.url;
    }
  };

  return (
    <button onClick={handlePayment} disabled={loading}>
      {loading ? "Processing..." : "Pay $29.99"}
    </button>
  );
}

Using priceId (Recommended for most providers):

const result = await charge({
  priceId: "price_1234567890",
  currency: "USD",
  email: "customer@example.com",
});

Using productId (Works with all providers):

const result = await charge({
  productId: "prod_1234567890",
  currency: "USD",
  email: "customer@example.com",
});

useSubscription

Subscription management hook.

Returns:

Property Type Description
subscribe(input: SubscribeInput) function Create subscription
cancel(subscriptionId: string) function Cancel subscription
pause(subscriptionId: string) function Pause subscription
resume(subscriptionId: string) function Resume subscription
loading boolean Loading state
error Error | null Error object (if any)
subscription SubscriptionResult | null Current subscription

Subscribe Parameters:

Parameter Type Required Description
plan string Plan identifier
currency string ISO 4217 currency code (e.g., USD)
email string Customer email address

Example:

import { useSubscription } from "@paylayer/react";

function SubscriptionButton() {
  const { subscribe, cancel, pause, resume, loading, subscription } =
    useSubscription();

  const handleSubscribe = async () => {
    try {
      const result = await subscribe({
        plan: "pro-monthly",
        currency: "USD",
        email: "customer@example.com",
      });

      if (result.url) {
        window.location.href = result.url;
      }
    } catch (err) {
      console.error("Subscription failed:", err);
    }
  };

  return (
    <div>
      <button onClick={handleSubscribe} disabled={loading}>
        Subscribe
      </button>
      {subscription && (
        <div>
          <p>Status: {subscription.status}</p>
          <button onClick={() => cancel(subscription.id)}>Cancel</button>
        </div>
      )}
    </div>
  );
}

useBillingPortal

Billing portal access hook.

Returns:

Property Type Description
open(input: { email: string }) function Open billing portal (auto-redirects)
loading boolean Loading state

Parameters:

Parameter Type Required Description
email string Customer email address

Example:

import { useBillingPortal } from "@paylayer/react";

function BillingPortalButton() {
  const { open, loading } = useBillingPortal();

  const handleOpenPortal = async () => {
    try {
      await open({ email: "user@example.com" });
    } catch (err) {
      console.error("Failed to open portal:", err);
    }
  };

  return (
    <button onClick={handleOpenPortal} disabled={loading}>
      {loading ? "Opening..." : "Manage Billing"}
    </button>
  );
}

What customers can do:

  • Update payment methods
  • View billing history
  • Cancel subscriptions
  • Update billing information
  • Download invoices

📘 TypeScript Support

The SDK is written in TypeScript and provides full type definitions:

import { useCharge } from "@paylayer/react";
import type { ChargeResult, ChargeInput } from "@paylayer/react";

const { charge } = useCharge();

const result: ChargeResult = await charge({
  amount: 29.99,
  currency: "USD",
  email: "customer@example.com",
});

Available Types

All types are exported from @paylayer/react:

import type {
  UseChargeReturn,
  UseSubscriptionReturn,
  UseBillingPortalReturn,
  ChargeInput,
  ChargeResult,
  SubscribeInput,
  SubscriptionResult,
  ProviderName,
  ProviderConfig,
  PayLayerConfig,
  CurrencyCode,
  CustomerInfo,
  Provider,
} from "@paylayer/react";

import { Currency } from "@paylayer/react";

Currency Enum

The SDK includes a comprehensive Currency enum with 150+ currencies for type safety and autocomplete:

import { useCharge, Currency } from "@paylayer/react";

const { charge } = useCharge();

const result = await charge({
  amount: 29.99,
  currency: Currency.USD, // TypeScript autocomplete available
  email: "customer@example.com",
});

Common Currencies:

  • Currency.USD, Currency.EUR, Currency.GBP, Currency.JPY
  • Currency.AUD, Currency.CAD, Currency.CHF, Currency.CNY
  • Currency.HKD, Currency.NZD, Currency.SGD

For a complete list, use your IDE's autocomplete or refer to the TypeScript definitions.


🔧 API Reference

setConfig

Loads the PayLayer configuration.

Parameters:

Parameter Type Required Description
config PayLayerConfig The PayLayer configuration object

When to use:

  • Call setConfig() at the top level of your app, in the same file where you render PayLayerProvider
  • Must be called before rendering PayLayerProvider
  • Must be called outside of components, hooks, or conditional blocks
  • Use when you want to configure PayLayer programmatically instead of using environment variables

Example:

import { setConfig, type PayLayerConfig } from "@paylayer/react";

const config: PayLayerConfig = {
  provider: "stripe",
  environment: "sandbox",
  stripe: {
    secretKey: process.env.STRIPE_SECRET_KEY!,
  },
};

setConfig(config);

PayLayerProvider

Root provider component that provides PayLayer context to your React tree.

Props:

Property Type Required Description
children ReactNode React children

Configuration Priority:

  1. Config loaded via setConfig()
  2. Environment variables (if setConfig() was not called)

Example:

import { PayLayerProvider } from "@paylayer/react";

function App() {
  return (
    <PayLayerProvider>
      <YourApp />
    </PayLayerProvider>
  );
}

🔄 Provider Abstraction

PayLayer abstracts away provider-specific details. Switch between Stripe, Paddle, PayPal, Lemon Squeezy, or Polar by changing your configuration - no code changes needed.

Provider Status Features
Stripe Payments, subscriptions, and billing portal
Paddle Merchant of record, subscriptions, and checkout
PayPal Payments and subscriptions
Lemon Squeezy Checkout and subscriptions
Polar.sh Billing infrastructure and subscriptions

All providers are fully implemented with proper error handling and API integration.

Example:

const config: PayLayerConfig = {
  provider: "stripe", // or "paddle", "paypal", "lemonsqueezy", "polar"
};

🔒 Security

Important: All API keys and secrets should be stored on your backend. The React SDK makes HTTP requests to your API endpoints. Never expose payment provider credentials in your frontend code.


❓ Troubleshooting

Config Not Loading

If you're getting errors about the provider not being configured:

  1. Make sure you're calling setConfig() at the top level before rendering PayLayerProvider:

    setConfig(config);
    function App() {
      return <PayLayerProvider>...</PayLayerProvider>;
    }
  2. Verify your config structure - Ensure your config matches the PayLayerConfig type:

    import type { PayLayerConfig } from "@paylayer/react";
    
    const config: PayLayerConfig = {
      provider: "stripe",
    };

Using Environment Variables Instead

If you prefer to use environment variables instead of calling setConfig(), simply don't call setConfig(). PayLayer will automatically read from environment variables:

# .env
PAYLAYER_PROVIDER=stripe
STRIPE_SECRET_KEY=sk_test_...

No setConfig() call needed - just use <PayLayerProvider> directly.


📚 Examples

Next.js Quickstarter

A beautiful, production-ready Next.js starter template with PayLayer integration:

  • PayLayer Next.js Quickstarter - Complete Next.js 16 application with:
    • Beautiful, animated UI with Tailwind CSS
    • Ready-to-use charge and subscription flows
    • Webhook handling
    • TypeScript support
    • Production-ready setup

Other Examples

See the examples directory for additional integration examples.


  • @paylayer/core - The server-side Node.js SDK for PayLayer. Use this for backend payment processing, webhooks, and server-side operations.

📄 License

MIT


Made with ❤️ by PayLayer