> ## Documentation Index
> Fetch the complete documentation index at: https://docs.cocartapi.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Checkout with Braintree

> Complete guide to integrating Braintree Hosted Fields with Checkout API

<Warning>
  This tutorial was written by [Claude Code (an AI)](https://claude.com/product/claude-code) 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.
</Warning>

<Note>
  This guide covers integrating Braintree with CoCart Preview API. Requires CoCart v4.6+ and a configured Braintree payment gateway.
</Note>

## Overview

Braintree integration with CoCart uses Hosted Fields for secure tokenization of payment data. This ensures sensitive card information never touches your server while providing PCI DSS SAQ A compliance and PayPal's fraud protection.

Braintree (owned by PayPal) supports multiple payment methods including credit cards, PayPal, Venmo, Apple Pay, and Google Pay.

## Prerequisites

Before implementing Braintree checkout, ensure you have:

1. Braintree payment gateway configured in WooCommerce
2. Braintree JavaScript SDK (v3) loaded in your frontend
3. A valid cart with items added
4. Customer billing address information
5. Braintree sandbox or production credentials

## Integration Flow

```mermaid theme={"system"}
sequenceDiagram
    participant Customer
    participant Frontend
    participant Braintree SDK
    participant CoCart API
    participant WooCommerce
    participant Braintree Server

    Frontend->>CoCart API: POST /checkout/payment-context
    Note over CoCart API: payment_method: 'braintree'
    CoCart API->>Braintree Server: Generate client token
    Braintree Server-->>CoCart API: client_token
    CoCart API-->>Frontend: {client_token, merchant_id}

    Frontend->>Braintree SDK: Initialize client
    Note over Braintree SDK: braintree.client.create()
    Braintree SDK-->>Frontend: clientInstance

    Frontend->>Braintree SDK: Create Hosted Fields
    Note over Braintree SDK: hostedFields.create()
    Braintree SDK-->>Frontend: Hosted Fields rendered
    Note over Frontend: cardNumber, cvv, expiryDate, postalCode

    Customer->>Braintree SDK: Enter payment details
    Customer->>Frontend: Submit form

    Frontend->>Braintree SDK: tokenize()
    Note over Braintree SDK: Validate & tokenize card
    Braintree SDK->>Braintree Server: Create payment nonce
    Braintree Server-->>Braintree SDK: nonce (1-hour valid)
    Braintree SDK-->>Frontend: {nonce, card_type, last_four}

    Frontend->>CoCart API: PUT /checkout
    Note over Frontend: payment_method_nonce, billing_address
    CoCart API->>WooCommerce: Process order
    WooCommerce->>Braintree Server: Process payment with nonce
    Braintree Server-->>WooCommerce: Transaction approved
    WooCommerce-->>CoCart API: Order created
    CoCart API-->>Frontend: {order_id, order_number}

    Frontend->>Customer: Show success message
    Frontend->>Customer: Redirect to thank you page
```

<Steps>
  <Step title="Load Braintree SDK">
    Initialize the Braintree JavaScript SDK
  </Step>

  <Step title="Request Client Token">
    Obtain a client authorization token from your server
  </Step>

  <Step title="Initialize Hosted Fields">
    Create secure iframe-based payment input fields
  </Step>

  <Step title="Collect Payment Details">
    Securely collect card information from customers
  </Step>

  <Step title="Tokenize Payment Data">
    Generate a payment method nonce (temporary token)
  </Step>

  <Step title="Complete Checkout">
    Submit checkout with payment nonce to CoCart for processing
  </Step>
</Steps>

## Step 1: Load Braintree SDK

Include the Braintree Web SDK in your checkout page:

```html theme={"system"}
<!-- Load Braintree JavaScript SDK v3 -->
<script src="https://js.braintreegateway.com/web/3.97.2/js/client.min.js"></script>
<script src="https://js.braintreegateway.com/web/3.97.2/js/hosted-fields.min.js"></script>

<!-- Optional: For PayPal, Venmo, or other payment methods -->
<!-- <script src="https://js.braintreegateway.com/web/3.97.2/js/paypal-checkout.min.js"></script> -->
<!-- <script src="https://js.braintreegateway.com/web/3.97.2/js/venmo.min.js"></script> -->
```

<Warning>
  **Important**: Always use a specific version number in production. Check [Braintree's release notes](https://github.com/braintree/braintree-web/releases) for the latest stable version.
</Warning>

## Step 2: Request Client Token

Request a client authorization token from CoCart:

```javascript theme={"system"}
async function createBraintreePaymentContext() {
    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: 'braintree'
        })
    });

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

    const context = await response.json();

    // Context contains:
    // - client_token: Authorization token for Braintree SDK
    // - merchant_id: Your Braintree merchant ID
    // - environment: 'sandbox' or 'production'

    return context;
}
```

## Step 3: HTML Structure

Create a checkout form with containers for Hosted Fields:

```html theme={"system"}
<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>

        <!-- Braintree Hosted Fields will be injected into these containers -->
        <div class="hosted-fields">
            <label for="card-number">Card Number</label>
            <div id="card-number" class="hosted-field"></div>

            <div class="card-row">
                <div class="field-group">
                    <label for="expiration-date">Expiration Date</label>
                    <div id="expiration-date" class="hosted-field"></div>
                </div>

                <div class="field-group">
                    <label for="cvv">CVV</label>
                    <div id="cvv" class="hosted-field"></div>
                </div>

                <div class="field-group">
                    <label for="postal-code">Postal Code</label>
                    <div id="postal-code" class="hosted-field"></div>
                </div>
            </div>
        </div>

        <div id="braintree-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 Braintree Client

Initialize the Braintree client with your authorization token:

```javascript theme={"system"}
async function setupBraintreeCheckout() {
    try {
        // Get payment context with client token
        const context = await createBraintreePaymentContext();

        // Create Braintree client
        const clientInstance = await braintree.client.create({
            authorization: context.client_token
        });

        console.log('Braintree client initialized');

        // Initialize Hosted Fields
        const hostedFieldsInstance = await setupHostedFields(clientInstance);

        // Setup form submission
        setupFormSubmission(hostedFieldsInstance);

        console.log('Braintree checkout initialized successfully');

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

## Step 5: Initialize Hosted Fields

Configure and create Hosted Fields for secure payment input:

```javascript theme={"system"}
async function setupHostedFields(clientInstance) {
    const hostedFieldsInstance = await braintree.hostedFields.create({
        client: clientInstance,
        styles: {
            'input': {
                'font-size': '16px',
                'font-family': '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
                'color': '#3a3a3a'
            },
            'input.invalid': {
                'color': '#e53e3e'
            },
            'input.valid': {
                'color': '#38a169'
            },
            ':focus': {
                'color': '#2c5282'
            }
        },
        fields: {
            number: {
                selector: '#card-number',
                placeholder: '4111 1111 1111 1111',
                prefill: '' // Optional: Pre-fill for testing
            },
            cvv: {
                selector: '#cvv',
                placeholder: '123',
                maxlength: 4 // Supports 4-digit CVV for Amex
            },
            expirationDate: {
                selector: '#expiration-date',
                placeholder: 'MM/YY'
            },
            postalCode: {
                selector: '#postal-code',
                placeholder: '12345'
            }
        }
    });

    // Setup field validation event listeners
    setupFieldValidation(hostedFieldsInstance);

    return hostedFieldsInstance;
}

function setupFieldValidation(hostedFieldsInstance) {
    // Listen to field events for validation feedback
    hostedFieldsInstance.on('validityChange', function(event) {
        const field = event.fields[event.emittedBy];
        const fieldContainer = document.querySelector('#' + event.emittedBy).parentElement;

        if (field.isValid) {
            fieldContainer.classList.remove('invalid');
            fieldContainer.classList.add('valid');
        } else if (field.isPotentiallyValid) {
            fieldContainer.classList.remove('invalid', 'valid');
        } else {
            fieldContainer.classList.remove('valid');
            fieldContainer.classList.add('invalid');
        }
    });

    // Detect card type
    hostedFieldsInstance.on('cardTypeChange', function(event) {
        if (event.cards.length === 1) {
            const cardType = event.cards[0].type;
            console.log('Card type detected:', cardType);
            // You can update UI to show card brand icon
        }
    });

    // Handle field blur events
    hostedFieldsInstance.on('blur', function(event) {
        console.log('Field blurred:', event.emittedBy);
    });

    // Handle field focus events
    hostedFieldsInstance.on('focus', function(event) {
        console.log('Field focused:', event.emittedBy);
    });
}
```

## Step 6: Handle Form Submission

Process the checkout when the user submits the form:

```javascript theme={"system"}
function setupFormSubmission(hostedFieldsInstance) {
    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 fields
            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(hostedFieldsInstance, billingAddress);

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

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

            if (error.code === 'HOSTED_FIELDS_FIELDS_EMPTY') {
                showError('Please fill in all payment fields.');
            } else if (error.code === 'HOSTED_FIELDS_FIELDS_INVALID') {
                showError('Please check your payment information.');
            } else if (error.code === 'HOSTED_FIELDS_TOKENIZATION_NETWORK_ERROR') {
                showError('Network error. Please check your connection and try again.');
            } else if (error.code === 'HOSTED_FIELDS_TOKENIZATION_FAIL_ON_DUPLICATE') {
                showError('This card has already been used. Please try a different card.');
            } else {
                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 7: Tokenize Payment Data

Request a payment method nonce from Braintree:

```javascript theme={"system"}
async function tokenizePaymentData(hostedFieldsInstance, billingAddress) {
    try {
        // Tokenize the payment information
        const payload = await hostedFieldsInstance.tokenize({
            billingAddress: {
                postalCode: billingAddress.postcode, // Will use Hosted Field postal code if available
                streetAddress: billingAddress.address_1,
                locality: billingAddress.city,
                region: billingAddress.state,
                countryCodeAlpha2: billingAddress.country
            },
            // Optional: Enable 3D Secure for additional security
            // threeDSecure: true
        });

        // Payload contains:
        // - nonce: One-time use payment method token
        // - details: Card details (type, last 4 digits, etc.)
        // - type: Payment method type (e.g., "CreditCard")
        // - binData: Bank identification data

        console.log('Tokenization successful');
        console.log('Card type:', payload.details.cardType);
        console.log('Last 4 digits:', payload.details.lastFour);

        return {
            payment_method_nonce: payload.nonce,
            card_type: payload.details.cardType,
            card_last_four: payload.details.lastFour,
            card_bin: payload.binData?.bin || null,
            device_data: payload.deviceData || null // Fraud detection data
        };

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

// Helper function to get billing address from form
function 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')
    };
}

// Helper function to validate form
function validateForm() {
    const form = document.getElementById('checkout-form');
    const requiredFields = form.querySelectorAll('[required]');
    let isValid = true;

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

    return isValid;
}
```

## Step 8: Process Checkout

Submit the checkout with tokenized payment data:

```javascript theme={"system"}
async function processBraintreeCheckout(billingAddress, paymentData) {
    const cartKey = localStorage.getItem('cart_key');

    const checkoutData = {
        billing_address: billingAddress,
        payment_method: 'braintree',
        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 Braintree errors
        if (result.data?.gateway_error) {
            throw new BraintreeError(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 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 Braintree errors
class BraintreeError extends Error {
    constructor(gatewayError) {
        super(gatewayError.message || 'Payment processing failed');
        this.code = gatewayError.code;
        this.processorCode = gatewayError.processor_response_code;
        this.processorText = gatewayError.processor_response_text;
    }

    getDisplayMessage() {
        // Return user-friendly error messages
        const code = this.processorCode;

        // Processor response codes vary by processor
        // Common decline codes:
        if (['2000', '2001', '2002', '2003'].includes(code)) {
            return 'Transaction declined. Please try a different card.';
        }
        if (code === '2004') {
            return 'Card has expired. Please use a different card.';
        }
        if (code === '2005') {
            return 'Invalid credit card number.';
        }
        if (code === '2006' || code === '2007') {
            return 'Invalid expiration date.';
        }
        if (code === '2008' || code === '2009') {
            return 'Duplicate transaction detected.';
        }
        if (code === '2010') {
            return 'Invalid CVV. Please check your card security code.';
        }
        if (code === '2011' || code === '2012') {
            return 'Transaction declined by fraud service.';
        }
        if (['2013', '2014', '2015'].includes(code)) {
            return 'Transaction declined. Please contact your card issuer.';
        }

        return this.processorText || this.message || 'Payment processing failed. Please try again.';
    }
}
```

## Complete Integration Example

Here's a complete working implementation:

```javascript theme={"system"}
class BraintreeCheckout {
    constructor() {
        this.clientInstance = null;
        this.hostedFieldsInstance = null;
        this.formValid = false;
    }

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

            // Initialize Braintree client
            this.clientInstance = await braintree.client.create({
                authorization: context.client_token
            });

            // Initialize Hosted Fields
            this.hostedFieldsInstance = await this.setupHostedFields();

            // Setup form validation
            this.setupFormValidation();

            // Setup form submission
            this.setupFormSubmission();

            console.log('Braintree checkout initialized successfully');
        } catch (error) {
            console.error('Braintree 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: 'braintree' })
        });

        const context = await response.json();

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

        return context;
    }

    async setupHostedFields() {
        const hostedFieldsInstance = await braintree.hostedFields.create({
            client: this.clientInstance,
            styles: {
                'input': {
                    'font-size': '16px',
                    'font-family': '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
                    'color': '#3a3a3a'
                },
                'input.invalid': {
                    'color': '#e53e3e'
                },
                'input.valid': {
                    'color': '#38a169'
                }
            },
            fields: {
                number: {
                    selector: '#card-number',
                    placeholder: '4111 1111 1111 1111'
                },
                cvv: {
                    selector: '#cvv',
                    placeholder: '123',
                    maxlength: 4
                },
                expirationDate: {
                    selector: '#expiration-date',
                    placeholder: 'MM/YY'
                },
                postalCode: {
                    selector: '#postal-code',
                    placeholder: '12345'
                }
            }
        });

        // Setup field event listeners
        hostedFieldsInstance.on('validityChange', this.handleFieldValidityChange.bind(this));
        hostedFieldsInstance.on('cardTypeChange', this.handleCardTypeChange.bind(this));

        return hostedFieldsInstance;
    }

    handleFieldValidityChange(event) {
        const field = event.fields[event.emittedBy];
        const fieldContainer = document.querySelector('#' + event.emittedBy).parentElement;

        if (field.isValid) {
            fieldContainer.classList.remove('invalid');
            fieldContainer.classList.add('valid');
        } else if (!field.isPotentiallyValid) {
            fieldContainer.classList.remove('valid');
            fieldContainer.classList.add('invalid');
        } else {
            fieldContainer.classList.remove('invalid', 'valid');
        }

        this.updateFormValidity();
    }

    handleCardTypeChange(event) {
        if (event.cards.length === 1) {
            const cardType = event.cards[0].type;
            console.log('Card type detected:', cardType);
            // Update UI to show card brand icon
            document.body.setAttribute('data-card-type', cardType);
        } else {
            document.body.removeAttribute('data-card-type');
        }
    }

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

        inputs.forEach(input => {
            input.addEventListener('blur', () => this.updateFormValidity());
            input.addEventListener('input', () => this.updateFormValidity());
        });

        this.updateFormValidity();
    }

    updateFormValidity() {
        const form = document.getElementById('checkout-form');
        const requiredFields = form.querySelectorAll('input[required]');
        let isValid = true;

        requiredFields.forEach(field => {
            if (!field.value.trim()) {
                isValid = false;
            }
        });

        this.formValid = isValid;
    }

    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(billingAddress);

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

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

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

    async tokenizePaymentData(billingAddress) {
        const payload = await this.hostedFieldsInstance.tokenize({
            billingAddress: {
                postalCode: billingAddress.postcode,
                streetAddress: billingAddress.address_1,
                locality: billingAddress.city,
                region: billingAddress.state,
                countryCodeAlpha2: billingAddress.country
            }
        });

        return {
            payment_method_nonce: payload.nonce,
            card_type: payload.details.cardType,
            card_last_four: payload.details.lastFour,
            card_bin: payload.binData?.bin || null,
            device_data: payload.deviceData || null
        };
    }

    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: 'braintree',
                payment_data: paymentData
            })
        });

        const result = await response.json();

        if (!response.ok) {
            if (result.data?.gateway_error) {
                throw new BraintreeError(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 cart
        localStorage.removeItem('cart_key');

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

    getHostedFieldsErrorMessage(error) {
        switch (error.code) {
            case 'HOSTED_FIELDS_FIELDS_EMPTY':
                return 'Please fill in all payment fields.';
            case 'HOSTED_FIELDS_FIELDS_INVALID':
                return 'Please check your payment information.';
            case 'HOSTED_FIELDS_TOKENIZATION_NETWORK_ERROR':
                return 'Network error. Please check your connection and try again.';
            case 'HOSTED_FIELDS_TOKENIZATION_FAIL_ON_DUPLICATE':
                return 'This card has already been used. Please try a different card.';
            default:
                return error.message || 'Payment validation failed.';
        }
    }

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

    showSuccess(message) {
        const errorElement = document.getElementById('braintree-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 BraintreeCheckout();
    await checkout.initialize();
});
```

## Styling Hosted Fields

Add CSS to style the Hosted Fields containers and validation states:

```css theme={"system"}
/* Hosted Fields Containers */
.hosted-field {
    height: 40px;
    padding: 10px;
    border: 1px solid #cbd5e0;
    border-radius: 4px;
    background-color: white;
    transition: border-color 0.2s ease;
}

.hosted-field:focus-within {
    border-color: #4299e1;
    outline: none;
    box-shadow: 0 0 0 3px rgba(66, 153, 225, 0.1);
}

/* Validation States */
.field-group.valid .hosted-field {
    border-color: #38a169;
}

.field-group.invalid .hosted-field {
    border-color: #e53e3e;
}

/* Card Row Layout */
.card-row {
    display: grid;
    grid-template-columns: 1fr 1fr 1fr;
    gap: 1rem;
    margin-top: 1rem;
}

.field-group {
    display: flex;
    flex-direction: column;
}

.field-group label {
    margin-bottom: 0.5rem;
    font-size: 0.875rem;
    font-weight: 500;
    color: #4a5568;
}

/* Error Messages */
.error-message {
    margin-top: 1rem;
    padding: 0.75rem;
    background-color: #fed7d7;
    border-left: 4px solid #e53e3e;
    color: #742a2a;
    border-radius: 4px;
}

.success-message {
    margin-top: 1rem;
    padding: 0.75rem;
    background-color: #c6f6d5;
    border-left: 4px solid #38a169;
    color: #22543d;
    border-radius: 4px;
}

/* Card Brand Icons */
[data-card-type="visa"] .card-icon::before {
    content: "💳 Visa";
}

[data-card-type="mastercard"] .card-icon::before {
    content: "💳 Mastercard";
}

[data-card-type="american-express"] .card-icon::before {
    content: "💳 Amex";
}

[data-card-type="discover"] .card-icon::before {
    content: "💳 Discover";
}
```

## Error Handling

Handle common Braintree error scenarios:

```javascript theme={"system"}
function handleBraintreeErrors(error) {
    // Hosted Fields errors
    const hostedFieldsErrors = {
        'HOSTED_FIELDS_FIELDS_EMPTY': 'Please fill in all payment fields.',
        'HOSTED_FIELDS_FIELDS_INVALID': 'Please check your payment information.',
        'HOSTED_FIELDS_TOKENIZATION_NETWORK_ERROR': 'Network error. Please try again.',
        'HOSTED_FIELDS_TOKENIZATION_FAIL_ON_DUPLICATE': 'Duplicate transaction detected.',
        'HOSTED_FIELDS_TOKENIZATION_CVV_VERIFICATION_FAILED': 'CVV verification failed.',
        'HOSTED_FIELDS_FAILED_TOKENIZATION': 'Payment validation failed.'
    };

    // Client errors
    const clientErrors = {
        'CLIENT_AUTHORIZATION_INVALID': 'Payment system authorization failed.',
        'CLIENT_GATEWAY_NETWORK': 'Network error connecting to payment gateway.',
        'CLIENT_REQUEST_TIMEOUT': 'Request timeout. Please try again.',
        'CLIENT_RATE_LIMITED': 'Too many requests. Please wait and try again.'
    };

    if (hostedFieldsErrors[error.code]) {
        return hostedFieldsErrors[error.code];
    }

    if (clientErrors[error.code]) {
        return clientErrors[error.code];
    }

    return error.message || 'Payment processing failed. Please try again.';
}
```

## Testing

For development and testing with Braintree:

### Test Card Numbers

Use these test cards in the **sandbox environment**:

* **Visa**: `4111111111111111`
* **Visa (Declined)**: `4000111111111115`
* **MasterCard**: `5555555555554444`
* **American Express**: `378282246310005`
* **Discover**: `6011111111111117`
* **JCB**: `3530111333300000`

### Test Scenarios

* **Any CVV**: Use any 3-digit (or 4-digit for Amex) number
* **Any Expiration**: Use any future date
* **Declined Transaction**: Use card number ending in `0002`
* **Processor Declined**: Use amount `2000.00` or higher in cents

### Testing 3D Secure

```javascript theme={"system"}
// Enable 3D Secure during tokenization
const payload = await hostedFieldsInstance.tokenize({
    threeDSecure: {
        amount: '100.00', // Transaction amount
        email: billingAddress.email,
        billingAddress: {
            givenName: billingAddress.first_name,
            surname: billingAddress.last_name,
            phoneNumber: billingAddress.phone,
            streetAddress: billingAddress.address_1,
            locality: billingAddress.city,
            region: billingAddress.state,
            postalCode: billingAddress.postcode,
            countryCodeAlpha2: billingAddress.country
        }
    }
});
```

## Best Practices

<CardGroup cols={2}>
  <Card title="Security" icon="shield-check">
    * Always use Hosted Fields for tokenization
    * Never store raw card data
    * Use HTTPS for all requests
    * Implement proper form validation
    * Enable 3D Secure for high-value transactions
    * Use device data collection for fraud detection
  </Card>

  <Card title="User Experience" icon="user">
    * Show real-time field validation
    * Display card brand icons
    * Provide clear error messages
    * Handle declined cards gracefully
    * Support keyboard navigation
    * Auto-focus next field when complete
  </Card>

  <Card title="Compliance" icon="certificate">
    * Follow PCI DSS SAQ A guidelines
    * Use Hosted Fields for PCI compliance
    * Implement proper error handling
    * Log transactions for auditing
    * Test with various card types
    * Include PayPal privacy notice
  </Card>

  <Card title="Performance" icon="gauge-high">
    * Load SDK from CDN
    * Use specific version numbers
    * Cache client tokens appropriately
    * Implement proper timeouts
    * Handle network failures
    * Monitor transaction success rates
  </Card>
</CardGroup>

## Advanced Features

### PayPal Integration

Add PayPal as an additional payment option:

```javascript theme={"system"}
// Load PayPal component
const paypalCheckout = await braintree.paypalCheckout.create({
    client: clientInstance
});

// Setup PayPal button
await paypalCheckout.loadPayPalSDK({
    vault: true // Enable vault for saving payment methods
});

paypal.Buttons({
    fundingSource: paypal.FUNDING.PAYPAL,
    createBillingAgreement: async function() {
        return await paypalCheckout.createPayment({
            flow: 'vault'
        });
    },
    onApprove: async function(data, actions) {
        const payload = await paypalCheckout.tokenizePayment(data);
        // Use payload.nonce for checkout
    }
}).render('#paypal-button-container');
```

### Venmo Integration

Add Venmo support for mobile users:

```javascript theme={"system"}
const venmo = await braintree.venmo.create({
    client: clientInstance,
    allowNewBrowserTab: false,
    mobileWebFallBack: true
});

if (venmo.isBrowserSupported()) {
    // Show Venmo button
    document.getElementById('venmo-button').addEventListener('click', async () => {
        try {
            const payload = await venmo.tokenize();
            // Use payload.nonce for checkout
        } catch (error) {
            console.error('Venmo error:', error);
        }
    });
}
```

### Vaulting Payment Methods

Save customer payment methods for future use:

```javascript theme={"system"}
// When processing checkout with vaulted payment method
const paymentData = {
    payment_method_token: 'SAVED_PAYMENT_TOKEN',
    // Optional: Request CVV for vaulted cards
    cvv: formData.get('cvv')
};
```

## Privacy Notice Requirement

<Warning>
  **Important**: Braintree requires you to display a privacy notice about PayPal's data collection. Include this text near your payment form:

  "PayPal will collect and process information about your payment for the purposes of completing this transaction. For information on how PayPal handles your personal data, please see PayPal's [Privacy Statement](https://www.paypal.com/privacy)."
</Warning>

## Troubleshooting

### Common Issues

<AccordionGroup>
  <Accordion title="Hosted Fields Not Loading">
    **Problem**: Braintree Hosted Fields don't appear on the page.

    **Solution**: Check these common issues:

    ```javascript theme={"system"}
    // 1. Verify Braintree SDK is loaded
    if (typeof braintree === 'undefined') {
        console.error('Braintree SDK not loaded');
    }

    // 2. Check client token is valid
    console.log('Client token:', clientToken);

    // 3. Verify container elements exist
    const cardNumber = document.getElementById('card-number');
    if (!cardNumber) {
        console.error('Card number container not found');
    }

    // 4. Check for initialization errors
    braintree.client.create({
        authorization: clientToken
    }).then(clientInstance => {
        console.log('Client created successfully');
        return braintree.hostedFields.create({
            client: clientInstance,
            fields: {...}
        });
    }).catch(error => {
        console.error('Initialization error:', error);
    });
    ```

    Common causes:

    * Braintree SDK script not loaded or failed to load
    * Invalid or expired client token
    * Container elements missing from DOM
    * CORS issues (check browser console)
    * JavaScript errors preventing initialization
  </Accordion>

  <Accordion title="Tokenization Fails">
    **Problem**: Getting errors when trying to tokenize card data.

    **Solution**: Debug the tokenization process:

    ```javascript theme={"system"}
    hostedFieldsInstance.tokenize().then(payload => {
        console.log('Tokenization successful:', payload.nonce);
    }).catch(error => {
        console.error('Tokenization error:', error);

        // Check error code for specific issues
        switch(error.code) {
            case 'HOSTED_FIELDS_FIELDS_EMPTY':
                showError('Please fill in all card fields');
                break;
            case 'HOSTED_FIELDS_FIELDS_INVALID':
                showError('Please check your card information');
                // error.details has field-specific errors
                console.log('Invalid fields:', error.details);
                break;
            case 'HOSTED_FIELDS_TOKENIZATION_FAIL_ON_DUPLICATE':
                showError('This card was already tokenized');
                break;
            case 'HOSTED_FIELDS_TOKENIZATION_CVV_VERIFICATION_FAILED':
                showError('CVV verification failed');
                break;
            default:
                showError('Payment processing failed');
        }
    });
    ```

    Verification steps:

    * Ensure all required fields are filled
    * Validate card number, expiration, CVV are correct
    * Check for duplicate tokenization attempts
    * Verify no JavaScript errors in console
  </Accordion>

  <Accordion title="3D Secure Authentication Not Working">
    **Problem**: 3D Secure modal doesn't appear or fails.

    **Solution**: Ensure proper 3DS configuration:

    ```javascript theme={"system"}
    // Must provide amount and billing address for 3DS
    const threeDSecureParameters = {
        amount: '10.00', // Required - must match order total
        email: billingAddress.email,
        billingAddress: {
            givenName: billingAddress.first_name,
            surname: billingAddress.last_name,
            streetAddress: billingAddress.address_1,
            locality: billingAddress.city,
            region: billingAddress.state,
            postalCode: billingAddress.postcode,
            countryCodeAlpha2: billingAddress.country
        }
    };

    threeDSecureInstance.verifyCard({
        nonce: payload.nonce,
        bin: payload.details.bin,
        ...threeDSecureParameters,
        onLookupComplete: function(data, next) {
            console.log('3DS lookup complete:', data);
            next();
        }
    }).then(response => {
        console.log('3DS verification:', response);
        if (response.liabilityShifted) {
            // 3DS successful, proceed with checkout
        }
    }).catch(error => {
        console.error('3DS error:', error);
    });
    ```

    Common issues:

    * Missing or invalid amount parameter
    * Incomplete billing address
    * Popup blockers preventing 3DS modal
    * Test cards not triggering 3DS in sandbox
    * Network timeout during verification
  </Accordion>

  <Accordion title="Styling Not Applied to Hosted Fields">
    **Problem**: Custom styles not appearing on card fields.

    **Solution**: Verify style configuration:

    ```javascript theme={"system"}
    const styles = {
        'input': {
            'font-size': '16px',
            'font-family': 'helvetica, arial, sans-serif',
            'color': '#3A3A3A'
        },
        ':focus': {
            'color': 'black'
        },
        '.valid': {
            'color': 'green'
        },
        '.invalid': {
            'color': 'red'
        }
    };

    braintree.hostedFields.create({
        client: clientInstance,
        styles: styles, // Must be here, not in individual fields
        fields: {
            number: {
                selector: '#card-number',
                // Don't put styles here
            },
            // ...
        }
    });
    ```

    Important notes:

    * Styles must be in the `styles` property at root level
    * Use CSS property names in kebab-case (e.g., `'font-size'`)
    * Cannot use external stylesheets (security restriction)
    * Test in multiple browsers as rendering may differ
    * Some CSS properties are restricted for security
  </Accordion>

  <Accordion title="Payment Method Not Accepted">
    **Problem**: Backend doesn't recognize Braintree payment method.

    **Solution**: Verify configuration:

    ```javascript theme={"system"}
    // Ensure correct payment method ID
    const checkoutData = {
        billing_address: billingAddress,
        payment_method: 'braintree_credit_card', // Must match WooCommerce setting
        payment_data: {
            nonce: payload.nonce,
            device_data: deviceData // If fraud protection enabled
        }
    };
    ```

    Checklist:

    * Use `'braintree_credit_card'` as payment\_method
    * Verify Braintree gateway is active in WooCommerce
    * Check Braintree credentials in WooCommerce settings
    * Ensure nonce is fresh (expires after \~3 minutes)
    * Include device\_data if Advanced Fraud Tools enabled
  </Accordion>

  <Accordion title="Client Token Expired or Invalid">
    **Problem**: "Authorization is invalid" or token expired errors.

    **Solution**: Generate fresh tokens:

    ```javascript theme={"system"}
    // Client tokens expire - regenerate before each checkout
    async function getClientToken() {
        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: 'braintree_credit_card'
            })
        });

        const data = await response.json();
        return data.client_token;
    }

    // Reinitialize if token expired
    async function reinitializeBraintree() {
        const newToken = await getClientToken();

        // Teardown old instance
        if (hostedFieldsInstance) {
            hostedFieldsInstance.teardown();
        }

        // Create new instance with fresh token
        await initializeBraintree(newToken);
    }
    ```

    Notes:

    * Client tokens typically last 24 hours
    * Payment nonces expire after \~3 minutes
    * Regenerate token if page has been open for extended period
    * Always use fresh token for new sessions
  </Accordion>

  <Accordion title="Checkout Completes But Order Not Created">
    **Problem**: Braintree payment succeeds but WooCommerce order fails.

    **Solution**: Debug checkout flow:

    ```javascript theme={"system"}
    try {
        // 1. Verify nonce is valid
        console.log('Payment nonce:', payload.nonce);
        console.log('Nonce details:', payload.details);

        // 2. Check billing address is complete
        console.log('Billing address:', billingAddress);
        if (!billingAddress.email || !billingAddress.first_name) {
            throw new Error('Incomplete billing information');
        }

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

        const result = await response.json();
        console.log('Checkout response:', result);

        if (!response.ok) {
            console.error('Checkout failed:', result);
            throw new Error(result.message || 'Checkout failed');
        }

        if (!result.order_id) {
            throw new Error('Order was not created');
        }

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

    Check these:

    * Browser console for errors
    * PHP error logs on server
    * WooCommerce Status → Logs
    * Braintree dashboard for transaction status
    * Cart hasn't been emptied prematurely
  </Accordion>

  <Accordion title="Network or CORS Errors">
    **Problem**: CORS errors or network failures when loading Braintree.

    **Solution**: Verify server configuration:

    ```javascript theme={"system"}
    // Check if Braintree endpoints are accessible
    fetch('https://api.braintreegateway.com/merchants/YOUR_MERCHANT_ID/client_api/v1/configuration')
        .then(response => {
            console.log('Braintree API accessible:', response.ok);
        })
        .catch(error => {
            console.error('Braintree API error:', error);
        });
    ```

    Common causes:

    * Firewall blocking Braintree endpoints
    * Content Security Policy (CSP) restrictions
    * Ad blockers interfering with payment scripts
    * Invalid merchant ID or credentials
    * Network proxy or VPN interference

    CSP Headers needed:

    ```
    script-src: https://js.braintreegateway.com
    frame-src: https://assets.braintreegateway.com
    connect-src: https://api.braintreegateway.com
    ```
  </Accordion>

  <Accordion title="Sandbox vs Production Issues">
    **Problem**: Works in sandbox but fails in production.

    **Solution**: Verify production configuration:

    ```javascript theme={"system"}
    // Check environment setting
    console.log('Braintree environment:', braintreeConfig.environment);
    // Should be 'sandbox' for testing, 'production' for live

    // Ensure credentials match environment
    // Sandbox credentials start with "sandbox_"
    // Production credentials are different
    ```

    Pre-launch checklist:

    * Switch to production credentials in WooCommerce
    * Update Braintree merchant ID to production
    * Enable production mode in Braintree settings
    * Test with real card (small amount)
    * Verify webhook URL is accessible from internet
    * Enable production fraud protection
    * Test 3D Secure with production cards
    * Monitor first few transactions closely
  </Accordion>

  <Accordion title="Device Data Not Collecting">
    **Problem**: Advanced Fraud Tools not working, device data missing.

    **Solution**: Properly initialize data collector:

    ```javascript theme={"system"}
    // Create data collector instance
    braintree.dataCollector.create({
        client: clientInstance,
        kount: true // Enable Kount fraud protection
    }).then(dataCollectorInstance => {
        // Get device data
        const deviceData = dataCollectorInstance.deviceData;
        console.log('Device data:', deviceData);

        // Include in payment submission
        return {
            nonce: payload.nonce,
            device_data: deviceData
        };
    }).catch(error => {
        console.error('Data collector error:', error);
        // Proceed without device data if necessary
    });
    ```

    Notes:

    * Device data improves fraud detection
    * May increase page load time slightly
    * Optional but recommended for production
    * Check Braintree dashboard to verify data is being received
  </Accordion>
</AccordionGroup>

### Debug Mode

Enable comprehensive logging for troubleshooting:

```javascript theme={"system"}
// Add detailed Braintree logging
const DEBUG = process.env.NODE_ENV === 'development';

if (DEBUG) {
    console.log('[Braintree] Creating client with token:', clientToken);

    braintree.client.create({
        authorization: clientToken
    }).then(clientInstance => {
        console.log('[Braintree] Client created successfully');
        return braintree.hostedFields.create({
            client: clientInstance,
            fields: fields
        });
    }).then(hostedFieldsInstance => {
        console.log('[Braintree] Hosted Fields created');

        // Log field events
        hostedFieldsInstance.on('validityChange', (event) => {
            console.log('[Braintree] Validity changed:', event);
        });

        hostedFieldsInstance.on('cardTypeChange', (event) => {
            console.log('[Braintree] Card type:', event.cards[0]?.type);
        });

        hostedFieldsInstance.on('empty', (event) => {
            console.log('[Braintree] Field emptied:', event.emittedBy);
        });

        hostedFieldsInstance.on('focus', (event) => {
            console.log('[Braintree] Field focused:', event.emittedBy);
        });
    }).catch(error => {
        console.error('[Braintree] Error:', error);
    });
}
```

In WooCommerce:

1. Go to WooCommerce → Settings → Payments → Braintree
2. Enable "Debug mode" or "Logging"
3. Check logs at WooCommerce → Status → Logs

### Getting Help

If issues persist:

1. **Braintree Support Portal**: Visit [Braintree Developer Documentation](https://developer.paypal.com/braintree/docs)
2. **Braintree Community**: Check [Braintree Community Forums](https://www.braintreepayments.com/community)
3. **WooCommerce Logs**: Review logs at WooCommerce → Status → Logs
4. **Braintree Dashboard**: Monitor transactions and error logs in your Braintree account
5. **Browser Console**: Always check for JavaScript errors and network issues

<Note>
  Always test your Braintree integration thoroughly using the sandbox environment before going live. Ensure your webhook endpoints are configured to handle transaction notifications and updates. Monitor your Braintree dashboard for declined transactions and implement appropriate retry logic.
</Note>
