Browser Checkout
Open Stripe checkout in the user's browser
Browser Checkout
The simplest purchase flow opens Stripe Checkout in the user's browser. After payment, the license is automatically retrieved.
When to Use
- First-time customers who need to enter payment details
- Simple integrations with minimal UI requirements
- When you want Stripe to handle all PCI compliance
For returning customers with saved cards, consider Terminal Purchase instead.
How It Works
┌──────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Your CLI App │────▶│ Tuish API │────▶│ Stripe │
│ │ │ │ │ │
│ open_checkout() │ │ Creates │ │ Hosted │
│ wait_for_ │ │ session │ │ checkout │
│ checkout() │◀────│ │◀────│ │
└──────────────────┘ └─────────────────┘ └─────────────────┘
│ │
│ ┌────────────────┐ │
└───────────────────▶│ Browser │◀────────┘
│ (Stripe UI) │
└────────────────┘- SDK creates a checkout session via the API
- Browser opens with Stripe's hosted checkout page
- User completes payment in browser
- SDK polls for completion
- On success, license is saved and verified
Basic Usage
// 1. Create checkout session and open browser
const session = await tuish.purchaseInBrowser();
console.log('Opening browser for checkout...');
// 2. Wait for completion (polls server)
const result = await tuish.waitForCheckoutComplete(session.sessionId, {
onPoll: (status) => {
if (status === 'pending') {
console.log('Waiting for payment...');
}
},
});
if (result.valid) {
console.log('Purchase complete!', result.license);
} else {
console.log('Purchase failed:', result.reason);
}ctx := context.Background()
// 1. Create checkout session and open browser
session, err := sdk.PurchaseInBrowser(ctx, "")
if err != nil {
log.Fatal(err)
}
fmt.Printf("Complete your purchase at:\n%s\n", session.CheckoutURL)
// 2. Wait for checkout completion (polls every 2 seconds)
result, err := sdk.WaitForCheckoutComplete(ctx, session.SessionID, 0, 0)
if err != nil {
log.Fatal(err)
}
if result.Valid {
fmt.Println("Purchase complete! License activated.")
fmt.Printf("License ID: %s\n", result.License.ID)
} else {
fmt.Printf("Checkout failed: %s\n", result.Reason)
}// Check if already licensed
let result = tuish.check_license();
if !result.valid {
println!("Opening browser for purchase...");
// Open checkout in browser
let session = tuish.open_checkout(Some("user@example.com")).await?;
println!("Checkout URL: {}", session.checkout_url);
// Wait for completion (polls every 2 seconds)
println!("Waiting for purchase to complete...");
let result = tuish.wait_for_checkout(&session.session_id).await?;
if result.valid {
println!("Purchase complete! License activated.");
} else {
println!("Purchase was not completed.");
}
}# 1. Create checkout session and open browser
session = client.purchase_in_browser()
print(f"Complete your purchase at: {session.checkout_url}")
# 2. Wait for completion
result = client.wait_for_checkout_complete(session.session_id)
if result.valid:
print("Purchase complete! License activated.")
else:
print(f"Checkout failed: {result.reason}")Options
Pre-fill Customer Email
await tuish.purchaseInBrowser({
email: 'customer@example.com', // Pre-fill email field
});session, err := sdk.PurchaseInBrowser(ctx, "customer@example.com")let session = tuish.open_checkout(Some("customer@example.com")).await?;session = client.purchase_in_browser(email="customer@example.com")Get URL Without Opening Browser
const session = await tuish.purchaseInBrowser({
openBrowser: false, // Don't auto-open
});
console.log('Open this URL:', session.checkoutUrl);// PurchaseInBrowser returns the URL without opening browser
session, err := sdk.PurchaseInBrowser(ctx, "user@example.com")
if err != nil {
log.Fatal(err)
}
// Don't auto-open browser, just print the URL
fmt.Printf("Open this URL: %s\n", session.CheckoutURL)Note: The Go SDK's
PurchaseInBrowseralways returns the checkout URL. Browser opening may fail silently if no browser is available.
// Get URL without opening
let session = tuish.purchase_in_browser(Some("user@example.com")).await?;
println!("Please open: {}", session.checkout_url);
// Open manually if needed
open::that(&session.checkout_url)?;Custom Polling Options
await tuish.waitForCheckoutComplete(sessionId, {
pollIntervalMs: 3000, // Poll every 3 seconds
timeoutMs: 300000, // Timeout after 5 minutes
onPoll: (status) => {
console.log('Status:', status);
},
});result, err := sdk.WaitForCheckoutComplete(
ctx,
session.SessionID,
3*time.Second, // Poll every 3 seconds
5*time.Minute, // Timeout after 5 minutes
)Complete Example
import { Tuish } from '@tuish/sdk';
import ora from 'ora';
const tuish = new Tuish({ productId: 'prod_xxx', publicKey: '...' });
async function purchase() {
// Check if already licensed
const existing = await tuish.checkLicense();
if (existing.valid) {
console.log('Already licensed!');
return;
}
// Start checkout
const session = await tuish.purchaseInBrowser();
const spinner = ora('Complete payment in browser...').start();
const result = await tuish.waitForCheckoutComplete(session.sessionId, {
onPoll: (status) => {
if (status === 'complete') {
spinner.succeed('Payment complete!');
} else if (status === 'expired') {
spinner.fail('Session expired');
}
},
});
if (result.valid) {
console.log(`Licensed until: ${result.license?.expiresAt || 'forever'}`);
}
}package main
import (
"context"
"fmt"
"log"
"time"
tuish "github.com/tuish/sdk-go"
)
func main() {
sdk, err := tuish.New(tuish.Config{
ProductID: "prod_xxx",
PublicKey: "MCowBQYDK2VwAyEA...",
APIKey: "sk_live_xxx",
})
if err != nil {
log.Fatal(err)
}
ctx := context.Background()
// Check existing license
result, _ := sdk.CheckLicense(ctx)
if result.Valid {
fmt.Println("Already licensed!")
return
}
// Start purchase
fmt.Println("No license found. Starting purchase...")
session, err := sdk.PurchaseInBrowser(ctx, "")
if err != nil {
log.Fatal(err)
}
fmt.Println("Browser opened. Complete your purchase...")
fmt.Printf("URL: %s\n", session.CheckoutURL)
// Wait with timeout
ctx, cancel := context.WithTimeout(ctx, 10*time.Minute)
defer cancel()
result, err = sdk.WaitForCheckoutComplete(ctx, session.SessionID, 2*time.Second, 0)
if err != nil {
log.Fatal(err)
}
if result.Valid {
fmt.Printf("\nPurchase complete!\n")
fmt.Printf("License: %s\n", result.License.ID)
fmt.Printf("Features: %v\n", result.License.Features)
} else {
fmt.Printf("\nCheckout failed: %s\n", result.Reason)
}
}use tuish::{Tuish, TuishError};
use std::io::{self, Write};
#[tokio::main]
async fn main() -> Result<(), TuishError> {
let mut tuish = Tuish::builder()
.product_id("prod_xxx")
.public_key("MCowBQYDK2VwAyEA...")
.api_key("ak_xxx")
.build()?;
// Check existing license
let result = tuish.check_license();
if result.valid {
println!("License valid!");
return Ok(());
}
// Prompt for purchase
print!("No license found. Purchase now? (y/n): ");
io::stdout().flush()?;
let mut input = String::new();
io::stdin().read_line(&mut input)?;
if input.trim().to_lowercase() != "y" {
return Ok(());
}
// Get email
print!("Enter your email: ");
io::stdout().flush()?;
let mut email = String::new();
io::stdin().read_line(&mut email)?;
// Start checkout
println!("\nOpening browser for checkout...");
let session = tuish.open_checkout(Some(email.trim())).await?;
println!("If browser didn't open, visit:");
println!("{}\n", session.checkout_url);
// Wait for completion
println!("Waiting for purchase...");
let result = tuish.wait_for_checkout(&session.session_id).await?;
if result.valid {
println!("\nPurchase complete!");
if let Some(license) = &result.license {
println!("License ID: {}", license.id);
}
}
Ok(())
}Error Handling
try {
const session = await tuish.purchaseInBrowser();
const result = await tuish.waitForCheckoutComplete(session.sessionId);
} catch (error) {
if (error.code === 'network_error') {
console.log('Network error. Check your internet connection.');
} else if (error.code === 'session_expired') {
console.log('Session expired. Please try again.');
}
}match tuish.open_checkout(None).await {
Ok(session) => {
println!("Opened: {}", session.checkout_url);
}
Err(TuishError::NetworkError(msg)) => {
println!("Network error: {}", msg);
}
Err(TuishError::ApiError { status, message }) => {
println!("API error ({}): {}", status, message);
}
Err(e) => {
println!("Unexpected error: {}", e);
}
}Related Topics
- Terminal Purchase - For returning customers
- Choosing Your Flow - When to use each flow
- Checkout API - API documentation