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:
Quick Start
Get up and running in four simple steps:
import HenrySDK from "@henrylabs/sdk" ;
const client = new HenrySDK ({
apiKey: process . env . HENRY_API_KEY ! ,
});
Search Products
Find products from supported merchants: const { data : products } = await client . products . search ({
query: 'running shoes' ,
});
// Display products to user
Initialize Cart
Add products to the cart using our simple API: await client . cart . items . add ({
'x-user-id' : 'user_123' ,
productsDetails: [
{
productId: 'product_123' ,
name: 'Premium Sneakers' ,
price: '129.99' ,
quantity: 1 ,
productLink: 'https://store.com/sneakers' ,
productImageLink: 'https://image.url' ,
metadata: {
Size: '10' ,
Color: 'Black' ,
},
},
],
});
Create Checkout Session
Initiate checkout to get the hosted checkout URL: const checkout = await client . cart . createCheckout ({
'x-user-id' : 'user_123' ,
});
const checkoutUrl = checkout . data . checkout_url ;
Redirect to Checkout
Send your user to the hosted checkout page: // Option 1: Full page redirect
window . location . href = checkoutUrl ;
// Option 2: Hosted iframe
const iframe = document . createElement ( 'iframe' );
iframe . src = checkoutUrl ;
iframe . style . width = '100%' ;
iframe . style . height = '600px' ;
document . getElementById ( 'checkout-container' )?. appendChild ( iframe );
// Option 3: Modal popup
window . open ( checkoutUrl , 'checkout' , 'width=600,height=800' );
Preview Checkout Totals (Optional)
Use the session APIs to preview taxes and shipping before opening the hosted experience. Collect the buyer’s shipping details in your app, generate a quote, and read the normalized address if you want to display it back to the shopper.
const quote = await client . checkout . session . createQuote ({
"x-user-id" : "user_123" ,
shippingDetails: {
fullName: "Jamie Merchant" ,
email: "[email protected] " ,
phoneNumber: "+19175551234" ,
addressLine1: "350 5th Ave" ,
city: "New York" ,
stateOrProvince: "NY" ,
postalCode: "10118" ,
countryCode: "US" ,
},
});
const sessionToken = quote . data . session_token ;
const orderMetadata = quote . data . order_metadata ;
// Optional: show Henry-calculated totals before redirecting the buyer
console . log ( orderMetadata . total_price , orderMetadata . tax );
const shipping = await client . checkout . session . retrieveShippingInfo ({
"x-session-token" : sessionToken ,
});
const { shippingDetails , hasShipping } = shipping . data ;
if ( hasShipping ) {
console . log ( "Validated shipping:" , shippingDetails );
}
Integration Methods
Method 1: Redirect
The simplest integration - redirect users to Henry’s hosted checkout:
async function handleCheckout () {
const checkout = await client . cart . createCheckout ({
"x-user-id" : "user_123" ,
});
window . location . href = checkout . data . checkout_url ;
}
Method 2: Hosted Iframe
Keep users on your site with an embedded checkout:
< div id = "checkout-container" >
< iframe
id = "henry-checkout"
src = "{checkoutUrl}"
width = "100%"
height = "600"
frameborder = "0"
allow = "payment"
>
</ iframe >
</ div >
// Listen for checkout events
window . addEventListener ( "message" , ( event ) => {
// Verify the origin for security
if (
event . origin !== "https://checkout.henrylabs.ai" &&
event . origin !== "https://checkout.sandbox.henrylabs.ai"
) {
return ;
}
const payload = event . data as {
action ?: string ;
orderId ?: string ;
status ?: string ;
} | null ;
if ( ! payload ?. action ) return ;
const { status , action , orderId } = payload ;
// Handle checkout events
switch ( action ) {
case "orderCompleted" :
console . log ( `Order ${ orderId } completed with status: ${ status } ` );
handleOrderSuccess ( orderId );
break ;
case "checkoutClosed" :
console . log ( `Checkout closed with status: ${ status } ` );
handleCheckoutClosed ();
break ;
}
});
Balance between redirect and embedded:
async function openCheckoutModal () {
const checkout = await client . cart . createCheckout ({
"x-user-id" : "user_123" ,
});
const checkoutUrl = checkout . data . checkout_url ;
const modal = window . open (
checkoutUrl ,
"henry-checkout" ,
"width=600,height=800,left=200,top=100" ,
);
// Listen for checkout events
window . addEventListener ( "message" , ( event ) => {
if (
event . origin !== "https://checkout.henrylabs.ai" &&
event . origin !== "https://checkout.sandbox.henrylabs.ai"
) {
return ;
}
const payload = event . data as { action ?: string ; orderId ?: string } | null ;
if ( ! payload ?. action ) return ;
if ( payload . action === "orderCompleted" ) {
modal ?. close ();
handleOrderSuccess ( payload . orderId );
} else if ( payload . action === "checkoutClosed" ) {
modal ?. close ();
handleCheckoutCancelled ();
}
});
}
Event Handling
When using iframe or popup integration, Henry sends postMessage events to communicate checkout status:
Available Events
Implementation Example
// Track all checkout events
const eventLog : Array <{
action : string ;
orderId ?: string ;
status : string ;
timestamp : string ;
}> = [];
window . addEventListener ( "message" , ( event ) => {
// Security: Verify the origin
if (
event . origin !== "https://checkout.henrylabs.ai" &&
event . origin !== "https://checkout.sandbox.henrylabs.ai"
)
return ;
if ( event . data ) {
const { status , action , orderId } = event . data ;
// Log all events from iframe
if ( action === "orderCompleted" || action === "checkoutClosed" ) {
const newEvent = {
action ,
orderId ,
status ,
timestamp: new Date (). toLocaleTimeString (),
};
eventLog . push ( newEvent );
console . log (
"ACTION:" ,
JSON . stringify ({
action ,
orderId ,
status ,
}),
);
// Handle specific events
if ( action === "orderCompleted" ) {
// Order successful - show confirmation
showOrderConfirmation ( orderId );
// Optionally close/hide the iframe
document . getElementById ( "henry-checkout" ) ! . style . display = "none" ;
} else if ( action === "checkoutClosed" ) {
// User closed checkout - clean up UI
hideCheckoutModal ();
}
}
}
});
Always verify the event origin to prevent security vulnerabilities. Only
accept messages from Henry’s checkout domains: -
https://checkout.henrylabs.ai (production) -
https://checkout.sandbox.henrylabs.ai (sandbox)
Handling Results
Track order status after checkout:
// Check order status
async function getOrderStatus ( orderId : string ) {
const response = await client . orders . retrieveStatus ( orderId );
return response . data ;
}
// Poll for updates
function pollOrderStatus (
orderId : string ,
callback : ( order : Awaited < ReturnType < typeof getOrderStatus >>) => void ,
) {
const interval = setInterval ( async () => {
const order = await getOrderStatus ( orderId );
callback ( order );
if ([ "delivered" , "cancelled" , "failed" ]. includes ( order . status )) {
clearInterval ( interval );
}
}, 5000 );
return () => clearInterval ( interval );
}
Webhook support for real-time updates is coming soon.
Mobile Integration
Swift
import WebKit
class CheckoutViewController : UIViewController {
@IBOutlet weak var webView: WKWebView !
func loadCheckout ( url : String ) {
if let checkoutURL = URL ( string : url) {
webView. load ( URLRequest ( url : checkoutURL))
}
}
}
Kotlin
class CheckoutActivity : AppCompatActivity () {
private lateinit var webView: WebView
fun loadCheckout (url: String ) {
webView. loadUrl (url)
webView.settings.javaScriptEnabled = true
}
}
React Native
import { WebView } from "react-native-webview" ;
function CheckoutScreen ({ checkoutUrl }) {
return (
< WebView
source = {{ uri : checkoutUrl }}
onMessage = {(event) => {
const data = JSON . parse ( event . nativeEvent . data );
if ( data . status === "complete" ) {
navigation . navigate ( "Success" );
}
}}
/>
);
}
Next Steps
Ready to implement Hosted Checkout?
Test Integration
Use test mode to validate your implementation
Go Live
Switch to production API key when ready
Hosted Checkout is the recommended starting point for most businesses. You can
always upgrade to Headless Checkout as your needs grow (requires Pro tier).