Getting Started with the Payin SDK
@paymentlabs/paymentlabs-js
A framework-agnostic TypeScript SDK for integrating Payment Labs payment widgets into your application.
What This SDK Provides
The Payment Labs SDK is a payment processing widget library that handles the payment collection portion of your checkout flow. Here's what it does and doesn't do:
✅ What the SDK Provides:
- Payment Method Widgets: Embedded payment forms for collecting credit cards, bank accounts, and other payment instruments
- Secure Payment Processing: PCI-compliant payment tokenization and submission
- Checkout Data Access: Retrieve payment details, amounts, merchant info, and payment status
- Subscription Management: Handle recurring payment setup and subscription plan selection
- Authentication Flows: Manage guest checkout, user login, and OTP verification
- Event System: Real-time payment status updates and error handling
- Theme Customization: Match your brand colors and styling
❌ What the SDK Does NOT Provide:
- Checkout UI: No cart display, product listings, or order summary components
- E-commerce Logic: No inventory management, shipping calculation, or tax computation
- Order Management: No order creation, fulfillment, or customer management
- Payment Processing Backend: You need existing PaymentLabs checkout sessions
🏗️ Architecture Overview
Your Application Payment Labs SDK Payment Labs Backend
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ • Product Pages │ │ • Payment Widget│ │ • Checkout API │
│ • Shopping Cart │ ┌────────► │ • Event Handlers│ ┌────────► │ • Authentication│
│ • Order Summary │ │ │ • Data Access │ │ │ • Transaction │
│ • Checkout UI │ │ │ • Theme Support │ │ │ Processing │
└─────────────────┘ │ └─────────────────┘ │ └─────────────────┘
│ │ │ │
└─────────────┘ └──────────┘
You build the checkout experience SDK handles payment collection Payment Labs processes payments
Table of Contents
- Installation
- Quick Start
- API Reference
- Checkout Widget
- Subscription Widget
- Event Handling
- Theming
- Examples
- TypeScript Support
- Data Types
- Error Handling
- Best Practices
Installation
NPM/Yarn/PNPM
npm
npm install @paymentlabs/paymentlabs-js
yarn
yarn add @paymentlabs/paymentlabs-js
pnpm
pnpm add @paymentlabs/paymentlabs-js
CDN (Script Tag)
<script src="https://sdk.paymentlabs.io/payin/latest/paymentlabs.js"></script>
<script>
// PaymentLabs is available globally
const sdk = PaymentLabs.create({
environment: 'sandbox',
merchantId: 'C-220c070ec91944b8ad39814ec43d9c05'
});
</script>
Specific Version
<!-- Use a specific version instead of 'latest' for production -->
<script src="https://sdk.paymentlabs.io/payin/v1.2.3/paymentlabs.js"></script>
Quick Start
Prerequisites
Before using the SDK, you need:
- Payment Labs Account
- Checkout: Create a checkout via Payment Labs API or dashboard
- Checkout ID: The unique identifier for your checkout (format:
S-01ARZ3NDEKTSV4RRFFQ69G5FAV
)
Creating Checkouts
The SDK requires pre-created checkouts from Payment Labs. Here's how to create them:
For One-time Payments (Checkout)
Your backend needs to create a checkout before your frontend can use the SDK.
API Reference: Create Checkout
For Recurring Payments (Subscriptions)
For subscription billing, you need to create subscription plans and plan offers using the Payment Labs API:
- Create Subscription Plan: Create Subscription Plan
- Create Plan Offer: Create Plan Offer
Use the Plan Offer ID with sdk.createWidget('subscription', offerId, ...)
.
Guide: Getting Started with Subscriptions
Complete Integration Example
Here's how the backend and frontend work together:
import { PaymentLabs, PaymentWidget } from '@paymentlabs/paymentlabs-js';
// Initialize the SDK
const sdk = PaymentLabs.create({
environment: 'sandbox', // or 'production'
merchantId: 'C-220c070ec91944b8ad39814ec43d9c05'
});
// Create a checkout widget
const widget = await sdk.createWidget('checkout', 'your-checkout-id', {
containerId: 'checkout-container',
theme: {
colors: {
seed: {
brand: '#f44336',
neutral: '#1f62e6'
}
}
}
});
// Listen to events
widget.on('checkout-loaded', () => {
const checkoutData = widget.getCheckoutData();
console.log('Checkout loaded:', checkoutData);
});
// Submit payment
widget.submitPayment();
API Reference
PaymentLabs
The main SDK class for creating and managing payment widgets.
PaymentLabs.create(config)
PaymentLabs.create(config)
Creates a new Payment Labs SDK instance.
Parameters:
config
(object):environment
(string): The environment to use. Options:'sandbox'
,'production'
merchantId
(string):
Returns: PaymentLabs
instance
sdk.createWidget(type, id, options)
sdk.createWidget(type, id, options)
Creates a payment widget.
Parameters:
type
(string): Widget type. Options:'checkout'
,'subscription'
id
(string): Checkout ID or subscription offer IDoptions
(object):containerId
(string): ID of the HTML element where the widget will be renderedtheme
(ThemeConfig, optional): Custom theme configuration
Returns: Promise
PaymentWidget
The widget instance that handles payment processing and events.
Methods
widget.on(event, callback)
widget.on(event, callback)
Registers an event listener.
Parameters:
event
(string): Event namecallback
(function): Event handler function
widget.submitPayment()
widget.submitPayment()
Submits the payment for processing.
widget.getCheckoutData()
widget.getCheckoutData()
Retrieves the current checkout data.
Returns: Checkout | null
widget.getSubscriptionOfferData()
widget.getSubscriptionOfferData()
Retrieves the current subscription offer data.
Returns: SubscriptionOffer | null
Checkout Widget
The checkout widget handles one-time payments and payment method collection.
Basic Usage
import { PaymentLabs, PaymentWidget } from '@paymentlabs/paymentlabs-js';
class CheckoutManager {
private widget: PaymentWidget | null = null;
async initializeCheckout(checkoutId: string, containerId: string) {
const sdk = PaymentLabs.create({
environment: 'sandbox',
merchantId: 'C-220c070ec91944b8ad39814ec43d9c05'
});
this.widget = await sdk.createWidget('checkout', checkoutId, {
containerId,
theme: this.getTheme()
});
this.setupEventListeners();
}
private setupEventListeners() {
if (!this.widget) return;
// Widget loaded
this.widget.on('checkout-loaded', () => {
const checkoutData = this.widget!.getCheckoutData();
console.log('Checkout loaded:', checkoutData);
});
// Payment completed
this.widget.on('checkout-completed', () => {
const checkoutData = this.widget!.getCheckoutData();
console.log('Payment completed:', checkoutData);
this.handleSuccess();
});
// Payment failed
this.widget.on('checkout-failed', (data: { errorReason: string }) => {
console.error('Payment failed:', data.errorReason);
this.handleFailure(data.errorReason);
});
// Checkout expired
this.widget.on('checkout-expired', () => {
console.log('Checkout expired');
this.handleExpired();
});
// Payment processing
this.widget.on('payment-processing', (isProcessing: boolean) => {
this.setLoadingState(isProcessing);
});
// Checkout processing
this.widget.on('checkout-processing', () => {
this.setProcessingState(true);
});
}
submitPayment() {
try {
this.widget?.submitPayment();
} catch (error) {
console.error('Error submitting payment:', error);
}
}
private handleSuccess() {
// Handle successful payment
}
private handleFailure(reason: string) {
// Handle payment failure
}
private handleExpired() {
// Handle expired checkout
}
private setLoadingState(isLoading: boolean) {
// Update UI loading state
}
private setProcessingState(isProcessing: boolean) {
// Update UI processing state
}
private getTheme() {
return {
colors: {
seed: {
brand: '#f44336',
neutral: '#1f62e6',
info: '#1c57cc',
positive: '#e9f1ff',
negative: '#a7c5ff',
warning: '#6599ff'
}
}
};
}
}
Subscription Widget
The subscription widget handles recurring payments and subscription management.
Basic Usage
import { PaymentLabs, PaymentWidget, SubscriptionOffer, SubscriptionPlan } from '@paymentlabs/paymentlabs-js';
class SubscriptionManager {
private widget: PaymentWidget | null = null;
private subscriptionOfferData: SubscriptionOffer | null = null;
private subscriptionPlanData: SubscriptionPlan | null = null;
async initializeSubscription(subscriptionOfferId: string, containerId: string) {
const sdk = PaymentLabs.create({
environment: 'sandbox',
merchantId: 'C-220c070ec91944b8ad39814ec43d9c05'
});
this.widget = await sdk.createWidget('subscription', subscriptionOfferId, {
containerId,
theme: this.getTheme()
});
this.setupEventListeners();
}
private setupEventListeners() {
if (!this.widget) return;
// Subscription offer loaded
this.widget.on('subscription-loaded', () => {
const subscriptionOffer = this.widget!.getSubscriptionOfferData();
if (subscriptionOffer) {
this.subscriptionOfferData = subscriptionOffer;
console.log('Subscription loaded:', subscriptionOffer);
}
});
// Subscription completed
this.widget.on('subscription-completed', () => {
console.log('Subscription completed');
this.handleSuccess();
});
// Subscription plan changed
this.widget.on('subscription-plan-changed', (plan: SubscriptionPlan) => {
this.subscriptionPlanData = plan;
console.log('Plan changed:', plan);
this.updatePlanDisplay(plan);
});
// Subscription failed
this.widget.on('subscription-failed', (data: { errorReason: string }) => {
console.error('Subscription failed:', data.errorReason);
this.handleFailure(data.errorReason);
});
// Subscription expired
this.widget.on('subscription-expired', () => {
const subscriptionOffer = this.widget!.getSubscriptionOfferData();
if (subscriptionOffer) {
this.subscriptionOfferData = subscriptionOffer;
}
console.log('Subscription expired');
this.handleExpired();
});
// Payment processing
this.widget.on('payment-processing', (isProcessing: boolean) => {
this.setLoadingState(isProcessing);
});
// Checkout processing
this.widget.on('checkout-processing', () => {
this.setProcessingState(true);
});
}
submitPayment() {
try {
this.widget?.submitPayment();
} catch (error) {
console.error('Error submitting payment:', error);
}
}
private handleSuccess() {
// Handle successful subscription
}
private handleFailure(reason: string) {
// Handle subscription failure
}
private handleExpired() {
// Handle expired subscription offer
}
private updatePlanDisplay(plan: SubscriptionPlan) {
// Update UI with new plan details
}
private setLoadingState(isLoading: boolean) {
// Update UI loading state
}
private setProcessingState(isProcessing: boolean) {
// Update UI processing state
}
private getTheme() {
return {
colors: {
seed: {
brand: '#f44336',
neutral: '#1f62e6',
info: '#1c57cc',
positive: '#e9f1ff',
negative: '#a7c5ff',
warning: '#6599ff'
}
}
};
}
}
Event Handling
The SDK provides a comprehensive event system for tracking widget state and user interactions.
Available Events
Checkout Events
checkout-loaded
: Fired when the checkout widget is fully loadedcheckout-completed
: Fired when payment is successfully completedcheckout-failed
: Fired when payment failscheckout-expired
: Fired when the checkout session expirescheckout-processing
: Fired when checkout processing beginspayment-processing
: Fired when payment processing state changes
Subscription Events
subscription-loaded
: Fired when the subscription widget is fully loadedsubscription-completed
: Fired when subscription is successfully createdsubscription-failed
: Fired when subscription creation failssubscription-expired
: Fired when the subscription offer expiressubscription-plan-changed
: Fired when user selects a different subscription planpayment-processing
: Fired when payment processing state changescheckout-processing
: Fired when checkout processing begins
Event Handler Example
function setupEventHandlers(widget: PaymentWidget) {
// Generic event handler
const handleEvent = (eventName: string, data?: any) => {
console.log(`Event: ${eventName}`, data);
// Update analytics
analytics.track(eventName, data);
// Update UI state
updateUIState(eventName, data);
};
// Register all events
const events = [
'checkout-loaded',
'checkout-completed',
'checkout-failed',
'checkout-expired',
'checkout-processing',
'payment-processing'
];
events.forEach(event => {
widget.on(event, (data) => handleEvent(event, data));
});
}
Theming
Customize the widget appearance using the theme configuration.
Theme Configuration
interface ThemeConfig {
colors: {
seed: {
brand: string; // Primary brand color (hex)
neutral: string; // Neutral color (hex)
info: string; // Info color (hex)
positive: string; // Success color (hex)
negative: string; // Error color (hex)
warning: string; // Warning color (hex)
};
};
}
Theme Example
const theme: ThemeConfig = {
colors: {
seed: {
brand: '#f44336', // Red
neutral: '#1f62e6', // Blue
info: '#1c57cc', // Dark blue
positive: '#e9f1ff', // Light blue
negative: '#a7c5ff', // Light red
warning: '#6599ff' // Orange
}
}
};
const widget = await sdk.createWidget('checkout', checkoutId, {
containerId: 'checkout-container',
theme
});
Examples
CDN Example (Script Tag)
<!DOCTYPE html>
<html>
<head>
<title>Payment Labs CDN Example</title>
</head>
<body>
<div id="checkout-container"></div>
<button id="pay-button" style="display: none;">Pay Now</button>
<div id="status"></div>
<!-- Load PaymentLabs SDK from CDN -->
<script src="https://sdk.paymentlabs.io/sdk/latest/paymentlabs.js"></script>
<script>
class CDNCheckoutApp {
constructor() {
this.widget = null;
this.checkoutData = null;
this.statusDiv = document.getElementById('status');
this.init();
}
async init() {
try {
// Payment Labs is available globally from CDN
const sdk = PaymentLabs.create({
environment: 'production', // or 'sandbox' for testing
merchantId: 'C-220c070ec91944b8ad39814ec43d9c05'
});
this.updateStatus('Loading checkout...');
this.widget = await sdk.createWidget('checkout', 'your-checkout-id', {
containerId: 'checkout-container',
theme: {
colors: {
seed: {
brand: '#f44336',
neutral: '#1f62e6',
info: '#1c57cc',
positive: '#e9f1ff',
negative: '#a7c5ff',
warning: '#6599ff'
}
}
}
});
this.setupEvents();
} catch (error) {
this.updateStatus(`Initialization failed: ${error.message}`, 'error');
}
}
setupEvents() {
this.widget.on('checkout-loaded', () => {
this.checkoutData = this.widget.getCheckoutData();
this.updateStatus(`Checkout loaded: ${this.checkoutData.amount.currency} ${this.checkoutData.amount.value / 100}`);
document.getElementById('pay-button').style.display = 'block';
});
this.widget.on('checkout-completed', () => {
this.updateStatus('Payment completed successfully!', 'success');
document.getElementById('pay-button').style.display = 'none';
});
this.widget.on('checkout-failed', (data) => {
this.updateStatus(`Payment failed: ${data.errorReason}`, 'error');
});
this.widget.on('checkout-expired', () => {
this.updateStatus('Checkout session expired. Please refresh to try again.', 'warning');
});
this.widget.on('payment-processing', (isProcessing) => {
const button = document.getElementById('pay-button');
button.disabled = isProcessing;
button.textContent = isProcessing ? 'Processing...' : 'Pay Now';
if (isProcessing) {
this.updateStatus('Processing payment...', 'info');
}
});
}
submitPayment() {
try {
this.widget.submitPayment();
} catch (error) {
this.updateStatus(`Payment submission failed: ${error.message}`, 'error');
}
}
updateStatus(message, type = 'info') {
this.statusDiv.textContent = message;
this.statusDiv.className = `status-${type}`;
}
}
// Initialize the app when DOM is ready
document.addEventListener('DOMContentLoaded', () => {
const app = new CDNCheckoutApp();
document.getElementById('pay-button').addEventListener('click', () => {
app.submitPayment();
});
});
</script>
<style>
body {
font-family: Arial, sans-serif;
max-width: 600px;
margin: 50px auto;
padding: 20px;
}
#checkout-container {
border: 1px solid #ddd;
border-radius: 8px;
padding: 20px;
margin: 20px 0;
min-height: 200px;
}
#pay-button {
background-color: #f44336;
color: white;
border: none;
padding: 12px 24px;
border-radius: 6px;
cursor: pointer;
font-size: 16px;
width: 100%;
margin: 10px 0;
}
#pay-button:hover:not(:disabled) {
background-color: #d32f2f;
}
#pay-button:disabled {
background-color: #ccc;
cursor: not-allowed;
}
#status {
padding: 10px;
border-radius: 4px;
margin: 10px 0;
}
.status-info {
background-color: #e3f2fd;
color: #1976d2;
border: 1px solid #bbdefb;
}
.status-success {
background-color: #e8f5e8;
color: #2e7d32;
border: 1px solid #c8e6c9;
}
.status-error {
background-color: #ffebee;
color: #c62828;
border: 1px solid #ffcdd2;
}
.status-warning {
background-color: #fff3e0;
color: #ef6c00;
border: 1px solid #ffcc02;
}
</style>
</body>
</html>
Vanilla JavaScript Example (Module Import)
<!DOCTYPE html>
<html>
<head>
<title>Payment Labs Checkout</title>
</head>
<body>
<div id="checkout-container"></div>
<button id="pay-button" style="display: none;">Pay Now</button>
<script type="module">
import { PaymentLabs } from '@paymentlabs/paymentlabs-js';
class CheckoutApp {
constructor() {
this.widget = null;
this.checkoutData = null;
this.init();
}
async init() {
const sdk = PaymentLabs.create({
environment: 'sandbox',
merchantId: 'C-220c070ec91944b8ad39814ec43d9c05'
});
this.widget = await sdk.createWidget('checkout', 'your-checkout-id', {
containerId: 'checkout-container'
});
this.setupEvents();
}
setupEvents() {
this.widget.on('checkout-loaded', () => {
this.checkoutData = this.widget.getCheckoutData();
document.getElementById('pay-button').style.display = 'block';
});
this.widget.on('checkout-completed', () => {
alert('Payment completed successfully!');
});
this.widget.on('checkout-failed', (data) => {
alert(`Payment failed: ${data.errorReason}`);
});
}
submitPayment() {
this.widget.submitPayment();
}
}
const app = new CheckoutApp();
document.getElementById('pay-button').addEventListener('click', () => {
app.submitPayment();
});
</script>
</body>
</html>
React Example
import React, { useEffect, useRef, useState } from 'react';
import { PaymentLabs, PaymentWidget } from '@paymentlabs/paymentlabs-js';
interface CheckoutComponentProps {
checkoutId: string;
onSuccess: () => void;
onFailure: (reason: string) => void;
}
export function CheckoutComponent({ checkoutId, onSuccess, onFailure }: CheckoutComponentProps) {
const [isLoading, setIsLoading] = useState(false);
const [isReady, setIsReady] = useState(false);
const widgetRef = useRef<PaymentWidget | null>(null);
useEffect(() => {
const initializeWidget = async () => {
const sdk = PaymentLabs.create({
environment: 'sandbox',
merchantId: 'C-220c070ec91944b8ad39814ec43d9c05'
});
const widget = await sdk.createWidget('checkout', checkoutId, {
containerId: 'checkout-container'
});
widgetRef.current = widget;
widget.on('checkout-loaded', () => {
setIsReady(true);
});
widget.on('checkout-completed', () => {
onSuccess();
});
widget.on('checkout-failed', (data) => {
onFailure(data.errorReason);
});
widget.on('payment-processing', (isProcessing) => {
setIsLoading(isProcessing);
});
};
initializeWidget();
}, [checkoutId, onSuccess, onFailure]);
const handleSubmit = () => {
widgetRef.current?.submitPayment();
};
return (
<div>
<div id="checkout-container" />
{isReady && (
<button
onClick={handleSubmit}
disabled={isLoading}
>
{isLoading ? 'Processing...' : 'Pay Now'}
</button>
)}
</div>
);
}
Vue.js Example
<template>
<div>
<div id="checkout-container"></div>
<button
v-if="isReady"
@click="submitPayment"
:disabled="isLoading"
>
{{ isLoading ? 'Processing...' : 'Pay Now' }}
</button>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue';
import { PaymentLabs, PaymentWidget } from '@paymentlabs/paymentlabs-js';
interface Props {
checkoutId: string;
}
interface Emits {
(e: 'success'): void;
(e: 'failure', reason: string): void;
}
const props = defineProps<Props>();
const emit = defineEmits<Emits>();
const isLoading = ref(false);
const isReady = ref(false);
const widget = ref<PaymentWidget | null>(null);
onMounted(async () => {
const sdk = PaymentLabs.create({
environment: 'sandbox',
merchantId: 'C-220c070ec91944b8ad39814ec43d9c05'
});
widget.value = await sdk.createWidget('checkout', props.checkoutId, {
containerId: 'checkout-container'
});
widget.value.on('checkout-loaded', () => {
isReady.value = true;
});
widget.value.on('checkout-completed', () => {
emit('success');
});
widget.value.on('checkout-failed', (data) => {
emit('failure', data.errorReason);
});
widget.value.on('payment-processing', (isProcessing) => {
isLoading.value = isProcessing;
});
});
const submitPayment = () => {
widget.value?.submitPayment();
};
</script>
TypeScript Support
The SDK is written in TypeScript and provides full type definitions.
Type Definitions
// Main SDK types
import {
PaymentLabs,
PaymentWidget,
SubscriptionOffer,
SubscriptionPlan
} from '@paymentlabs/paymentlabs-js';
// Theme configuration
import { ThemeConfig } from '@paymentlabs/paymentlabs-js/dist/types/theme';
// External model types (available through the SDK)
import {
Checkout,
CheckoutErrorReason
} from '@paymentlabs/paymentlabs-js';
// Environment types
type SDKEnvironment = 'sandbox' | 'production';
// Widget types
type WidgetType = 'checkout' | 'subscription';
Type-Safe Event Handling
interface CheckoutEvents {
'checkout-loaded': () => void;
'checkout-completed': () => void;
'checkout-failed': (data: { errorReason: CheckoutErrorReason }) => void;
'checkout-expired': () => void;
'checkout-processing': () => void;
'payment-processing': (isProcessing: boolean) => void;
}
interface SubscriptionEvents {
'subscription-loaded': () => void;
'subscription-completed': () => void;
'subscription-failed': (data: { errorReason: CheckoutErrorReason }) => void;
'subscription-expired': () => void;
'subscription-plan-changed': (plan: SubscriptionPlan) => void;
'payment-processing': (isProcessing: boolean) => void;
'checkout-processing': () => void;
}
// Type-safe event registration
function setupTypedEvents(widget: PaymentWidget) {
widget.on('checkout-loaded', () => {
// TypeScript knows this is a checkout-loaded event
});
widget.on('subscription-plan-changed', (plan) => {
// TypeScript knows plan is SubscriptionPlan
console.log(plan.name, plan.amount);
});
}
Error Handling
The SDK provides comprehensive error handling for various scenarios.
Common Error Scenarios
class PaymentErrorHandler {
handleWidgetError(widget: PaymentWidget) {
widget.on('checkout-failed', (data) => {
switch (data.errorReason) {
case 'MISSING_INSTRUMENT':
this.showMissingInstrumentError();
break;
case 'INVALID_INSTRUMENT':
this.showInvalidInstrumentError();
break;
case 'CHARGE_INSTRUMENT_ERROR':
this.showChargeError();
break;
case 'APPLICATION_ERROR':
this.showApplicationError();
break;
case 'UNEXPECTED_ERROR':
this.showUnexpectedError();
break;
default:
this.showGenericError(data.errorReason);
}
});
widget.on('checkout-expired', () => {
this.showExpiredSessionError();
});
}
private showMissingInstrumentError() {
// Handle missing payment instrument
}
private showInvalidInstrumentError() {
// Handle invalid payment instrument
}
private showChargeError() {
// Handle charge error
}
private showApplicationError() {
// Handle application error
}
private showUnexpectedError() {
// Handle unexpected error
}
private showGenericError(reason: string) {
// Handle generic errors
}
private showExpiredSessionError() {
// Handle expired session
}
}
Data Types
The SDK uses well-defined TypeScript interfaces for all data objects. Here are the key types you'll work with:
Checkout
Represents checkout/payment data. This is the main type returned by getCheckoutData()
.
interface Checkout {
id: string; // Unique checkout identifier
buyerEmail: string; // Buyer's email address
merchantId: string; // Unique merchant identifier
merchantName: string; // Name of the merchant
buyerId: string; // Unique buyer identifier
alias: string; // Description/reference
amount: Money; // Amount to be charged
status: CheckoutStatus; // Current checkout status
errorReason?: CheckoutErrorReason; // Error reason if status is failed
enabledCheckoutInstruments: InstrumentType[]; // Available payment method types
requiresFraudSession: boolean; // Whether fraud session is required
expiresAt: string; // ISO 8601 expiration timestamp
links: {
embeddable: string; // Embeddable checkout URL
checkout: string; // Full checkout URL
};
redirectLinks?: RedirectLinks; // Optional redirect URLs
termsOfServiceUrl: string; // Terms of service URL
privacyPolicyUrl: string; // Privacy policy URL
}
type CheckoutStatus =
| 'PENDING' // Awaiting payment
| 'EXPIRED' // Checkout expired
| 'PROCESSING' // Payment in progress
| 'PAID' // Payment successful
| 'FAILED' // Payment failed
| 'UNKNOWN' // Unknown status
| 'CREATING' // Checkout being created
| 'ACTIVE' // Checkout is active
| 'TRIAL'; // Trial period
type CheckoutErrorReason =
| 'MISSING_INSTRUMENT' // No payment instrument provided
| 'INVALID_INSTRUMENT' // Invalid payment instrument
| 'CHARGE_INSTRUMENT_ERROR' // Error charging the instrument
| 'APPLICATION_ERROR' // Application-level error
| 'UNEXPECTED_ERROR'; // Unexpected system error
SubscriptionOffer
Represents subscription offer data.
interface SubscriptionOffer {
id: string; // Unique offer identifier
merchantId: string; // Merchant identifier
userId: string; // User identifier
subscriptionPlanIds: string[]; // Array of plan IDs in this offer
subscriptionPlans: SubscriptionPlan[]; // Available subscription plans
expiresAt: string; // ISO 8601 expiration timestamp
links: {
embeddable: string; // Embeddable URL for the offer
self: string; // Direct link to the offer
};
}
SubscriptionPlan
Represents individual subscription plan within an offer.
interface SubscriptionPlan {
id: string; // Unique plan identifier
merchantId: string; // Merchant identifier
merchantDisplayName: string; // Merchant display name
sort: string; // Sorting information for ordering/grouping
name: string; // Plan display name
description?: string; // Plan description
enabledCheckoutInstruments: InstrumentType[]; // Available payment methods
schedule: Schedule; // Billing schedule
amount: Money; // Amount to be charged
trialPeriod?: Schedule; // Optional trial period
status: SubscriptionPlanStatus; // Plan status
termsOfServiceUrl?: string; // Terms of service URL
privacyPolicyUrl?: string; // Privacy policy URL
}
interface Schedule {
interval: ScheduleInterval; // Billing interval
intervalCount: number; // Number of intervals between billings
}
type ScheduleInterval =
| 'daily' // Daily billing
| 'weekly' // Weekly billing
| 'monthly' // Monthly billing
| 'yearly'; // Annual billing
type SubscriptionPlanStatus =
| 'ACTIVE' // Plan is active and can be subscribed to
| 'CANCELED'; // Plan cannot be subscribed to
interface Money {
value: number; // Amount in float point value
currency: string; // ISO 4217 currency code
formattedValue: string; // Formatted value with currency
digits: number; // Decimal digits for the currency
wholeValue: number; // Value without decimal points (e.g., cents)
}
Subscription
Represents an active subscription.
interface Subscription {
id: string; // Unique subscription identifier
merchantId: string; // Merchant identifier
userId: string; // User identifier
subscriptionPlanId: string; // Plan identifier
subscriptionPlanName: string; // Plan name
subscribeDate: string; // ISO 8601 subscription date
checkoutInstrument: InstrumentResponse; // Payment instrument
instrumentType: InstrumentType; // Instrument type
schedule: Schedule; // Billing schedule
amount: Money; // Amount to be charged
status: CheckoutStatus; // Subscription status
nextPaymentDate?: string; // Next payment date
termsOfServiceUrl?: string; // Terms of service URL
privacyPolicyUrl?: string; // Privacy policy URL
}
CheckoutErrorReason
Defines possible checkout error reasons.
type CheckoutErrorReason =
| 'MISSING_INSTRUMENT' // No payment instrument provided
| 'INVALID_INSTRUMENT' // Invalid payment instrument
| 'CHARGE_INSTRUMENT_ERROR' // Error charging the instrument
| 'APPLICATION_ERROR' // Application-level error
| 'UNEXPECTED_ERROR'; // Unexpected system error
ThemeConfig
Defines widget appearance customization.
interface ThemeConfig {
colors: {
seed: {
brand: string; // Primary brand color (hex)
neutral: string; // Neutral/secondary color (hex)
info: string; // Information color (hex)
positive: string; // Success/positive color (hex)
negative: string; // Error/negative color (hex)
warning: string; // Warning color (hex)
};
};
}
WidgetOptions
Defines the configuration options for creating payment widgets. This is the main options object passed to sdk.createWidget()
.
interface WidgetOptions {
containerId: string; // ID of the HTML element where the widget will be rendered
theme?: ThemeConfig; // Optional theme configuration for widget appearance
}
Properties:
containerId
(string, required): The ID of the HTML element where the payment widget will be rendered. This element must exist in the DOM before creating the widget.theme
(ThemeConfig, optional): Custom theme configuration to customize the widget's appearance. If not provided, the widget will use default styling.
Usage Example:
const widget = await sdk.createWidget('checkout', checkoutId, {
containerId: 'payment-container',
theme: {
colors: {
seed: {
brand: '#f44336',
neutral: '#1f62e6',
info: '#1c57cc',
positive: '#e9f1ff',
negative: '#a7c5ff',
warning: '#6599ff'
}
}
}
});
Event Data Types
Event handlers receive typed data based on the event:
// Checkout Events
interface CheckoutFailedEvent {
errorReason: CheckoutErrorReason;
}
// Subscription Events
interface SubscriptionFailedEvent {
errorReason: CheckoutErrorReason;
}
interface SubscriptionPlanChangedEvent {
plan: SubscriptionPlan;
}
// Payment Processing Events
type PaymentProcessingEvent = boolean; // true = processing, false = idle
Usage Example with Types
import {
PaymentLabs,
PaymentWidget,
Checkout,
SubscriptionOffer,
SubscriptionPlan,
CheckoutErrorReason
} from '@paymentlabs/paymentlabs-js';
class TypedPaymentHandler {
private widget: PaymentWidget | null = null;
async initializeCheckout(checkoutId: string) {
const sdk = PaymentLabs.create({
environment: 'sandbox',
merchantId: 'C-220c070ec91944b8ad39814ec43d9c05'
});
this.widget = await sdk.createWidget('checkout', checkoutId, {
containerId: 'checkout-container'
});
// Type-safe event handling
this.widget.on('checkout-loaded', () => {
const checkoutData: Checkout | null = this.widget!.getCheckoutData();
if (checkoutData) {
console.log(`Checkout for ${checkoutData.amount.currency} ${checkoutData.amount.value / 100}`);
}
});
this.widget.on('checkout-failed', (event: { errorReason: CheckoutErrorReason }) => {
this.handlePaymentError(event.errorReason);
});
this.widget.on('subscription-plan-changed', (plan: SubscriptionPlan) => {
console.log(`Plan changed to: ${plan.name} - ${plan.amount.currency} ${plan.amount.value / 100}/${plan.schedule.interval}`);
});
}
private handlePaymentError(reason: CheckoutErrorReason) {
switch (reason) {
case 'MISSING_INSTRUMENT':
console.error('No payment method was provided. Please select a payment method.');
break;
case 'INVALID_INSTRUMENT':
console.error('The payment method is invalid. Please check your payment details.');
break;
case 'CHARGE_INSTRUMENT_ERROR':
console.error('There was an error processing your payment. Please try again.');
break;
case 'APPLICATION_ERROR':
console.error('An application error occurred. Please contact support.');
break;
case 'UNEXPECTED_ERROR':
console.error('An unexpected error occurred. Please try again later.');
break;
default:
console.error(`Payment failed: ${reason}`);
}
}
}
Best Practices
- Environment Configuration: Always use the appropriate environment for your use case
- Error Handling: Implement comprehensive error handling for all events
- Loading States: Use the
payment-processing
andcheckout-processing
events to show appropriate loading states - Theme Consistency: Ensure your theme colors match your brand guidelines
- Event Cleanup: Remove event listeners when components unmount to prevent memory leaks
- Type Safety: Use TypeScript for better development experience and error prevention
Support
For technical support and questions, please refer to the official Payment Labs documentation or contact our support team.
Updated 5 days ago