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 {
mode: "dark" | "light"; // light is the default mode
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
});
function updateThemeMode(mode: "light" | "dark"){
await sdk.updateTheme(mode);
}
Important: Theme Switching Side Effect
When changing the
themeMode
, all secure input fields
(💳 Cardholder Name, Card Number, Expiration Date, Security Code)
will be re-rendered for security reasons.This means any values entered in these fields may be cleared.
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 {
mode: "light" | "dark";
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 about 10 hours ago