v1.0Stable

SablePay Developer Docs

Accept stablecoin payments in your app with our simple API and SDKs.

REST API Android SDK Multi-Network Real-time
Last updated: April 16, 2026API Version 1.0
User Guide
Complete visual walkthrough from registration to accepting your first payment
Registration
KYB Verification
Wallet Setup
POS Payments
API Keys

~10 min read • Step-by-step with screenshots

API Keys & Authentication
Understanding API key formats and required headers

API Key Format

Productionsable_sk_live_XXXXXXXXXXX...
Sandboxsable_sk_sand_XXXXXXXXXXX...

Required Headers

HeaderValueDescription
X-API-KeyYour API keyStarts with sable_sk_live_ or sable_sk_sand_
X-Merchant-IDYour Merchant IDUUID from your dashboard
Content-Typeapplication/jsonRequired for POST requests
POST/api/v1/payments/create
Create a new payment request. Returns a payment link URL for customer. Amount is interpreted based on the merchant's region: if the region has currency conversion enabled (e.g., PAK), the amount is treated as local currency (PKR) and converted to USD; otherwise it is treated as USD.

Request Body

FieldTypeRequiredDescription
amountnumberRequiredPayment amount (0.01 - 1,000,000). Interpreted as local currency if merchant region has conversion enabled (e.g., PKR for PAK), otherwise treated as USD.
itemsarrayOptionalArray of items (max 100)
metadataobjectOptionalCustom key-value pairs (orderId, etc.)

Code Examples

Terminal
curl -X POST "https://sandbox-api.sablepay.io/api/v1/payments/create" \
  -H "Content-Type: application/json" \
  -H "X-API-Key: sable_sk_live_YOUR_API_KEY_HERE" \
  -H "X-Merchant-ID: YOUR_MERCHANT_ID" \
  -d '{
    "amount": 25.99,
    "metadata": {
      "orderId": "ORD-12345"
    }
  }'

Success Response (USD region)

JSON
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "status": "PENDING",
  "amount": 25.99,
  "acceptedTokens": "ALL_STABLECOINS",
  "paymentLink": "https://sablepay.io/pay/550e8400...",
  "businessName": "SF Cali CaffeCoffe",
  "paymentExpirationTime": 10,
  "expiresAt": "2026-02-02T12:10:00.000Z",
  "createdAt": "2026-02-02T12:00:00.000Z",
  "currency_symbol": "$",
  "stablecoin_label": "STABLECOIN"
}

Success Response (PAK region with conversion)

JSON
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "status": "PENDING",
  "amount": 5000,
  "acceptedTokens": "ALL_STABLECOINS",
  "paymentLink": "https://sablepay.io/pay/550e8400...",
  "businessName": "Karachi Store",
  "paymentExpirationTime": 10,
  "expiresAt": "2026-02-02T12:10:00.000Z",
  "createdAt": "2026-02-02T12:00:00.000Z",
  "currency_symbol": "?",
  "stablecoin_label": "USDC",
  "converted_currency": "17.99",
  "conversion_rate": "278.05",
  "conversion_rate_fluctuation": "0.10",
  "conversion_currency_symbol": "USD"
}
GET/api/v1/paymentsComing Soon
List all payments for your merchant account with pagination and filtering. This endpoint is currently available only through the SablePay Dashboard. Public API key access is planned for a future release.
GET/api/v1/payments/{id}
Get the current status of a payment. Poll this endpoint to track payment completion.

Payment Status Values

pendingWaiting for customer to pay (10 min timeout)
completedPayment successful - includes txHash
failedPayment failed or rejected
expiredPayment expired after 10 minutes

Example Request

Terminal
curl -X GET "https://sandbox-api.sablepay.io/api/v1/payments/PAYMENT_ID" \
  -H "X-API-Key: sable_sk_live_YOUR_API_KEY_HERE" \
  -H "X-Merchant-ID: YOUR_MERCHANT_ID"

Completed Payment Response (USD region)

JSON
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "amount": 25.99,
  "acceptedTokens": "ALL_STABLECOINS",
  "status": "completed",
  "terminalId": "t-001",
  "terminalName": "Main Counter",
  "txHash": "0x1234567890abcdef...",
  "paidToken": "USDC",
  "paidNetwork": "base",
  "paidAmount": "25.99",
  "createdAt": "2026-02-02T12:00:00.000Z",
  "completedAt": "2026-02-02T12:05:00.000Z",
  "expiresAt": "2026-02-02T12:10:00.000Z",
  "currency_symbol": "$",
  "stablecoin_label": "STABLECOIN"
}

Completed Payment Response (PAK region)

JSON
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "amount": 5000,
  "originalAmount": 5000,
  "acceptedTokens": "ALL_STABLECOINS",
  "status": "completed",
  "terminalId": "t-002",
  "terminalName": "Karachi POS",
  "txHash": "0xabcdef1234567890...",
  "paidToken": "USDC",
  "paidNetwork": "polygon",
  "paidAmount": "17.99",
  "createdAt": "2026-02-02T12:00:00.000Z",
  "completedAt": "2026-02-02T12:03:00.000Z",
  "expiresAt": "2026-02-02T12:10:00.000Z",
  "currency_symbol": "?",
  "stablecoin_label": "USDC",
  "converted_currency": "17.99",
  "conversion_rate": "278.05",
  "conversion_rate_fluctuation": "0.10",
  "conversion_currency_symbol": "USD"
}
Webhooks
Receive real-time notifications for payment events

Webhook Events

payment.createdPayment was created and is awaiting customer action
payment.confirmedPayment was successful - includes txHash and on-chain details
payment.failedPayment failed or was rejected
payment.expiredPayment expired (10 min timeout)

Webhook Payload

All webhook payloads share a common envelope. The data field varies by event type.

JSON
// payment.created
{
  "id": "evt_a1b2c3d4e5f67890",
  "type": "payment.created",
  "timestamp": "2026-02-02T12:00:00.000Z",
  "data": {
    "paymentId": "550e8400-e29b-41d4-a716-446655440000",
    "amount": 25.99,
    "originalAmount": 25.99,
    "originalCurrency": "USD",
    "acceptedTokens": ["USDC", "USDT"],
    "status": "PENDING",
    "paymentLink": "https://sablepay.io/pay/550e8400...",
    "expiresAt": "2026-02-02T12:10:00.000Z"
  }
}
JSON
// payment.confirmed
{
  "id": "evt_f8e7d6c5b4a39210",
  "type": "payment.confirmed",
  "timestamp": "2026-02-02T12:05:00.000Z",
  "data": {
    "paymentId": "550e8400-e29b-41d4-a716-446655440000",
    "amount": 25.99,
    "originalAmount": 25.99,
    "currency": "USDC",
    "status": "completed",
    "txHash": "0x1234567890abcdef...",
    "fromAddress": "0xCustomerWallet...",
    "toAddress": "0xMerchantWallet...",
    "networkName": "base",
    "completedAt": "2026-02-02T12:05:00.000Z"
  }
}

Verify Webhook Signature

Always verify the webhook signature using the X-SablePay-Signature header. The signature is an HMAC-SHA256 hash of the payload body:

JavaScript
const crypto = require('crypto');

function verifyWebhook(payload, signature, webhookSecret) {
  const expectedSignature = crypto
    .createHmac('sha256', webhookSecret)
    .update(JSON.stringify(payload))
    .digest('hex');
  
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expectedSignature)
  );
}

// In your webhook handler:
app.post('/webhook', (req, res) => {
  const signature = req.headers['x-sablepay-signature'];
  
  if (!verifyWebhook(req.body, signature, process.env.WEBHOOK_SECRET)) {
    return res.status(401).json({ error: 'Invalid signature' });
  }
  
  const { type, data } = req.body;
  
  if (type === 'payment.confirmed') {
    // Update order status, send confirmation email, etc.
    console.log('Payment completed:', data.paymentId);
    console.log('TX Hash:', data.txHash);
    console.log('Network:', data.networkName);
  }
  
  res.json({ received: true });
});

Webhook Best Practices

Return a 200 response quickly (within 5 seconds)
Process webhooks asynchronously using a queue
Handle duplicate events idempotently (use payment ID as key)
We retry failed webhooks up to 5 times with exponential backoff
Error Handling
Common errors and how to handle them

Error Response Format

JSON
{
  "error": "Error message description",
  "requestId": "req_1706871234567_abc123"
}

Common Error Codes

CodeErrorSolution
400Invalid amountAmount must be between 0.01 and 1,000,000
401Missing API keyAdd X-API-Key header
401Invalid API credentialsCheck API key is active and matches Merchant ID
403Insufficient permissionsAPI key lacks required scope (payments:create)
404Payment not foundCheck payment ID exists
429Rate limit exceededWait 1 minute and retry
500Server errorRetry after a few seconds or contact support

Rate Limits

30/min
Create Payment
100/min
Get Payment Status
API Playground
Test the API directly from your browser
Sandbox Only(Production disabled for security)
Terminal
curl -X POST "https://sandbox-api.sablepay.io/api/v1/payments/create" \
  -H "Content-Type: application/json" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "X-Merchant-ID: YOUR_MERCHANT_ID" \
  -d '{"amount": 10}'
Android SDK
Native Android integration for POS terminals

Requirements

Android 10+
API 29+
Kotlin 1.9+
Coroutines
Gradle 8.0+
Build tool

Installation

Add to your build.gradle.kts:

Kotlin
dependencies {
    implementation("io.sablepay:sdk:1.0.5")
}

Initialize SDK

Kotlin
import io.sablepay.sdk.SablePay

class MyApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        
        SablePay.initialize(
            context = this,
            apiKey = "sable_sk_live_YOUR_API_KEY_HERE",
            merchantId = "your-merchant-uuid-here",
            baseUrl = "https://sandbox-api.sablepay.io/api/v1/",
            enableLogging = BuildConfig.DEBUG
        )
    }
}

Collect Payment

Launch a full payment screen with QR code, countdown timer, and auto-polling - all in one line:

Kotlin
import io.sablepay.sdk.SablePay
import io.sablepay.sdk.launchPayment

// Launch payment screen (one line!)
SablePay.launchPayment(this, 10.50)  // amount

Handle Payment Result

The SDK handles QR display, status polling, and success/failure screens automatically. Handle the result in your Activity:

Kotlin
import io.sablepay.sdk.handlePaymentResult

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)
    SablePay.handlePaymentResult(requestCode, resultCode, data) { result ->
        result.onSuccess { payment ->
            // Payment successful!
            println("Payment ID: ${payment.paymentId}")
            println("TX Hash: ${payment.transactionHash}")
            println("Paid: ${payment.formattedPaidAmount}")
        }
        result.onFailure { error ->
            // Payment failed or cancelled
            println("Error: ${error.message}")
        }
    }
}

Cleanup

When the merchant logs out or switches accounts:

Kotlin
SablePay.release()  // Clears stored credentials
React SDK
React components and hooks for web integration — @sablepay/react-sablepay-js

Requirements

React 18+
Hooks support
TypeScript 5+
Full type support
Node.js 18+
Build environment

Installation

Terminal
npm install @sablepay/react-sablepay-js

Quick Start

1. Initialize the SDK

JavaScript
// app/layout.tsx (Next.js App Router) or _app.tsx (Pages Router)
import { SablePay } from '@sablepay/react-sablepay-js';

SablePay.initialize({
  apiKey: process.env.NEXT_PUBLIC_SABLEPAY_API_KEY!,
  merchantId: process.env.NEXT_PUBLIC_SABLEPAY_MERCHANT_ID!,
  enableLogging: process.env.NODE_ENV === 'development',
});

2. Use the PaymentButton (Easiest)

JavaScript
import { PaymentButton } from '@sablepay/react-sablepay-js/react';

export default function CheckoutPage() {
  return (
    <PaymentButton
      amount={10.50}
      onSuccess={(status) => {
        console.log('Payment complete!', status.txHash);
      }}
      onError={(err) => {
        console.error('Payment failed:', err);
      }}
    />
  );
}

3. Or use the Hook (More Control)

JavaScript
import { usePaymentFlow } from '@sablepay/react-sablepay-js/react';

export default function PaymentPage() {
  const { state, startPayment, cancel, qrDataUrl, error } = usePaymentFlow({
    onPaymentCompleted(status) {
      console.log('Done!', status.txHash);
    },
  });

  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

JavaScript
// app/providers.tsx
'use client';
import { SablePayProvider } from '@sablepay/react-sablepay-js/react';

export function Providers({ children }: { children: React.ReactNode }) {
  return (
    <SablePayProvider
      apiKey={process.env.NEXT_PUBLIC_SABLEPAY_API_KEY!}
      merchantId={process.env.NEXT_PUBLIC_SABLEPAY_MERCHANT_ID!}
    >
      {children}
    </SablePayProvider>
  );
}

// In any child component
import { useSablePay } from '@sablepay/react-sablepay-js/react';

function MyComponent() {
  const sdk = useSablePay();
  // sdk.createPayment(), sdk.getPaymentStatus(), etc.
}

API Reference

SablePay (Core)

JavaScript
// 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.txHash, 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.

JavaScript
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.

JavaScript
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>).

JavaScript
const sdk = useSablePay();

React Components

<PaymentButton>

JavaScript
<PaymentButton
  amount={10.50}
  label="Checkout"
  metadata={{ orderId: 'ORD-123' }}
  onSuccess={(status) => {}}
  onError={(message) => {}}
  onCancel={() => {}}
  className="my-button"
  disabled={false}
/>

<PaymentQrCode>

JavaScript
<PaymentQrCode
  paymentResponse={response}
  // Or: paymentLink="https://pay.sablepay.io/..."
  config={{ width: 400, height: 400 }}
  onGenerated={(dataUrl) => {}}
  onError={(error) => {}}
/>

<PaymentStatus>

JavaScript
<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

JavaScript
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

JavaScript
import { ApiException } from '@sablepay/react-sablepay-js';

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)

JavaScript
import { QrCodeGenerator } from '@sablepay/react-sablepay-js';

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)

JavaScript
import { PaymentPoller } from '@sablepay/react-sablepay-js';

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

Terminal
NEXT_PUBLIC_SABLEPAY_API_KEY=sable_sk_sand_...
NEXT_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

StateDescription
idleNo payment in progress
creatingPayment being created via API
awaitingQR code displayed, waiting for customer
processingCustomer scanned, processing on blockchain
completedPayment successful
failedPayment failed, expired, or error

Next.js Integration

App Router

JavaScript
// app/layout.tsx
import { SablePay } from '@sablepay/react-sablepay-js';

// Initialize outside component (runs once)
if (!SablePay.isInitialized()) {
  SablePay.initialize({
    apiKey: process.env.NEXT_PUBLIC_SABLEPAY_API_KEY!,
    merchantId: process.env.NEXT_PUBLIC_SABLEPAY_MERCHANT_ID!,
  });
}

export default function RootLayout({ children }) {
  return <html><body>{children}</body></html>;
}

Pages Router

JavaScript
// pages/_app.tsx
import { SablePay } from '@sablepay/react-sablepay-js';

if (!SablePay.isInitialized()) {
  SablePay.initialize({
    apiKey: process.env.NEXT_PUBLIC_SABLEPAY_API_KEY!,
    merchantId: process.env.NEXT_PUBLIC_SABLEPAY_MERCHANT_ID!,
  });
}

export default function MyApp({ Component, pageProps }) {
  return <Component {...pageProps} />;
}
React Native SDK
React Native hooks and utilities for mobile payment integration - @sablepay/react-native-sablepay-js

Requirements

React Native 0.70+
Mobile runtime
Expo Compatible
Managed workflow ready
Node.js 18+
Build environment

Installation

Terminal
npm install @sablepay/react-native-sablepay-js

Quick Start

1. Initialize the SDK

JavaScript
// App.tsx
import { SablePay } from '@sablepay/react-native-sablepay-js';

SablePay.initialize({
  apiKey: 'sable_sk_sand_...',
  merchantId: '00000000-0000-0000-0000-000000000000',
  enableLogging: __DEV__,
});

2. Create a Payment (Simplest)

JavaScript
import React, { useState } from 'react';
import { View, Button, Image } from 'react-native';
import { SablePay, QrCodeGenerator } from '@sablepay/react-native-sablepay-js';

export default function CheckoutScreen() {
  const [qrUrl, setQrUrl] = useState('');

  const pay = async () => {
    const sdk = SablePay.getInstance();
    const response = await sdk.createPayment({ amount: 10.50 });
    const qrGen = new QrCodeGenerator();
    const dataUrl = await qrGen.generatePaymentQr(response);
    setQrUrl(dataUrl || '');
  };

  return (
    <View>
      <Button title="Pay $10.50" onPress={pay} />
      {qrUrl ? <Image source={{ uri: qrUrl }} style={{ width: 300, height: 300 }} /> : null}
    </View>
  );
}

3. Use usePaymentFlow (Recommended)

JavaScript
import React, { useEffect } from 'react';
import { View, Button, Image, Text } from 'react-native';
import { usePaymentFlow } from '@sablepay/react-native-sablepay-js';

export default function PaymentScreen() {
  const { state, qrDataUrl, error, startPayment, cancel, release } = usePaymentFlow();

  useEffect(() => {
    return () => release();
  }, [release]);

  return (
    <View>
      {state.type === 'idle' && <Button title="Pay $10.50" onPress={() => startPayment(10.50)} />}
      {state.type === 'creating' && <Text>Creating payment...</Text>}
      {state.type === 'awaiting' && qrDataUrl && (
        <>
          <Image source={{ uri: qrDataUrl }} style={{ width: 300, height: 300 }} />
          <Text>Scan the QR code with your wallet</Text>
          <Button title="Cancel" onPress={cancel} />
        </>
      )}
      {state.type === 'processing' && <Text>Processing payment...</Text>}
      {state.type === 'completed' && <Text>Payment complete!</Text>}
      {state.type === 'failed' && <Text>Error: {error}</Text>}
    </View>
  );
}

Using useSablePay Hook

JavaScript
import React from 'react';
import { View, Button, Text } from 'react-native';
import { useSablePay } from '@sablepay/react-native-sablepay-js';

function PaymentComponent() {
  const { createPayment, getPaymentStatus, getEnvironment } = useSablePay();

  const handlePay = async () => {
    const response = await createPayment({ amount: 25.00 });
    const status = await getPaymentStatus(response.paymentId);
    console.log('Status:', status.status);
  };

  return (
    <View>
      <Text>Environment: {getEnvironment()}</Text>
      <Button title="Pay $25.00" onPress={handlePay} />
    </View>
  );
}

API Reference

SablePay (Core)

JavaScript
// 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 Native Hooks

usePaymentFlow(options?) - Recommended

Complete payment lifecycle management with reactive state.

JavaScript
const {
  state,           // PaymentFlowState
  startPayment,    // (amount, metadata?, callbacks?, options?) => Promise<void>
  cancel,          // () => void
  release,         // () => 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) => {},
  onStateChange: (state) => {},
});

useSablePay() - Core Access

Convenient access to core SDK methods inside components.

JavaScript
const {
  sdk,              // SablePay instance
  createPayment,    // (request) => Promise<CreatePaymentResponse>
  getPaymentStatus, // (id) => Promise<PaymentStatusResponse>
  listPayments,     // (limit?, offset?) => Promise<PaymentStatusResponse[]>
  getEnvironment,   // () => 'sandbox' | 'production' | null
  isConfigured,     // () => boolean
  createPaymentFlow // (options?) => PaymentFlow
} = useSablePay();

Models

JavaScript
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
  transactionId?: string | null;
  paidToken?: string | null;
  paidNetwork?: string | null;
  paidAmount?: number | null;
  metadata?: Record<string, unknown> | null;
  createdAt: string;
  completedAt?: string | null;
  expiresAt?: string | null;
}

Error Handling

JavaScript
import { ApiException } from '@sablepay/react-native-sablepay-js';

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)

JavaScript
import { QrCodeGenerator } from '@sablepay/react-native-sablepay-js';

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)

JavaScript
import { PaymentPoller } from '@sablepay/react-native-sablepay-js';

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 Setup

JavaScript
// src/config/environment.ts
export const environment = {
  production: false,
  sablepayApiKey: 'sable_sk_sand_...',
  sablepayMerchantId: '00000000-0000-0000-0000-000000000000',
  sablepayBaseUrl: 'https://sandbox-api.sablepay.io/api/v1/',
};
Sandbox keys: sable_sk_sand_... ? auto-routes to https://sandbox-api.sablepay.io
Production keys: sable_sk_live_... ? auto-routes to https://api.sablepay.io

Payment Flow States

StateDescription
idleNo payment in progress
creatingPayment being created via API
awaitingQR code displayed, waiting for customer
processingCustomer scanned, processing on blockchain
completedPayment successful
failedPayment failed, expired, or error

Expo / App Lifecycle Integration

Initialize Once at App Startup

JavaScript
import React, { useEffect, useState } from 'react';
import { View, Text } from 'react-native';
import { SablePay } from '@sablepay/react-native-sablepay-js';

export default function App() {
  const [ready, setReady] = useState(false);

  useEffect(() => {
    SablePay.initialize({
      apiKey: 'sable_sk_sand_...',
      merchantId: '00000000-0000-0000-0000-000000000000',
      enableLogging: __DEV__,
    });

    setReady(true);

    return () => SablePay.release();
  }, []);

  if (!ready) return <Text>Loading...</Text>;

  return <View>{/* Your payment screens */}</View>;
}
Angular SDK
Minimal Angular integration for stablecoin payments - @sablepay/angular-sablepay-js v1.3.1

Compatibility

Angular 13+
Standalone & NgModule

1. Install

Terminal
npm install @sablepay/angular-sablepay-js

2. Initialize once (app startup)

typescript
import { SablePay } from '@sablepay/angular-sablepay-js';

SablePay.initialize({
  apiKey: 'sable_sk_sand_...',
  merchantId: '00000000-0000-0000-0000-000000000000',
  enableLogging: false,
});

3. Collect Payment (one line)

SablePay.launchPayment() handles QR generation, polling, and result - all in a single call.

typescript
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { SablePay } from '@sablepay/angular-sablepay-js';

@Component({
  selector: 'app-checkout',
  standalone: true,
  imports: [CommonModule],
  template: `
    <button (click)="pay()" [disabled]="loading">Pay</button>
    <img *ngIf="qrUrl" [src]="qrUrl" alt="Scan to pay" width="300" height="300" />
    <p *ngIf="status">{{ status }}</p>
  `,
})
export class CheckoutComponent {
  loading = false;
  qrUrl = '';
  status = '';

  pay() {
    this.loading = true;

    // Launch payment - handles QR, polling, and result automatically
    SablePay.launchPayment(10.50, {
      onPaymentCreated: (response, qrDataUrl) => {
        this.qrUrl = qrDataUrl;
      },
      onSuccess: (status) => {
        this.status = 'Payment complete! TX: ' + status.transactionId;
        this.loading = false;
      },
      onFailure: (error) => {
        this.status = 'Failed: ' + error.message;
        this.loading = false;
      },
    });
  }
}

Cancel / Cleanup

typescript
SablePay.cancelPayment();  // Cancel active payment
SablePay.release();        // Clear credentials (on logout)

Optional: Environment values

typescript
export const environment = {
  production: false,
  sablepayApiKey: 'sable_sk_sand_...',
  sablepayMerchantId: '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

API Reference

MethodDescription
SablePay.initialize(config)Set API key, merchant ID, and options. Call once at startup.
SablePay.launchPayment(amount, callbacks)Create payment, generate QR, poll status, and fire callbacks.
SablePay.cancelPayment()Cancel the currently active payment.
SablePay.release()Clear stored credentials (call on logout).

Callbacks

typescript
SablePay.launchPayment(amount, {
  onPaymentCreated: (response, qrDataUrl) => { },  // Payment created, QR ready
  onSuccess: (status) => { },                       // Payment completed
  onFailure: (error) => { },                        // Payment failed or error
});
Flutter SDK
Official Dart SDK for Flutter payment integration

Requirements

Flutter 3.19+
Stable channel
Dart 3.3+
Null safety
Android & Web
Example available

Installation

Terminal
flutter pub add sablepay_flutter

Quick Start

1. Initialize the SDK

Dart
import 'package:sablepay_flutter/sablepay_flutter.dart';

Future<void> initSablePay() async {
  await SablePay.initialize(
    apiKey: const String.fromEnvironment('SABLEPAY_API_KEY'),
    merchantId: const String.fromEnvironment('SABLEPAY_MERCHANT_ID'),
    enableLogging: true,
  );
}

2. Create a Payment

Dart
final sdk = SablePay.instance;

final payment = await sdk.createPayment(
  amount: 10.50,
  metadata: {'orderId': 'ORD-12345'},
);

print(payment.paymentId);
print(payment.paymentLink);

3. Render QR + Poll Status

Dart
final qrGenerator = QrCodeGenerator(width: 320, height: 320);
final qrDataUrl = await qrGenerator.generatePaymentQr(payment);

final poller = PaymentPoller((id) => SablePay.instance.getPaymentStatus(id));

final status = await poller.pollUntilTerminal(payment.paymentId);

if (status.status == PaymentState.completed) {
  print('Paid on: ${status.paidNetwork}');
} else {
  print('Payment ended with ${status.status}');
}

Environment Setup

Terminal
# .env (or --dart-define values)
SABLEPAY_API_KEY=sable_sk_sand_...
SABLEPAY_MERCHANT_ID=00000000-0000-0000-0000-000000000000
Sandbox keys: sable_sk_sand_... route to sandbox API automatically.
Production keys: sable_sk_live_... route to production API automatically.

Android & Web Example App

Use the official Flutter example app for Android and Web integration patterns:

Terminal
# Web setup from the example repo
cp .env.example .env

# Keep this for Web runs
SABLEPAY_BASE_URL=http://localhost:8787/api/v1/

# Terminal 1: start proxy
cd proxy
cp .env.example .env
# Add your real SablePay API key + Merchant ID in proxy/.env
npm install
npm start

# Terminal 2: run Flutter Web
flutter pub get
flutter run -d chrome --web-port 54410

# Keep WEB_ORIGIN in proxy/.env aligned with the Flutter web port
WEB_ORIGIN=http://localhost:54410

# For Android/mobile sandbox direct API usage
# SABLEPAY_BASE_URL=https://sandbox-api.sablepay.io/api/v1/
Example Projects & SDKs
Ready-to-use code examples and SDKs to get you started

Android SDK

Available

Native Android SDK with Kotlin support. Includes complete POS integration example.

  • Android Kotlin example
  • QR Code generation
  • Payment status polling

React SDK

Available

Official JavaScript/TypeScript SDK for React and Next.js. Pre-built components, hooks, and full payment flow management.

  • React hooks & components
  • QR code generation (PNG, SVG)
  • Automatic status polling
  • Next.js App Router & Pages Router

React Native SDK

Available

Official TypeScript SDK for React Native and Expo. Includes hooks, QR generation, and full payment lifecycle management.

  • React Native hooks
  • QR code generation (PNG, SVG)
  • Automatic status polling
  • Expo-compatible initialization

Angular SDK

Available

Official TypeScript SDK for Angular. Services, payment flow management, QR codes, and automatic polling.

  • Angular 14+ (standalone & NgModule)
  • PaymentFlowService & SablePayService
  • QR code generation (PNG, SVG)
  • Automatic status polling
  • Full TypeScript support

iOS SDK

Native Swift SDK for iOS applications

Coming Soon

Flutter SDK

Available

Official Dart SDK with QR code generation, polling, and payment flow helpers for Flutter apps.

  • Published on pub.dev as sablepay_flutter
  • Android and Web example app available
  • Web proxy pattern to avoid CORS and protect API keys
Security Best Practices
Keep your integration secure
!

Never expose API keys in client-side code

Always call the SablePay API from your server, not from browser JavaScript.

Store keys in environment variables

Use .env files or secret managers. Never commit keys to git.

Use sandbox keys for testing

Keys starting with sable_sk_sand_ are for development only.

Rotate keys regularly

API keys expire after 90 days. Rotate at 75 days to avoid disruption.

Changelog
Latest updates and improvements
v1.0.5 (Android)May 25, 2026

Android SDK v1.0.5

  • Updated Android SDK to version 1.0.5 (io.sablepay:sdk:1.0.5)
  • Bug fixes and stability improvements for POS terminals
v1.2.0March 16, 2026

Flutter SDK Docs Update

  • Added dedicated Flutter SDK docs section with install, initialization, and payment flow examples
  • Added Flutter-specific QR and polling examples for mobile checkout screens
  • Updated SDK navigation and search metadata to surface Flutter docs directly
  • Linked official Flutter package and example app: pub.dev/package and Android/Web reference implementation
v1.1.0February 16, 2026

Angular SDK Release

  • Angular SDK (@sablepay/angular-sablepay-js) — Angular 14+ support
  • PaymentFlowService & SablePayService for Angular
  • QR code generation, automatic polling, retry logic
  • Coffee Shop POS example application
v1.0.0February 2, 2026

Initial Release

  • Payment API with QR code generation
  • Multi-network support (Ethereum, Polygon, Base, Arbitrum, Optimism)
  • USDC, USDT, and DAI stablecoin support
  • Webhook notifications for payment events
  • Android SDK (Maven Central)
  • Merchant dashboard with analytics
Support & Contact
Get help when you need it

Quick Reference

Current API URL

https://sandbox-api.sablepay.io/api/v1

Environment

Sandbox

Create Payment

POST /payments/create

Get Status

GET /payments/{id}

Payment Expiry

10 minutes

API Key Prefix

sable_sk_live_ | sable_sk_sand_

Ready to Accept Stablecoin Payments?

Create your account and start accepting crypto in minutes.