Skip to main content
This tutorial was written by Claude Code (an AI) and has not yet been reviewed. Follow along with caution. If the tutorial was helpful or a specific part was not clear/correct, please provide feedback at the bottom of the page. Thank you.
This guide covers general payment processing concepts with CoCart Preview API. For specific gateway implementations, see our dedicated tutorials.

Overview

The CoCart Checkout API supports various payment processing patterns, from simple server-side methods to complex client-side integrations. Understanding these fundamentals will help you implement any payment gateway effectively.

Payment Method Types

Server-Side Payment Methods

These payment methods process entirely on the server and require minimal client-side code:

Bank Transfer (BACS)

Customer transfers money directly to your bank account

Check Payments

Customer mails a physical check for the order

Cash on Delivery

Payment collected when the order is delivered

Cryptocurrency

Various crypto payment gateways (BitPay, Coinbase, etc.)
Implementation: Simply pass the payment method ID - no additional payment data required.
const checkoutData = {
    billing_address: billingAddress,
    payment_method: 'bacs', // or 'cheque', 'cod', etc.
    shipping_method: 'flat_rate:1'
};

Client-Side Payment Methods

These require customer interaction and client-side processing before checkout:

Credit Card Gateways

Stripe, Square, Authorize.Net - require card tokenization

Digital Wallets

PayPal, Apple Pay, Google Pay - handle payment authorization

Buy Now, Pay Later

Klarna, Afterpay, Sezzle - require payment plan setup

Bank Payments

ACH, SEPA, Open Banking - require bank authorization
Implementation: Requires direct gateway SDK integration, payment tokenization, and payment reference submission.

Payment Processing Approaches

For production applications, use direct gateway integration:
  • Credit Card Processing
  • Digital Wallet Processing
  • Server-Side Processing
// Example: Stripe integration
const stripe = Stripe('pk_live_your_publishable_key'); // Match WooCommerce settings

// 1. Create payment method using gateway SDK
const {paymentMethod, error} = await stripe.createPaymentMethod({
    type: 'card',
    card: cardElement,
    billing_details: billingData
});

if (error) {
    throw new Error(error.message);
}

// 2. Submit checkout with payment method reference
const response = await fetch('/wp-json/cocart/preview/checkout', {
    method: 'PUT',
    headers: {
        'Cart-Key': cartKey,
        'Content-Type': 'application/json',
    },
    body: JSON.stringify({
        billing_address: billingData,
        payment_method: 'stripe',
        payment_method_data: {
            payment_method: paymentMethod.id // Only the reference
        }
    })
});

if (!response.ok) {
    throw new Error('Checkout failed');
}

return response.json();

Development and Debugging

For development purposes, admin users can access payment gateway debug information:
// Admin-only: Get payment methods with debug info
const response = await fetch('/wp-json/cocart/preview/checkout/payment-methods', {
    headers: {
        'Authorization': `Bearer ${adminJwtToken}`, // Admin user required
        'Cart-Key': cartKey
    }
});

const data = await response.json();

if (data.debug_info) {
    console.group('Gateway Debug Information');
    console.warn('🚨 Debug info - DO NOT use in production');

    Object.entries(data.debug_info.payment_context).forEach(([gateway, config]) => {
        console.log(`${gateway}:`, config);
    });

    console.groupEnd();
}
Debug information is only available to users with manage_options capability and should never be used in production applications.

Payment Method Data Structures

Understanding how to structure payment data for different gateway types:

Credit Card Gateways

For tokenized card payments (Stripe, Authorize.Net, Square):
{
    billing_address: { /* customer billing data */ },
    payment_method: 'stripe',
    payment_method_data: {
        payment_method: 'pm_1234567890abcdef', // Stripe payment method ID
        // OR for Authorize.Net:
        // payment_nonce: 'nonce_from_acceptjs',
        // OR for Square:
        // source_id: 'source_id_from_square_sdk'
    }
}

Digital Wallets

For wallet-based payments (PayPal, Apple Pay, Google Pay):
{
    billing_address: { /* customer billing data */ },
    payment_method: 'paypal',
    payment_method_data: {
        paypal_order_id: 'PAYPAL_ORDER_123',
        paypal_payer_id: 'PAYER_ID_456',
        transaction_id: 'TXN_789'
    }
}

Server-Side Methods

For methods processed entirely server-side:
{
    billing_address: { /* customer billing data */ },
    payment_method: 'bacs', // or 'cheque', 'cod'
    // No payment_method_data needed
}

Payment Method Configuration

Gateway Credential Alignment

Critical: Frontend SDK credentials must match WooCommerce gateway settings:

Stripe

Frontend: Stripe('pk_live_abc123') WooCommerce: Same publishable key in Stripe settings

PayPal

Frontend: client: {live: 'client_id_123'} WooCommerce: Same client ID in PayPal settings

Authorize.Net

Frontend: clientKey: 'public_key_123' WooCommerce: Same public client key in Auth.Net settings

Square

Frontend: applicationId: 'app_id_123' WooCommerce: Same application ID in Square settings

Checkout Request Structure

Complete Checkout Example

const checkoutData = {
    // Required customer information
    billing_address: {
        first_name: "John",
        last_name: "Doe",
        email: "john@example.com",
        address_1: "123 Main St",
        city: "Anytown",
        state: "CA",
        postcode: "12345",
        country: "US",
        phone: "555-1234" // Optional but recommended
    },

    // Optional shipping address (if different from billing)
    shipping_address: {
        first_name: "John",
        last_name: "Doe",
        address_1: "456 Work St",
        city: "Anytown",
        state: "CA",
        postcode: "12345",
        country: "US"
    },

    payment_method: "stripe", // Gateway ID
    shipping_method: "flat_rate:1", // Optional

    // Gateway-specific payment data
    payment_data: {
        // See gateway-specific examples below
    }
};

Gateway-Specific Payment Data

  • Stripe
  • PayPal
  • Square
  • Authorize.Net
payment_data: {
    payment_intent_id: "pi_1234567890",
    payment_method_id: "pm_1234567890",
    client_secret: "pi_1234567890_secret_xyz",

    // Optional fields
    receipt_email: "customer@example.com",
    setup_future_usage: "off_session", // For saving payment methods
    metadata: {
        order_type: "online",
        customer_notes: "Rush delivery"
    }
}

Common Integration Patterns

Pattern 1: Simple Server-Side Payment

For payment methods that don’t require client-side processing:
async function processSimplePayment(billingAddress, paymentMethod) {
    const cartKey = localStorage.getItem('cart_key');

    const response = await fetch('/wp-json/cocart/preview/checkout', {
        method: 'PUT',
        headers: {
            'Cart-Key': cartKey,
            'Content-Type': 'application/json',
        },
        body: JSON.stringify({
            billing_address: billingAddress,
            payment_method: paymentMethod // e.g., 'bacs', 'cheque', 'cod'
        })
    });

    return handleCheckoutResponse(response);
}

Pattern 2: Token-Based Payment

For gateways that tokenize payment methods:
async function processTokenBasedPayment(billingAddress, paymentMethod, paymentToken) {
    const cartKey = localStorage.getItem('cart_key');

    const response = await fetch('/wp-json/cocart/preview/checkout', {
        method: 'PUT',
        headers: {
            'Cart-Key': cartKey,
            'Content-Type': 'application/json',
        },
        body: JSON.stringify({
            billing_address: billingAddress,
            payment_method: paymentMethod,
            payment_data: {
                token: paymentToken,
                // Add other gateway-specific fields
            }
        })
    });

    return handleCheckoutResponse(response);
}

Pattern 3: Multi-Step Payment Flow

For complex payment flows with multiple client-server interactions:
async function processMultiStepPayment(billingAddress, paymentMethod) {
    try {
        // Step 1: Create payment context
        const context = await createPaymentContext(paymentMethod);

        // Step 2: Initialize client-side payment processing
        const paymentResult = await initializePaymentGateway(context);

        // Step 3: Collect payment information from user
        const paymentData = await collectPaymentData(paymentResult);

        // Step 4: Process payment with gateway
        const processedPayment = await processPaymentWithGateway(paymentData);

        // Step 5: Submit checkout with payment data
        const checkoutResult = await submitCheckout(billingAddress, paymentMethod, processedPayment);

        return checkoutResult;

    } catch (error) {
        console.error('Multi-step payment error:', error);
        throw error;
    }
}

Error Handling

Common Error Types

Validation Errors

Missing required fields, invalid data formats

Payment Errors

Declined cards, insufficient funds, gateway issues

Network Errors

Timeout, connection issues, server errors

Business Logic Errors

Out of stock, invalid shipping, cart changes

Error Response Structure

{
    "code": "cocart_payment_failed",
    "message": "Payment processing failed",
    "data": {
        "status": 400,
        "error_type": "payment_error",
        "gateway_error": {
            "code": "card_declined",
            "message": "Your card was declined",
            "decline_code": "insufficient_funds"
        }
    }
}

Error Handling Implementation

async function handleCheckoutResponse(response) {
    const result = await response.json();

    if (!response.ok) {
        throw new CheckoutError(result);
    }

    return result;
}

class CheckoutError extends Error {
    constructor(errorResponse) {
        super(errorResponse.message || 'Checkout failed');
        this.code = errorResponse.code;
        this.data = errorResponse.data;
        this.status = errorResponse.data?.status || 400;
    }

    isPaymentError() {
        return this.data?.error_type === 'payment_error';
    }

    isValidationError() {
        return this.data?.error_type === 'validation_error';
    }

    getGatewayError() {
        return this.data?.gateway_error;
    }

    getDisplayMessage() {
        // Return user-friendly error message
        const gatewayError = this.getGatewayError();

        if (gatewayError) {
            switch (gatewayError.code) {
                case 'card_declined':
                    return 'Your card was declined. Please try a different payment method.';
                case 'insufficient_funds':
                    return 'Insufficient funds. Please check your account balance.';
                case 'expired_card':
                    return 'Your card has expired. Please use a different card.';
                default:
                    return gatewayError.message || this.message;
            }
        }

        return this.message;
    }
}

Best Practices

Security

  • Never expose secret keys in client-side code
  • Use HTTPS for all API requests
  • Validate all input data on both client and server
  • Implement proper CORS policies
  • Use Content Security Policy (CSP) headers
  • Never store credit card numbers
  • Use gateway tokenization for card storage
  • Implement proper data encryption
  • Follow PCI DSS requirements
  • Regular security audits and testing

Performance

  • Load payment SDKs asynchronously
  • Cache payment contexts when possible
  • Minimize API calls during checkout
  • Implement proper loading states
  • Use request timeouts
  • Cache payment method configurations
  • Use database transactions for checkout
  • Implement proper error logging
  • Monitor gateway response times
  • Set up proper webhook handling

User Experience

  • Use progressive disclosure for complex forms
  • Implement real-time validation
  • Provide clear error messages
  • Support keyboard navigation
  • Test with screen readers
  • Show clear progress indicators
  • Handle payment redirects gracefully
  • Provide payment method options
  • Support guest and registered users
  • Implement proper success/failure flows

Testing Strategies

Development Testing

// Mock payment context for testing
const mockPaymentContext = {
    gateway_id: 'stripe',
    title: 'Credit Card (Test)',
    cart_total: '29.99',
    currency: 'USD',
    client_secret: 'pi_test_1234567890_secret_test',
    public_key: 'pk_test_1234567890'
};

// Test payment processing with mock data
async function testPaymentProcessing() {
    try {
        const result = await processCheckout({
            billing_address: getMockBillingAddress(),
            payment_method: 'stripe',
            payment_data: getMockPaymentData()
        });

        console.log('Test checkout successful:', result);
    } catch (error) {
        console.error('Test checkout failed:', error);
    }
}

Integration Testing

  • Test with gateway sandbox/test environments
  • Verify webhook endpoint functionality
  • Test error scenarios and edge cases
  • Validate payment data structure
  • Test across different browsers and devices

Production Monitoring

  • Monitor checkout success/failure rates
  • Track gateway response times
  • Log payment errors for analysis
  • Set up alerts for critical failures
  • Monitor webhook delivery status
Remember that payment processing involves sensitive financial data. Always prioritize security, follow industry best practices, and thoroughly test your implementation before going live.
I