Documentation Index Fetch the complete documentation index at: https://docs.withgale.com/llms.txt
Use this file to discover all available pages before exploring further.
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
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
Customer adds items to cart on your website
Your site creates a checkout session via POST /v2/checkout
Gale API returns a checkout_url
Your site embeds the checkout URL in an iframe
Customer completes payment in the iframe (stays on your domain)
Gale processes the payment securely
Gale sends webhook notification with order.created event
Your site shows confirmation to customer
Implementation Steps
Create Checkout Session via API
When customer is ready to checkout, create a checkout session with their items
Embed the Checkout Iframe
Use the returned checkout_url in an iframe on your checkout page
Handle Payment Confirmation
Listen for webhooks or postMessage events to confirm payment
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.
All monetary amounts are integers in cents (e.g., 2995 = $29.95).
{
"customer" : {
"email" : "customer@example.com" ,
"first_name" : "Jane" ,
"last_name" : "Doe" ,
"phone" : "+1-555-123-4567"
},
"reference_id" : "your-cart-123" ,
"line_items" : [
{
"product_id" : "PROD-001" ,
"name" : "Digital Thermometer" ,
"quantity" : 1 ,
"price" : 2995 ,
"currency" : "USD"
}
],
"shipping_info" : {
"address_line_1" : "123 Main St" ,
"city" : "New York" ,
"state" : "NY" ,
"postal_code" : "10001" ,
"country" : "US"
},
"shipping" : 995 ,
"tax" : 245 ,
"success_url" : "https://yoursite.com/checkout/success" ,
"failure_url" : "https://yoursite.com/checkout/cancel" ,
"metadata" : {
"platform" : "your_platform"
}
}
Response:
{
"checkout_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:
<! DOCTYPE html >
< html >
< head >
< title > Checkout - Your Store </ title >
< style >
#gale-checkout {
width : 100 % ;
height : 600 px ;
border : none ;
border-radius : 8 px ;
}
.checkout-container {
max-width : 800 px ;
margin : 0 auto ;
padding : 20 px ;
}
</ 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 >
import { useEffect , useState } from 'react' ;
function GaleCheckout ({ cartId }) {
const [ checkoutUrl , setCheckoutUrl ] = useState ( null );
useEffect (() => {
// Create checkout session
const createCheckout = async () => {
const response = await fetch ( 'https://api.withgale.com/v2/checkout' , {
method: 'POST' ,
headers: {
'Authorization' : `Bearer ${ process . env . GALE_API_KEY } ` ,
'Content-Type' : 'application/json'
},
body: JSON . stringify ({
// checkout data
})
});
const { checkout_url } = await response . json ();
setCheckoutUrl ( checkout_url );
};
createCheckout ();
}, []);
useEffect (() => {
// Listen for checkout events
const handleMessage = ( event ) => {
if ( event . origin !== 'https://checkout.withgale.com' ) return ;
if ( event . data . type === 'checkout.completed' ) {
// Handle success
window . location . href = `/success?order= ${ event . data . order_id } ` ;
}
};
window . addEventListener ( 'message' , handleMessage );
return () => window . removeEventListener ( 'message' , handleMessage );
}, []);
if ( ! checkoutUrl ) return < div > Loading checkout... </ div > ;
return (
< div className = "checkout-container" >
< h1 > Complete Your Purchase </ h1 >
< iframe
src = { checkoutUrl }
width = "100%"
height = "600px"
allow = "payment"
style = { { border: 'none' , borderRadius: '8px' } }
/>
</ div >
);
}
export default GaleCheckout ;
'use client' ;
import { useEffect , useState } from 'react' ;
import { useRouter } from 'next/navigation' ;
export default function CheckoutPage () {
const [ checkoutUrl , setCheckoutUrl ] = useState < string | null >( null );
const router = useRouter ();
useEffect (() => {
// Create checkout session on mount
const initCheckout = async () => {
const response = await fetch ( '/api/create-checkout' , {
method: 'POST' ,
body: JSON . stringify ({
items: [ ... ], // your cart items
customer: { ... } // customer info
})
});
const { checkout_url } = await response . json ();
setCheckoutUrl ( checkout_url );
};
initCheckout ();
}, []);
useEffect (() => {
const handleCheckoutEvent = ( event : MessageEvent ) => {
if ( event . origin !== 'https://checkout.withgale.com' ) return ;
switch ( event . data . type ) {
case 'checkout.completed' :
router . push ( `/success?order= ${ event . data . order_id } ` );
break ;
case 'checkout.cancelled' :
router . push ( '/checkout/cancel' );
break ;
}
};
window . addEventListener ( 'message' , handleCheckoutEvent );
return () => window . removeEventListener ( 'message' , handleCheckoutEvent );
}, [ router ]);
return (
< div className = "max-w-4xl mx-auto p-6" >
< h1 className = "text-2xl font-bold mb-6" > Secure Checkout </ h1 >
{ checkoutUrl ? (
< iframe
src = { checkoutUrl }
className = "w-full h-[600px] border-0 rounded-lg"
allow = "payment"
/>
) : (
< div > Loading... </ div >
) }
</ div >
);
}
Step 3: Handle Payment Confirmation
Option A: Webhooks (Recommended)
// /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 ;
}
});
Option C: Polling (Not Recommended)
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" : [
{
"product_id" : "MONTHLY_VITAMINS" ,
"name" : "Monthly Vitamin Subscription" ,
"quantity" : 1 ,
"price" : 2999 ,
"currency" : "USD"
}
],
"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 : 800 px ;
height : 650 px ;
border : 1 px solid #e5e7eb ;
border-radius : 12 px ;
box-shadow : 0 4 px 6 px rgba ( 0 , 0 , 0 , 0.1 );
}
/* Responsive */
@media ( max-width : 768 px ) {
#gale-checkout {
height : 700 px ;
}
}
Success/Failure URLs
Customize where customers land after checkout:
{
"success_url" : "https://yoursite.com/thank-you?session_id={CHECKOUT_SESSION_ID}" ,
"failure_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
// 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 => ({
product_id: item . id ,
name: item . title ,
price: Math . round ( item . price * 100 ), // Convert to cents
quantity: item . quantity ,
currency: 'USD'
})),
metadata: {
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
Create a test cart
Embed checkout iframe
Use test HSA card: 4111111111111111
Complete payment
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
Not receiving postMessage events
Solution: Verify origin check matches https://checkout.withgale.com exactly
Checkout shows 'Session Expired'
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
Webhooks Setup Configure real-time payment notifications
Custom API Need more control? Try custom API integration
Subscriptions Learn about subscription management
Test Cards Test your integration with test HSA cards
Support
Questions about embedded checkout?