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:- Authorize.Net payment gateway configured in WooCommerce
- Accept.js library loaded in your frontend
- A valid cart with items added
- Customer billing address information
- 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:Copy
<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
Copy
// 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:Copy
<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:Copy
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:Copy
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:Copy
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:Copy
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:Copy
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:Copy
// 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:Copy
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.