FortiBlox LogoFortiBlox Docs
NexusBilling & Pricing

Quick Start - Payment Integration

Hands-on examples for integrating FortiBlox Nexus billing - credit card and cryptocurrency payments

Quick Start - Payment Integration

Get started quickly with complete, production-ready code examples for both credit card and cryptocurrency payments.

Prerequisites

Before you begin:

# Install required packages
npm install @stripe/stripe-js @solana/web3.js @solana/spl-token

You'll need:

  • FortiBlox API key
  • Stripe Publishable Key (for credit card payments)
  • X1 Blockchain wallet (for crypto payments)

Example 1: Subscribe with Credit Card

Complete example with error handling and best practices.

Frontend (React)

import React, { useState } from 'react';
import { loadStripe } from '@stripe/stripe-js';

// Initialize Stripe with your Publishable Key
const stripePromise = loadStripe('pk_test_YOUR_PUBLISHABLE_KEY');

function SubscriptionPage() {
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);

  async function handleSubscribe(tier) {
    setLoading(true);
    setError(null);

    try {
      // Step 1: Create checkout session
      const response = await fetch('https://nexus.fortiblox.com/api/billing/create-checkout', {
        method: 'POST',
        headers: {
          'Authorization': `Bearer ${YOUR_API_KEY}`,
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({
          tier: tier,
          success_url: `${window.location.origin}/success?session_id={CHECKOUT_SESSION_ID}`,
          cancel_url: `${window.location.origin}/pricing`
        })
      });

      if (!response.ok) {
        const errorData = await response.json();
        throw new Error(errorData.message || 'Failed to create checkout session');
      }

      const session = await response.json();

      // Step 2: Redirect to Stripe Checkout
      const stripe = await stripePromise;
      const result = await stripe.redirectToCheckout({
        sessionId: session.id
      });

      if (result.error) {
        // Handle Stripe error
        setError(result.error.message);
        console.error('Stripe error:', result.error);
      }
    } catch (err) {
      setError(err.message);
      console.error('Subscription error:', err);
    } finally {
      setLoading(false);
    }
  }

  return (
    <div className="subscription-page">
      <h1>Choose Your Plan</h1>

      {error && (
        <div className="error-message">
          <strong>Error:</strong> {error}
        </div>
      )}

      <div className="pricing-cards">
        <PricingCard
          tier="developer"
          name="Developer"
          price={49}
          features={[
            '50M credits/month',
            '500 requests/second',
            'Email support',
            'gRPC Testnet access'
          ]}
          onSubscribe={handleSubscribe}
          loading={loading}
        />

        <PricingCard
          tier="business"
          name="Business"
          price={499}
          features={[
            '500M credits/month',
            '2,000 requests/second',
            'Priority support',
            'Webhooks',
            'gRPC Testnet access'
          ]}
          onSubscribe={handleSubscribe}
          loading={loading}
          popular={true}
        />

        <PricingCard
          tier="professional"
          name="Professional"
          price={999}
          features={[
            '2B credits/month',
            '5,000 requests/second',
            'Dedicated support',
            'Webhooks',
            'gRPC Mainnet access'
          ]}
          onSubscribe={handleSubscribe}
          loading={loading}
        />
      </div>
    </div>
  );
}

function PricingCard({ tier, name, price, features, onSubscribe, loading, popular }) {
  return (
    <div className={`pricing-card ${popular ? 'popular' : ''}`}>
      {popular && <div className="badge">Most Popular</div>}

      <h2>{name}</h2>
      <div className="price">
        <span className="currency">$</span>
        <span className="amount">{price}</span>
        <span className="period">/month</span>
      </div>

      <ul className="features">
        {features.map((feature, index) => (
          <li key={index}>✓ {feature}</li>
        ))}
      </ul>

      <button
        onClick={() => onSubscribe(tier)}
        disabled={loading}
        className="subscribe-button"
      >
        {loading ? 'Processing...' : 'Subscribe'}
      </button>
    </div>
  );
}

export default SubscriptionPage;

Success Page (Handle Return from Stripe)

import React, { useEffect, useState } from 'react';
import { useSearchParams } from 'react-router-dom';

function SuccessPage() {
  const [searchParams] = useSearchParams();
  const [status, setStatus] = useState('verifying');
  const [subscription, setSubscription] = useState(null);
  const [error, setError] = useState(null);

  useEffect(() => {
    const sessionId = searchParams.get('session_id');

    if (!sessionId) {
      setError('No session ID provided');
      setStatus('error');
      return;
    }

    verifySession(sessionId);
  }, [searchParams]);

  async function verifySession(sessionId) {
    try {
      const response = await fetch('https://nexus.fortiblox.com/api/billing/verify-session', {
        method: 'POST',
        headers: {
          'Authorization': `Bearer ${YOUR_API_KEY}`,
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({ session_id: sessionId })
      });

      if (!response.ok) {
        throw new Error('Failed to verify session');
      }

      const result = await response.json();

      if (result.success) {
        setSubscription(result.subscription);
        setStatus('success');

        // Optional: Track conversion
        trackConversion(result.subscription.tier, result.subscription.amount);
      } else {
        throw new Error(result.message || 'Verification failed');
      }
    } catch (err) {
      setError(err.message);
      setStatus('error');
    }
  }

  function trackConversion(tier, amount) {
    // Analytics tracking
    if (window.gtag) {
      window.gtag('event', 'purchase', {
        transaction_id: subscription?.id,
        value: amount / 100,
        currency: 'USD',
        items: [{
          item_id: tier,
          item_name: `FortiBlox ${tier} tier`,
          price: amount / 100
        }]
      });
    }
  }

  if (status === 'verifying') {
    return (
      <div className="success-page">
        <div className="spinner"></div>
        <h2>Verifying your subscription...</h2>
      </div>
    );
  }

  if (status === 'error') {
    return (
      <div className="success-page error">
        <h2>Payment Verification Failed</h2>
        <p>{error}</p>
        <a href="/pricing" className="button">Try Again</a>
      </div>
    );
  }

  return (
    <div className="success-page">
      <div className="success-icon">✓</div>
      <h1>Subscription Activated!</h1>

      <div className="subscription-details">
        <h3>Your {subscription.tier} subscription is now active</h3>

        <div className="details">
          <div className="detail-item">
            <span className="label">Credits:</span>
            <span className="value">{subscription.credits_allocated.toLocaleString()}</span>
          </div>

          <div className="detail-item">
            <span className="label">Rate Limit:</span>
            <span className="value">{subscription.rate_limit} requests/second</span>
          </div>

          <div className="detail-item">
            <span className="label">Next Billing:</span>
            <span className="value">
              {new Date(subscription.current_period_end).toLocaleDateString()}
            </span>
          </div>
        </div>
      </div>

      <div className="actions">
        <a href="/dashboard" className="button primary">Go to Dashboard</a>
        <a href="/docs" className="button secondary">View API Docs</a>
      </div>
    </div>
  );
}

export default SuccessPage;

Backend API (Node.js/Express)

const express = require('express');
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);

const router = express.Router();

// Create Stripe Checkout Session
router.post('/billing/create-checkout', async (req, res) => {
  try {
    const { tier, success_url, cancel_url } = req.body;
    const userId = req.user.id; // From auth middleware

    // Get price ID for tier
    const priceIds = {
      developer: process.env.STRIPE_PRICE_DEVELOPER,
      business: process.env.STRIPE_PRICE_BUSINESS,
      professional: process.env.STRIPE_PRICE_PROFESSIONAL
    };

    const priceId = priceIds[tier];
    if (!priceId) {
      return res.status(400).json({ error: 'Invalid tier' });
    }

    // Create or retrieve Stripe customer
    let customerId = req.user.stripe_customer_id;

    if (!customerId) {
      const customer = await stripe.customers.create({
        email: req.user.email,
        metadata: {
          user_id: userId
        }
      });

      customerId = customer.id;

      // Save customer ID to database
      await db.query(
        'UPDATE users SET stripe_customer_id = $1 WHERE id = $2',
        [customerId, userId]
      );
    }

    // Create checkout session
    const session = await stripe.checkout.sessions.create({
      customer: customerId,
      mode: 'subscription',
      payment_method_types: ['card'],
      line_items: [{
        price: priceId,
        quantity: 1
      }],
      success_url: success_url,
      cancel_url: cancel_url,
      metadata: {
        user_id: userId,
        tier: tier
      },
      allow_promotion_codes: true,
      billing_address_collection: 'required',
      automatic_tax: { enabled: true }
    });

    res.json({ id: session.id, url: session.url });
  } catch (error) {
    console.error('Checkout error:', error);
    res.status(500).json({ error: error.message });
  }
});

// Verify session after redirect
router.post('/billing/verify-session', async (req, res) => {
  try {
    const { session_id } = req.body;

    const session = await stripe.checkout.sessions.retrieve(session_id);

    if (session.payment_status === 'paid') {
      // Get subscription details
      const subscription = await stripe.subscriptions.retrieve(session.subscription);

      res.json({
        success: true,
        subscription: {
          id: subscription.id,
          tier: session.metadata.tier,
          status: subscription.status,
          amount: subscription.items.data[0].price.unit_amount,
          current_period_end: new Date(subscription.current_period_end * 1000),
          credits_allocated: getTierCredits(session.metadata.tier),
          rate_limit: getTierRateLimit(session.metadata.tier)
        }
      });
    } else {
      res.json({ success: false, message: 'Payment not completed' });
    }
  } catch (error) {
    console.error('Verification error:', error);
    res.status(500).json({ error: error.message });
  }
});

// Handle Stripe webhooks
router.post('/billing/webhook', express.raw({ type: 'application/json' }), async (req, res) => {
  const sig = req.headers['stripe-signature'];

  try {
    const event = stripe.webhooks.constructEvent(
      req.body,
      sig,
      process.env.STRIPE_WEBHOOK_SECRET
    );

    switch (event.type) {
      case 'customer.subscription.created':
      case 'customer.subscription.updated':
        await handleSubscriptionUpdate(event.data.object);
        break;

      case 'customer.subscription.deleted':
        await handleSubscriptionCancelled(event.data.object);
        break;

      case 'invoice.paid':
        await handleInvoicePaid(event.data.object);
        break;

      case 'invoice.payment_failed':
        await handlePaymentFailed(event.data.object);
        break;
    }

    res.json({ received: true });
  } catch (error) {
    console.error('Webhook error:', error);
    res.status(400).send(`Webhook Error: ${error.message}`);
  }
});

async function handleSubscriptionUpdate(subscription) {
  const userId = subscription.metadata.user_id;
  const tier = subscription.metadata.tier;

  await db.query(`
    UPDATE users
    SET
      subscription_tier = $1,
      subscription_status = $2,
      stripe_subscription_id = $3,
      credits_allocated = $4,
      credits_used = 0,
      updated_at = NOW()
    WHERE id = $5
  `, [tier, subscription.status, subscription.id, getTierCredits(tier), userId]);

  console.log(`Subscription updated for user ${userId}: ${tier}`);
}

async function handleSubscriptionCancelled(subscription) {
  const userId = subscription.metadata.user_id;

  await db.query(`
    UPDATE users
    SET
      subscription_tier = 'free',
      subscription_status = 'cancelled',
      stripe_subscription_id = NULL,
      credits_allocated = 1000000,
      updated_at = NOW()
    WHERE id = $1
  `, [userId]);

  console.log(`Subscription cancelled for user ${userId}`);
}

function getTierCredits(tier) {
  const credits = {
    free: 1000000,
    developer: 50000000,
    business: 500000000,
    professional: 2000000000
  };
  return credits[tier] || credits.free;
}

function getTierRateLimit(tier) {
  const limits = {
    free: 100,
    developer: 500,
    business: 2000,
    professional: 5000
  };
  return limits[tier] || limits.free;
}

module.exports = router;

Example 2: Pay with USDC (Crypto)

Complete example using X1 Blockchain and Phantom wallet.

Frontend (React + Phantom Wallet)

import React, { useState, useEffect } from 'react';
import { Connection, PublicKey, Transaction } from '@solana/web3.js';
import {
  getAssociatedTokenAddress,
  createTransferInstruction,
  TOKEN_PROGRAM_ID
} from '@solana/spl-token';

const USDC_MINT = new PublicKey('EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v');
const SOLANA_NETWORK = 'https://api.mainnet-beta.solana.com';

function CryptoPaymentPage() {
  const [wallet, setWallet] = useState(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);
  const [paymentDetails, setPaymentDetails] = useState(null);
  const [txSignature, setTxSignature] = useState(null);

  useEffect(() => {
    // Check if Phantom is installed
    if (window.solana?.isPhantom) {
      setWallet(window.solana);
    }
  }, []);

  async function connectWallet() {
    try {
      if (!wallet) {
        alert('Please install Phantom wallet');
        window.open('https://phantom.app', '_blank');
        return;
      }

      await wallet.connect();
      console.log('Connected to wallet:', wallet.publicKey.toString());
    } catch (err) {
      setError('Failed to connect wallet: ' + err.message);
    }
  }

  async function initiatePayment(tier) {
    setLoading(true);
    setError(null);

    try {
      // Step 1: Get payment details from API
      const response = await fetch('https://nexus.fortiblox.com/api/crypto/initiate-payment', {
        method: 'POST',
        headers: {
          'Authorization': `Bearer ${YOUR_API_KEY}`,
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({ tier, token: 'USDC' })
      });

      if (!response.ok) {
        throw new Error('Failed to initiate payment');
      }

      const details = await response.json();
      setPaymentDetails(details);

      // Step 2: Send payment
      await sendUSDCPayment(details);
    } catch (err) {
      setError(err.message);
      console.error('Payment error:', err);
    } finally {
      setLoading(false);
    }
  }

  async function sendUSDCPayment(details) {
    if (!wallet.publicKey) {
      await connectWallet();
      return;
    }

    try {
      const connection = new Connection(SOLANA_NETWORK);

      // Convert amount to lamports (6 decimals for USDC)
      const amount = details.amount * Math.pow(10, 6);

      // Get token accounts
      const fromTokenAccount = await getAssociatedTokenAddress(
        USDC_MINT,
        wallet.publicKey
      );

      const toTokenAccount = await getAssociatedTokenAddress(
        USDC_MINT,
        new PublicKey(details.payment_address)
      );

      // Create transfer instruction
      const transferInstruction = createTransferInstruction(
        fromTokenAccount,
        toTokenAccount,
        wallet.publicKey,
        amount,
        [],
        TOKEN_PROGRAM_ID
      );

      // Create transaction
      const transaction = new Transaction().add(transferInstruction);
      transaction.feePayer = wallet.publicKey;

      // Get recent blockhash
      const { blockhash } = await connection.getLatestBlockhash();
      transaction.recentBlockhash = blockhash;

      // Sign and send transaction
      const signed = await wallet.signTransaction(transaction);
      const signature = await connection.sendRawTransaction(signed.serialize());

      setTxSignature(signature);
      console.log('Transaction sent:', signature);

      // Wait for confirmation
      await connection.confirmTransaction(signature, 'confirmed');
      console.log('Transaction confirmed');

      // Verify payment with backend
      await verifyPayment(details.payment_id, signature);
    } catch (err) {
      throw new Error('Transaction failed: ' + err.message);
    }
  }

  async function verifyPayment(paymentId, signature) {
    try {
      const response = await fetch('https://nexus.fortiblox.com/api/crypto/verify-payment', {
        method: 'POST',
        headers: {
          'Authorization': `Bearer ${YOUR_API_KEY}`,
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({
          payment_id: paymentId,
          signature: signature
        })
      });

      const result = await response.json();

      if (result.success) {
        // Payment verified, subscription activated
        window.location.href = '/success?crypto=true&signature=' + signature;
      } else {
        throw new Error('Payment verification failed');
      }
    } catch (err) {
      throw new Error('Verification failed: ' + err.message);
    }
  }

  return (
    <div className="crypto-payment-page">
      <h1>Pay with Cryptocurrency</h1>
      <p className="discount-badge">Save 8% compared to credit card pricing</p>

      {!wallet?.publicKey ? (
        <div className="connect-wallet">
          <h2>Connect Your Wallet</h2>
          <p>You'll need an X1 Blockchain wallet with USDC to continue</p>
          <button onClick={connectWallet} className="button primary">
            Connect Phantom Wallet
          </button>
        </div>
      ) : (
        <div className="wallet-connected">
          <p>Connected: {wallet.publicKey.toString().slice(0, 8)}...{wallet.publicKey.toString().slice(-8)}</p>

          {error && (
            <div className="error-message">{error}</div>
          )}

          {txSignature && (
            <div className="success-message">
              <h3>Payment Sent!</h3>
              <p>Transaction: {txSignature.slice(0, 16)}...</p>
              <a
                href={`https://solscan.io/tx/${txSignature}`}
                target="_blank"
                rel="noopener noreferrer"
              >
                View on Solscan
              </a>
            </div>
          )}

          <div className="pricing-cards">
            <CryptoPricingCard
              tier="developer"
              name="Developer"
              price={45}
              onPay={initiatePayment}
              loading={loading}
            />

            <CryptoPricingCard
              tier="business"
              name="Business"
              price={459}
              onPay={initiatePayment}
              loading={loading}
              popular={true}
            />

            <CryptoPricingCard
              tier="professional"
              name="Professional"
              price={919}
              onPay={initiatePayment}
              loading={loading}
            />
          </div>
        </div>
      )}
    </div>
  );
}

function CryptoPricingCard({ tier, name, price, onPay, loading, popular }) {
  const cardPrice = 49; // Original price for comparison
  const savings = cardPrice - price;
  const savingsPercent = Math.round((savings / cardPrice) * 100);

  return (
    <div className={`pricing-card ${popular ? 'popular' : ''}`}>
      {popular && <div className="badge">Most Popular</div>}

      <h2>{name}</h2>

      <div className="price">
        <div className="crypto-price">
          <span className="amount">{price}</span>
          <span className="currency">USDC</span>
        </div>
        <div className="savings">
          Save {savingsPercent}% (${savings}/mo)
        </div>
      </div>

      <button
        onClick={() => onPay(tier)}
        disabled={loading}
        className="subscribe-button"
      >
        {loading ? 'Processing...' : `Pay ${price} USDC`}
      </button>
    </div>
  );
}

export default CryptoPaymentPage;

Backend API (Crypto Payments)

const express = require('express');
const { Connection, PublicKey } = require('@solana/web3.js');
const router = express.Router();

const SOLANA_CONNECTION = new Connection('https://api.mainnet-beta.solana.com');
const USDC_MINT = 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v';

// Payment address where users send USDC
const PAYMENT_WALLET = process.env.CRYPTO_PAYMENT_WALLET;

// Initiate crypto payment
router.post('/crypto/initiate-payment', async (req, res) => {
  try {
    const { tier, token } = req.body;
    const userId = req.user.id;

    // Get price for tier (8% discount)
    const prices = {
      developer: 45,
      business: 459,
      professional: 919
    };

    const amount = prices[tier];
    if (!amount) {
      return res.status(400).json({ error: 'Invalid tier' });
    }

    // Create payment record
    const paymentId = `pay_${Date.now()}_${userId}`;
    const expiresAt = new Date(Date.now() + 60 * 60 * 1000); // 1 hour

    await db.query(`
      INSERT INTO crypto_payments (
        id, user_id, tier, amount, token, status, expires_at
      ) VALUES ($1, $2, $3, $4, $5, $6, $7)
    `, [paymentId, userId, tier, amount, token, 'pending', expiresAt]);

    res.json({
      payment_id: paymentId,
      amount: amount,
      token: token,
      payment_address: PAYMENT_WALLET,
      expires_at: expiresAt,
      qr_code: generateQRCode(PAYMENT_WALLET, amount, token)
    });
  } catch (error) {
    console.error('Payment initiation error:', error);
    res.status(500).json({ error: error.message });
  }
});

// Verify crypto payment
router.post('/crypto/verify-payment', async (req, res) => {
  try {
    const { payment_id, signature } = req.body;

    // Get payment record
    const payment = await db.query(
      'SELECT * FROM crypto_payments WHERE id = $1',
      [payment_id]
    );

    if (!payment.rows.length) {
      return res.status(404).json({ error: 'Payment not found' });
    }

    const paymentData = payment.rows[0];

    // Verify transaction on X1 Blockchain
    const transaction = await SOLANA_CONNECTION.getTransaction(signature, {
      commitment: 'confirmed'
    });

    if (!transaction) {
      return res.status(400).json({ error: 'Transaction not found' });
    }

    // Verify transaction details
    const verified = await verifyTransaction(
      transaction,
      PAYMENT_WALLET,
      paymentData.amount,
      paymentData.token
    );

    if (!verified) {
      return res.status(400).json({ error: 'Transaction verification failed' });
    }

    // Update payment status
    await db.query(`
      UPDATE crypto_payments
      SET status = 'confirmed', signature = $1, confirmed_at = NOW()
      WHERE id = $2
    `, [signature, payment_id]);

    // Activate subscription
    await activateSubscription(paymentData.user_id, paymentData.tier);

    res.json({
      success: true,
      message: 'Payment verified and subscription activated',
      subscription: {
        tier: paymentData.tier,
        status: 'active',
        expires_at: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000)
      }
    });
  } catch (error) {
    console.error('Verification error:', error);
    res.status(500).json({ error: error.message });
  }
});

async function verifyTransaction(transaction, expectedRecipient, expectedAmount, token) {
  // Verify transaction succeeded
  if (transaction.meta.err) {
    return false;
  }

  // Verify recipient and amount
  // (Simplified - in production, parse transaction data more thoroughly)
  const accountKeys = transaction.transaction.message.accountKeys;
  const recipientKey = new PublicKey(expectedRecipient);

  // Check if recipient is in transaction
  const hasRecipient = accountKeys.some(key =>
    key.equals(recipientKey)
  );

  if (!hasRecipient) {
    return false;
  }

  // In production: Parse transfer instruction and verify exact amount
  // This is a simplified check
  return true;
}

async function activateSubscription(userId, tier) {
  const credits = {
    developer: 50000000,
    business: 500000000,
    professional: 2000000000
  };

  const expiresAt = new Date(Date.now() + 30 * 24 * 60 * 60 * 1000); // 30 days

  await db.query(`
    UPDATE users
    SET
      subscription_tier = $1,
      subscription_status = 'active',
      credits_allocated = $2,
      credits_used = 0,
      subscription_expires_at = $3,
      updated_at = NOW()
    WHERE id = $4
  `, [tier, credits[tier], expiresAt, userId]);

  console.log(`Crypto subscription activated for user ${userId}: ${tier}`);
}

module.exports = router;

Example 3: Check Subscription Status

// Utility function to check subscription status
async function getSubscriptionStatus(apiKey) {
  try {
    const response = await fetch('https://nexus.fortiblox.com/api/subscription', {
      headers: {
        'Authorization': `Bearer ${apiKey}`
      }
    });

    const subscription = await response.json();

    return {
      tier: subscription.tier,
      status: subscription.status,
      isActive: subscription.status === 'active',
      isPastDue: subscription.status === 'past_due',
      isCancelled: subscription.status === 'cancelled',
      creditsUsed: subscription.credits_used,
      creditsRemaining: subscription.credits_remaining,
      creditsTotal: subscription.credits_allocated,
      usagePercent: (subscription.credits_used / subscription.credits_allocated) * 100,
      resetDate: new Date(subscription.current_period_end),
      daysUntilReset: Math.ceil(
        (new Date(subscription.current_period_end) - new Date()) / (1000 * 60 * 60 * 24)
      )
    };
  } catch (error) {
    console.error('Failed to get subscription status:', error);
    return null;
  }
}

// Usage in your app
const status = await getSubscriptionStatus(YOUR_API_KEY);

if (status.isPastDue) {
  showWarning('Your payment failed. Please update your payment method.');
} else if (status.usagePercent > 90) {
  showWarning(`You've used ${status.usagePercent.toFixed(1)}% of your credits. Consider upgrading.`);
} else if (status.daysUntilReset <= 3) {
  showInfo(`Your credits reset in ${status.daysUntilReset} days.`);
}

Example 4: Handle Rate Limiting

// Implement exponential backoff for rate limits
async function makeAPIRequest(url, options, maxRetries = 3) {
  let retries = 0;

  while (retries < maxRetries) {
    try {
      const response = await fetch(url, options);

      if (response.status === 429) {
        // Rate limit hit
        const retryAfter = response.headers.get('Retry-After') || Math.pow(2, retries);

        console.warn(`Rate limit hit. Retrying after ${retryAfter} seconds...`);

        // Wait before retrying
        await new Promise(resolve => setTimeout(resolve, retryAfter * 1000));

        retries++;
        continue;
      }

      if (!response.ok) {
        throw new Error(`API error: ${response.status}`);
      }

      return await response.json();
    } catch (error) {
      if (retries === maxRetries - 1) {
        throw error;
      }

      retries++;
      await new Promise(resolve => setTimeout(resolve, 1000 * Math.pow(2, retries)));
    }
  }
}

// Usage
const balance = await makeAPIRequest('https://nexus.fortiblox.com/api/rpc/getBalance', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${YOUR_API_KEY}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({ address: 'x1abc...' })
});

Testing

Test Credit Card Payments

// Use Stripe test cards
const TEST_CARDS = {
  success: '4242424242424242',
  decline: '4000000000000002',
  requires3DS: '4000002500003155'
};

// In development, use test Publishable Key
const stripe = loadStripe('pk_test_YOUR_TEST_KEY');

Test Crypto Payments

// Use X1 Blockchain Devnet for testing
const TEST_CONNECTION = new Connection('https://api.devnet.solana.com');

// Use test USDC on Devnet
const TEST_USDC_MINT = new PublicKey('4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU');

// Test payment with smaller amounts
const testPayment = {
  amount: 0.01, // 0.01 USDC for testing
  token: 'USDC',
  network: 'devnet'
};

Production Checklist

Before deploying to production:

  • Switch to live Stripe keys (pk_live_, sk_live_)
  • Update Stripe webhook endpoint to production URL
  • Test full payment flow on production
  • Set up error monitoring (Sentry, etc.)
  • Configure proper CORS settings
  • Enable rate limiting on payment endpoints
  • Set up payment failure notifications
  • Test webhook delivery
  • Verify transaction on X1 Blockchain mainnet
  • Set up analytics tracking
  • Configure proper error messages
  • Test mobile responsiveness
  • Set up backup payment verification

Support

Need help integrating payments?

Email: [email protected] Discord: Join our community Documentation: Full billing docs


Last Updated: 2025-11-21