Guest Checkout Guide ​
Guest checkout allows you to accept payments from users without requiring them to create an account. This is perfect for donations, one-time purchases, and reducing friction in the checkout process.
Overview ​
With guest checkout, you can:
- Accept payments without user authentication
- Collect minimal information (email, name, optional phone)
- Track guest payments by email
- Optionally convert guest customers to authenticated users later
Configuration ​
Guest checkout is enabled by default. Configure it in your environment:
bash
# Enable guest checkout (default: true)
GUEST_CHECKOUT_ENABLED=true
# Require email for guest checkout (default: true)
GUEST_REQUIRE_EMAIL=true
# Guest token expiration in seconds (default: 3600)
GUEST_TOKEN_EXPIRATION=3600Basic Guest Payment Flow ​
1. Create Payment Intent ​
typescript
const response = await fetch(
'https://your-instance.pubflow.com/bridge-payment/payments/intents',
{
method: 'POST',
headers: {
'Content-Type': 'application/json'
// No authentication required!
},
body: JSON.stringify({
subtotal_cents: 4500,
tax_cents: 500,
total_cents: 5000,
currency: 'USD',
concept: 'Donation',
description: 'Monthly donation to support our cause',
provider_id: 'stripe',
guest_data: {
email: 'donor@example.com',
name: 'John Doe',
phone: '+1234567890' // Optional
}
})
}
);
const { client_secret, id } = await response.json();2. Confirm Payment (Frontend) ​
typescript
import { loadStripe } from '@stripe/stripe-js';
const stripe = await loadStripe('pk_test_...');
const { error } = await stripe.confirmCardPayment(client_secret, {
payment_method: {
card: cardElement,
billing_details: {
email: 'donor@example.com',
name: 'John Doe'
}
}
});
if (error) {
console.error('Payment failed:', error.message);
} else {
console.log('Payment successful!');
}3. Retrieve Guest Payments ​
typescript
// List all payments for a guest email
const response = await fetch(
'https://your-instance.pubflow.com/bridge-payment/payments/guest/donor@example.com'
);
const payments = await response.json();Complete Example: Donation Form ​
React Component ​
tsx
import { useState } from 'react';
import { loadStripe } from '@stripe/stripe-js';
import { Elements, CardElement, useStripe, useElements } from '@stripe/react-stripe-js';
const stripePromise = loadStripe(process.env.NEXT_PUBLIC_STRIPE_KEY!);
function DonationForm() {
const stripe = useStripe();
const elements = useElements();
const [formData, setFormData] = useState({
email: '',
name: '',
amount: 50
});
const [loading, setLoading] = useState(false);
const [success, setSuccess] = useState(false);
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
if (!stripe || !elements) return;
setLoading(true);
try {
// 1. Create payment intent
const response = await fetch('/api/create-donation', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
amount: formData.amount * 100, // Convert to cents
guest_data: {
email: formData.email,
name: formData.name
}
})
});
const { client_secret } = await response.json();
// 2. Confirm payment
const { error } = await stripe.confirmCardPayment(client_secret, {
payment_method: {
card: elements.getElement(CardElement)!,
billing_details: {
email: formData.email,
name: formData.name
}
}
});
if (error) {
alert(`Payment failed: ${error.message}`);
} else {
setSuccess(true);
}
} catch (error) {
console.error('Error:', error);
alert('An error occurred. Please try again.');
} finally {
setLoading(false);
}
};
if (success) {
return (
<div className="success-message">
<h2>Thank you for your donation!</h2>
<p>A receipt has been sent to {formData.email}</p>
</div>
);
}
return (
<form onSubmit={handleSubmit}>
<div>
<label>Email</label>
<input
type="email"
value={formData.email}
onChange={(e) => setFormData({ ...formData, email: e.target.value })}
required
/>
</div>
<div>
<label>Name</label>
<input
type="text"
value={formData.name}
onChange={(e) => setFormData({ ...formData, name: e.target.value })}
required
/>
</div>
<div>
<label>Amount ($)</label>
<input
type="number"
value={formData.amount}
onChange={(e) => setFormData({ ...formData, amount: Number(e.target.value) })}
min="1"
required
/>
</div>
<div>
<label>Card Details</label>
<CardElement />
</div>
<button type="submit" disabled={!stripe || loading}>
{loading ? 'Processing...' : `Donate $${formData.amount}`}
</button>
</form>
);
}
export default function DonatePage() {
return (
<Elements stripe={stripePromise}>
<DonationForm />
</Elements>
);
}API Route (Next.js) ​
typescript
// app/api/create-donation/route.ts
export async function POST(request: Request) {
const { amount, guest_data } = await request.json();
const response = await fetch(
`${process.env.BRIDGE_PAYMENTS_URL}/bridge-payment/payments/intents`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
total_cents: amount,
currency: 'USD',
concept: 'Donation',
description: 'One-time donation',
provider_id: 'stripe',
guest_data
})
}
);
return response.json();
}Saving Payment Methods for Guests ​
Guests can save payment methods for future use:
typescript
const response = await fetch(
'https://your-instance.pubflow.com/bridge-payment/payments/intents',
{
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
total_cents: 5000,
currency: 'USD',
concept: 'First Donation',
setup_future_usage: 'off_session', // Save payment method
guest_data: {
email: 'donor@example.com',
name: 'John Doe'
}
})
}
);Best Practices ​
1. Collect Minimal Information ​
Only ask for email and name. Phone is optional.
2. Provide Clear Messaging ​
Explain that no account is required and their information is secure.
3. Send Confirmation Emails ​
Use external webhooks to send receipt emails to guest customers.
4. Track Guest Conversions ​
Monitor which guests convert to registered users.
5. Handle Errors Gracefully ​
Provide clear error messages and allow retry without re-entering information.
Security Considerations ​
- Guest emails are validated and normalized (lowercase)
- Guest tokens expire after configured time (default: 1 hour)
- Rate limiting applies to guest checkout to prevent abuse
- All guest payments are tracked and auditable
Converting Guests to Users ​
When a guest creates an account, you can link their previous payments:
typescript
// After user registration, update guest payments
const response = await fetch(
`${baseUrl}/bridge-payment/admin/link-guest-payments`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Session-ID': newUserSessionId
},
body: JSON.stringify({
guest_email: 'donor@example.com'
})
}
);Next Steps ​
- Saved Payment Methods - Store payment methods
- External Webhooks - Send confirmation emails
- Testing - Test guest checkout flows