@paylayer/react
v0.1.2
Published
React SDK for PayLayer - Build billing once. Switch providers anytime.
Downloads
307
Maintainers
Readme
💳 @paylayer/react
Build billing once. Switch providers anytime.
The official React SDK for PayLayer. Simple, type-safe hooks for integrating payments into your React application.
Features • Quick Start • Hooks • API Reference • Configuration
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,useBillingPortalfor 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
- Installation
- Quick Start
- Configuration
- Hooks
- API Reference
- TypeScript Support
- Provider Abstraction
- Security
- Troubleshooting
- Examples
- License
✨ 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 renderPayLayerProvider. 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: "[email protected]",
});
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 renderPayLayerProvider. It must be called beforePayLayerProvideris 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:
amount- Direct amount value (e.g.,29.99)priceId- Provider-specific price ID (recommended for most providers)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
amountdirectly - it's the simplest option - For other providers: Use
priceIdorproductIdfor 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: "[email protected]",
});
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: "[email protected]",
});Using productId (Works with all providers):
const result = await charge({
productId: "prod_1234567890",
currency: "USD",
email: "[email protected]",
});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: "[email protected]",
});
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: "[email protected]" });
} 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: "[email protected]",
});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: "[email protected]",
});Common Currencies:
Currency.USD,Currency.EUR,Currency.GBP,Currency.JPYCurrency.AUD,Currency.CAD,Currency.CHF,Currency.CNYCurrency.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 renderPayLayerProvider - 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:
- Config loaded via
setConfig() - 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:
Make sure you're calling
setConfig()at the top level before renderingPayLayerProvider:setConfig(config); function App() { return <PayLayerProvider>...</PayLayerProvider>; }Verify your config structure - Ensure your config matches the
PayLayerConfigtype: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.
🔗 Related Packages
- @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
