> ## 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 Razorpay

> Complete guide to integrating Razorpay Standard Checkout 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 Razorpay with CoCart Preview API. Requires CoCart v4.6+ and a configured Razorpay payment gateway.
</Note>

## Overview

Razorpay integration with CoCart uses Razorpay Checkout for secure payment processing with automatic tokenization. This ensures PCI DSS Level 1 compliance while providing access to 100+ payment methods popular in India and Southeast Asia.

Razorpay is the leading payment gateway in India, serving over 10 million businesses with support for UPI, cards, wallets, net banking, and EMI.

## Prerequisites

Before implementing Razorpay checkout, ensure you have:

1. Razorpay payment gateway configured in WooCommerce
2. Razorpay API keys (Key ID and Key Secret)
3. A valid cart with items added
4. Customer billing address information
5. Razorpay account (test mode available)

## Integration Flow

<Steps>
  <Step title="Create Order">
    Generate a Razorpay order from your server
  </Step>

  <Step title="Load Razorpay Checkout">
    Initialize the Razorpay Checkout JavaScript library
  </Step>

  <Step title="Configure Checkout">
    Set up checkout options with order details
  </Step>

  <Step title="Open Checkout Modal">
    Display the Razorpay payment modal to customer
  </Step>

  <Step title="Handle Payment Response">
    Capture payment details and signature
  </Step>

  <Step title="Complete Checkout">
    Submit payment details to CoCart for verification
  </Step>
</Steps>

## Step 1: Load Razorpay Checkout

Include the Razorpay Checkout script in your page:

```html theme={"system"}
<!-- Load Razorpay Checkout -->
<script src="https://checkout.razorpay.com/v1/checkout.js"></script>
```

<Note>
  The Razorpay Checkout library is always served over HTTPS and automatically stays up to date.
</Note>

## Step 2: HTML Structure

Create a checkout form for customer information:

```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" required>
        <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="Postal Code" required>
        <select name="billing_country" required>
            <option value="IN">India</option>
            <option value="MY">Malaysia</option>
            <option value="SG">Singapore</option>
            <option value="AE">United Arab Emirates</option>
            <!-- Add other supported countries -->
        </select>
    </div>

    <!-- Order Summary -->
    <div class="order-summary">
        <h3>Order Summary</h3>
        <div id="cart-items"></div>
        <div class="total">
            <strong>Total:</strong>
            <span id="order-total">₹0.00</span>
        </div>
    </div>

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

    <button type="submit" id="submit-button">
        <span id="button-text">Pay with Razorpay</span>
        <span id="button-spinner" class="spinner" style="display: none;"></span>
    </button>
</form>
```

## Step 3: Create Razorpay Order

Request a Razorpay order from your server via CoCart:

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

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

    const context = await response.json();

    // Context contains:
    // - order_id: Razorpay order ID
    // - amount: Order amount in smallest currency unit (paise for INR)
    // - currency: Currency code (INR, MYR, etc.)
    // - key_id: Your Razorpay Key ID
    // - name: Store name
    // - description: Order description
    // - image: Store logo URL

    return context;
}
```

## Step 4: Initialize Razorpay Checkout

Set up and configure Razorpay Checkout:

```javascript theme={"system"}
async function setupRazorpayCheckout() {
    try {
        // Get form data
        const form = document.getElementById('checkout-form');
        const formData = new FormData(form);
        const billingAddress = getBillingAddressFromForm(formData);

        // Create Razorpay order
        const orderContext = await createRazorpayOrder();

        // Configure Razorpay options
        const options = {
            key: orderContext.key_id,
            amount: orderContext.amount,
            currency: orderContext.currency,
            name: orderContext.name || 'Your Store Name',
            description: orderContext.description || 'Order Payment',
            image: orderContext.image || '',
            order_id: orderContext.order_id,

            // Pre-fill customer information
            prefill: {
                name: `${billingAddress.first_name} ${billingAddress.last_name}`,
                email: billingAddress.email,
                contact: billingAddress.phone
            },

            // Customer notes
            notes: {
                address: billingAddress.address_1,
                city: billingAddress.city,
                state: billingAddress.state,
                postal_code: billingAddress.postcode
            },

            // Theme customization
            theme: {
                color: '#6a42d7' // Match your brand color
            },

            // Payment success handler
            handler: async function(response) {
                await handleRazorpaySuccess(response, billingAddress);
            },

            // Modal callbacks
            modal: {
                ondismiss: function() {
                    console.log('Razorpay checkout cancelled');
                    showError('Payment cancelled. Please try again.');
                },
                // Escape key handler
                escape: true,
                // Animation
                animation: true
            },

            // Retry settings for failed payments
            retry: {
                enabled: true,
                max_count: 3
            }
        };

        // Create Razorpay instance
        const razorpay = new Razorpay(options);

        // Handle payment failure
        razorpay.on('payment.failed', function(response) {
            handleRazorpayFailure(response);
        });

        // Open Razorpay checkout
        razorpay.open();

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

## Step 5: Handle Payment Success

Process successful payment response:

```javascript theme={"system"}
async function handleRazorpaySuccess(response, billingAddress) {
    try {
        // Response contains:
        // - razorpay_payment_id: Payment ID
        // - razorpay_order_id: Order ID
        // - razorpay_signature: Signature for verification

        console.log('Payment successful:', response.razorpay_payment_id);

        // Prepare payment data
        const paymentData = {
            razorpay_payment_id: response.razorpay_payment_id,
            razorpay_order_id: response.razorpay_order_id,
            razorpay_signature: response.razorpay_signature
        };

        // Submit to CoCart for verification and order creation
        await processRazorpayCheckout(billingAddress, paymentData);

    } catch (error) {
        console.error('Payment processing error:', error);
        showError(error.message || 'Payment processing failed. Please contact support.');
    }
}

async function processRazorpayCheckout(billingAddress, paymentData) {
    const cartKey = localStorage.getItem('cart_key');

    const checkoutData = {
        billing_address: billingAddress,
        payment_method: 'razorpay',
        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 Razorpay errors
        if (result.data?.gateway_error) {
            throw new RazorpayError(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;
}
```

## Step 6: Handle Payment Failure

Manage failed payment scenarios:

```javascript theme={"system"}
function handleRazorpayFailure(response) {
    console.error('Payment failed:', response.error);

    // Error response contains:
    // - code: Error code
    // - description: Error description
    // - source: Error source (customer, business, etc.)
    // - step: Step where error occurred
    // - reason: Failure reason
    // - metadata: Additional metadata

    const error = response.error;
    let errorMessage = 'Payment failed. ';

    // Provide user-friendly error messages
    switch (error.code) {
        case 'BAD_REQUEST_ERROR':
            errorMessage += 'Invalid payment details. Please check and try again.';
            break;
        case 'GATEWAY_ERROR':
            errorMessage += 'Payment gateway error. Please try again.';
            break;
        case 'SERVER_ERROR':
            errorMessage += 'Server error. Please try again later.';
            break;
        case 'INSUFFICIENT_FUNDS':
            errorMessage += 'Insufficient funds. Please try a different payment method.';
            break;
        case 'INVALID_CARD':
            errorMessage += 'Invalid card details. Please check your card information.';
            break;
        default:
            errorMessage += error.description || 'Please try again.';
    }

    showError(errorMessage);
}

// Custom error class for Razorpay errors
class RazorpayError extends Error {
    constructor(gatewayError) {
        super(gatewayError.message || 'Payment processing failed');
        this.code = gatewayError.code;
        this.description = gatewayError.description;
        this.field = gatewayError.field;
    }

    getDisplayMessage() {
        // Return user-friendly error messages
        const errorMap = {
            'payment_verification_failed': 'Payment verification failed. Please contact support.',
            'payment_already_captured': 'This payment has already been processed.',
            'payment_cancelled': 'Payment was cancelled.',
            'invalid_signature': 'Invalid payment signature. Please try again.',
            'payment_timeout': 'Payment timed out. Please try again.'
        };

        return errorMap[this.code] || this.description || this.message;
    }
}
```

## Complete Integration Example

Here's a complete working implementation:

```javascript theme={"system"}
class RazorpayCheckout {
    constructor() {
        this.orderContext = null;
        this.razorpayInstance = null;
        this.formValid = false;
    }

    async initialize() {
        try {
            // Setup form validation
            this.setupFormValidation();

            // Setup form submission
            this.setupFormSubmission();

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

    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 fill in all required fields.');
                }

                // Create Razorpay order
                await this.createOrder();

                // Open Razorpay checkout
                await this.openCheckout();

            } catch (error) {
                console.error('Checkout error:', error);
                this.showError(error.message || 'Checkout failed. Please try again.');

                submitButton.disabled = false;
                submitButton.textContent = originalText;
            }
        });
    }

    async createOrder() {
        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: 'razorpay' })
        });

        const context = await response.json();

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

        this.orderContext = context;
        return context;
    }

    async openCheckout() {
        const form = document.getElementById('checkout-form');
        const formData = new FormData(form);
        const billingAddress = this.getBillingAddressFromForm(formData);

        const options = {
            key: this.orderContext.key_id,
            amount: this.orderContext.amount,
            currency: this.orderContext.currency,
            name: this.orderContext.name || 'Your Store',
            description: this.orderContext.description || 'Order Payment',
            image: this.orderContext.image || '',
            order_id: this.orderContext.order_id,

            prefill: {
                name: `${billingAddress.first_name} ${billingAddress.last_name}`,
                email: billingAddress.email,
                contact: billingAddress.phone
            },

            notes: {
                address: billingAddress.address_1,
                city: billingAddress.city,
                state: billingAddress.state,
                postal_code: billingAddress.postcode
            },

            theme: {
                color: '#6a42d7'
            },

            handler: (response) => this.handleSuccess(response, billingAddress),

            modal: {
                ondismiss: () => {
                    console.log('Payment cancelled');
                    this.showError('Payment cancelled. Please try again.');
                    this.resetForm();
                },
                escape: true,
                animation: true
            },

            retry: {
                enabled: true,
                max_count: 3
            }
        };

        this.razorpayInstance = new Razorpay(options);

        this.razorpayInstance.on('payment.failed', (response) => {
            this.handleFailure(response);
        });

        this.razorpayInstance.open();
    }

    async handleSuccess(response, billingAddress) {
        try {
            const paymentData = {
                razorpay_payment_id: response.razorpay_payment_id,
                razorpay_order_id: response.razorpay_order_id,
                razorpay_signature: response.razorpay_signature
            };

            await this.processCheckout(billingAddress, paymentData);

        } catch (error) {
            console.error('Payment processing error:', error);
            this.showError(error.message || 'Payment processing failed.');
            this.resetForm();
        }
    }

    handleFailure(response) {
        const error = response.error;
        let errorMessage = 'Payment failed. ';

        switch (error.code) {
            case 'BAD_REQUEST_ERROR':
                errorMessage += 'Invalid payment details.';
                break;
            case 'GATEWAY_ERROR':
                errorMessage += 'Payment gateway error.';
                break;
            case 'SERVER_ERROR':
                errorMessage += 'Server error. Please try again later.';
                break;
            default:
                errorMessage += error.description || 'Please try again.';
        }

        this.showError(errorMessage);
        this.resetForm();
    }

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

        const result = await response.json();

        if (!response.ok) {
            if (result.data?.gateway_error) {
                throw new RazorpayError(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 to thank you page
        if (result.payment_result?.redirect_url) {
            setTimeout(() => {
                window.location.href = result.payment_result.redirect_url;
            }, 2000);
        }
    }

    resetForm() {
        const submitButton = document.querySelector('#submit-button');
        submitButton.disabled = false;
        submitButton.textContent = 'Pay with Razorpay';
    }

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

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

## Styling

Add CSS for a polished checkout experience:

```css theme={"system"}
/* Billing Section */
.billing-section {
    margin-bottom: 2rem;
}

.billing-section input,
.billing-section select {
    width: 100%;
    padding: 10px;
    margin-bottom: 1rem;
    border: 1px solid #cbd5e0;
    border-radius: 4px;
    font-size: 14px;
}

.billing-section input:focus,
.billing-section select:focus {
    border-color: #6a42d7;
    outline: none;
    box-shadow: 0 0 0 3px rgba(106, 66, 215, 0.1);
}

/* Order Summary */
.order-summary {
    background: #f7fafc;
    padding: 1.5rem;
    border-radius: 8px;
    margin-bottom: 1.5rem;
}

.order-summary .total {
    display: flex;
    justify-content: space-between;
    margin-top: 1rem;
    padding-top: 1rem;
    border-top: 2px solid #cbd5e0;
    font-size: 1.25rem;
}

/* Submit Button */
#submit-button {
    width: 100%;
    padding: 14px;
    background: #6a42d7;
    color: white;
    border: none;
    border-radius: 4px;
    font-size: 16px;
    font-weight: 600;
    cursor: pointer;
    transition: background 0.2s ease;
}

#submit-button:hover:not(:disabled) {
    background: #5932d1;
}

#submit-button:disabled {
    opacity: 0.6;
    cursor: not-allowed;
}

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

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

/* Input validation */
input.error {
    border-color: #e53e3e;
}
```

## Testing

For development and testing with Razorpay:

### Test Mode Credentials

Use these credentials from your Razorpay Test Mode dashboard:

* **Key ID**: Starts with `rzp_test_`
* **Key Secret**: Use for server-side operations

### Test Payment Methods

**Test Card Numbers:**

* **Success**: `4111 1111 1111 1111` (Visa)
* **Success**: `5555 5555 5555 4444` (Mastercard)
* **Failure**: Any card number ending in `0002`

**Test Card Details:**

* **CVV**: Any 3 digits
* **Expiry**: Any future date
* **Cardholder**: Any name

**Test UPI IDs:**

* **Success**: `success@razorpay`
* **Failure**: `failure@razorpay`

**Test Net Banking:**

* Select any bank from test mode
* Use credentials provided in test interface

### Test Wallets

All wallet payments in test mode are automatically successful.

## Supported Payment Methods

Razorpay supports 100+ payment methods:

### Cards

* Credit Cards (Visa, Mastercard, RuPay, Amex)
* Debit Cards (All major banks)
* International Cards

### UPI

* Google Pay
* PhonePe
* Paytm
* BHIM UPI
* Other UPI apps

### Wallets

* Paytm
* PhonePe
* Mobikwik
* FreeCharge
* Amazon Pay

### Net Banking

* All major Indian banks
* Corporate net banking

### EMI

* Credit Card EMI
* Debit Card EMI
* Cardless EMI

### Other Methods

* PayLater (LazyPay, Simpl, etc.)
* NACH/eNACH
* Bank Transfer (NEFT/RTGS)

## Troubleshooting

### Common Issues

<AccordionGroup>
  <Accordion title="Razorpay Checkout Script Not Loading">
    **Problem**: Razorpay object is undefined.

    **Solution**:

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

    // Correct script
    <script src="https://checkout.razorpay.com/v1/checkout.js"></script>
    ```

    * Check browser console for errors
    * Verify no ad blockers blocking script
    * Ensure internet connectivity
  </Accordion>

  <Accordion title="Checkout Modal Not Opening">
    **Problem**: rzp.open() doesn't show modal.

    **Solution**:

    ```javascript theme={"system"}
    const rzp = new Razorpay(options);

    // Check for popup blockers
    try {
        rzp.open();
    } catch (error) {
        console.error('Modal error:', error);
        alert('Please disable popup blocker');
    }

    // Listen for errors
    rzp.on('payment.failed', function(response) {
        console.error('Payment failed:', response.error);
    });
    ```

    * Disable browser popup blockers
    * Ensure HTTPS is enabled
    * Verify all required options are provided
  </Accordion>

  <Accordion title="Payment Fails with Error Codes">
    **Problem**: Getting Razorpay error codes.

    **Solution**:

    ```javascript theme={"system"}
    handler: function(response) {
        console.log('Payment ID:', response.razorpay_payment_id);
        console.log('Order ID:', response.razorpay_order_id);
        console.log('Signature:', response.razorpay_signature);
    },
    modal: {
        ondismiss: function() {
            console.log('Modal dismissed by user');
        }
    }
    ```

    Common errors:

    * `BAD_REQUEST_ERROR`: Invalid parameters
    * `GATEWAY_ERROR`: Payment gateway issue
    * `SERVER_ERROR`: Razorpay server issue
    * `PAYMENT_DECLINED`: Card/bank declined
  </Accordion>

  <Accordion title="Payment Method Not Recognized">
    **Problem**: Backend doesn't accept Razorpay.

    **Solution**:

    ```javascript theme={"system"}
    const checkoutData = {
        payment_method: 'razorpay', // Correct ID
        payment_data: {
            razorpay_payment_id: response.razorpay_payment_id,
            razorpay_order_id: response.razorpay_order_id,
            razorpay_signature: response.razorpay_signature
        }
    };
    ```

    * Verify Razorpay gateway enabled in WooCommerce
    * Check API keys are configured
    * Ensure payment method ID is correct
  </Accordion>

  <Accordion title="Test Mode vs Live Mode">
    **Problem**: Works in test but not live.

    **Solution**:

    ```javascript theme={"system"}
    // Test mode: use test API key (starts with rzp_test_)
    // Live mode: use live API key (starts with rzp_live_)

    const options = {
        key: isTestMode ? 'rzp_test_xxx' : 'rzp_live_xxx',
        // ...
    };
    ```

    Checklist:

    * Switch to live API keys in WooCommerce
    * Verify account is activated
    * Complete KYC if required
    * Test with real card (small amount)
  </Accordion>

  <Accordion title="UPI Payment Issues">
    **Problem**: UPI payments failing.

    **Solution**:

    * Ensure customer has valid UPI ID
    * Verify UPI is enabled in Razorpay dashboard
    * Check UPI app is installed on device
    * Test with different UPI apps (GPay, PhonePe, Paytm)
    * Verify amount is within UPI limits
  </Accordion>
</AccordionGroup>

### Debug Mode

```javascript theme={"system"}
if (DEBUG) {
    console.log('[Razorpay] Key:', options.key);
    console.log('[Razorpay] Amount:', options.amount);
    console.log('[Razorpay] Currency:', options.currency);

    // Log all callbacks
    options.handler = function(response) {
        console.log('[Razorpay] Success:', response);
    };

    options.modal = {
        ondismiss: function() {
            console.log('[Razorpay] Modal dismissed');
        }
    };
}
```

### Getting Help

1. **Razorpay Documentation**: [Razorpay Checkout Docs](https://razorpay.com/docs/payments/payment-gateway/web-integration/)
2. **Razorpay Support**: Contact through dashboard
3. **WooCommerce Logs**: Check WooCommerce → Status → Logs

## Best Practices

<CardGroup cols={2}>
  <Card title="Security" icon="shield-check">
    * Always verify payment signature on server
    * Never expose Key Secret on frontend
    * Use HTTPS for all requests
    * Implement webhook verification
    * Store payment IDs securely
    * Enable fraud detection features
  </Card>

  <Card title="User Experience" icon="user">
    * Pre-fill customer information
    * Show clear order summary
    * Handle modal dismissal gracefully
    * Provide payment retry options
    * Display multiple payment methods
    * Support preferred languages
  </Card>

  <Card title="Compliance" icon="certificate">
    * PCI DSS Level 1 certified
    * RBI compliant tokenization
    * Implement proper error logging
    * Follow data retention policies
    * Enable auto-refunds when needed
    * Maintain transaction records
  </Card>

  <Card title="Performance" icon="gauge-high">
    * Load Checkout.js from CDN
    * Cache order contexts briefly
    * Implement proper timeouts
    * Handle network failures
    * Monitor success rates
    * Set up webhook endpoints
  </Card>
</CardGroup>

## Advanced Features

### Payment Capture

By default, payments are auto-captured. For manual capture:

```javascript theme={"system"}
// Create order with manual capture
const options = {
    // ... other options
    payment_capture: 0, // Manual capture

    handler: async function(response) {
        // Payment authorized but not captured
        // Capture later via API
        await capturePayment(response.razorpay_payment_id);
    }
};
```

### Recurring Payments

Set up subscriptions and recurring payments:

```javascript theme={"system"}
const options = {
    // ... other options
    recurring: 1,
    subscription_id: 'sub_xxxxxxxxxxxxx',

    handler: function(response) {
        // Handle subscription payment
    }
};
```

### Offers and Discounts

Apply offers during checkout:

```javascript theme={"system"}
const options = {
    // ... other options
    offers: {
        enabled: true
    },

    handler: function(response) {
        // Offer applied: response.razorpay_offer_id
    }
};
```

## Webhook Handling

Razorpay sends webhooks for payment events:

```javascript theme={"system"}
// Server-side webhook handler (example)
app.post('/webhooks/razorpay', async (req, res) => {
    const secret = process.env.RAZORPAY_WEBHOOK_SECRET;
    const signature = req.headers['x-razorpay-signature'];

    // Verify webhook signature
    const expectedSignature = crypto
        .createHmac('sha256', secret)
        .update(JSON.stringify(req.body))
        .digest('hex');

    if (signature === expectedSignature) {
        const event = req.body.event;
        const payment = req.body.payload.payment.entity;

        switch (event) {
            case 'payment.captured':
                await handlePaymentCaptured(payment);
                break;
            case 'payment.failed':
                await handlePaymentFailed(payment);
                break;
            case 'refund.created':
                await handleRefundCreated(payment);
                break;
        }

        res.sendStatus(200);
    } else {
        res.sendStatus(400);
    }
});
```

## Error Handling

Handle common Razorpay errors:

```javascript theme={"system"}
function handleRazorpayErrors(error) {
    const errorMap = {
        'BAD_REQUEST_ERROR': 'Invalid request. Please check your details.',
        'GATEWAY_ERROR': 'Payment gateway error. Please try again.',
        'SERVER_ERROR': 'Server error. Please try again later.',
        'TIMEOUT_ERROR': 'Request timed out. Please try again.',
        'INSUFFICIENT_FUNDS': 'Insufficient funds in your account.',
        'INVALID_CARD': 'Invalid card details.',
        'CARD_DECLINED': 'Card declined by bank.',
        'AUTHENTICATION_ERROR': 'Authentication failed.',
        'NETWORK_ERROR': 'Network error. Please check your connection.'
    };

    return errorMap[error.code] || error.description || 'Payment failed';
}
```

## Troubleshooting

Common issues and solutions:

**Checkout modal not opening**: Verify Checkout.js is loaded and Key ID is correct.

**Payment verification fails**: Ensure signature verification logic is implemented correctly on server.

**Webhook not received**: Check webhook URL configuration in Razorpay dashboard.

**Amount mismatch**: Razorpay amounts are in smallest currency unit (paise for INR).

**Order creation fails**: Verify API credentials and order amount is within limits.

<Note>
  Always test your Razorpay integration thoroughly using test mode before going live. Configure webhooks to handle asynchronous payment updates. Monitor your Razorpay dashboard for failed payments and disputes. Implement proper logging for debugging payment issues.
</Note>

## Additional Resources

* [Razorpay Documentation](https://razorpay.com/docs/)
* [Razorpay Dashboard](https://dashboard.razorpay.com/)
* [API Reference](https://razorpay.com/docs/api/)
* [Payment Methods Guide](https://razorpay.com/docs/payments/payment-methods/)
* [Test Cards & UPI](https://razorpay.com/docs/payments/payments/test-card-upi-details/)
* [Webhook Documentation](https://razorpay.com/docs/webhooks/)
