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 integrating Authorize.Net with CoCart Preview API. Requires CoCart v4.6+ and a configured Authorize.Net payment gateway.

Overview

Authorize.Net integration with CoCart uses Accept.js for secure tokenization of payment data. This ensures sensitive card information never touches your server while providing PCI DSS compliance and fraud protection.

Prerequisites

Before implementing Authorize.Net checkout, ensure you have:
  1. Authorize.Net payment gateway configured in WooCommerce
  2. Accept.js library loaded in your frontend
  3. A valid cart with items added
  4. Customer billing address information
  5. API Login ID and Public Client Key from Authorize.Net

Integration Flow

1

Load Accept.js Library

Initialize Accept.js with your store’s public client key
2

Collect Payment Details

Securely collect card information from customers using form validation
3

Tokenize Payment Data

Use Accept.js to create secure payment nonce tokens
4

Complete Checkout

Submit checkout with payment nonce to CoCart for processing

Step 1: Load Accept.js Library

Include the Accept.js library in your checkout page:
<script type="text/javascript"
        src="https://js.authorize.net/v1/Accept.js"
        charset="utf-8">
</script>

<!-- For sandbox testing, use: -->
<!-- <script type="text/javascript"
          src="https://jstest.authorize.net/v1/Accept.js"
          charset="utf-8">
     </script> -->

Step 2: Initialize Accept.js

Set up Accept.js with your store’s configuration:
  • JavaScript
  • HTML
// Initialize Accept.js with your store's credentials
const acceptJSConfig = {
    // Must match WooCommerce Authorize.Net settings
    clientKey: 'your_public_client_key', // From Authorize.Net merchant account
    apiLoginID: 'your_api_login_id',     // From Authorize.Net merchant account
    environment: 'sandbox' // or 'production' based on your store settings
};

// Validate Accept.js library is loaded
if (typeof Accept === 'undefined') {
    throw new Error('Accept.js library not loaded. Please include the script.');
}

console.log('Accept.js initialized successfully');
Important: Your Accept.js configuration (client key, API login ID, environment) must exactly match your WooCommerce Authorize.Net gateway settings.

Step 3: HTML Structure

Create a checkout form with secure payment fields:
<form id="checkout-form">
    <!-- Customer Information -->
    <div class="billing-section">
        <h3>Billing Information</h3>
        <input type="text" name="billing_first_name" placeholder="First Name" required>
        <input type="text" name="billing_last_name" placeholder="Last Name" required>
        <input type="email" name="billing_email" placeholder="Email" required>
        <input type="tel" name="billing_phone" placeholder="Phone">
        <input type="text" name="billing_address_1" placeholder="Address" required>
        <input type="text" name="billing_city" placeholder="City" required>
        <input type="text" name="billing_state" placeholder="State" required>
        <input type="text" name="billing_postcode" placeholder="ZIP Code" required>
        <select name="billing_country" required>
            <option value="US">United States</option>
            <!-- Add other countries -->
        </select>
    </div>

    <!-- Payment Information -->
    <div class="payment-section">
        <h3>Payment Information</h3>

        <!-- Credit Card Fields -->
        <div class="card-fields">
            <input type="text"
                   id="card-number"
                   name="cardNumber"
                   placeholder="Card Number"
                   maxlength="20"
                   autocomplete="cc-number"
                   required>

            <div class="card-row">
                <input type="text"
                       id="expiry-month"
                       name="expMonth"
                       placeholder="MM"
                       maxlength="2"
                       autocomplete="cc-exp-month"
                       required>

                <input type="text"
                       id="expiry-year"
                       name="expYear"
                       placeholder="YYYY"
                       maxlength="4"
                       autocomplete="cc-exp-year"
                       required>

                <input type="text"
                       id="cvv"
                       name="cardCode"
                       placeholder="CVV"
                       maxlength="4"
                       autocomplete="cc-csc"
                       required>
            </div>
        </div>

        <div id="authorizenet-error-message" class="error-message" style="display: none;"></div>
    </div>

    <button type="submit" id="submit-button">
        <span id="button-text">Complete Order</span>
        <span id="button-spinner" class="spinner" style="display: none;"></span>
    </button>
</form>

Step 4: Initialize Accept.js and Handle Form

Set up Accept.js with payment processing:
async function setupAuthorizeNetCheckout() {
    try {
        // Create payment context
        const context = await createAuthorizeNetPaymentContext();

        // Store context for later use
        window.authorizeNetContext = context;

        // Setup form submission
        setupFormSubmission(context);

        // Setup card number formatting
        setupCardFormatting();

        console.log('Authorize.Net checkout initialized successfully');

    } catch (error) {
        console.error('Authorize.Net setup error:', error);
        showError('Payment setup failed. Please refresh and try again.');
    }
}

function setupFormSubmission(context) {
    const form = document.getElementById('checkout-form');
    const submitButton = document.getElementById('submit-button');
    const buttonText = document.getElementById('button-text');
    const buttonSpinner = document.getElementById('button-spinner');

    form.addEventListener('submit', async (event) => {
        event.preventDefault();

        // Disable submit button
        submitButton.disabled = true;
        buttonText.textContent = 'Processing...';
        buttonSpinner.style.display = 'inline-block';

        try {
            // Validate form
            if (!validateForm()) {
                throw new Error('Please fill in all required fields correctly.');
            }

            // Get form data
            const formData = new FormData(form);
            const billingAddress = getBillingAddressFromForm(formData);

            // Tokenize payment data
            const paymentData = await tokenizePaymentData(context, formData);

            // Process checkout
            await processAuthorizeNetCheckout(billingAddress, paymentData);

        } catch (error) {
            console.error('Checkout error:', error);
            showError(error.message || 'Checkout failed. Please try again.');
        } finally {
            // Re-enable submit button
            submitButton.disabled = false;
            buttonText.textContent = 'Complete Order';
            buttonSpinner.style.display = 'none';
        }
    });
}

Step 5: Tokenize Payment Data with Accept.js

Use Accept.js to securely tokenize the payment information:
function tokenizePaymentData(context, formData) {
    return new Promise((resolve, reject) => {
        // Prepare payment data for Accept.js
        const authData = {
            clientKey: context.public_client_key,
            apiLoginID: context.api_login_id
        };

        const cardData = {
            cardNumber: formData.get('cardNumber').replace(/\s/g, ''), // Remove spaces
            month: formData.get('expMonth').padStart(2, '0'), // Ensure 2 digits
            year: formData.get('expYear'),
            cardCode: formData.get('cardCode')
        };

        // Create secure data object
        const secureData = {
            authData: authData,
            cardData: cardData
        };

        // Call Accept.js to tokenize the data
        Accept.dispatchData(secureData, function(response) {
            if (response.messages.resultCode === 'Error') {
                // Handle tokenization errors
                const errorMessage = response.messages.message.map(msg => msg.text).join(', ');
                reject(new Error(`Payment validation failed: ${errorMessage}`));
            } else {
                // Success - return the opaque data
                resolve({
                    data_descriptor: response.opaqueData.dataDescriptor,
                    data_value: response.opaqueData.dataValue,
                    // Include card info for reference (last 4 digits only)
                    card_last_four: cardData.cardNumber.slice(-4),
                    card_type: getCardType(cardData.cardNumber),
                    expiry_month: cardData.month,
                    expiry_year: cardData.year
                });
            }
        });
    });
}

// Helper function to determine card type
function getCardType(cardNumber) {
    const number = cardNumber.replace(/\s/g, '');

    if (/^4/.test(number)) return 'visa';
    if (/^5[1-5]/.test(number) || /^2[2-7]/.test(number)) return 'mastercard';
    if (/^3[47]/.test(number)) return 'amex';
    if (/^6(?:011|5)/.test(number)) return 'discover';

    return 'unknown';
}

Step 6: Process Checkout

Submit the checkout with tokenized payment data:
async function processAuthorizeNetCheckout(billingAddress, paymentData) {
    const cartKey = localStorage.getItem('cart_key');

    const checkoutData = {
        billing_address: billingAddress,
        payment_method: 'authorizenet',
        payment_data: paymentData
    };

    const response = await fetch('https://yoursite.com/wp-json/cocart/preview/checkout', {
        method: 'PUT',
        headers: {
            'Cart-Key': cartKey,
            'Content-Type': 'application/json',
        },
        body: JSON.stringify(checkoutData)
    });

    const result = await response.json();

    if (!response.ok) {
        // Handle specific Authorize.Net errors
        if (result.data?.gateway_error) {
            throw new AuthorizeNetError(result.data.gateway_error);
        }
        throw new Error(result.message || `HTTP ${response.status}`);
    }

    // Handle successful checkout
    if (result.order_id) {
        showSuccess(`Order #${result.order_number} completed successfully!`);

        // Clear sensitive form data
        clearPaymentForm();

        // Clear cart
        localStorage.removeItem('cart_key');

        // Redirect to thank you page
        if (result.payment_result?.redirect_url) {
            setTimeout(() => {
                window.location.href = result.payment_result.redirect_url;
            }, 2000);
        }
    }

    return result;
}

// Custom error class for Authorize.Net errors
class AuthorizeNetError extends Error {
    constructor(gatewayError) {
        super(gatewayError.message || 'Payment processing failed');
        this.code = gatewayError.code;
        this.reasonCode = gatewayError.reason_code;
        this.reasonText = gatewayError.reason_text;
    }

    getDisplayMessage() {
        // Return user-friendly error messages based on reason codes
        switch (this.reasonCode) {
            case '2':
                return 'Transaction declined. Please try a different card.';
            case '3':
                return 'Transaction error. Please try again.';
            case '4':
                return 'Card declined. Please contact your card issuer.';
            case '5':
                return 'Invalid amount. Please try again.';
            case '6':
                return 'Invalid credit card number.';
            case '7':
                return 'Invalid expiration date.';
            case '8':
                return 'Card has expired. Please use a different card.';
            case '11':
                return 'Duplicate transaction detected.';
            case '13':
                return 'Merchant account does not accept this card type.';
            case '17':
                return 'Merchant account cannot accept this card.';
            case '27':
                return 'Transaction resulted in an AVS mismatch.';
            case '28':
                return 'Merchant account does not accept this card type.';
            default:
                return this.reasonText || this.message;
        }
    }
}

Complete Integration Example

Here’s a complete working implementation:
class AuthorizeNetCheckout {
    constructor() {
        this.context = null;
        this.formValid = false;
    }

    async initialize() {
        try {
            // Create payment context
            this.context = await this.createPaymentContext();

            // Setup form validation
            this.setupFormValidation();

            // Setup form submission
            this.setupFormSubmission();

            // Setup card formatting
            this.setupCardFormatting();

            console.log('Authorize.Net checkout initialized successfully');
        } catch (error) {
            console.error('Authorize.Net initialization error:', error);
            this.showError('Payment system unavailable. Please try again later.');
        }
    }

    async createPaymentContext() {
        const cartKey = localStorage.getItem('cart_key');

        const response = await fetch('/wp-json/cocart/preview/checkout/payment-context', {
            method: 'POST',
            headers: {
                'Cart-Key': cartKey,
                'Content-Type': 'application/json',
            },
            body: JSON.stringify({ payment_method: 'authorizenet' })
        });

        const context = await response.json();

        if (!response.ok) {
            throw new Error(context.message || 'Failed to create payment context');
        }

        return context;
    }

    setupFormValidation() {
        const form = document.getElementById('checkout-form');
        const requiredFields = form.querySelectorAll('input[required]');

        const validateForm = () => {
            let isValid = true;

            // Validate required fields
            requiredFields.forEach(field => {
                if (!field.value.trim()) {
                    isValid = false;
                    field.classList.add('error');
                } else {
                    field.classList.remove('error');
                }
            });

            // Validate card number
            const cardNumber = document.getElementById('card-number').value.replace(/\s/g, '');
            if (!this.isValidCardNumber(cardNumber)) {
                isValid = false;
                document.getElementById('card-number').classList.add('error');
            }

            // Validate expiry
            const month = document.getElementById('expiry-month').value;
            const year = document.getElementById('expiry-year').value;
            if (!this.isValidExpiry(month, year)) {
                isValid = false;
                document.getElementById('expiry-month').classList.add('error');
                document.getElementById('expiry-year').classList.add('error');
            }

            // Validate CVV
            const cvv = document.getElementById('cvv').value;
            if (!this.isValidCvv(cvv, cardNumber)) {
                isValid = false;
                document.getElementById('cvv').classList.add('error');
            }

            this.formValid = isValid;
            return isValid;
        };

        // Add event listeners
        requiredFields.forEach(field => {
            field.addEventListener('blur', validateForm);
            field.addEventListener('input', validateForm);
        });

        // Initial validation
        validateForm();
    }

    setupCardFormatting() {
        const cardNumberField = document.getElementById('card-number');

        cardNumberField.addEventListener('input', (e) => {
            let value = e.target.value.replace(/\s/g, '').replace(/[^0-9]/gi, '');
            let formattedValue = value.match(/.{1,4}/g)?.join(' ') || value;

            if (formattedValue !== e.target.value) {
                e.target.value = formattedValue;
            }
        });

        // Auto-advance fields
        const monthField = document.getElementById('expiry-month');
        const yearField = document.getElementById('expiry-year');
        const cvvField = document.getElementById('cvv');

        monthField.addEventListener('input', (e) => {
            if (e.target.value.length === 2) {
                yearField.focus();
            }
        });

        yearField.addEventListener('input', (e) => {
            if (e.target.value.length === 4) {
                cvvField.focus();
            }
        });
    }

    setupFormSubmission() {
        const form = document.getElementById('checkout-form');

        form.addEventListener('submit', async (event) => {
            event.preventDefault();

            const submitButton = form.querySelector('[type="submit"]');
            const originalText = submitButton.textContent;

            try {
                submitButton.disabled = true;
                submitButton.textContent = 'Processing...';

                if (!this.formValid) {
                    throw new Error('Please correct the errors in your form.');
                }

                const formData = new FormData(form);
                const billingAddress = this.getBillingAddressFromForm(formData);

                // Tokenize payment data
                const paymentData = await this.tokenizePaymentData(formData);

                // Process checkout
                await this.processCheckout(billingAddress, paymentData);

            } catch (error) {
                console.error('Checkout error:', error);

                if (error instanceof AuthorizeNetError) {
                    this.showError(error.getDisplayMessage());
                } else {
                    this.showError(error.message || 'Checkout failed. Please try again.');
                }
            } finally {
                submitButton.disabled = false;
                submitButton.textContent = originalText;
            }
        });
    }

    tokenizePaymentData(formData) {
        return new Promise((resolve, reject) => {
            const authData = {
                clientKey: this.context.public_client_key,
                apiLoginID: this.context.api_login_id
            };

            const cardData = {
                cardNumber: formData.get('cardNumber').replace(/\s/g, ''),
                month: formData.get('expMonth').padStart(2, '0'),
                year: formData.get('expYear'),
                cardCode: formData.get('cardCode')
            };

            const secureData = { authData, cardData };

            Accept.dispatchData(secureData, (response) => {
                if (response.messages.resultCode === 'Error') {
                    const errorMessage = response.messages.message
                        .map(msg => msg.text)
                        .join(', ');
                    reject(new Error(`Payment validation failed: ${errorMessage}`));
                } else {
                    resolve({
                        data_descriptor: response.opaqueData.dataDescriptor,
                        data_value: response.opaqueData.dataValue,
                        card_last_four: cardData.cardNumber.slice(-4),
                        card_type: this.getCardType(cardData.cardNumber),
                        expiry_month: cardData.month,
                        expiry_year: cardData.year
                    });
                }
            });
        });
    }

    async processCheckout(billingAddress, paymentData) {
        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: 'authorizenet',
                payment_data: paymentData
            })
        });

        const result = await response.json();

        if (!response.ok) {
            if (result.data?.gateway_error) {
                throw new AuthorizeNetError(result.data.gateway_error);
            }
            throw new Error(result.message || `HTTP ${response.status}`);
        }

        this.handleCheckoutSuccess(result);
        return result;
    }

    getBillingAddressFromForm(formData) {
        return {
            first_name: formData.get('billing_first_name'),
            last_name: formData.get('billing_last_name'),
            email: formData.get('billing_email'),
            phone: formData.get('billing_phone'),
            address_1: formData.get('billing_address_1'),
            city: formData.get('billing_city'),
            state: formData.get('billing_state'),
            postcode: formData.get('billing_postcode'),
            country: formData.get('billing_country')
        };
    }

    handleCheckoutSuccess(result) {
        this.showSuccess(`Order #${result.order_number} completed successfully!`);

        // Clear sensitive form data
        this.clearPaymentForm();

        // Clear cart
        localStorage.removeItem('cart_key');

        // Redirect after delay
        if (result.payment_result?.redirect_url) {
            setTimeout(() => {
                window.location.href = result.payment_result.redirect_url;
            }, 2000);
        }
    }

    clearPaymentForm() {
        document.getElementById('card-number').value = '';
        document.getElementById('expiry-month').value = '';
        document.getElementById('expiry-year').value = '';
        document.getElementById('cvv').value = '';
    }

    // Validation methods
    isValidCardNumber(cardNumber) {
        // Luhn algorithm validation
        if (!/^\d+$/.test(cardNumber) || cardNumber.length < 13) return false;

        let sum = 0;
        let isEven = false;

        for (let i = cardNumber.length - 1; i >= 0; i--) {
            let digit = parseInt(cardNumber.charAt(i), 10);

            if (isEven) {
                digit *= 2;
                if (digit > 9) {
                    digit -= 9;
                }
            }

            sum += digit;
            isEven = !isEven;
        }

        return sum % 10 === 0;
    }

    isValidExpiry(month, year) {
        if (!/^\d{1,2}$/.test(month) || !/^\d{4}$/.test(year)) return false;

        const expiry = new Date(parseInt(year), parseInt(month) - 1);
        const now = new Date();
        now.setDate(1); // Set to first day of current month

        return expiry >= now;
    }

    isValidCvv(cvv, cardNumber) {
        if (!/^\d+$/.test(cvv)) return false;

        // American Express uses 4-digit CVV
        if (this.getCardType(cardNumber) === 'amex') {
            return cvv.length === 4;
        }

        return cvv.length === 3;
    }

    getCardType(cardNumber) {
        const number = cardNumber.replace(/\s/g, '');

        if (/^4/.test(number)) return 'visa';
        if (/^5[1-5]/.test(number) || /^2[2-7]/.test(number)) return 'mastercard';
        if (/^3[47]/.test(number)) return 'amex';
        if (/^6(?:011|5)/.test(number)) return 'discover';

        return 'unknown';
    }

    showError(message) {
        const errorElement = document.getElementById('authorizenet-error-message');
        errorElement.textContent = message;
        errorElement.style.display = 'block';
        errorElement.className = 'error-message';
    }

    showSuccess(message) {
        const errorElement = document.getElementById('authorizenet-error-message');
        errorElement.textContent = message;
        errorElement.style.display = 'block';
        errorElement.className = 'success-message';
    }
}

// Initialize when DOM is loaded
document.addEventListener('DOMContentLoaded', async () => {
    const checkout = new AuthorizeNetCheckout();
    await checkout.initialize();
});

Error Handling

Handle common Authorize.Net error scenarios:
function handleAuthorizeNetErrors(error) {
    // Common Authorize.Net response codes
    const errorMessages = {
        '1': 'Transaction approved',
        '2': 'Transaction declined',
        '3': 'Transaction error',
        '4': 'Transaction held for review',

        // Specific decline reasons
        '6': 'Invalid credit card number',
        '7': 'Invalid expiration date',
        '8': 'Credit card has expired',
        '11': 'Duplicate transaction',
        '13': 'Merchant does not accept this card type',
        '17': 'Merchant cannot accept this card',
        '19': 'Transaction cannot be processed',
        '27': 'Address verification failed',
        '28': 'Card type not accepted by merchant',
        '44': 'Card code verification failed',
        '45': 'Card code and address verification failed'
    };

    const reasonCode = error.reasonCode || error.code;
    return errorMessages[reasonCode] || error.message || 'Payment processing failed';
}

Testing

For development and testing with Authorize.Net:

Test Card Numbers

  • Visa: 4007000000027
  • MasterCard: 5424000000000015
  • American Express: 374200000000004
  • Discover: 6011000000000012

Test Scenarios

  • Declined transaction: Use amount $0.01
  • Error transaction: Use amount $0.02
  • Held transaction: Use amount $0.03

Best Practices

Security

  • Always use Accept.js for tokenization
  • Never store raw card data
  • Use HTTPS for all requests
  • Implement proper form validation
  • Clear sensitive data after processing

User Experience

  • Format card numbers with spaces
  • Auto-advance between form fields
  • Show real-time validation errors
  • Provide clear error messages
  • Handle declined cards gracefully

Compliance

  • Follow PCI DSS guidelines
  • Use Accept.js for PCI compliance
  • Implement proper error handling
  • Log transactions for auditing
  • Test with various card types

Performance

  • Load Accept.js asynchronously
  • Cache payment contexts
  • Implement proper timeouts
  • Handle network failures
  • Monitor transaction success rates

Advanced Features

Customer Information Manager (CIM)

For storing customer payment methods:
// When processing checkout with saved payment option
const paymentData = {
    customer_profile_id: 'CUSTOMER_PROFILE_ID',
    payment_profile_id: 'PAYMENT_PROFILE_ID',
    // Include CVV for saved cards
    card_code: formData.get('cardCode')
};

Recurring Billing

For subscription payments:
const paymentData = {
    data_descriptor: response.opaqueData.dataDescriptor,
    data_value: response.opaqueData.dataValue,
    subscription: {
        interval_length: 1,
        interval_unit: 'months',
        start_date: '2024-01-01',
        total_occurrences: 12
    }
};
Always test your Authorize.Net integration thoroughly using the sandbox environment before going live. Ensure your webhook endpoints are configured to handle transaction notifications and updates.
I