JSPM

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

SablePay Angular SDK for crypto payment integration with QR codes, polling, and pre-built UI components

Package Exports

  • @sablepay/angular-sablepay-js

Readme

@sablepay/angular-sablepay-js

The official SablePay Angular SDK for Angular applications.

Accept crypto payments (USDC, USDT, and more) with QR codes, automatic status polling, and pre-built services.


Features

  • Full TypeScript support with complete type definitions
  • Angular ServicesSablePayService, PaymentFlowService
  • 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
  • Compatible with Angular 14+ — Works with both standalone and NgModule-based apps
  • Tree-shakeable — Import only what you need
  • Zero config — Auto-detects sandbox vs production from API key
  • NPM deployable — Ready to publish to npm

Installation

npm install @sablepay/angular-sablepay-js
# or
yarn add @sablepay/angular-sablepay-js
# or
pnpm add @sablepay/angular-sablepay-js

Quick Start

1. Initialize the SDK

// In app.component.ts or APP_INITIALIZER
import { SablePay } from '@sablepay/angular-sablepay-js';
import { environment } from './environments/environment';

SablePay.initialize({
  apiKey: environment.sablepayApiKey,
  merchantId: environment.sablepayMerchantId,
  enableLogging: !environment.production,
});

2. Create a Payment (Simplest)

import { Component } from '@angular/core';
import { SablePay, QrCodeGenerator } from '@sablepay/angular-sablepay-js';

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

  async pay() {
    this.loading = true;
    const sdk = SablePay.getInstance();
    const response = await sdk.createPayment({ amount: 10.50 });

    const qrGen = new QrCodeGenerator();
    this.qrUrl = (await qrGen.generatePaymentQr(response)) || '';
    this.loading = false;
  }
}
import { Component, OnDestroy } from '@angular/core';
import { PaymentFlowService } from '@sablepay/angular-sablepay-js';

@Component({
  selector: 'app-payment',
  template: `
    <div [ngSwitch]="flowService.state.type">
      <button *ngSwitchCase="'idle'" (click)="startPayment()">Pay $10.50</button>
      <p *ngSwitchCase="'creating'">Creating payment...</p>
      <div *ngSwitchCase="'awaiting'">
        <img [src]="flowService.qrDataUrl" alt="Scan to pay" width="300" />
        <p>Scan the QR code with your wallet</p>
        <button (click)="cancel()">Cancel</button>
      </div>
      <p *ngSwitchCase="'processing'">Processing payment...</p>
      <p *ngSwitchCase="'completed'">Payment complete!</p>
      <p *ngSwitchCase="'failed'">Error: {{ flowService.error }}</p>
    </div>
  `,
})
export class PaymentComponent implements OnDestroy {
  flowService = new PaymentFlowService();

  async startPayment() {
    await this.flowService.startPayment(10.50, undefined, {
      onPaymentCompleted: (status) => {
        console.log('Done!', status.transactionId);
      },
      onPaymentFailed: (error) => {
        console.error('Failed:', error.message);
      },
    });
  }

  cancel() {
    this.flowService.cancel();
  }

  ngOnDestroy() {
    this.flowService.release();
  }
}

Using SablePayService

The SablePayService wraps the core SablePay singleton for convenience:

import { Component, OnInit, OnDestroy } from '@angular/core';
import { SablePayService } from '@sablepay/angular-sablepay-js';
import { environment } from '../environments/environment';

@Component({
  selector: 'app-root',
  template: `<router-outlet></router-outlet>`,
})
export class AppComponent implements OnInit, OnDestroy {
  private sablePayService = new SablePayService();

  ngOnInit() {
    this.sablePayService.initialize({
      apiKey: environment.sablepayApiKey,
      merchantId: environment.sablepayMerchantId,
      enableLogging: !environment.production,
    });
  }

  ngOnDestroy() {
    this.sablePayService.release();
  }
}

// In a child component:
import { SablePayService } from '@sablepay/angular-sablepay-js';

@Component({ ... })
export class CheckoutComponent {
  private sablePayService = new SablePayService();

  async checkout() {
    const response = await this.sablePayService.createPayment({ amount: 25.00 });
    console.log('Payment ID:', response.paymentId);
    console.log('Payment Link:', response.paymentLink);
  }
}

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();

SablePayService

const service = new SablePayService();

service.initialize(config);           // Initialize SDK
service.isInitialized();              // Check if initialized
service.createPayment(request);       // Create a payment
service.getPaymentStatus(paymentId);  // Get payment status
service.listPayments(limit, offset);  // List payments
service.getEnvironment();             // Get environment
service.createPaymentFlow(options);   // Create payment flow
service.release();                    // Release resources

PaymentFlowService

Complete payment lifecycle management with state tracking.

const flowService = new PaymentFlowService();

// Properties
flowService.state;            // PaymentFlowState
flowService.isActive;         // boolean
flowService.qrDataUrl;        // string | null
flowService.paymentResponse;  // CreatePaymentResponse | null
flowService.paymentStatus;    // PaymentStatusResponse | null
flowService.error;            // string | null

// Methods
await flowService.startPayment(amount, metadata?, callbacks?, options?);
flowService.cancel();
flowService.release();        // Call in ngOnDestroy

Callbacks:

await flowService.startPayment(10.50, undefined, {
  onPaymentCreated: (response, qrUrl) => { },
  onStatusUpdate: (status) => { },
  onPaymentCompleted: (status) => { },
  onPaymentFailed: (error) => { },
  onStateChange: (state) => { },
});

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

import { ApiException } from '@sablepay/angular-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)

import { QrCodeGenerator } from '@sablepay/angular-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)

import { PaymentPoller } from '@sablepay/angular-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

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
  • Sandbox web URL: https://sandbox.sablepay.io
  • Live web URL: https://www.sablepay.io/

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

Angular Integration Examples

Standalone Component (Angular 14+)

import { Component, OnDestroy } from '@angular/core';
import { CommonModule } from '@angular/common';
import { SablePay, QrCodeGenerator, isCompleted, isExpired } from '@sablepay/angular-sablepay-js';

@Component({
  selector: 'app-payment',
  standalone: true,
  imports: [CommonModule],
  template: `
    <button (click)="pay()">Pay $10.50</button>
    <img *ngIf="qrUrl" [src]="qrUrl" alt="Scan to pay" />
    <p *ngIf="status">{{ status }}</p>
  `,
})
export class PaymentComponent implements OnDestroy {
  qrUrl = '';
  status = '';
  private paymentId = '';

  async pay() {
    const sdk = SablePay.getInstance();
    const response = await sdk.createPayment({ amount: 10.50 });
    this.paymentId = response.paymentId;

    const qrGen = new QrCodeGenerator();
    this.qrUrl = (await qrGen.generatePaymentQr(response)) || '';

    this.pollStatus();
  }

  private async pollStatus() {
    const sdk = SablePay.getInstance();
    const statusResp = await sdk.getPaymentStatus(this.paymentId);
    this.status = statusResp.status;

    if (!isCompleted(statusResp.status) && !isExpired(statusResp.status)) {
      setTimeout(() => this.pollStatus(), 3000);
    }
  }

  ngOnDestroy() {}
}

NgModule-based (Angular 14+)

// app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import { SablePay } from '@sablepay/angular-sablepay-js';
import { environment } from './environments/environment';

@NgModule({
  declarations: [AppComponent],
  imports: [BrowserModule],
  bootstrap: [AppComponent],
})
export class AppModule {
  constructor() {
    SablePay.initialize({
      apiKey: environment.sablepayApiKey,
      merchantId: environment.sablepayMerchantId,
      enableLogging: !environment.production,
    });
  }
}

Using APP_INITIALIZER

import { APP_INITIALIZER, NgModule } from '@angular/core';
import { SablePay } from '@sablepay/angular-sablepay-js';
import { environment } from './environments/environment';

function initializeSablePay(): () => void {
  return () => {
    SablePay.initialize({
      apiKey: environment.sablepayApiKey,
      merchantId: environment.sablepayMerchantId,
      enableLogging: !environment.production,
    });
  };
}

@NgModule({
  providers: [
    {
      provide: APP_INITIALIZER,
      useFactory: initializeSablePay,
      multi: true,
    },
  ],
  ...
})
export class AppModule {}

CORS / Proxy Configuration

During development, you may need to proxy API calls to avoid CORS issues. Add a proxy config:

proxy.conf.json

{
  "/api/proxy": {
    "target": "https://sandbox-api.sablepay.io/api/v1",
    "secure": true,
    "changeOrigin": true,
    "pathRewrite": {
      "^/api/proxy": ""
    }
  }
}

angular.json

{
  "architect": {
    "serve": {
      "options": {
        "proxyConfig": "src/proxy.conf.json"
      }
    }
  }
}

Then initialize with the proxy URL:

SablePay.initialize({
  apiKey: environment.sablepayApiKey,
  merchantId: environment.sablepayMerchantId,
  baseUrl: `${window.location.origin}/api/proxy/`,
});

Example App

A full working Angular example app is included in example-app/. It features a Coffee Shop POS and Payment Status Lookup.

cd example-app

# 1. Update credentials in src/environments/environment.ts

# 2. Install dependencies
npm install

# 3. Run the app
npm start

Open http://localhost:4200 to see the example.


Building the SDK

npm run build       # Build CJS + ESM + type declarations
npm test            # Run tests
npm run typecheck   # Type checking only

Publishing to npm

# Build the SDK
npm run build

# Login to npm
npm login

# Publish
npm publish --access public

License

MIT — see LICENSE