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

# Using Redirects Instead of Webhooks

> Learn how to handle checkout completion using redirects instead of webhooks for simpler implementation and testing

# Using Redirects Instead of Webhooks

When creating checkout sessions with Flex, you have two options for handling successful payments:

1. **Webhooks** - Flex sends webhook events to your server when payments complete
2. **Redirects** - Users are redirected to your success URL, and you fetch the checkout session details

This guide covers how to use the redirect approach, which can be simpler to implement and test.

## Overview

Instead of setting up webhook endpoints, you can:

1. Create a checkout session with `success_url` and `cancel_url` parameters
2. Redirect users to the checkout page
3. When payment completes, users are redirected to your `success_url` with the checkout session ID as a query parameter
4. Fetch the checkout session details from your success page using the Flex API

## Creating a Checkout Session with Redirect URLs

When creating a checkout session, specify the URLs where users should be redirected:

```bash theme={null}
curl -X POST https://api.withflex.com/v1/checkout/sessions \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "success_url": "https://yoursite.com/success?session_id={CHECKOUT_SESSION_ID}",
    "cancel_url": "https://yoursite.com/cancel",
    "line_items": [{
      "price_id": "price_123",
      "quantity": 1
    }],
    "mode": "payment"
  }'
```

> **Important:** Include `{CHECKOUT_SESSION_ID}` in your success URL.\
> Flex will replace this with the actual checkout session ID when redirecting the user.

## Handling the Success Redirect

When a payment is successful, the user will be redirected to your success URL with the checkout session ID as a query parameter. On your success page, extract this ID and fetch the full checkout session details.

### Example: Node.js/Express

```javascript theme={null}
app.get('/success', async (req, res) => {
  const sessionId = req.query.session_id;
  
  if (!sessionId) {
    return res.status(400).send('Missing session_id parameter');
  }

  try {
    // Fetch the checkout session details
    const response = await fetch(`https://api.withflex.com/v1/checkout/sessions/${sessionId}`, {
      headers: {
        'Authorization': `Bearer ${YOUR_API_KEY}`,
        'Content-Type': 'application/json'
      }
    });

    const { checkout_session } = await response.json();
    
    // Check if payment was successful
    if (checkout_session.status === 'complete') {
      // Payment successful! Handle accordingly
      console.log('Payment completed:', checkout_session.id);
      console.log('Customer email:', checkout_session.customer_details?.email);
      console.log('Amount paid:', checkout_session.amount_total);
      
      // Update your database, send confirmation email, etc.
      await fulfillOrder(checkout_session);
      
      res.render('success', { session: checkout_session });
    } else {
      res.status(400).render('error', { 
        message: 'Payment not completed' 
      });
    }
  } catch (error) {
    console.error('Error fetching checkout session:', error);
    res.status(500).render('error', { 
      message: 'Failed to verify payment' 
    });
  }
});
```

### Example: Python/Django

```python theme={null}
from django.http import JsonResponse, HttpResponse
from django.shortcuts import render
import requests

def success_view(request):
    session_id = request.GET.get('session_id')
    
    if not session_id:
        return HttpResponse('Missing session_id parameter', status=400)
    
    try:
        # Fetch checkout session from Flex API
        response = requests.get(
            f'https://api.withflex.com/v1/checkout/sessions/{session_id}',
            headers={
                'Authorization': f'Bearer {YOUR_API_KEY}',
                'Content-Type': 'application/json'
            }
        )
        response.raise_for_status()
        
        data = response.json()
        checkout_session = data['checkout_session']
        
        if checkout_session['status'] == 'complete':
            # Payment successful - fulfill order
            fulfill_order(checkout_session)
            
            return render(request, 'success.html', {
                'session': checkout_session
            })
        else:
            return HttpResponse('Payment not completed', status=400)
            
    except requests.exceptions.RequestException as e:
        print(f'Error fetching checkout session: {e}')
        return HttpResponse('Failed to verify payment', status=500)
```

### Example: PHP

```php theme={null}
<?php
function handleSuccess() {
    $sessionId = $_GET['session_id'] ?? null;
    
    if (!$sessionId) {
        http_response_code(400);
        echo 'Missing session_id parameter';
        return;
    }
    
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, "https://api.withflex.com/v1/checkout/sessions/$sessionId");
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_HTTPHEADER, [
        'Authorization: Bearer ' . YOUR_API_KEY,
        'Content-Type: application/json'
    ]);
    
    $response = curl_exec($ch);
    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);
    
    if ($httpCode === 200) {
        $data = json_decode($response, true);
        $checkoutSession = $data['checkout_session'];
        
        if ($checkoutSession['status'] === 'complete') {
            // Payment successful
            fulfillOrder($checkoutSession);
            
            // Render success page
            include 'success.php';
        } else {
            http_response_code(400);
            echo 'Payment not completed';
        }
    } else {
        http_response_code(500);
        echo 'Failed to verify payment';
    }
}

handleSuccess();
?>
```

## API Endpoint Details

### GET /v1/checkout/sessions/{id}

Retrieves a checkout session by ID.

**URL**: `https://api.withflex.com/v1/checkout/sessions/{CHECKOUT_SESSION_ID}`

**Method**: `GET`

**Headers**:

* `Authorization: Bearer YOUR_API_KEY`
* `Content-Type: application/json`

**Response**:

```json theme={null}
{
  "checkout_session": {
    "id": "cs_1234567890",
    "status": "complete",
    "amount_total": 2000,
    "currency": "usd",
    "customer_details": {
      "email": "customer@example.com",
      "name": "John Doe"
    },
    "success_url": "https://yoursite.com/success?session_id=cs_1234567890",
    "cancel_url": "https://yoursite.com/cancel",
    "line_items": [...],
    "created": 1647887400,
    "expires_at": 1647973800
  }
}
```

**Key Fields**:

* `status`: Payment status (`open`, `complete`, `expired`)
* `amount_total`: Total amount in cents
* `customer_details`: Customer information
* `line_items`: Items purchased

## Handling Cancellations

Users who cancel the checkout process are redirected to your `cancel_url`. No checkout session ID is provided in this case.

```javascript theme={null}
app.get('/cancel', (req, res) => {
  res.render('cancelled', {
    message: 'Your payment was cancelled. You can try again.'
  });
});
```

## Security Considerations

### 1. Verify the Checkout Session

Always fetch the checkout session from the Flex API to verify its authenticity. Never trust data passed via URL parameters alone.

### 2. Check Payment Status

Ensure the checkout session status is `complete` before fulfilling orders:

```javascript theme={null}
if (checkout_session.status !== 'complete') {
  throw new Error('Payment not completed');
}
```

### 3. Prevent Duplicate Processing

Implement idempotency to prevent processing the same order multiple times:

```javascript theme={null}
// Check if order was already processed
const existingOrder = await Order.findOne({ 
  checkout_session_id: sessionId 
});

if (existingOrder) {
  return res.redirect('/already-processed');
}

// Process new order
await createOrder(checkout_session);
```

### 4. Handle Expired Sessions

Checkout sessions expire after 24 hours. Handle expired sessions gracefully:

```javascript theme={null}
if (checkout_session.status === 'expired') {
  return res.render('expired', {
    message: 'This checkout session has expired. Please start over.'
  });
}
```

## Testing

### Test Mode

Use test API keys to create test checkout sessions. Test payments will use Stripe's test card numbers.

### Local Development

For local testing, use tools like ngrok to create public URLs:

```bash theme={null}
# Start your local server
npm start  # or your preferred method

# In another terminal, expose your local server
ngrok http 3000

# Use the ngrok URLs in your checkout session
{
  "success_url": "https://abc123.ngrok.io/success?session_id={CHECKOUT_SESSION_ID}",
  "cancel_url": "https://abc123.ngrok.io/cancel"
}
```

## When to Use Redirects vs Webhooks

### Use Redirects When

* You want simple, quick implementation
* Your fulfillment process can happen synchronously
* You primarily need to handle successful payments
* You want to show immediate confirmation to users

***

### Use Webhooks When

* You need to handle all payment events (failed, disputed, etc.)
* You have complex background processing requirements
* You need to handle payments that complete after the user leaves your site
* You want to decouple payment processing from user sessions

## Combining Both Approaches

You can use both redirects and webhooks together:

* Use redirects for immediate user feedback and basic order fulfillment
* Use webhooks for comprehensive event handling and background processing

This provides the best user experience while ensuring robust payment processing.

***

<div className="bg-gray-50 border border-gray-200 rounded-lg p-4 mt-8">
  <p className="text-gray-600 text-sm">
    <strong>Need help?</strong> If you have questions about implementing redirects or need assistance with your integration, please reach out to our support team.
  </p>
</div>
