Skip to main content

Embedded Checkout Integration

Embed Gale’s secure checkout directly on your website using an iframe. Customers stay on your domain while Gale handles payment processing, eligibility verification, and compliance.

When to Use Embedded Checkout

Perfect for you if:
  • You want customers to stay on your website during checkout
  • You need a branded checkout experience
  • You’re building a platform or multi-tenant solution
  • You want Gale to handle checkout UI and compliance
  • You can integrate via API to create checkout sessions

How It Works

  1. Customer adds items to cart on your website
  2. Your site creates a checkout session via POST /v2/checkout
  3. Gale API returns a checkout_url
  4. Your site embeds the checkout URL in an iframe
  5. Customer completes payment in the iframe (stays on your domain)
  6. Gale processes the payment securely
  7. Gale sends webhook notification with order.created event
  8. Your site shows confirmation to customer

Implementation Steps

1

Create Checkout Session via API

When customer is ready to checkout, create a checkout session with their items
2

Embed the Checkout Iframe

Use the returned checkout_url in an iframe on your checkout page
3

Handle Payment Confirmation

Listen for webhooks or postMessage events to confirm payment
4

Fulfill the Order

Process the order and show confirmation to customer

Step 1: Create Checkout Session

Create a checkout session with customer info and line items:
POST /v2/checkout
{
  "customer": {
    "email": "customer@example.com",
    "first_name": "Jane",
    "last_name": "Doe",
    "phone": "+1-555-123-4567"
  },
  "line_items": [
    {
      "merchant_product_id": "PROD-001",
      "name": "Digital Thermometer",
      "quantity": 1,
      "price_cents": 2995
    }
  ],
  "shipping_info": {
    "address_line_1": "123 Main St",
    "city": "New York",
    "state": "NY",
    "zip": "10001",
    "country": "US"
  },
  "amounts": {
    "shipping_cents": 995,
    "tax_cents": 245
  },
  "success_url": "https://yoursite.com/checkout/success",
  "cancel_url": "https://yoursite.com/checkout/cancel",
  "metadata": {
    "internal_order_id": "your-cart-123"
  }
}
Response:
{
  "id": "checkout_abc123xyz",
  "checkout_url": "https://checkout.withgale.com/c/checkout_abc123xyz",
  "status": "open",
  "expires_at": "2025-10-19T14:30:00Z",
  "created_at": "2025-10-18T14:30:00Z"
}

Step 2: Embed Checkout

Use the checkout_url in an iframe on your page:
  • HTML
  • React
  • Next.js
<!DOCTYPE html>
<html>
<head>
  <title>Checkout - Your Store</title>
  <style>
    #gale-checkout {
      width: 100%;
      height: 600px;
      border: none;
      border-radius: 8px;
    }

    .checkout-container {
      max-width: 800px;
      margin: 0 auto;
      padding: 20px;
    }
  </style>
</head>
<body>
  <div class="checkout-container">
    <h1>Complete Your Purchase</h1>
    <iframe
      id="gale-checkout"
      src="https://checkout.withgale.com/c/checkout_abc123xyz"
      allow="payment">
    </iframe>
  </div>

  <script>
    // Listen for checkout completion
    window.addEventListener('message', (event) => {
      if (event.origin !== 'https://checkout.withgale.com') return;

      if (event.data.type === 'checkout.completed') {
        // Redirect to success page
        window.location.href = '/checkout/success?order_id=' + event.data.order_id;
      }

      if (event.data.type === 'checkout.cancelled') {
        // Handle cancellation
        window.location.href = '/checkout/cancel';
      }
    });
  </script>
</body>
</html>

Step 3: Handle Payment Confirmation

// /api/webhooks/gale
app.post('/webhooks/gale', async (req, res) => {
  const event = req.body;

  // Verify webhook signature
  const isValid = verifyGaleSignature(
    req.headers['x-gale-signature'],
    req.body
  );

  if (!isValid) {
    return res.status(401).send('Invalid signature');
  }

  switch (event.type) {
    case 'order.created':
      // Order was created successfully
      const { id, customer, line_items } = event.data;

      await fulfillOrder(id, line_items);
      await sendConfirmationEmail(customer.email);
      break;

    case 'checkout.failed':
      // Payment failed
      await handlePaymentFailure(event.data.checkout_id);
      break;
  }

  res.status(200).send('OK');
});
Learn more about Webhooks →

Option B: PostMessage Events

Listen for iframe postMessage events:
window.addEventListener('message', (event) => {
  if (event.origin !== 'https://checkout.withgale.com') return;

  switch (event.data.type) {
    case 'checkout.completed':
      // Payment successful
      console.log('Checkout ID:', event.data.checkout_id);
      console.log('Order ID:', event.data.order_id);

      // Show success message or redirect
      window.location.href = '/success';
      break;

    case 'checkout.cancelled':
      // Customer cancelled
      window.location.href = '/checkout';
      break;

    case 'checkout.error':
      // Error occurred
      console.error('Checkout error:', event.data.error);
      break;
  }
});
Check order status via webhooks instead of polling. If you must poll, use the Order API:
const checkOrderStatus = async (orderId) => {
  const response = await fetch(
    `https://api.withgale.com/v2/orders/${orderId}`,
    {
      headers: {
        'Authorization': `Bearer ${API_KEY}`
      }
    }
  );

  const order = await response.json();

  if (order.status === 'completed') {
    // Payment completed
    return true;
  }

  return false;
};

Subscription Support

Embedded checkout fully supports subscriptions:
{
  "line_items": [
    {
      "merchant_product_id": "MONTHLY_VITAMINS",
      "name": "Monthly Vitamin Subscription",
      "quantity": 1,
      "price_cents": 2999
    }
  ],
  "payment_type": "subscription",
  "subscription": {
    "interval": "monthly",
    "interval_count": 1,
    "trial_period_days": 7
  }
}
Customers will see subscription terms in the checkout and agree to recurring billing.

Customization

Iframe Styling

#gale-checkout {
  width: 100%;
  max-width: 800px;
  height: 650px;
  border: 1px solid #e5e7eb;
  border-radius: 12px;
  box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}

/* Responsive */
@media (max-width: 768px) {
  #gale-checkout {
    height: 700px;
  }
}

Success/Cancel URLs

Customize where customers land after checkout:
{
  "success_url": "https://yoursite.com/thank-you?session_id={CHECKOUT_SESSION_ID}",
  "cancel_url": "https://yoursite.com/checkout/cancel"
}
Variables available:
  • {CHECKOUT_SESSION_ID} - Checkout session identifier
  • {ORDER_ID} - Order identifier (after payment)
  • {CUSTOMER_EMAIL} - Customer’s email

Platform Integration Examples

Multi-Tenant Platform

// Create checkout for specific merchant/store
const createMerchantCheckout = async (merchantId, storeId, checkoutData) => {
  const response = await fetch('https://api.withgale.com/v2/checkout', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${getMerchantApiKey(merchantId)}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      ...checkoutData,
      metadata: {
        merchant_id: merchantId,
        store_id: storeId,
        platform: 'your_platform_name'
      }
    })
  });

  return response.json();
};

Shopify App Example

// Shopify app creating embedded checkout
app.post('/shopify/create-checkout', async (req, res) => {
  const { shop, cart } = req.body;

  // Create Gale checkout session
  const galeCheckout = await fetch('https://api.withgale.com/v2/checkout', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${getShopApiKey(shop)}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      customer: {
        email: cart.customer.email,
        first_name: cart.customer.firstName,
        last_name: cart.customer.lastName,
        phone: cart.customer.phone
      },
      line_items: cart.lineItems.map(item => ({
        merchant_product_id: item.id,
        name: item.title,
        price_cents: Math.round(item.price * 100), // Convert to cents
        quantity: item.quantity
      })),
      metadata: {
        shopify_cart_id: cart.id,
        shop: shop
      }
    })
  });

  const checkout = await galeCheckout.json();
  res.json({ checkout_url: checkout.checkout_url });
});

Security Best Practices

Verify Webhooks

Always verify webhook signatures before processing

Use HTTPS

Only embed checkout on HTTPS pages

Validate Origins

Check event.origin in postMessage listeners

Secure API Keys

Never expose API keys in frontend code

Testing

Test Mode

Use test API keys for development:
Authorization: Bearer glm_test_YOUR_TEST_KEY

Test the Flow

  1. Create a test cart
  2. Embed checkout iframe
  3. Use test HSA card: 4111111111111111
  4. Complete payment
  5. Verify webhook received
See all test cards →

Troubleshooting

Possible causes:
  • Incorrect checkout_url
  • Missing allow="payment" attribute
  • HTTPS required
  • Content Security Policy blocking iframe
Solution: Check browser console for errors, ensure HTTPS, add CSP exception
Solution: Verify origin check matches https://checkout.withgale.com exactly
Solution: Checkout sessions expire after 24 hours. Create a new checkout session.
Solution: Some mobile browsers have iframe limitations. Consider using redirect flow (payment links) for mobile.

API Reference

Complete API documentation:

Next Steps

Support

Questions about embedded checkout?