@sablepay/react-sablepay-js
v1.1.6
Published
SablePay React SDK for React Crypto payment integration with QR codes, polling, and pre-built UI components
Readme
@sablepay/sdk
The official SablePay JavaScript/TypeScript SDK for React and Next.js applications.
Accept crypto payments (USDC, USDT, and more) with QR codes, automatic status polling, and pre-built UI components.
Features
- Full TypeScript support with complete type definitions
- React hooks —
usePaymentFlow(),usePayment(),useSablePay() - Pre-built components —
<PaymentButton>,<PaymentQrCode>,<PaymentStatus> - Context Provider —
<SablePayProvider>for app-wide SDK access - QR code generation — Built-in QR code generation (PNG, SVG, Canvas)
- Automatic polling — Poll payment status with exponential backoff
- Retry logic — Automatic retry with exponential backoff for network errors
- Payment flow management — End-to-end payment lifecycle handling
- SSR compatible — Works with Next.js (App Router & Pages Router)
- Tree-shakeable — Import only what you need
- Zero config — Auto-detects sandbox vs production from API key
Installation
npm install @sablepay/sdk
# or
yarn add @sablepay/sdk
# or
pnpm add @sablepay/sdkQuick Start
1. Initialize the SDK
// app/layout.tsx (Next.js App Router) or _app.tsx (Pages Router)
import { SablePay } from '@sablepay/sdk';
SablePay.initialize({
apiKey: process.env. PUBLIC_SABLEPAY_API_KEY!,
merchantId: process.env.PUBLIC_SABLEPAY_MERCHANT_ID!,
enableLogging: true,
});2. Use the PaymentButton (Easiest)
import { PaymentButton } from '@sablepay/sdk/react';
export default function CheckoutPage() {
return (
<PaymentButton
amount={10.50}
onSuccess={(status) => {
console.log('Payment complete!', status.transactionId);
}}
onError={(err) => {
console.error('Payment failed:', err);
}}
/>
);
}3. Or use the Hook (More Control)
import { usePaymentFlow } from '@sablepay/sdk/react';
export default function PaymentPage() {
const { state, startPayment, cancel, qrDataUrl, error } = usePaymentFlow({
onPaymentCompleted(status) {
console.log('Done!', status.transactionId);
},
});
return (
<div>
{state.type === 'idle' && (
<button onClick={() => startPayment(10.50)}>Pay $10.50</button>
)}
{state.type === 'creating' && <p>Creating payment...</p>}
{state.type === 'awaiting' && qrDataUrl && (
<div>
<img src={qrDataUrl} alt="Scan to pay" width={300} height={300} />
<p>Scan the QR code with your wallet</p>
</div>
)}
{state.type === 'processing' && <p>Processing payment...</p>}
{state.type === 'completed' && <p>✅ Payment complete!</p>}
{state.type === 'failed' && <p>❌ {error}</p>}
{state.type !== 'idle' && state.type !== 'completed' && (
<button onClick={cancel}>Cancel</button>
)}
</div>
);
}Usage with Context Provider
// app/providers.tsx
'use client';
import { SablePayProvider } from '@sablepay/sdk/react';
export function Providers({ children }: { children: React.ReactNode }) {
return (
<SablePayProvider
apiKey={process.env. PUBLIC_SABLEPAY_API_KEY!}
merchantId={process.env.PUBLIC_SABLEPAY_MERCHANT_ID!}
>
{children}
</SablePayProvider>
);
}
// In any child component
import { useSablePay } from '@sablepay/sdk/react';
function MyComponent() {
const sdk = useSablePay();
// sdk.createPayment(), sdk.getPaymentStatus(), etc.
}API Reference
SablePay (Core)
// Initialize (call once)
SablePay.initialize({
apiKey: 'sable_sk_sand_...', // Required
merchantId: 'uuid-here', // Required
baseUrl: '...', // Optional (auto-detected)
enableLogging: true, // Optional (default: false)
});
// Get instance
const sdk = SablePay.getInstance();
// Create a payment
const response = await sdk.createPayment({
amount: 10.50,
metadata: { orderId: 'ORD-123' },
});
// response.paymentId, response.paymentLink, response.status, etc.
// Get payment status
const status = await sdk.getPaymentStatus('payment-id');
// status.status, status.transactionId, status.paidToken, etc.
// List payments
const payments = await sdk.listPayments(20, 0);
// Check environment
sdk.getEnvironment(); // 'sandbox' | 'production'
// Check if configured
sdk.isConfigured(); // boolean
// Create a payment flow
const flow = SablePay.createPaymentFlow();
// Release resources
SablePay.release();React Hooks
usePaymentFlow(options?) — Recommended
Complete payment lifecycle management with reactive state.
const {
state, // PaymentFlowState
startPayment, // (amount, metadata?) => Promise<void>
cancel, // () => void
isActive, // boolean
qrDataUrl, // string | null
paymentResponse, // CreatePaymentResponse | null
paymentStatus, // PaymentStatusResponse | null
error, // string | null
} = usePaymentFlow({
onPaymentCreated: (response, qrUrl) => {},
onStatusUpdate: (status) => {},
onPaymentCompleted: (status) => {},
onPaymentFailed: (error) => {},
qrCodeConfig: { width: 400 },
pollOptions: { intervalMs: 3000 },
});usePayment() — Low-level
Direct API access with loading/error state.
const {
createPayment, // (request) => Promise<CreatePaymentResponse>
getPaymentStatus, // (id) => Promise<PaymentStatusResponse>
listPayments, // (limit?, offset?) => Promise<PaymentStatusResponse[]>
loading, // boolean
error, // Error | null
clearError, // () => void
} = usePayment();useSablePay() — SDK Instance
Access the raw SDK instance (requires <SablePayProvider>).
const sdk = useSablePay();React Components
<PaymentButton>
<PaymentButton
amount={10.50}
label="Checkout"
metadata={{ orderId: 'ORD-123' }}
onSuccess={(status) => {}}
onError={(message) => {}}
onCancel={() => {}}
className="my-button"
disabled={false}
/><PaymentQrCode>
<PaymentQrCode
paymentResponse={response}
// Or: paymentLink="https://pay.sablepay.io/..."
config={{ width: 400, height: 400 }}
onGenerated={(dataUrl) => {}}
onError={(error) => {}}
/><PaymentStatus>
<PaymentStatus
paymentId="payment-id"
autoPoll={true}
pollInterval={3000}
onStatusChange={(status) => {}}
onTerminal={(status) => {}}
/>
{/* Or with custom rendering */}
<PaymentStatus paymentId="payment-id">
{(status, loading, error) => (
<div>{status?.status}</div>
)}
</PaymentStatus>Models
interface CreatePaymentRequest {
amount: number; // USD amount (0.01 - 1,000,000)
items?: PaymentItem[]; // Optional line items
metadata?: Record<string, string>; // Optional metadata
}
interface CreatePaymentResponse {
paymentId: string;
status: string;
amount: number;
acceptedTokens: string;
paymentLink: string | null;
businessName: string;
expiresAt: string | null;
createdAt: string;
}
interface PaymentStatusResponse {
paymentId: string;
amount: number;
status: string; // PENDING | COMPLETED | FAILED | EXPIRED
txHash?: string | null; // Blockchain TX hash
paidToken?: string | null;
paidNetwork?: string | null;
paidAmount?: number | null;
metadata?: Record<string, unknown> | null;
createdAt: string;
completedAt?: string | null;
expiresAt?: string | null;
}Error Handling
import { ApiException } from '@sablepay/sdk';
try {
await sdk.createPayment({ amount: 10.50 });
} catch (error) {
if (error instanceof ApiException) {
console.log(error.statusCode); // 400, 401, 429, 500, etc.
console.log(error.message); // Error message
console.log(error.requestId); // For debugging
console.log(error.isRetryable); // true for 5xx and 429
console.log(error.isAuthError); // true for 401
console.log(error.retryAfter); // Seconds to wait (for 429)
}
}QR Code Generator (Standalone)
import { QrCodeGenerator } from '@sablepay/sdk';
const generator = new QrCodeGenerator({ width: 400, height: 400 });
// From payment response
const dataUrl = await generator.generatePaymentQr(response);
// From any text
const qr = await generator.generate('https://example.com');
// As SVG
const svg = await generator.generateSvg('https://example.com');Payment Poller (Standalone)
import { PaymentPoller } from '@sablepay/sdk';
const poller = new PaymentPoller((id) => sdk.getPaymentStatus(id));
// Callback-based
poller.startPolling('payment-id', (result) => {
if (result.success) console.log(result.data.status);
});
poller.stopPolling();
// Promise-based (awaits terminal state)
const finalStatus = await poller.pollUntilTerminal('payment-id');Environment Variables
PUBLIC_SABLEPAY_API_KEY=sable_sk_sand_...
PUBLIC_SABLEPAY_MERCHANT_ID=00000000-0000-0000-0000-000000000000- Sandbox keys:
sable_sk_sand_...→ auto-routes to sandbox API - Production keys:
sable_sk_live_...→ auto-routes to production API
Payment Flow States
| State | Description |
|-------|-------------|
| idle | No payment in progress |
| creating | Payment being created via API |
| awaiting | QR code displayed, waiting for customer |
| processing | Customer scanned, processing on blockchain |
| completed | Payment successful |
| failed | Payment failed, expired, or error |
Next.js Integration
App Router
// app/layout.tsx
import { SablePay } from '@sablepay/sdk';
// Initialize outside component (runs once)
if (!SablePay.isInitialized()) {
SablePay.initialize({
apiKey: process.env. PUBLIC_SABLEPAY_API_KEY!,
merchantId: process.env.PUBLIC_SABLEPAY_MERCHANT_ID!,
});
}
export default function RootLayout({ children }) {
return <html><body>{children}</body></html>;
}Pages Router
// pages/_app.tsx
import { SablePay } from '@sablepay/sdk';
if (!SablePay.isInitialized()) {
SablePay.initialize({
apiKey: process.env. PUBLIC_SABLEPAY_API_KEY!,
merchantId: process.env.PUBLIC_SABLEPAY_MERCHANT_ID!,
});
}
export default function MyApp({ Component, pageProps }) {
return <Component {...pageProps} />;
}Example App
A full working Next.js example app demonstrating all SDK features (PaymentButton, QR codes, hooks, provider pattern, and payment status pages) is available in a separate repository:
git clone https://github.com/Sable-Payments-Inc/sablepay-react-sdk-example.git
cd sablepay-react-sdk-example
npm install
npm run devSee the example app's README for setup instructions and environment variable configuration.
Building
npm run build # Build CJS + ESM + type declarations
npm test # Run tests
npm run typecheck # Type checking onlyLicense
MIT — see LICENSE
