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

# Hosted Checkout

> Integrate the secure hosted checkout page for browser-based payments

# Hosted Checkout Integration

The Payment Gateway provides a fully-hosted, PCI-compliant checkout page that handles card collection and payment processing. This eliminates your PCI compliance burden and provides a secure, optimized payment experience.

<Info>
  **Checkout URL:** `https://checkout-page-amber.vercel.app`

  Built with Next.js 15, React 19, and TypeScript
</Info>

***

## Why Use Hosted Checkout?

<CardGroup cols={2}>
  <Card title="Zero PCI Compliance Burden" icon="shield-check">
    Card data never touches your server

    No PCI-DSS compliance requirements for your infrastructure
  </Card>

  <Card title="Enterprise-Grade Security" icon="lock">
    Client-side validation with Luhn algorithm

    HTTPS enforcement, input sanitization, automatic expiration
  </Card>

  <Card title="Optimized Conversion" icon="chart-line">
    Mobile-first responsive design

    Real-time validation, clear error messages, smooth animations
  </Card>

  <Card title="Zero Maintenance" icon="wrench">
    Fully managed and updated

    Works across all modern browsers, automatic security patches
  </Card>
</CardGroup>

***

## Security Features

### PCI-DSS Compliance

<Warning>
  **Card Data Isolation:**
  Card numbers, CVV, and expiry dates **never** touch your server. All sensitive data is collected directly by the checkout page and sent to the Payment API over encrypted connections.
</Warning>

<AccordionGroup>
  <Accordion title="Client-Side Security" icon="browser">
    **Card Validation:**

    * Luhn algorithm validation (detects typos)
    * Expiry date validation (rejects expired cards)
    * CVV format validation (3-4 digits)
    * Real-time error feedback

    **Input Sanitization:**

    * Special characters blocked
    * Paste prevention on CVV field
    * Numeric-only inputs for card numbers
  </Accordion>

  <Accordion title="Transport Security" icon="shield-halved">
    **HTTPS Enforcement:**

    * TLS 1.3 encryption for all requests
    * Strict-Transport-Security headers
    * Content-Security-Policy enforcement

    **Authentication:**

    * Client secret validation (not API keys)
    * One-time use secrets per payment intent
    * Automatic expiration after 1 hour
  </Accordion>

  <Accordion title="Session Security" icon="clock">
    **Expiration Handling:**

    * Payment intents expire after 1 hour
    * Automatic redirect on expiration
    * Clear expiration messaging

    **Attempt Limiting:**

    * Maximum 5 payment attempts per intent
    * Progressive delay between attempts
    * Automatic intent invalidation after max attempts
  </Accordion>

  <Accordion title="Data Protection" icon="database">
    **No Data Persistence:**

    * Card data never logged or stored client-side
    * Tokenization happens server-side immediately
    * Only last 4 digits stored for receipts

    **Error Handling:**

    * Generic error messages (no sensitive data exposed)
    * No card details in error logs
    * Rate limiting on validation failures
  </Accordion>
</AccordionGroup>

***

## How It Works

### Payment Flow

```mermaid theme={null}
sequenceDiagram
    participant Merchant as Your Server
    participant Customer as Customer Browser
    participant Checkout as Checkout Page
    participant API as Payment API
    participant Token as Tokenization Service
    
    Note over Merchant,Token: 1. Create Payment Intent
    Merchant->>API: POST /payment-intents<br/>{amount, success_url}
    API-->>Merchant: {checkout_url, client_secret}
    
    Note over Merchant,Token: 2. Redirect to Checkout
    Merchant->>Customer: Redirect to checkout_url
    Customer->>Checkout: Load checkout page
    Checkout->>API: GET /payment-intents/:id<br/>(validate client_secret)
    API-->>Checkout: {amount, currency, status}
    
    Note over Merchant,Token: 3. Customer Enters Card
    Customer->>Checkout: Fill card details
    Checkout->>Checkout: Validate (Luhn, expiry, CVV)
    
    Note over Merchant,Token: 4. Submit Payment
    Checkout->>API: POST /payment-intents/:id/confirm<br/>{card, client_secret}
    API->>Token: Tokenize card (gRPC)
    Token-->>API: {token}
    API->>API: Process payment
    API-->>Checkout: {status: "authorized", redirect_url}
    
    Note over Merchant,Token: 5. Redirect Back
    Checkout->>Customer: Redirect to success_url
    Customer->>Merchant: Return to your site
```

***

## Integration Steps

<Steps>
  <Step title="Create Payment Intent (Server-Side)">
    Create a payment intent with your API key:

    ```javascript theme={null}
    const response = await fetch('https://paymentgateway.redahaloubi.com/api/v1/payment-intents', {
      method: 'POST',
      headers: {
        'X-API-Key': process.env.PAYMENT_GATEWAY_API_KEY,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        amount: 9999,
        currency: 'USD',
        success_url: 'https://yourstore.com/order/success?session_id={CHECKOUT_SESSION_ID}',
        cancel_url: 'https://yourstore.com/order/cancel',
        description: 'Order #12345'
      })
    });

    const { checkout_url, client_secret, id } = await response.json();
    ```
  </Step>

  <Step title="Redirect Customer to Checkout">
    Use the `checkout_url` from the response:

    ```javascript theme={null}
    // Option 1: Immediate redirect
    window.location.href = checkout_url;

    // Option 2: Button click
    <a href={checkout_url} className="btn btn-primary">
      Complete Payment
    </a>
    ```

    **Checkout URL format:**

    ```
    https://checkout-page-amber.vercel.app/checkout/{intent_id}?client_secret={secret}
    ```
  </Step>

  <Step title="Customer Completes Payment">
    The checkout page:

    * Validates payment intent hasn't expired
    * Displays order summary (amount, currency)
    * Collects card details with real-time validation
    * Submits payment to API with client\_secret
    * Shows success animation or error message
    * Redirects to your success/cancel URL
  </Step>

  <Step title="Handle Customer Return">
    Verify payment on your server:

    ```javascript theme={null}
    app.get('/order/success', async (req, res) => {
      const paymentIntentId = req.query.session_id;
      
      // Verify payment status server-side
      const response = await fetch(
        `https://paymentgateway.redahaloubi.com/api/v1/payment-intents/${paymentIntentId}`,
        {
          headers: { 'X-API-Key': process.env.PAYMENT_GATEWAY_API_KEY }
        }
      );
      
      const intent = await response.json();
      
      if (intent.data.status === 'authorized' || intent.data.status === 'captured') {
        // Payment successful - fulfill order
        await fulfillOrder(intent.data.payment_id);
        res.render('success', { order: intent.data });
      } else {
        // Payment incomplete
        res.redirect('/order/cancel');
      }
    });
    ```
  </Step>
</Steps>

***

## Checkout Page Features

### User Experience

<CardGroup cols={2}>
  <Card title="Responsive Design" icon="mobile">
    Mobile-first layout optimized for all screen sizes

    Touch-friendly inputs, large tap targets
  </Card>

  <Card title="Real-Time Validation" icon="check">
    Instant feedback on card input errors

    Clear, actionable error messages
  </Card>

  <Card title="Smart Card Detection" icon="credit-card">
    Automatic brand detection (Visa, Mastercard, etc.)

    Dynamic card logo display
  </Card>

  <Card title="Accessibility" icon="universal-access">
    WCAG 2.1 AA compliant

    Screen reader friendly, keyboard navigation
  </Card>
</CardGroup>

### Payment Summary Display

The checkout page automatically displays:

```
┌─────────────────────────────────────┐
│   Order #12345 - Premium Plan       │
│                                     │
│   Amount:           $99.99          │
│   Currency:         USD             │
│                                     │
│   [Card Number Input]               │
│   [Cardholder Name]                 │
│   [MM/YY]        [CVV]              │
│                                     │
│   [ Pay $99.99 ]                    │
│                                     │
│   🔒 Secured by Payment Gateway     │
└─────────────────────────────────────┘
```

***

## Supported Payment Methods

<Tabs>
  <Tab title="Credit Cards">
    **Visa**

    * All Visa cards supported
    * Test card: 4242 4242 4242 4242

    **Mastercard**

    * All Mastercard types supported
    * Test card: 5555 5555 5555 4444

    **Card Requirements:**

    * 13-19 digit card number
    * Valid expiry date (MM/YY format)
    * 3-4 digit CVV/CVC code
    * Cardholder name
  </Tab>

  <Tab title="Validation Rules">
    **Card Number:**

    * Luhn algorithm validation
    * Automatic formatting (spaces every 4 digits)
    * Brand detection (Visa, Mastercard)

    **Expiry Date:**

    * Must be future date
    * MM/YY format enforced
    * Automatic slash insertion

    **CVV:**

    * 3 digits (Visa, Mastercard)
    * 4 digits (Amex - future support)
    * Paste disabled for security

    **Cardholder Name:**

    * Minimum 3 characters
    * Letters and spaces only
    * Title case formatting
  </Tab>

  <Tab title="Test Cards">
    **Successful Payments:**

    ```
    Visa:       4242 4242 4242 4242
    Mastercard: 5555 5555 5555 4444
    Expiry:     Any future date (e.g., 12/2027)
    CVV:        Any 3 digits (e.g., 123)
    ```

    **Declined Scenarios:**

    ```
    Insufficient Funds: 4000 0000 0000 9995
    Expired Card:       4000 0000 0000 0069
    CVV Mismatch:       4000 0000 0000 0127
    Generic Decline:    4000 0000 0000 0002
    ```

    See [Payment API Test Cards](/api/payment-api#test-cards) for complete list.
  </Tab>
</Tabs>

***

## Error Handling

### Payment Intent Errors

<AccordionGroup>
  <Accordion title="Intent Expired (410 Gone)">
    **Displayed Message:**

    ```
    This payment session has expired
    Please return to merchant and try again
    ```

    **Action:**

    * Automatic redirect to `cancel_url` after 5 seconds
    * "Return to Merchant" button shown immediately

    **Cause:**
    Payment intent created more than 1 hour ago
  </Accordion>

  <Accordion title="Invalid Client Secret (401 Unauthorized)">
    **Displayed Message:**

    ```
    Invalid or expired payment session
    Please contact merchant support
    ```

    **Action:**

    * Show support contact information
    * Log error for merchant review

    **Cause:**

    * Tampered client\_secret parameter
    * Client secret from different payment intent
  </Accordion>

  <Accordion title="Intent Already Completed (422 Unprocessable)">
    **Displayed Message:**

    ```
    This payment has already been completed
    Redirecting to confirmation page...
    ```

    **Action:**

    * Redirect to `success_url` immediately

    **Cause:**

    * Customer refreshed page after successful payment
    * Attempting to pay twice with same intent
  </Accordion>

  <Accordion title="Maximum Attempts Reached (410 Gone)">
    **Displayed Message:**

    ```
    Too many payment attempts
    Please create a new payment session
    ```

    **Action:**

    * Redirect to `cancel_url`
    * Show "Contact Support" button

    **Cause:**

    * 5 failed payment attempts on this intent
  </Accordion>
</AccordionGroup>

### Card Validation Errors

<CardGroup cols={2}>
  <Card title="Invalid Card Number" icon="credit-card">
    **Error:** "Invalid card number"

    **Triggers:**

    * Luhn check fails
    * Less than 13 or more than 19 digits
    * Non-numeric characters
  </Card>

  <Card title="Expired Card" icon="calendar-xmark">
    **Error:** "Card has expired"

    **Triggers:**

    * Expiry date in the past
    * Invalid month (>12)
  </Card>

  <Card title="Invalid CVV" icon="hashtag">
    **Error:** "Invalid security code"

    **Triggers:**

    * Less than 3 digits
    * Non-numeric characters
  </Card>

  <Card title="Missing Name" icon="user">
    **Error:** "Cardholder name required"

    **Triggers:**

    * Empty field
    * Less than 3 characters
  </Card>
</CardGroup>

### Payment Declined Errors

```javascript theme={null}
// Example error response structure
{
  "success": false,
  "error": {
    "code": "PAYMENT_DECLINED",
    "message": "Card declined: insufficient funds",
    "remaining_attempts": 4
  }
}
```

**User-Friendly Messages:**

| Error Code          | Displayed Message                                                | Action                 |
| ------------------- | ---------------------------------------------------------------- | ---------------------- |
| PAYMENT\_DECLINED   | "Your card was declined. Please try a different payment method." | Show retry button      |
| INSUFFICIENT\_FUNDS | "Insufficient funds. Please use a different card."               | Show retry button      |
| CVV\_MISMATCH       | "Security code is incorrect. Please check and try again."        | Highlight CVV field    |
| EXPIRED\_CARD       | "This card has expired. Please use a different card."            | Highlight expiry field |

***

## Customization Options

### URL Configuration

<ParamField path="success_url" type="string" required>
  Where to redirect customer after successful payment

  **Placeholder:** Use `{CHECKOUT_SESSION_ID}` to include payment intent ID

  **Example:**

  ```
  https://yourstore.com/order/success?session_id={CHECKOUT_SESSION_ID}&order=12345
  ```

  **Result:**

  ```
  https://yourstore.com/order/success?session_id=pi_abc123&order=12345
  ```
</ParamField>

<ParamField path="cancel_url" type="string">
  Where to redirect customer on cancellation or failure

  **Example:**

  ```
  https://yourstore.com/order/cancel
  ```

  **Triggers:**

  * Customer clicks "Cancel" button
  * Payment intent expires
  * Maximum attempts reached
</ParamField>

### Metadata

Pass custom data that appears on receipts and webhooks:

```json theme={null}
{
  "amount": 9999,
  "currency": "USD",
  "description": "Order #12345 - Premium Plan",
  "metadata": {
    "order_id": "12345",
    "customer_id": "cus_abc123",
    "sku": "PREMIUM_ANNUAL",
    "source": "web"
  }
}
```

**Metadata is:**

* Returned in webhook events
* Visible in payment dashboard
* Searchable for reporting
* Limited to 50 keys, 500 chars per value

***

## Best Practices

<AccordionGroup>
  <Accordion title="Always Verify Server-Side" icon="server">
    **Never trust client-side redirects alone.**

    ```javascript theme={null}
    // ❌ BAD: Trust the redirect
    app.get('/success', (req, res) => {
      res.render('order-success');
    });

    // ✅ GOOD: Verify payment status
    app.get('/success', async (req, res) => {
      const intent = await verifyPaymentIntent(req.query.session_id);
      if (intent.status === 'authorized') {
        await fulfillOrder(intent.payment_id);
        res.render('order-success');
      } else {
        res.redirect('/order/cancel');
      }
    });
    ```
  </Accordion>

  <Accordion title="Handle All Redirect Scenarios" icon="arrows-split-up-and-left">
    **Implement both success and cancel handlers.**

    ```javascript theme={null}
    // Success handler
    app.get('/order/success', handleSuccess);

    // Cancel handler (payment failed, expired, or user canceled)
    app.get('/order/cancel', handleCancellation);
    ```

    **Cancel reasons:**

    * Customer clicked "Cancel" button
    * Payment declined after max attempts
    * Payment intent expired
    * Customer abandoned checkout
  </Accordion>

  <Accordion title="Use Webhooks for Critical Logic" icon="webhook">
    **Don't rely solely on redirect for order fulfillment.**

    Redirects can be missed (browser crash, network issue). Use webhooks as the primary fulfillment trigger:

    ```javascript theme={null}
    app.post('/webhooks/payment', (req, res) => {
      const event = req.body;
      
      if (event.event === 'payment.authorized') {
        // Fulfill order (primary path)
        await fulfillOrder(event.data.payment_id);
      }
      
      res.status(200).send('OK');
    });
    ```

    Use redirects for user feedback only.
  </Accordion>

  <Accordion title="Create Intent Per Order" icon="list-ol">
    **One payment intent = one order.**

    ```javascript theme={null}
    // ❌ BAD: Reuse intent for multiple orders
    const intent = await createPaymentIntent({ amount: 9999 });
    // Store intent.id for later...

    // ✅ GOOD: New intent per order
    app.post('/checkout', async (req, res) => {
      const intent = await createPaymentIntent({
        amount: order.total,
        metadata: { order_id: order.id }
      });
      res.redirect(intent.checkout_url);
    });
    ```
  </Accordion>

  <Accordion title="Set Descriptive Descriptions" icon="file-text">
    **Help customers recognize charges.**

    ```javascript theme={null}
    // ❌ BAD: Generic description
    description: "Payment"

    // ✅ GOOD: Descriptive
    description: "Order #12345 - Premium Plan (Annual)"
    ```

    Appears on:

    * Checkout page header
    * Customer bank statement
    * Payment receipts
  </Accordion>
</AccordionGroup>

***

## Browser Support

<CardGroup cols={2}>
  <Card title="Desktop Browsers" icon="desktop">
    **Chrome/Edge:** 88+

    **Firefox:** 85+

    **Safari:** 14+
  </Card>

  <Card title="Mobile Browsers" icon="mobile-screen">
    **iOS Safari:** 14+

    **Chrome Mobile:** 88+

    **Samsung Internet:** 13+
  </Card>
</CardGroup>

**Features:**

* ES6+ JavaScript
* CSS Grid & Flexbox
* Fetch API
* Web Crypto API (for validation)

***

## FAQ

<AccordionGroup>
  <Accordion title="Can I customize the checkout page design?">
    **Currently:** No, the checkout page uses a standard design for all merchants.

    **Future:** We're working on customization options:

    * Custom logo upload
    * Brand color selection
    * Custom success messages

    **Contact sales** for enterprise customization.
  </Accordion>

  <Accordion title="What happens if a customer closes the browser?">
    **Short answer:** Nothing is charged.

    **Details:**

    * Payment intent remains in `created` status
    * Customer can return to same checkout URL (if not expired)
    * Intent auto-expires after 1 hour
    * No money is held or charged
  </Accordion>

  <Accordion title="Can I collect billing address on checkout?">
    **Currently:** No, checkout collects card details only.

    **Workaround:** Collect billing address on your order form before redirecting to checkout. Pass it in `metadata`:

    ```json theme={null}
    {
      "metadata": {
        "billing_address": "123 Main St, City, State 12345",
        "billing_country": "US"
      }
    }
    ```
  </Accordion>

  <Accordion title="How do I test without real cards?">
    **Use test cards** (see [Test Cards](#supported-payment-methods) section).

    All test cards work in production environment without charging real money. Results are simulated based on card number.
  </Accordion>

  <Accordion title="Can customers save cards for future purchases?">
    **Currently:** No, each payment requires new card entry.

    **Future:** Card-on-file and subscription features are planned for Q2 2026.
  </Accordion>
</AccordionGroup>

***

## Troubleshooting

<AccordionGroup>
  <Accordion title="Checkout page shows 'Invalid payment session'">
    **Causes:**

    * Payment intent ID in URL doesn't exist
    * Client secret doesn't match intent
    * Intent was created for different merchant

    **Solution:**

    * Verify `checkout_url` from payment intent response is used exactly
    * Don't modify URL parameters
    * Check API key belongs to correct merchant
  </Accordion>

  <Accordion title="'This payment session has expired' error">
    **Cause:** Payment intent created more than 1 hour ago.

    **Solution:**

    * Create new payment intent
    * Reduce time between intent creation and checkout redirect
    * Consider creating intent when customer clicks "Checkout" button, not when cart is created
  </Accordion>

  <Accordion title="Customer redirected but payment status is 'created'">
    **Causes:**

    * Customer clicked browser back button before completing payment
    * Payment was declined but customer was redirected to success\_url (shouldn't happen)

    **Solution:**

    * Always verify payment status server-side (see [Best Practices](#best-practices))
    * Check for `status: 'authorized'` or `status: 'captured'` before fulfilling
  </Accordion>

  <Accordion title="Test cards not working">
    **Causes:**

    * Using real card number in test mode
    * Incorrect expiry format
    * Test card not in [supported list](#supported-payment-methods)

    **Solution:**

    * Use exact test card numbers (4242 4242 4242 4242 for success)
    * Use any future expiry date (e.g., 12/2027)
    * Use any 3-digit CVV
  </Accordion>
</AccordionGroup>

***

## Next Steps

<CardGroup cols={2}>
  <Card title="Payment API Reference" icon="book" href="/api/payment-api">
    Complete Payment Intent API documentation
  </Card>

  <Card title="Quick Start Guide" icon="rocket" href="/get-started/quick-start">
    Create your first payment in 5 minutes
  </Card>

  <Card title="Webhooks Guide" icon="webhook" href="/api/payment-api#webhooks">
    Implement webhook handlers
  </Card>

  <Card title="CLI Tool" icon="terminal" href="/tools/cli">
    Test payments from command line
  </Card>
</CardGroup>

***

**Questions?** Contact support at [redahaloubi8@gmail.com](mailto:redahaloubi8@gmail.com)
