Skip to main content
Implement comprehensive payment processing for telecommunications services including payment collection, stored payment methods, billing management, and payment failure handling. This use case covers the complete payment lifecycle from initial collection to ongoing billing.

Prerequisites

Before you begin, ensure you have:
  • Order Management: Understanding of order creation and pricing flows
  • Customer Management: Active customers with subscription services
  • Payment Gateway Integration: Access to payment processors (cards, bank transfers, digital wallets)
  • Billing System: Understanding of billing cycles and pricing models
  • Compliance: PCI DSS compliance for handling payment data

Overview

Payment processing encompasses:
  1. Payment session creation for secure payment collection
  2. Payment profile management for stored payment methods
  3. Payment processing and transaction handling
  4. Payment failure management and retry logic
  5. Billing and invoice management
  6. Promotional pricing and discount handling

Step-by-Step Implementation

Step 1: Create Payment Sessions for Orders

Create secure payment sessions to collect payment for orders:
// Create payment session for order
const createOrderPaymentSession = async (orderId, paymentOptions = {}) => {
  const sessionData = {
    orderId: orderId,
    successUrl: paymentOptions.successUrl || `${window.location.origin}/payment/success`,
    cancelUrl: paymentOptions.cancelUrl || `${window.location.origin}/payment/cancel`,
    paymentMethods: paymentOptions.paymentMethods || ['card', 'bank_transfer'],
    locale: paymentOptions.locale || 'en',
    metadata: {
      source: 'customer_portal',
      timestamp: new Date().toISOString()
    }
  };

  const response = await fetch('https://connect.your-domain.com/api/v2/payment-sessions', {
    method: 'POST',
    headers: {
      'Authorization': 'Bearer YOUR_ACCESS_TOKEN',
      'X-API-Key': 'YOUR_API_KEY',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(sessionData)
  });

  if (!response.ok) {
    const error = await response.json();
    throw new Error(`Payment session creation failed: ${error.message}`);
  }

  return await response.json();
};

// Monitor payment session status
const checkPaymentSessionStatus = async (paymentSessionId) => {
  const response = await fetch(`https://connect.your-domain.com/api/v2/payment-sessions/${paymentSessionId}`, {
    method: 'GET',
    headers: {
      'Authorization': 'Bearer YOUR_ACCESS_TOKEN',
      'X-API-Key': 'YOUR_API_KEY',
      'Content-Type': 'application/json'
    }
  });

  const session = await response.json();

  return {
    status: session.status,
    paymentId: session.paymentId,
    orderId: session.orderId,
    amount: session.amount,
    currency: session.currency,
    completedAt: session.completedAt,
    failureReason: session.failureReason
  };
};

// Payment session component
const PaymentSessionHandler = ({ orderId, onSuccess, onFailure }) => {
  const [paymentSession, setPaymentSession] = useState(null);
  const [loading, setLoading] = useState(false);

  const initiatePayment = async () => {
    setLoading(true);
    try {
      const session = await createOrderPaymentSession(orderId, {
        successUrl: `${window.location.origin}/orders/${orderId}/success`,
        cancelUrl: `${window.location.origin}/orders/${orderId}/cancel`
      });

      setPaymentSession(session);

      // Redirect to payment URL
      window.location.href = session.paymentUrl;
    } catch (error) {
      console.error('Payment session creation failed:', error);
      onFailure(error);
    } finally {
      setLoading(false);
    }
  };

  return (
    <div className="payment-session">
      <button onClick={initiatePayment} disabled={loading}>
        {loading ? 'Creating Payment Session...' : 'Pay Now'}
      </button>
    </div>
  );
};

Step 2: Manage Payment Profiles

Set up stored payment methods for recurring billing:
// Create payment profile session for storing payment methods
const createPaymentProfileSession = async (returnUrl, customerContext) => {
  const sessionData = {
    returnUrl: returnUrl,
    paymentMethods: ['card', 'bank_account'],
    metadata: {
      customerId: customerContext.customerId,
      source: 'billing_setup'
    }
  };

  const response = await fetch('https://connect.your-domain.com/api/v2/payment-profiles/sessions', {
    method: 'POST',
    headers: {
      'Authorization': 'Bearer YOUR_ACCESS_TOKEN',
      'X-API-Key': 'YOUR_API_KEY',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(sessionData)
  });

  return await response.json();
};

// Get stored payment profiles
const getPaymentProfiles = async () => {
  const response = await fetch('https://connect.your-domain.com/api/v2/payment-profiles', {
    method: 'GET',
    headers: {
      'Authorization': 'Bearer YOUR_ACCESS_TOKEN',
      'X-API-Key': 'YOUR_API_KEY',
      'Content-Type': 'application/json'
    }
  });

  return await response.json();
};

// Get specific payment profile
const getPaymentProfile = async (paymentProfileId) => {
  const response = await fetch(`https://connect.your-domain.com/api/v2/payment-profiles/${paymentProfileId}`, {
    method: 'GET',
    headers: {
      'Authorization': 'Bearer YOUR_ACCESS_TOKEN',
      'X-API-Key': 'YOUR_API_KEY',
      'Content-Type': 'application/json'
    }
  });

  return await response.json();
};

Step 3: Process Payments

Handle payment processing and transaction management:
// List payments with filtering
const getPayments = async (filters = {}) => {
  const params = new URLSearchParams();

  if (filters.customerId) params.append('customerId', filters.customerId);
  if (filters.status) params.append('status', filters.status);
  if (filters.dateFrom) params.append('dateFrom', filters.dateFrom);
  if (filters.dateTo) params.append('dateTo', filters.dateTo);
  if (filters.limit) params.append('limit', filters.limit);
  if (filters.offset) params.append('offset', filters.offset);

  const response = await fetch(`https://connect.your-domain.com/api/v2/payments?${params}`, {
    method: 'GET',
    headers: {
      'Authorization': 'Bearer YOUR_ACCESS_TOKEN',
      'X-API-Key': 'YOUR_API_KEY',
      'Content-Type': 'application/json'
    }
  });

  return await response.json();
};

// Get specific payment details
const getPaymentDetails = async (paymentId) => {
  const response = await fetch(`https://connect.your-domain.com/api/v2/payments/${paymentId}`, {
    method: 'GET',
    headers: {
      'Authorization': 'Bearer YOUR_ACCESS_TOKEN',
      'X-API-Key': 'YOUR_API_KEY',
      'Content-Type': 'application/json'
    }
  });

  return await response.json();
};

// Payment management component
const PaymentManager = ({ customerId }) => {
  const [payments, setPayments] = useState([]);
  const [paymentProfiles, setPaymentProfiles] = useState([]);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    Promise.all([
      getPayments({ customerId, limit: 50 }),
      getPaymentProfiles()
    ]).then(([paymentsData, profilesData]) => {
      setPayments(paymentsData.items || []);
      setPaymentProfiles(profilesData.items || []);
      setLoading(false);
    });
  }, [customerId]);

  const handleAddPaymentMethod = async () => {
    try {
      const session = await createPaymentProfileSession(
        `${window.location.origin}/billing/payment-methods`,
        { customerId }
      );
      window.location.href = session.paymentUrl;
    } catch (error) {
      console.error('Failed to create payment profile session:', error);
    }
  };

  if (loading) return <div>Loading payment information...</div>;

  return (
    <div className="payment-manager">
      <h3>Payment Methods</h3>
      <div className="payment-profiles">
        {paymentProfiles.map(profile => (
          <div key={profile.paymentProfileId} className="payment-profile">
            <span>{profile.brand} •••• {profile.lastFour}</span>
            <span>Expires {profile.expiryMonth}/{profile.expiryYear}</span>
          </div>
        ))}
        <button onClick={handleAddPaymentMethod}>
          Add Payment Method
        </button>
      </div>

      <h3>Payment History</h3>
      <div className="payment-history">
        {payments.map(payment => (
          <div key={payment.paymentId} className="payment-item">
            <span>{payment.description}</span>
            <span>{payment.amount} {payment.currency}</span>
            <span className={`status ${payment.status}`}>
              {payment.status}
            </span>
            <span>{new Date(payment.createdAt).toLocaleDateString()}</span>
          </div>
        ))}
      </div>
    </div>
  );
};

Handling Payment Failures and Retries

Implement robust payment failure handling with retry logic:
// Payment failure handler with exponential backoff
class PaymentRetryHandler {
  constructor(maxRetries = 3) {
    this.maxRetries = maxRetries;
    this.retryDelays = [24, 72, 168]; // Hours: 1 day, 3 days, 1 week
  }

  async handlePaymentFailure(paymentSessionId, failureReason) {
    console.log(`Payment failed for session ${paymentSessionId}: ${failureReason}`);

    // Get payment session details
    const session = await checkPaymentSessionStatus(paymentSessionId);

    // Categorize failure type
    const failureCategory = this.categorizeFailure(failureReason);

    switch (failureCategory) {
      case 'insufficient_funds':
        await this.scheduleRetry(paymentSessionId, 24); // Retry in 24 hours
        await this.notifyCustomer(session.customerId, 'insufficient_funds');
        break;

      case 'expired_card':
        await this.requestPaymentMethodUpdate(session.customerId);
        break;

      case 'fraud_suspected':
        await this.escalateToFraud(session);
        break;

      case 'technical_error':
        await this.scheduleRetry(paymentSessionId, 1); // Retry in 1 hour
        break;

      default:
        await this.escalateToSupport(session);
    }
  }

  categorizeFailure(reason) {
    const failureMap = {
      'insufficient_funds': 'insufficient_funds',
      'card_declined': 'insufficient_funds',
      'expired_card': 'expired_card',
      'invalid_cvc': 'expired_card',
      'fraud_suspected': 'fraud_suspected',
      'processing_error': 'technical_error',
      'network_error': 'technical_error'
    };

    return failureMap[reason] || 'unknown';
  }

  async scheduleRetry(paymentSessionId, delayHours) {
    // In production, this would schedule a background job
    setTimeout(async () => {
      try {
        // Create new payment session with same order
        const originalSession = await checkPaymentSessionStatus(paymentSessionId);
        const newSession = await createOrderPaymentSession(originalSession.orderId);

        // Notify customer of retry attempt
        await this.notifyCustomerRetry(originalSession.customerId, newSession.paymentUrl);
      } catch (error) {
        console.error('Payment retry failed:', error);
      }
    }, delayHours * 60 * 60 * 1000);
  }

  async notifyCustomer(customerId, failureType) {
    // Implement customer notification logic
    console.log(`Notifying customer ${customerId} about ${failureType}`);
  }

  async requestPaymentMethodUpdate(customerId) {
    // Create payment profile session for updating payment method
    const session = await createPaymentProfileSession(
      `${process.env.BASE_URL}/billing/update-payment`,
      { customerId }
    );

    // Send email with update link
    await this.notifyCustomer(customerId, 'payment_method_update_required');
  }
}

Promotional Pricing and Discounts

Handle promotional codes and discount applications:
// Get promotion by promo code
const getPromotionByCode = async (promoCode) => {
  const response = await fetch(`https://connect.your-domain.com/api/v2/discounts/promotions/promo-code/${promoCode}`, {
    method: 'GET',
    headers: {
      'Authorization': 'Bearer YOUR_ACCESS_TOKEN',
      'X-API-Key': 'YOUR_API_KEY',
      'Content-Type': 'application/json'
    }
  });

  if (!response.ok) {
    const error = await response.json();
    throw new Error(`Invalid promo code: ${error.message}`);
  }

  return await response.json();
};

// Apply promo code to order pricing
const applyPromoCodeToOrder = async (orderId, promoCode) => {
  try {
    // Validate promo code first
    const promotion = await getPromotionByCode(promoCode);

    // Calculate order price with promo code
    const pricingResponse = await fetch(`https://connect.your-domain.com/api/v2/orders/${orderId}/calculate-price`, {
      method: 'POST',
      headers: {
        'Authorization': 'Bearer YOUR_ACCESS_TOKEN',
        'X-API-Key': 'YOUR_API_KEY',
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        promoCode: promoCode
      })
    });

    const pricing = await pricingResponse.json();

    return {
      originalTotal: pricing.subtotal,
      discountAmount: pricing.discountAmount,
      finalTotal: pricing.total,
      promotion: promotion
    };
  } catch (error) {
    console.error('Failed to apply promo code:', error);
    throw error;
  }
};

// Promo code component
const PromoCodeInput = ({ orderId, onApplied, onError }) => {
  const [promoCode, setPromoCode] = useState('');
  const [loading, setLoading] = useState(false);
  const [applied, setApplied] = useState(null);

  const handleApply = async () => {
    if (!promoCode.trim()) return;

    setLoading(true);
    try {
      const result = await applyPromoCodeToOrder(orderId, promoCode);
      setApplied(result);
      onApplied(result);
    } catch (error) {
      onError(error.message);
    } finally {
      setLoading(false);
    }
  };

  return (
    <div className="promo-code-input">
      <input
        type="text"
        placeholder="Enter promo code"
        value={promoCode}
        onChange={(e) => setPromoCode(e.target.value.toUpperCase())}
        disabled={loading || applied}
      />
      <button onClick={handleApply} disabled={loading || applied}>
        {loading ? 'Applying...' : 'Apply'}
      </button>

      {applied && (
        <div className="promo-applied">
          <p>{applied.promotion.name} applied</p>
          <p>Discount: -{applied.discountAmount} {applied.promotion.currency}</p>
        </div>
      )}
    </div>
  );
};

Next Steps

After implementing payment processing:

Order fulfillment

Handle service provisioning after successful payment

Customer self-service

Enable customers to manage their payment methods

Best Practices

Security

  • Never store raw payment card data - use tokenized payment profiles
  • Implement PCI DSS compliance for card processing
  • Use HTTPS for all payment-related communications
  • Validate all payment webhooks and callbacks

User Experience

  • Provide clear payment status updates to customers
  • Implement user-friendly error messages for payment failures
  • Offer multiple payment methods when possible
  • Save successful payment methods for future use

Reliability

  • Implement robust retry logic for failed payments
  • Handle payment processor downtime gracefully
  • Monitor payment success rates and failure patterns
  • Set up alerts for payment processing issues

Common Questions

Q: How do I handle different currencies? A: The API supports multiple currencies. Specify the currency in payment sessions and ensure your payment processor supports the target currency. Q: Can I process refunds through the API? A: Refunds are typically handled through your payment processor’s dashboard or API, then reflected in the Connect API payment records. Q: How do I implement recurring billing? A: Use stored payment profiles with scheduled payment sessions. The billing system can automatically create payment sessions for recurring charges.