@getalternative/partner-sdk
v0.1.7
Published
Web SDK for embeddable payment widgets with Alternative Payments
Readme
@getalternative/partner-sdk
Web SDK for embeddable payment widgets with Alternative Payments.
Installation
npm install @getalternative/partner-sdkFeatures
- Invoice display - List and detail views for customer invoices
- Payment method selection - Show saved payment methods with selection
- Add payment method - Credit card (PCI-compliant via Evervault) and ACH bank account
- Payment processing - Confirmation screen and payment execution
- Success/Error handling - Result display with retry option
- Customizable theming - Colors, fonts, and styling via CSS variables
- Two usage modes - Full flow orchestrator OR individual components
Authentication
The SDK uses JWT token-based authentication. Tokens are generated server-side by your backend using the Partner API. This approach keeps your API credentials secure on your server.
Step 1: Generate Token (Backend)
Your backend calls the Alternative API to generate a checkout token:
# First, get an OAuth token using your client credentials
curl -X POST https://public-api.alternativepayments.io/oauth/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-H "Authorization: Basic $(echo -n 'CLIENT_ID:CLIENT_SECRET' | base64)" \
-d "grant_type=client_credentials"
# Then generate a checkout token for the specific customer/invoice
curl -X POST https://public-api.alternativepayments.io/v1/checkout-auth/init \
-H "Authorization: Bearer {oauth_token}" \
-H "Content-Type: application/json" \
-d '{
"customer_id": "cus_xxx",
"invoice_id": "inv_xxx"
}'Response:
{
"token": "eyJhbGciOiJIUzI1NiIs...",
"expires_at": 1703980800
}Step 2: Pass Token to Frontend
Create an endpoint on your backend that your frontend can call to get the token:
// Your backend (e.g., Express.js)
app.post('/api/checkout-token', async (req, res) => {
const { customerId, invoiceId } = req.body;
// Call Alternative API to get checkout token
const token = await alternativeApi.generateCheckoutToken(customerId, invoiceId);
res.json({ token: token.token, expiresAt: token.expires_at });
});Step 3: Initialize SDK (Frontend)
import { AlternativeClient } from '@getalternative/partner-sdk';
// Fetch token from your backend
const { token } = await fetch('/api/checkout-token', {
method: 'POST',
body: JSON.stringify({ customerId: 'cus_xxx', invoiceId: 'inv_xxx' }),
}).then(res => res.json());
// Initialize SDK with token
const client = await AlternativeClient.create({
accessToken: token,
environment: 'production',
onAccessTokenExpired: async () => {
// Called when token expires - fetch a new one
const response = await fetch('/api/checkout-token', {
method: 'POST',
body: JSON.stringify({ customerId: 'cus_xxx', invoiceId: 'inv_xxx' }),
});
const { token } = await response.json();
return token;
},
});Quick Start
Full Payment Flow
The easiest way to integrate - a complete payment widget that handles the entire flow:
import { AlternativeClient } from '@getalternative/partner-sdk';
// Use create() to automatically fetch theme from API
const client = await AlternativeClient.create({
accessToken: tokenFromBackend,
environment: 'production',
onAccessTokenExpired: async () => {
const response = await fetch('/api/checkout-token');
const { token } = await response.json();
return token;
},
});
// Mount the full payment flow
const flow = client.createPaymentFlow({
containerId: 'payment-container',
onPaymentSuccess: (payment) => {
console.log('Payment successful:', payment.id);
},
onPaymentError: (error) => {
console.error('Payment failed:', error.message);
},
onClose: () => {
console.log('Flow closed');
},
onGoBack: () => {
// Handle navigation when user clicks "Go back" on success screen
window.location.href = '/dashboard';
},
});
// Later: cleanup
flow.unmount();HTML:
<div id="payment-container"></div>Individual Components
For full control, use individual components to build your own flow:
import { AlternativeClient } from '@getalternative/partner-sdk';
const client = new AlternativeClient({
accessToken: tokenFromBackend,
environment: 'production',
});
// Invoice Detail
const invoiceDetail = client.components.invoiceDetail({
containerId: 'invoice-detail',
onPayNow: (invoice) => {
console.log('Pay now clicked for:', invoice.id);
// Navigate to payment method selection
},
onBack: () => {
// Navigate back to invoice list
},
});
// Payment Method Select
const paymentMethodSelect = client.components.paymentMethodSelect({
containerId: 'payment-methods',
onSelect: (paymentMethod) => {
console.log('Selected payment method:', paymentMethod.id);
// Navigate to confirmation
},
onAddNew: () => {
// Navigate to add payment method
},
onBack: () => {
// Navigate back
},
});
// Add Payment Method (Card or ACH)
const addPaymentMethod = client.components.addPaymentMethod({
containerId: 'add-payment-method',
defaultTab: 'card', // or 'ach'
onSuccess: (paymentMethod) => {
console.log('Added payment method:', paymentMethod.id);
// Navigate back to selection
},
onCancel: () => {
// Navigate back
},
});
// Payment Confirmation
const confirmation = client.components.paymentConfirmation({
containerId: 'confirmation',
paymentMethodId: 'pm_xxx',
onPaymentSuccess: (payment) => {
console.log('Payment successful:', payment.id);
// Show result
},
onPaymentError: (error) => {
console.error('Payment failed:', error.message);
// Show error
},
onBack: () => {
// Navigate back
},
});
// Payment Result
const result = client.components.paymentResult({
containerId: 'result',
status: 'success', // or 'error'
payment: payment, // for success
error: error, // for error
onDone: () => {
// Close or restart
},
onRetry: () => {
// Retry payment (for errors)
},
onGoBack: () => {
// Handle navigation when user clicks "Go back"
window.location.href = '/dashboard';
},
});Configuration
Client Configuration
interface AlternativeClientConfig {
// Required: JWT access token from checkout-auth/init
accessToken: string;
// Optional: Environment ('production' | 'staging')
environment?: 'production' | 'staging';
// Optional: Custom base URL (overrides environment)
baseUrl?: string;
// Optional: Request timeout in ms (default: 30000)
timeout?: number;
// Optional: Number of retries (default: 3)
retries?: number;
// Optional: Callback when token expires
onAccessTokenExpired?: () => Promise<string>;
}Theme Configuration
Customize the appearance of all components:
const theme = {
// Colors
primaryColor: '#0066cc', // Primary buttons, links
successColor: '#22c55e', // Success states
errorColor: '#ef4444', // Error states
warningColor: '#f59e0b', // Warning states
// Text colors
textPrimary: '#1a1a1a', // Primary text
textSecondary: '#6b7280', // Secondary text
textMuted: '#9ca3af', // Muted text
// Background colors
bgPrimary: '#ffffff', // Primary background
bgSecondary: '#f9fafb', // Secondary background
bgMuted: '#f3f4f6', // Muted background
// Border
borderColor: '#e5e7eb', // Border color
borderRadius: '8px', // Border radius
// Typography
fontFamily: 'Inter, system-ui, sans-serif',
};
// Apply at client level (all components)
const client = await AlternativeClient.create({
accessToken: token,
theme,
});
// Or override per component
const invoiceDetail = client.components.invoiceDetail({
containerId: 'invoice',
theme: { primaryColor: '#ff0000' }, // Override just this component
});Payment Flow
The full payment flow follows these screens:
- Invoice Detail - Show invoice amount and payment date
- Payment Method Select - Choose from saved payment methods or add new
- Add Payment Method - (If adding new) Card form via Evervault or ACH form
- Payment Confirmation - Review and confirm payment
- Payment Result - Success or error with retry option
const flow = client.createPaymentFlow({
containerId: 'payment-container',
onPaymentSuccess: (payment) => console.log('Success:', payment),
});Error Handling
The SDK provides typed errors for different scenarios:
import {
AccessTokenExpiredError,
ValidationError,
NotFoundError,
RateLimitError,
ServerError,
TimeoutError,
NetworkError,
} from '@getalternative/partner-sdk';
try {
// SDK operations
} catch (error) {
if (error instanceof AccessTokenExpiredError) {
console.log('Token expired - refresh needed');
} else if (error instanceof NotFoundError) {
console.log('Resource not found');
} else if (error instanceof ValidationError) {
console.log('Validation error:', error.details);
} else if (error instanceof RateLimitError) {
console.log('Rate limited, retry after:', error.retryAfter);
} else if (error instanceof ServerError) {
console.log('Server error:', error.statusCode);
} else if (error instanceof TimeoutError) {
console.log('Request timed out');
} else if (error instanceof NetworkError) {
console.log('Network error');
}
}Browser Support
This SDK runs entirely in the browser and bundles React internally. It works with any JavaScript framework or vanilla JS.
Supported browsers:
- Chrome 80+
- Firefox 75+
- Safari 13+
- Edge 80+
Development
# Install dependencies
npm install
# Run tests
npm test
# Run tests with coverage
npm run test:coverage
# Build
npm run build
# Lint
npm run lint
# Type check
npm run typecheckLicense
UNLICENSED - Private package for Alternative internal use.
