Checkout sessions provide a secure, hosted payment flow for your products. Create one-time or subscription checkout experiences that handle payment processing, tax calculation, and customer management.
Overview
Checkout sessions are temporary payment flows that:
Expire after 30 days (configurable)
Support multiple products and prices
Handle tax calculation automatically
Process payments via Stripe
Create orders and subscriptions upon success
Grant benefits automatically
Creating Checkout Sessions
Create checkout sessions programmatically via API or from checkout links.
const checkout = await polar . checkouts . custom . create ({
products: [ productId1 , productId2 ],
customer_email: "[email protected] " ,
success_url: "https://example.com/success?checkout_id={CHECKOUT_ID}" ,
metadata: {
user_id: "user_123"
}
});
// From your frontend (requires authentication)
const checkout = await polar . checkouts . custom . client . create ({
product_id: productId ,
customer_email: "[email protected] "
});
// Redirect to checkout
window . location . href = `https://polar.sh/checkout/ ${ checkout . client_secret } ` ;
Checkout Configuration
Products & Pricing
Configure which products customers can purchase:
{
products : [ productId1 , productId2 ], // Multiple products
currency : "usd" , // Optional: force currency
amount : 5000 , // Custom amount (for custom pricing only)
}
When multiple products are provided, customers can switch between them in the checkout UI.
Customer Pre-fill
Pre-populate customer information for faster checkout:
{
customer_id : existingCustomerId , // Link to existing customer
external_customer_id : "user_123" , // Or use your external ID
customer_email : "[email protected] " ,
customer_name : "Jane Doe" ,
customer_billing_address : {
line1 : "123 Main St" ,
city : "San Francisco" ,
state : "CA" ,
postal_code : "94102" ,
country : "US"
},
customer_tax_id : "GB123456789" , // VAT/Tax ID
customer_metadata : { // Copied to created customer
source : "website" ,
campaign : "summer_sale"
}
}
Billing Requirements
Standard
Full Address
Business
By default, customers only need to provide their country for tax calculation. { require_billing_address : false }
Require complete billing address (automatic for US customers). { require_billing_address : true }
Mark as business customer to require billing name and address. {
is_business_customer : true ,
customer_billing_name : "Acme Corp" ,
customer_tax_id : "GB123456789"
}
Seat-Based Configuration
For seat-based products, control the seat selection:
{
seats : 10 , // Pre-select 10 seats
min_seats : 5 , // Minimum selectable
max_seats : 50 // Maximum selectable
}
Trials
Control trial availability at checkout:
{
allow_trial : false , // Disable trial even if product has one
trial_interval : "day" , // Override product trial
trial_interval_count : 14 // 14-day trial
}
Customers can only redeem one trial per product. Attempting to use another trial will fail.
Discounts
Apply discounts or allow discount codes:
{
discount_id : discountId , // Pre-apply specific discount
allow_discount_codes : true // Allow customers to enter codes
}
Redirects
{
success_url : "https://example.com/success?checkout_id={CHECKOUT_ID}" ,
return_url : "https://example.com" , // Back button URL
embed_origin : "https://example.com" // For iframe embedding
}
Use {CHECKOUT_ID} in success_url to retrieve the checkout ID after success.
Checkout Flow
Create Session
Create a checkout session via API with product and customer details.
Customer Entry
Customer lands on checkout page and sees product details, pricing, and benefits.
Fill Details
Customer provides email, billing address, and payment information.
Tax Calculation
Taxes are calculated based on customer location and product type.
Payment Processing
Payment is processed via Stripe. 3DS authentication if required.
Confirmation
Order/subscription created
Benefits granted
Confirmation email sent
Redirect to success URL
Checkout Status
Checkouts progress through these statuses:
open
confirmed
succeeded
failed
expired
Initial state. Customer can complete checkout.
Payment confirmed, processing order/subscription.
Successfully completed. Order/subscription created.
Payment failed or processing error occurred.
Checkout expired (30 days by default).
Updating Checkout Sessions
Update open checkout sessions to modify configuration:
await polar . checkouts . custom . update ( checkoutId , {
customer_email: "[email protected] " ,
customer_billing_address: { /* ... */ },
amount: 7500 // Update custom amount
});
Only open checkouts can be updated. Confirmed, succeeded, or failed checkouts are immutable.
Client-Side Access
Access checkout sessions from your frontend using the client secret:
// Get checkout details
const checkout = await polar . checkouts . custom . client . get ( clientSecret );
// Update customer information
await polar . checkouts . custom . client . update ( clientSecret , {
customer_email: "[email protected] "
});
// Confirm payment (after Stripe payment intent)
await polar . checkouts . custom . client . confirm ( clientSecret , {
confirmation_token_id: stripeConfirmationToken
});
Embedding Checkout
Embed checkout in an iframe for seamless integration:
Set embed origin
const checkout = await polar . checkouts . custom . create ({
product_id: productId ,
embed_origin: "https://yourdomain.com"
});
Create iframe
< iframe
src = "https://polar.sh/checkout/{client_secret}"
width = "100%"
height = "800px"
frameborder = "0"
></ iframe >
Listen for events
window . addEventListener ( 'message' , ( event ) => {
if ( event . origin !== 'https://polar.sh' ) return ;
if ( event . data . type === 'checkout.succeeded' ) {
console . log ( 'Checkout completed!' , event . data . checkout_id );
}
});
Custom Fields
Collect additional data via custom fields attached to products:
// Custom field values are submitted during checkout
// and stored on the resulting order/subscription
{
custom_field_data : {
[ fieldId ]: "Customer's response"
}
}
Subscription Upgrades
Upgrade free subscriptions to paid tiers via checkout:
{
subscription_id : freeSubscriptionId ,
product_id : paidProductId
}
Only free subscriptions can be upgraded via checkout. Paid subscription changes use the subscriptions API.
Store custom data on checkout sessions:
{
metadata : {
order_source : "website" ,
affiliate_id : "aff_123" ,
campaign : "summer_2024"
}
}
Metadata is:
Copied to created orders and subscriptions
Included in webhooks
Searchable via API
Event Streaming
Subscribe to real-time checkout updates via Server-Sent Events:
const eventSource = new EventSource (
`https://api.polar.sh/v1/checkouts/custom/client/ ${ clientSecret } /stream`
);
eventSource . addEventListener ( 'checkout.updated' , ( event ) => {
const checkout = JSON . parse ( event . data );
console . log ( 'Status:' , checkout . status );
});
Payment Methods
Polar uses Stripe to process payments and supports:
Credit/debit cards (Visa, Mastercard, Amex, etc.)
Apple Pay & Google Pay
SEPA Direct Debit (EUR)
ACH Direct Debit (USD)
Link (Stripe’s one-click checkout)
Payment methods are automatically shown based on customer location and currency.
Tax Handling
Taxes are automatically calculated based on:
Customer billing address
Customer IP address (fallback)
Product tax category
Merchant of record jurisdiction
See Merchant of Record for tax details.
Error Handling
Status changes to failed. Customer can retry with a different payment method.
Already Active Subscription
Returns 403 if customer already has an active subscription to the product.
Returns 410. Create a new checkout session.
Returns 403 if customer attempts to use a trial they’ve already redeemed.
Returns 403 if the organization isn’t configured to accept payments.
Best Practices
Pre-fill email and billing information when possible to reduce friction and increase conversion.
Store tracking information (campaign IDs, affiliate codes, etc.) in metadata for attribution.
Always provide a success_url to redirect customers after payment. Include {CHECKOUT_ID} to retrieve details.
Listen for checkout.succeeded webhooks rather than relying solely on redirects.
Use sandbox mode to test the full checkout flow before going live.
API Reference
Create Checkout Create a new checkout session
Get Checkout Retrieve checkout details
Update Checkout Modify open checkout session
Confirm Checkout Process payment and complete