Skip to content

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:

  1. Setup & Authentication - Get API access and configure your environment
  2. Payment Flow Implementation - Implement the 5-step payment process
  3. User Experience Design - Create intuitive payment interfaces
  4. Error Handling - Handle edge cases and failures gracefully
  5. 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-go

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

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

Security 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

  1. Complete Integration: Follow this guide to implement the full payment flow
  2. Test Thoroughly: Use testnet to validate all scenarios
  3. Security Review: Ensure all security best practices are followed
  4. Performance Testing: Load test your integration
  5. Go Live: Deploy to production with monitoring

Support