Subscriptions Guide ​
Complete guide for implementing recurring billing and subscription management with Bridge Payments.
Overview ​
Bridge Payments subscriptions support:
- ✅ Flexible Billing - Daily, weekly, monthly, quarterly, yearly
- ✅ Free Trials - Configurable trial periods
- ✅ Multiple Providers - Stripe, PayPal, Authorize.net (ARB)
- ✅ Automatic Billing - Hands-off recurring payments
- ✅ Subscription Management - Update, pause, cancel
- ✅ Webhooks - Real-time subscription events
- ✅ Guest Subscriptions - No account required
Creating Subscriptions ​
Basic Subscription ​
bash
curl -X POST "https://your-instance.pubflow.com/bridge-payment/subscriptions" \
-H "Content-Type: application/json" \
-H "X-Session-ID: session_abc123" \
-d '{
"customer_id": "cust_123",
"payment_method_id": "pm_123",
"provider_id": "stripe",
"total_cents": 2000,
"currency": "USD",
"billing_interval": "monthly",
"concept": "Premium Monthly Plan"
}'Response:
json
{
"id": "sub_123",
"customer_id": "cust_123",
"payment_method_id": "pm_123",
"provider_id": "stripe",
"provider_subscription_id": "sub_stripe_xyz",
"status": "active",
"total_cents": 2000,
"currency": "USD",
"billing_interval": "monthly",
"current_period_start": "2025-01-15T10:30:00Z",
"current_period_end": "2025-02-15T10:30:00Z",
"next_billing_date": "2025-02-15T10:30:00Z",
"concept": "Premium Monthly Plan",
"created_at": "2025-01-15T10:30:00Z"
}Subscription with Trial ​
bash
curl -X POST "https://your-instance.pubflow.com/bridge-payment/subscriptions" \
-H "Content-Type: application/json" \
-H "X-Session-ID: session_abc123" \
-d '{
"customer_id": "cust_123",
"payment_method_id": "pm_123",
"provider_id": "stripe",
"total_cents": 2000,
"currency": "USD",
"billing_interval": "monthly",
"trial_days": 14,
"concept": "Premium Plan with 14-day Trial"
}'Custom Billing Interval ​
bash
curl -X POST "https://your-instance.pubflow.com/bridge-payment/subscriptions" \
-H "Content-Type: application/json" \
-H "X-Session-ID: session_abc123" \
-d '{
"customer_id": "cust_123",
"payment_method_id": "pm_123",
"provider_id": "stripe",
"total_cents": 5000,
"currency": "USD",
"billing_interval": "monthly",
"interval_multiplier": 3,
"concept": "Quarterly Plan (every 3 months)"
}'Frontend Implementation ​
React Subscription Component ​
jsx
import { useState, useEffect } from 'react';
function SubscriptionCheckout({ planId, amount, interval }) {
const [paymentMethods, setPaymentMethods] = useState([]);
const [selectedMethod, setSelectedMethod] = useState('');
const [loading, setLoading] = useState(false);
useEffect(() => {
// Load saved payment methods
fetch('/bridge-payment/payment-methods', {
headers: { 'X-Session-ID': sessionId }
})
.then(res => res.json())
.then(data => {
setPaymentMethods(data);
const defaultMethod = data.find(m => m.is_default);
if (defaultMethod) {
setSelectedMethod(defaultMethod.id);
}
});
}, []);
const handleSubscribe = async () => {
setLoading(true);
try {
// Get or create customer
const customerResponse = await fetch('/bridge-payment/customers', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Session-ID': sessionId
},
body: JSON.stringify({
provider_id: 'stripe'
})
});
const customer = await customerResponse.json();
// Create subscription
const subscriptionResponse = await fetch('/bridge-payment/subscriptions', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Session-ID': sessionId
},
body: JSON.stringify({
customer_id: customer.id,
payment_method_id: selectedMethod,
provider_id: 'stripe',
total_cents: amount,
currency: 'USD',
billing_interval: interval,
trial_days: 14,
concept: `${planId} Plan`
})
});
const subscription = await subscriptionResponse.json();
if (subscription.status === 'active' || subscription.status === 'trialing') {
alert('Subscription created successfully!');
window.location.href = '/dashboard';
}
} catch (error) {
console.error('Error:', error);
alert('Failed to create subscription');
} finally {
setLoading(false);
}
};
return (
<div className="max-w-md mx-auto p-6">
<h2 className="text-2xl font-bold mb-6">Subscribe to {planId}</h2>
<div className="bg-gray-50 p-4 rounded-lg mb-6">
<div className="text-3xl font-bold mb-2">
${(amount / 100).toFixed(2)}
<span className="text-lg text-gray-600">/{interval}</span>
</div>
<div className="text-sm text-gray-600">
14-day free trial, then ${(amount / 100).toFixed(2)}/{interval}
</div>
</div>
<div className="mb-6">
<label className="block text-sm font-medium mb-2">
Payment Method
</label>
<select
value={selectedMethod}
onChange={(e) => setSelectedMethod(e.target.value)}
className="w-full p-3 border rounded"
>
<option value="">Select payment method</option>
{paymentMethods.map(method => (
<option key={method.id} value={method.id}>
{method.card_brand} •••• {method.card_last_four}
{method.alias && ` (${method.alias})`}
</option>
))}
</select>
</div>
<button
onClick={handleSubscribe}
disabled={!selectedMethod || loading}
className="w-full bg-blue-600 text-white py-3 rounded-lg font-semibold disabled:opacity-50"
>
{loading ? 'Processing...' : 'Start Free Trial'}
</button>
<p className="text-xs text-gray-600 mt-4 text-center">
You won't be charged until your trial ends. Cancel anytime.
</p>
</div>
);
}Managing Subscriptions ​
List User Subscriptions ​
bash
curl -X GET "https://your-instance.pubflow.com/bridge-payment/subscriptions" \
-H "X-Session-ID: session_abc123"Get Subscription Details ​
bash
curl -X GET "https://your-instance.pubflow.com/bridge-payment/subscriptions/sub_123" \
-H "X-Session-ID: session_abc123"Update Subscription ​
bash
# Update payment method
curl -X PUT "https://your-instance.pubflow.com/bridge-payment/subscriptions/sub_123" \
-H "Content-Type: application/json" \
-H "X-Session-ID: session_abc123" \
-d '{
"payment_method_id": "pm_new_456"
}'
# Update amount
curl -X PUT "https://your-instance.pubflow.com/bridge-payment/subscriptions/sub_123" \
-H "Content-Type: application/json" \
-H "X-Session-ID: session_abc123" \
-d '{
"total_cents": 3000
}'Cancel Subscription ​
bash
# Cancel at end of billing period
curl -X POST "https://your-instance.pubflow.com/bridge-payment/subscriptions/sub_123/cancel" \
-H "Content-Type: application/json" \
-H "X-Session-ID: session_abc123" \
-d '{
"cancel_at_period_end": true
}'
# Cancel immediately
curl -X POST "https://your-instance.pubflow.com/bridge-payment/subscriptions/sub_123/cancel" \
-H "Content-Type: application/json" \
-H "X-Session-ID: session_abc123" \
-d '{
"cancel_at_period_end": false
}'Reactivate Subscription ​
bash
curl -X POST "https://your-instance.pubflow.com/bridge-payment/subscriptions/sub_123/reactivate" \
-H "X-Session-ID: session_abc123"Subscription Management UI ​
jsx
function SubscriptionManager({ subscriptionId }) {
const [subscription, setSubscription] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch(`/bridge-payment/subscriptions/${subscriptionId}`, {
headers: { 'X-Session-ID': sessionId }
})
.then(res => res.json())
.then(data => {
setSubscription(data);
setLoading(false);
});
}, [subscriptionId]);
const handleCancel = async () => {
if (!confirm('Cancel subscription?')) return;
await fetch(`/bridge-payment/subscriptions/${subscriptionId}/cancel`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Session-ID': sessionId
},
body: JSON.stringify({
cancel_at_period_end: true
})
});
alert('Subscription will cancel at end of billing period');
window.location.reload();
};
const handleReactivate = async () => {
await fetch(`/bridge-payment/subscriptions/${subscriptionId}/reactivate`, {
method: 'POST',
headers: { 'X-Session-ID': sessionId }
});
alert('Subscription reactivated');
window.location.reload();
};
if (loading) return <div>Loading...</div>;
return (
<div className="max-w-2xl mx-auto p-6">
<h2 className="text-2xl font-bold mb-6">Subscription Details</h2>
<div className="bg-white border rounded-lg p-6 mb-6">
<div className="flex justify-between items-start mb-4">
<div>
<h3 className="text-xl font-semibold">{subscription.concept}</h3>
<p className="text-gray-600">
${(subscription.total_cents / 100).toFixed(2)}/{subscription.billing_interval}
</p>
</div>
<span className={`px-3 py-1 rounded text-sm ${
subscription.status === 'active' ? 'bg-green-100 text-green-800' :
subscription.status === 'trialing' ? 'bg-blue-100 text-blue-800' :
subscription.status === 'canceled' ? 'bg-red-100 text-red-800' :
'bg-gray-100 text-gray-800'
}`}>
{subscription.status}
</span>
</div>
<div className="space-y-2 text-sm">
<div className="flex justify-between">
<span className="text-gray-600">Next billing date:</span>
<span className="font-medium">
{new Date(subscription.next_billing_date).toLocaleDateString()}
</span>
</div>
{subscription.trial_end && (
<div className="flex justify-between">
<span className="text-gray-600">Trial ends:</span>
<span className="font-medium">
{new Date(subscription.trial_end).toLocaleDateString()}
</span>
</div>
)}
{subscription.cancel_at_period_end && (
<div className="bg-yellow-50 p-3 rounded mt-4">
<p className="text-sm text-yellow-800">
Subscription will cancel on {new Date(subscription.current_period_end).toLocaleDateString()}
</p>
</div>
)}
</div>
</div>
<div className="flex gap-3">
{subscription.status === 'active' && !subscription.cancel_at_period_end && (
<button
onClick={handleCancel}
className="px-4 py-2 border border-red-600 text-red-600 rounded hover:bg-red-50"
>
Cancel Subscription
</button>
)}
{subscription.cancel_at_period_end && (
<button
onClick={handleReactivate}
className="px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700"
>
Reactivate Subscription
</button>
)}
</div>
</div>
);
}Webhook Events ​
Handle subscription events:
javascript
// Subscription created
{
"event": "subscription.created",
"data": {
"subscription_id": "sub_123",
"status": "trialing",
"trial_end": "2025-01-29T10:30:00Z"
}
}
// Subscription payment succeeded
{
"event": "invoice.payment_succeeded",
"data": {
"subscription_id": "sub_123",
"amount": 2000,
"currency": "USD"
}
}
// Subscription payment failed
{
"event": "invoice.payment_failed",
"data": {
"subscription_id": "sub_123",
"amount": 2000,
"attempt_count": 1
}
}
// Subscription canceled
{
"event": "subscription.deleted",
"data": {
"subscription_id": "sub_123",
"canceled_at": "2025-02-15T10:30:00Z"
}
}Best Practices ​
- Offer Free Trials - Increase conversion rates
- Save Payment Methods - Required for subscriptions
- Handle Failed Payments - Retry logic and notifications
- Provide Cancellation - Easy cancellation builds trust
- Send Notifications - Email receipts and reminders
- Monitor Churn - Track cancellations and reasons
Next Steps ​
- Subscriptions API - Complete API reference
- Saved Payment Methods - Manage payment methods
- Webhooks API - Handle subscription events