Two paths to checkout
Henry supports two checkout modes. Pick the one that fits your needs:
Hosted Checkout The checkoutUrl returned by cart.create is a fully hosted checkout page. Redirect, iframe, or popup. Henry collects shipping, payment, and tax.
Headless Checkout Call checkout.purchase from your server with card token and shipping info. Henry processes payment and places the order with the merchant. You own the UI completely.
Hosted Checkout
Overview
Henry’s Hosted Checkout is the fastest way to add commerce capabilities to your application. With our hosted checkout solution, you can accept payments without handling sensitive payment data or building complex UI components. Simply embed our checkout experience via iframe, webview, or redirect.
Perfect for : Startups and businesses that want to move fast and launch quickly.
Why Choose Hosted Checkout?
Launch in Minutes Pre-built UI components and hosted pages get you live fast
Zero PCI Burden We handle all payment security and compliance requirements
Mobile Ready Responsive design works seamlessly across all devices
How It Works
Hosted Checkout uses a simple redirect or embedded iframe approach:
Embed options
Full page redirect
Iframe embed
React Native
The simplest integration - just redirect the user to the checkout URL. // Server-side (e.g. Next.js API route)
const cart = await henry . cart . create ({ items });
res . redirect ( cart . data . checkoutUrl );
// Client-side
window . location . href = checkoutUrl ;
Keep users on your site with an embedded checkout experience. < div id = "checkout-container" >
< iframe
id = "henry-checkout"
src = "..."
width = "100%"
height = "700"
frameborder = "0"
allow = "payment"
style = "border-radius: 12px;"
></ iframe >
</ div >
const cart = await henry . cart . create ({ items });
document . getElementById ( 'henry-checkout' ). src = cart . data . checkoutUrl ;
// Listen for completion
window . addEventListener ( 'message' , event => {
if ( ! event . origin . endsWith ( '.henrylabs.ai' )) return ;
const { action , orderId } = event . data ?? {};
switch ( action ) {
case 'orderCompleted' :
console . log ( 'Order placed:' , orderId );
document . getElementById ( 'henry-checkout' ). style . display = 'none' ;
showOrderConfirmation ( orderId );
break ;
case 'checkoutClosed' :
document . getElementById ( 'checkout-container' ). style . display = 'none' ;
break ;
}
});
Use react-native-webview to embed Henry checkout in a mobile app. import { WebView } from 'react-native-webview' ;
function CheckoutScreen ({ checkoutUrl } : { checkoutUrl : string }) {
return (
< WebView
source = { { uri: checkoutUrl } }
onMessage = { event => {
const data = JSON . parse ( event . nativeEvent . data );
if ( data . action === 'orderCompleted' ) {
navigation . navigate ( 'OrderConfirmation' , { orderId: data . orderId });
}
} }
/>
);
}
Headless Checkout
Overview
Headless Checkout gives you full control over the buyer experience. You collect shipping and payment info in your own UI, then call Henry’s API server-side to process payment and place the order.
Headless checkout requires special enablement. Contact us to get set up.
Why Choose Headless Checkout?
Full UI Control Own every pixel of the checkout experience - no iframes, no redirects
Your Brand Seamlessly match your existing design system and user flows
Server-Side All payment processing happens on your server - no client-side exposure
How It Works
Initiate purchase
Track completion
Override quantities
Call cart.checkout.purchase from your server with the buyer’s shipping and card info. const purchase = await henry . cart . checkout . purchase ({
cartId: 'your-cart-uuid' ,
buyer: {
name: { firstName: 'Jane' , lastName: 'Doe' },
email: 'jane@example.com' ,
phone: '+19175551234' ,
shippingAddress: {
line1: '350 5th Ave' ,
line2: 'Floor 21' ,
city: 'New York' ,
province: 'NY' ,
postalCode: '10118' ,
countryCode: 'US' ,
},
card: {
nameOnCard: { firstName: 'Jane' , lastName: 'Doe' },
details: {
cardToken: '...' ,
},
// billingAddress optional - defaults to shippingAddress if omitted
},
},
});
console . log ( purchase . refId , purchase . status );
// → "ckp-ref_38lb3..." "pending"
Poll every ~2 seconds until the order reaches a terminal state. let order = purchase ;
while ( order . status === 'pending' || order . status === 'processing' ) {
await new Promise (( r ) => setTimeout ( r , 2000 ));
order = await henry . cart . checkout . pollPurchase ({ refId: purchase . refId });
}
if ( order . status === 'complete' ) {
const { subtotal , commissionFee , total } = order . result ! . costs ;
console . log ( `Order total: ${ total . amount } ${ total . currency } ` );
console . log ( 'Items:' , order . products );
} else {
console . error ( 'Order failed or cancelled:' , order . error );
}
Alternatively, skip polling entirely by attaching a webhook when you create the cart - Henry will fire it when the order completes. const cart = await henry . cart . create ({
items: [ ... ],
settings: {
events: [
{
type: 'order.purchase.complete' ,
data: [
{
type: 'send_webhook' ,
webhookUUID: 'your-webhook-uuid' ,
},
],
},
],
},
});
Pass overrideProducts to adjust quantities at purchase time without modifying the cart. await henry . cart . checkout . purchase ({
cartId ,
buyer: {
/* ... */
},
overrideProducts: {
'https://www.nike.com/t/air-max-270-shoes/AH8050-002' : 2 , // bump to 2
'https://www.adidas.com/us/ultraboost-22/GX5591.html' : null , // exclude
},
});
Next steps
Order Management List and track orders after checkout - filter by status, cartId, and more
Universal Cart Learn how to build and manage the cart before checkout