Start Checkout
Create checkout URLs without requiring prior user authentication. Automatically handles user creation and returns a payment URL.
Server-Side Only: This is a server-side API that requires authentication with your API key.
Overview
The Start Checkout API endpoint allows you to programmatically create payment sessions for your users. This is ideal for:
- E-commerce integration - Direct checkout from product pages
- Custom payment flows - Build your own checkout experience
- Automated billing - Programmatically start subscriptions
- Trial management - Include trial periods in checkout
API Reference
Endpoint
POST https://onlineapp.pro/api/v1/paywall/{paywallId}/start-checkout
Authentication
Include the x-api-key
header with your API key.
Get your API key
Get your paywall ID
Navigate to your paywall settings page and get the ID from the Paywall ID field:
Get price ID
Use the Get Prices API to retrieve available price IDs.
Request Body
{
"email": "user@example.com",
"priceId": "456",
"successUrl": "https://mysite.com/payment/success",
"errorUrl": "https://mysite.com/payment/error",
"shopUrl": "https://mysite.com/shop",
"ignoreActivePurchase": false,
"trial_days": 7,
"userMeta": {
"my-user-uuid": "pojfoih27938y50ujtb4ip1n2b",
"utm_source": "facebook"
}
}
Required Fields
Field | Type | Description |
---|---|---|
email | string | User’s email address |
priceId | string | Price ID from your paywall configuration |
successUrl | string | URL to redirect after successful payment |
errorUrl | string | URL to redirect after payment failure |
shopUrl | string | URL to redirect if user cancels checkout |
Optional Fields
Field | Type | Description |
---|---|---|
ignoreActivePurchase | boolean | Whether to ignore existing active purchases (default: false). By default, if a user already has an active subscription, calling start checkout returns a 409 error. To allow user subscription upgrades, pass this argument as true - the previous subscription will be cancelled after successful purchase. |
trial_days | number | Number of trial days to include in subscription |
userMeta | object | Custom metadata to associate with user (returned in webhooks) |
Response Format
{
"checkoutUrl": "https://checkout.stripe.com/c/pay/cs_test_...",
"userId": "uuid-of-user",
"acquiring": "stripe"
}
Basic Usage
Simple Checkout Creation
const createCheckout = async (userEmail, priceId) => {
const paywallId = '123';
try {
const response = await fetch(`https://onlineapp.pro/api/v1/paywall/${paywallId}/start-checkout`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-api-key': 'your-secret-api-key'
},
body: JSON.stringify({
email: userEmail,
priceId: priceId,
successUrl: 'https://mysite.com/payment/success',
errorUrl: 'https://mysite.com/payment/error',
shopUrl: 'https://mysite.com/shop'
})
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const data = await response.json();
return data;
} catch (error) {
console.error('Failed to create checkout:', error);
throw error;
}
};
// Usage
const checkout = await createCheckout('user@example.com', '456');
console.log('Checkout URL:', checkout.checkoutUrl);
Checkout with Trial
const createTrialCheckout = async (userEmail, priceId, trialDays = 7) => {
const paywallId = '123';
const response = await fetch(`https://onlineapp.pro/api/v1/paywall/${paywallId}/start-checkout`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-api-key': 'your-secret-api-key'
},
body: JSON.stringify({
email: userEmail,
priceId: priceId,
trial_days: trialDays,
successUrl: 'https://mysite.com/payment/success',
errorUrl: 'https://mysite.com/payment/error',
shopUrl: 'https://mysite.com/shop',
userMeta: {
trial_signup: 'true',
signup_source: 'landing_page'
}
})
});
return response.json();
};
Process Flow
Validate Parameters
- Check email format
- Verify price ID exists
- Validate URL formats
Authentication
- Verify API key
- Check paywall permissions
User Management
- Find existing user by email
- Create new user if needed
- Link user to paywall
Purchase Validation
- Check for active purchases (unless ignored)
- Validate price and paywall combination
Payment Session
- Create checkout session with payment processor
- Configure trial period if specified
- Set redirect URLs
Response
- Return checkout URL and user information
Error Handling
Common Error Responses
Status | Error | Description |
---|---|---|
400 | Bad Request | Missing or invalid parameters |
401 | Unauthorized | Invalid API key |
404 | Not Found | Paywall or price not found |
409 | Conflict | User has active purchase (when not ignored) |
500 | Internal Server Error | Payment processor error |
Error Handling Example
const handleCheckoutError = (error, response) => {
switch (response?.status) {
case 400:
return 'Please check your input and try again.';
case 401:
return 'Authentication failed. Please contact support.';
case 404:
return 'Selected plan is no longer available.';
case 409:
return 'You already have an active subscription.';
default:
return 'Something went wrong. Please try again later.';
}
};
Integration
Redirect to Checkout
// After receiving the checkout URL, redirect the user:
window.location.href = data.checkoutUrl;
// Or open in new tab:
window.open(data.checkoutUrl, '_blank');
React Component Example
import { useState } from 'react';
const CheckoutForm = ({ priceId, planName, trialDays = 0 }) => {
const [email, setEmail] = useState('');
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState('');
const handleSubmit = async (e) => {
e.preventDefault();
setIsLoading(true);
setError('');
try {
const response = await fetch('/api/create-checkout', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
email,
priceId,
trialDays,
userMeta: {
plan_selected: planName,
signup_page: window.location.pathname
}
})
});
const data = await response.json();
if (data.success) {
window.location.href = data.checkoutUrl;
} else {
setError(data.error || 'Failed to create checkout');
}
} catch (err) {
setError('Network error. Please try again.');
} finally {
setIsLoading(false);
}
};
return (
<form onSubmit={handleSubmit} className="checkout-form">
<h3>Subscribe to {planName}</h3>
{trialDays > 0 && (
<p className="trial-info">
Start with a {trialDays}-day free trial
</p>
)}
<div className="form-group">
<label htmlFor="email">Email Address</label>
<input
type="email"
id="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
required
placeholder="Enter your email"
/>
</div>
{error && (
<div className="error-message">
{error}
</div>
)}
<button
type="submit"
disabled={isLoading || !email}
className="checkout-button"
>
{isLoading ? 'Creating checkout...' :
trialDays > 0 ? 'Start Free Trial' : 'Subscribe Now'}
</button>
</form>
);
};
Payment Status Tracking
Webhooks Recommended: Webhooks provide the most reliable payment tracking, ensuring notification delivery even if users close their browsers after payment.
Webhook Integration
app.post('/webhook/paywall', express.raw({type: 'application/json'}), (req, res) => {
const event = req.body;
switch (event.type) {
case 'payment.completed':
console.log('Payment completed:', event.data);
updateUserSubscription(event.data.user.id, 'active');
break;
case 'subscription.created':
console.log('Subscription created:', event.data);
updateUserSubscription(event.data.user.id, 'active');
break;
case 'subscription.cancelled':
console.log('Subscription cancelled:', event.data);
updateUserSubscription(event.data.user.id, 'cancelled');
break;
case 'refund.created':
console.log('Refund created:', event.data);
handleRefund(event.data.user.id);
break;
}
res.json({received: true});
});
Security Guidelines
1. Secure API Key Storage
// Good: Server-side environment variable
const apiKey = process.env.PAYWALL_API_KEY;
// Bad: Client-side exposure
// const apiKey = 'sk_live_...'; // Never do this!
2. Input Validation
const validateCheckoutInput = (data) => {
const errors = [];
// Email validation
if (!data.email || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(data.email)) {
errors.push('Valid email is required');
}
// URL validation
const urlRegex = /^https?:\/\/.+/;
['successUrl', 'errorUrl', 'shopUrl'].forEach(field => {
if (data[field] && !urlRegex.test(data[field])) {
errors.push(`${field} must be a valid URL`);
}
});
return errors;
};
Best Practices
1. Always Use HTTPS
// Ensure all URLs use HTTPS
const ensureHttps = (url) => {
if (url.startsWith('http://')) {
return url.replace('http://', 'https://');
}
return url;
};
2. Handle User Experience
const createCheckoutWithUX = async (data) => {
// Show loading state
showLoadingSpinner('Creating checkout...');
try {
const result = await createCheckout(data);
// Show success message briefly before redirect
showSuccessMessage('Redirecting to payment...');
setTimeout(() => {
window.location.href = result.checkoutUrl;
}, 1000);
} catch (error) {
hideLoadingSpinner();
showErrorMessage('Failed to create checkout. Please try again.');
}
};
Next Steps
Last updated on