Integration Guide
This comprehensive guide walks you through integrating CyberPay's cross-chain payment system into your application, from initial setup to production deployment.
Overview
CyberPay integration follows a structured approach:
- Setup & Authentication - Get API access and configure your environment
- Payment Flow Implementation - Implement the 5-step payment process
- User Experience Design - Create intuitive payment interfaces
- Error Handling - Handle edge cases and failures gracefully
- Testing & Deployment - Validate integration and go live
Prerequisites
Before starting, ensure you have:
- ✅ CyberPay merchant account
- ✅ API keys (testnet and production)
- ✅ Basic understanding of REST APIs
- ✅ Web3 wallet integration (MetaMask, WalletConnect, etc.)
- ✅ HTTPS-enabled domain for webhooks
Step 1: Environment Setup
Install SDK
# JavaScript/TypeScript
npm install @cyberpay/sdk
# Python
pip install cyberpay-python
# Go
go get github.com/cyberpay/cyberpay-goConfigure Environment
// .env file
CYBERPAY_API_KEY=your_production_api_key
CYBERPAY_TESTNET_API_KEY=your_testnet_api_key
CYBERPAY_MERCHANT_ID=your_merchant_id
CYBERPAY_WEBHOOK_SECRET=your_webhook_secretInitialize SDK
import { CyberPaySDK } from '@cyberpay/sdk';
const sdk = new CyberPaySDK({
apiKey: process.env.CYBERPAY_API_KEY,
environment: process.env.NODE_ENV === 'production' ? 'production' : 'testnet'
});Step 2: Basic Payment Flow
2.1 Create Checkout Session
async function createCheckout(productInfo, amount, currency) {
try {
const checkout = await sdk.checkout.create({
merchantId: process.env.CYBERPAY_MERCHANT_ID,
amount: amount.toString(),
currency: currency,
productInfo: {
name: productInfo.name,
description: productInfo.description,
imageUrl: productInfo.imageUrl
},
returnUrl: `${process.env.BASE_URL}/payment/success`,
cancelUrl: `${process.env.BASE_URL}/payment/cancel`,
webhookUrl: `${process.env.BASE_URL}/api/webhooks/cyberpay`,
metadata: {
orderId: productInfo.orderId,
customerId: productInfo.customerId
}
});
return checkout;
} catch (error) {
console.error('Checkout creation failed:', error);
throw error;
}
}2.2 Get Payment Quote
async function getPaymentQuote(checkoutId, paymentToken, settlementToken, amount) {
try {
const quote = await sdk.quote.get({
checkoutId,
paymentToken,
settlementToken,
amount,
amountType: 'exactOutput',
slippageTolerance: 0.5
});
return quote;
} catch (error) {
if (error.code === 'INSUFFICIENT_LIQUIDITY') {
// Handle low liquidity
throw new Error('Insufficient liquidity for this payment amount');
}
throw error;
}
}2.3 Create Payment Order
async function createPaymentOrder(checkoutId, quoteId, userAddress) {
try {
const order = await sdk.order.create({
checkoutId,
quoteId,
userAddress,
metadata: {
userAgent: navigator.userAgent,
timestamp: Date.now()
}
});
return order;
} catch (error) {
if (error.code === 'QUOTE_EXPIRED') {
// Get new quote and retry
throw new Error('Quote expired, please try again');
}
throw error;
}
}2.4 Execute Payment
async function executePayment(orderId, userAddress, walletProvider) {
try {
// Get execution plan
const execution = await sdk.payment.execute({
orderId,
userAddress
});
// Execute transactions through user's wallet
const txHashes = [];
for (const tx of execution.transactions) {
const txHash = await walletProvider.sendTransaction({
to: tx.to,
data: tx.data,
value: tx.value,
gasLimit: tx.gasLimit
});
txHashes.push(txHash);
// Wait for confirmation before next step
await waitForConfirmation(txHash, walletProvider);
}
return { txHashes, orderId };
} catch (error) {
console.error('Payment execution failed:', error);
throw error;
}
}2.5 Monitor Payment Status
async function monitorPayment(orderId, onUpdate) {
const maxAttempts = 120; // 10 minutes
let attempts = 0;
while (attempts < maxAttempts) {
try {
const status = await sdk.payment.getStatus(orderId);
onUpdate(status);
if (['completed', 'failed', 'expired'].includes(status.status)) {
return status;
}
await new Promise(resolve => setTimeout(resolve, 5000));
attempts++;
} catch (error) {
console.error('Status check failed:', error);
attempts++;
}
}
throw new Error('Payment monitoring timeout');
}Step 3: Frontend Integration
React Payment Component
import React, { useState, useEffect } from 'react';
import { CyberPaySDK } from '@cyberpay/sdk';
interface PaymentComponentProps {
productInfo: {
name: string;
price: number;
currency: string;
};
onSuccess: (payment: any) => void;
onError: (error: Error) => void;
}
export function PaymentComponent({ productInfo, onSuccess, onError }: PaymentComponentProps) {
const [step, setStep] = useState<'init' | 'quote' | 'payment' | 'processing'>('init');
const [checkout, setCheckout] = useState(null);
const [quote, setQuote] = useState(null);
const [selectedToken, setSelectedToken] = useState(null);
const [userAddress, setUserAddress] = useState('');
// Initialize checkout
const initializePayment = async () => {
try {
const checkoutResponse = await fetch('/api/checkout/create', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(productInfo)
});
const checkoutData = await checkoutResponse.json();
setCheckout(checkoutData);
setStep('quote');
} catch (error) {
onError(error);
}
};
// Get quote for selected token
const getQuote = async (token) => {
try {
const quoteResponse = await fetch('/api/quote/get', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
checkoutId: checkout.checkoutId,
paymentToken: token,
amount: productInfo.price.toString()
})
});
const quoteData = await quoteResponse.json();
setQuote(quoteData);
setSelectedToken(token);
} catch (error) {
onError(error);
}
};
// Execute payment
const executePayment = async () => {
setStep('processing');
try {
// Create order
const orderResponse = await fetch('/api/order/create', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
checkoutId: checkout.checkoutId,
quoteId: quote.quoteId,
userAddress
})
});
const order = await orderResponse.json();
// Execute through wallet
const paymentResponse = await fetch('/api/payment/execute', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
orderId: order.orderId,
userAddress
})
});
const execution = await paymentResponse.json();
// Handle wallet interaction
await handleWalletExecution(execution);
// Monitor status
await monitorPaymentStatus(order.orderId);
} catch (error) {
onError(error);
}
};
const handleWalletExecution = async (execution) => {
// Implementation depends on wallet provider
// This is a simplified example
for (const tx of execution.transactions) {
await window.ethereum.request({
method: 'eth_sendTransaction',
params: [tx]
});
}
};
const monitorPaymentStatus = async (orderId) => {
// Poll for status updates
const checkStatus = async () => {
const statusResponse = await fetch(`/api/payment/status/${orderId}`);
const status = await statusResponse.json();
if (status.status === 'completed') {
onSuccess(status);
} else if (status.status === 'failed') {
onError(new Error(status.failureReason));
} else {
setTimeout(checkStatus, 5000);
}
};
checkStatus();
};
return (
<div className="payment-component">
{step === 'init' && (
<button onClick={initializePayment}>
Pay {productInfo.price} {productInfo.currency}
</button>
)}
{step === 'quote' && checkout && (
<div>
<h3>Select Payment Method</h3>
{checkout.availablePaymentMethods.map(network => (
<div key={network.chainId}>
<h4>{network.chainName}</h4>
{network.tokens.map(token => (
<button
key={token.address}
onClick={() => getQuote(token)}
>
Pay with {token.symbol}
</button>
))}
</div>
))}
</div>
)}
{step === 'payment' && quote && (
<div>
<h3>Payment Details</h3>
<p>Amount: {quote.paymentAmount} {selectedToken.symbol}</p>
<p>You'll receive: {quote.settlementAmount} {productInfo.currency}</p>
<p>Fees: {quote.fees.total.amount} {selectedToken.symbol}</p>
<input
type="text"
placeholder="Your wallet address"
value={userAddress}
onChange={(e) => setUserAddress(e.target.value)}
/>
<button onClick={executePayment}>
Confirm Payment
</button>
</div>
)}
{step === 'processing' && (
<div>
<h3>Processing Payment...</h3>
<p>Please confirm transactions in your wallet</p>
</div>
)}
</div>
);
}Step 4: Backend Implementation
Express.js Server Example
import express from 'express';
import { CyberPaySDK } from '@cyberpay/sdk';
const app = express();
const sdk = new CyberPaySDK({
apiKey: process.env.CYBERPAY_API_KEY
});
app.use(express.json());
// Create checkout
app.post('/api/checkout/create', async (req, res) => {
try {
const checkout = await sdk.checkout.create({
merchantId: process.env.CYBERPAY_MERCHANT_ID,
amount: req.body.price.toString(),
currency: req.body.currency,
productInfo: {
name: req.body.name,
description: req.body.description
},
returnUrl: `${process.env.BASE_URL}/success`,
webhookUrl: `${process.env.BASE_URL}/api/webhooks/cyberpay`,
metadata: {
userId: req.body.userId,
sessionId: req.sessionID
}
});
res.json(checkout);
} catch (error) {
res.status(500).json({ error: error.message });
}
});
// Get quote
app.post('/api/quote/get', async (req, res) => {
try {
const quote = await sdk.quote.get(req.body);
res.json(quote);
} catch (error) {
res.status(500).json({ error: error.message });
}
});
// Create order
app.post('/api/order/create', async (req, res) => {
try {
const order = await sdk.order.create(req.body);
res.json(order);
} catch (error) {
res.status(500).json({ error: error.message });
}
});
// Execute payment
app.post('/api/payment/execute', async (req, res) => {
try {
const execution = await sdk.payment.execute(req.body);
res.json(execution);
} catch (error) {
res.status(500).json({ error: error.message });
}
});
// Get payment status
app.get('/api/payment/status/:orderId', async (req, res) => {
try {
const status = await sdk.payment.getStatus(req.params.orderId);
res.json(status);
} catch (error) {
res.status(500).json({ error: error.message });
}
});
// Webhook handler
app.post('/api/webhooks/cyberpay', (req, res) => {
const signature = req.headers['x-cyberpay-signature'];
const payload = req.body;
// Verify webhook signature
if (!sdk.webhooks.verify(payload, signature, process.env.CYBERPAY_WEBHOOK_SECRET)) {
return res.status(401).send('Invalid signature');
}
// Process webhook
handleWebhook(payload);
res.status(200).send('OK');
});
function handleWebhook(payload) {
const { event, data } = payload;
switch (event) {
case 'checkout.created':
console.log('Checkout created:', data.checkoutId);
break;
case 'order.created':
console.log('Order created:', data.orderId);
break;
case 'payment.completed':
console.log('Payment completed:', data.orderId);
// Update order status in database
// Send confirmation email
// Fulfill order
break;
case 'payment.failed':
console.log('Payment failed:', data.orderId, data.failureReason);
// Handle failed payment
break;
}
}
app.listen(3000);Step 5: Error Handling & Edge Cases
Comprehensive Error Handling
class PaymentError extends Error {
constructor(
message: string,
public code: string,
public retryable: boolean = false,
public userMessage?: string
) {
super(message);
this.name = 'PaymentError';
}
}
async function handlePaymentFlow(productInfo, userAddress) {
try {
// Step 1: Create checkout
const checkout = await createCheckout(productInfo);
// Step 2: Get quote with retry logic
const quote = await retryWithBackoff(
() => getPaymentQuote(checkout.checkoutId, selectedToken),
3, // max retries
1000 // initial delay
);
// Step 3: Create order
const order = await createPaymentOrder(checkout.checkoutId, quote.quoteId, userAddress);
// Step 4: Execute payment
const execution = await executePayment(order.orderId, userAddress);
// Step 5: Monitor status
const finalStatus = await monitorPayment(order.orderId);
return finalStatus;
} catch (error) {
throw mapError(error);
}
}
function mapError(error: any): PaymentError {
switch (error.code) {
case 'INSUFFICIENT_BALANCE':
return new PaymentError(
'Insufficient balance',
'INSUFFICIENT_BALANCE',
false,
'You don\'t have enough tokens to complete this payment'
);
case 'QUOTE_EXPIRED':
return new PaymentError(
'Quote expired',
'QUOTE_EXPIRED',
true,
'Price quote expired, please try again'
);
case 'NETWORK_ERROR':
return new PaymentError(
'Network error',
'NETWORK_ERROR',
true,
'Network connection issue, please try again'
);
default:
return new PaymentError(
error.message || 'Unknown error',
'UNKNOWN_ERROR',
false,
'An unexpected error occurred'
);
}
}
async function retryWithBackoff(fn: () => Promise<any>, maxRetries: number, initialDelay: number) {
let lastError;
for (let attempt = 0; attempt <= maxRetries; attempt++) {
try {
return await fn();
} catch (error) {
lastError = error;
if (attempt === maxRetries || !isRetryableError(error)) {
throw error;
}
const delay = initialDelay * Math.pow(2, attempt);
await new Promise(resolve => setTimeout(resolve, delay));
}
}
throw lastError;
}
function isRetryableError(error: any): boolean {
const retryableCodes = [
'NETWORK_ERROR',
'TIMEOUT',
'RATE_LIMITED',
'SERVER_ERROR'
];
return retryableCodes.includes(error.code) || error.statusCode >= 500;
}Step 6: Testing
Unit Tests
import { describe, it, expect, jest } from '@jest/globals';
import { CyberPaySDK } from '@cyberpay/sdk';
describe('Payment Integration', () => {
const sdk = new CyberPaySDK({
apiKey: 'test_key',
environment: 'testnet'
});
it('should create checkout successfully', async () => {
const checkout = await sdk.checkout.create({
merchantId: 'test_merchant',
amount: '100.00',
currency: 'USDC',
productInfo: { name: 'Test Product' },
returnUrl: 'https://test.com/success'
});
expect(checkout.checkoutId).toBeDefined();
expect(checkout.amount).toBe('100.00');
});
it('should handle quote expiration', async () => {
// Mock expired quote
jest.spyOn(sdk.order, 'create').mockRejectedValue({
code: 'QUOTE_EXPIRED',
message: 'Quote has expired'
});
await expect(
sdk.order.create({
checkoutId: 'test_checkout',
quoteId: 'expired_quote',
userAddress: '0x123'
})
).rejects.toMatchObject({
code: 'QUOTE_EXPIRED'
});
});
});Integration Tests
describe('End-to-End Payment Flow', () => {
it('should complete full payment flow', async () => {
// Create checkout
const checkout = await sdk.checkout.create({
merchantId: process.env.TEST_MERCHANT_ID,
amount: '1.00',
currency: 'USDC',
productInfo: { name: 'Test Payment' },
returnUrl: 'https://test.com/success'
});
// Get quote
const quote = await sdk.quote.get({
checkoutId: checkout.checkoutId,
paymentToken: testTokens.USDC_POLYGON,
settlementToken: testTokens.USDC_ETHEREUM,
amount: '1.00'
});
// Create order
const order = await sdk.order.create({
checkoutId: checkout.checkoutId,
quoteId: quote.quoteId,
userAddress: testWallet.address
});
expect(order.status).toBe('pending_payment');
expect(order.orderId).toBeDefined();
});
});Step 7: Production Deployment
Environment Configuration
# Production environment variables
CYBERPAY_API_KEY=your_production_api_key
CYBERPAY_MERCHANT_ID=your_merchant_id
CYBERPAY_WEBHOOK_SECRET=your_webhook_secret
BASE_URL=https://yourapp.com
NODE_ENV=production
# Security settings
CORS_ORIGIN=https://yourapp.com
RATE_LIMIT_WINDOW=900000 # 15 minutes
RATE_LIMIT_MAX=100 # requests per windowSecurity Checklist
- ✅ API keys stored securely (environment variables)
- ✅ HTTPS enabled for all endpoints
- ✅ Webhook signature verification implemented
- ✅ CORS configured properly
- ✅ Rate limiting enabled
- ✅ Input validation on all endpoints
- ✅ Error messages don't expose sensitive data
- ✅ Logging configured (without sensitive data)
Monitoring & Alerting
// Add monitoring to critical functions
import { performance } from 'perf_hooks';
async function monitoredPaymentExecution(orderId: string) {
const startTime = performance.now();
try {
const result = await sdk.payment.execute({ orderId });
// Log success metrics
const duration = performance.now() - startTime;
console.log(`Payment execution succeeded: ${orderId}, duration: ${duration}ms`);
return result;
} catch (error) {
// Log error metrics
const duration = performance.now() - startTime;
console.error(`Payment execution failed: ${orderId}, duration: ${duration}ms, error: ${error.message}`);
// Send alert for critical errors
if (error.code === 'SERVER_ERROR') {
await sendAlert('Payment execution server error', { orderId, error: error.message });
}
throw error;
}
}Best Practices Summary
Development
- Use TypeScript for better type safety
- Implement comprehensive error handling
- Add retry logic for network operations
- Use environment variables for configuration
- Write unit and integration tests
Security
- Never expose API keys in client-side code
- Verify all webhook signatures
- Use HTTPS for all communications
- Implement proper CORS policies
- Validate all user inputs
Performance
- Cache SDK instances
- Implement request deduplication
- Use appropriate timeout values
- Monitor API usage and response times
- Implement circuit breakers for external calls
User Experience
- Provide clear payment status updates
- Handle wallet connection gracefully
- Show estimated completion times
- Implement proper loading states
- Provide helpful error messages
Next Steps
- Complete Integration: Follow this guide to implement the full payment flow
- Test Thoroughly: Use testnet to validate all scenarios
- Security Review: Ensure all security best practices are followed
- Performance Testing: Load test your integration
- Go Live: Deploy to production with monitoring
Support
- 📧 Email: support@cyberpay.org
- 💬 Telegram: @cyberpay_support
- 📖 Documentation: docs.cyberpay.org
