Setting Up Recurring Payments with Cryptocurrency: A Complete Guide
Recurring payments are essential for SaaS businesses, subscription services, and membership platforms. While traditional payment processors handle subscriptions well, cryptocurrency offers unique advantages for recurring billing. This guide shows you how to implement recurring crypto payments.
Why Recurring Crypto Payments?
Benefits for Businesses
- Lower Fees: Save on recurring transaction costs
- Global Subscriptions: Accept payments from anywhere without currency conversion
- No Chargebacks: Eliminate subscription chargeback risks
- Faster Settlement: Receive funds immediately after payment
- Automated Billing: Set up once, collect automatically
Benefits for Customers
- Privacy: No need to share credit card information
- Control: Customers control when to pay
- Lower Costs: Reduced fees passed to customers
- Global Access: Pay from any country
Implementation Approaches
Approach 1: Invoice-Based Recurring Payments
Create invoices automatically on a schedule:
// Schedule recurring invoice creation
const scheduleRecurringInvoice = async (customerId, amount, interval) => {
const cron = require('node-cron');
// Create invoice every month
cron.schedule('0 0 1 * *', async () => {
const invoice = await createInvoice({
amount: amount,
currency: 'USDT',
description: `Monthly subscription - ${customerId}`,
metadata: {
customerId: customerId,
subscriptionId: subscriptionId,
billingCycle: 'monthly'
}
});
// Send invoice to customer
await sendInvoiceEmail(customerId, invoice);
});
};Approach 2: Pre-Authorized Payments
Store customer wallet addresses and create invoices automatically:
// Store customer payment preferences
const customer = {
id: 'cust_123',
walletAddress: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb',
subscriptionPlan: 'pro',
billingAmount: '99.00',
billingInterval: 'monthly',
nextBillingDate: '2025-02-01'
};
// Automated billing function
async function processRecurringBilling() {
const dueSubscriptions = await getDueSubscriptions();
for (const subscription of dueSubscriptions) {
// Create invoice
const invoice = await createInvoice({
amount: subscription.amount,
currency: 'USDT',
description: `${subscription.planName} - ${subscription.billingCycle}`,
metadata: {
subscriptionId: subscription.id,
customerId: subscription.customerId
}
});
// Notify customer
await notifyCustomer(subscription.customerId, invoice);
// Update next billing date
await updateNextBillingDate(subscription.id);
}
}Best Practices
1. Clear Communication
Always notify customers before billing:
// Send reminder 3 days before billing
async function sendBillingReminder(subscription) {
await sendEmail({
to: subscription.customerEmail,
subject: 'Upcoming Payment: ${subscription.planName}',
body: `Your ${subscription.planName} subscription will renew on ${subscription.nextBillingDate} for ${subscription.amount} USDT.`
});
}2. Grace Periods
Give customers time to update payment methods:
const GRACE_PERIOD_DAYS = 7;
async function handleFailedPayment(subscription) {
// Mark subscription as past due
await updateSubscriptionStatus(subscription.id, 'past_due');
// Give grace period
const gracePeriodEnd = new Date();
gracePeriodEnd.setDate(gracePeriodEnd.getDate() + GRACE_PERIOD_DAYS);
await updateGracePeriodEnd(subscription.id, gracePeriodEnd);
// Notify customer
await sendPastDueNotification(subscription);
}3. Automatic Retry
Retry failed payments automatically:
async function retryFailedPayment(subscription, maxRetries = 3) {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
const invoice = await createInvoice({
amount: subscription.amount,
currency: 'USDT',
description: `Retry payment - ${subscription.planName}`
});
// Wait for payment
const paid = await waitForPayment(invoice.id, 24 * 60 * 60 * 1000); // 24 hours
if (paid) {
await updateSubscriptionStatus(subscription.id, 'active');
return true;
}
// Wait before next retry
await delay(attempt * 24 * 60 * 60 * 1000);
}
// Cancel subscription after max retries
await cancelSubscription(subscription.id);
return false;
}Subscription Management
Creating Subscriptions
async function createSubscription(customerId, planId) {
const plan = await getPlan(planId);
const subscription = {
customerId: customerId,
planId: planId,
amount: plan.amount,
interval: plan.interval, // 'monthly', 'yearly', etc.
status: 'active',
startDate: new Date(),
nextBillingDate: calculateNextBillingDate(plan.interval),
createdAt: new Date()
};
await saveSubscription(subscription);
// Create first invoice
const invoice = await createInvoice({
amount: plan.amount,
currency: 'USDT',
description: `${plan.name} subscription`,
metadata: {
subscriptionId: subscription.id,
billingCycle: 'initial'
}
});
return { subscription, invoice };
}Updating Subscriptions
async function updateSubscription(subscriptionId, updates) {
const subscription = await getSubscription(subscriptionId);
// Handle plan changes
if (updates.planId && updates.planId !== subscription.planId) {
const newPlan = await getPlan(updates.planId);
// Prorate if mid-cycle
if (shouldProrate(subscription)) {
const proratedAmount = calculateProration(
subscription,
newPlan,
subscription.nextBillingDate
);
// Create prorated invoice
await createInvoice({
amount: proratedAmount,
currency: 'USDT',
description: 'Plan upgrade proration'
});
}
subscription.planId = newPlan.id;
subscription.amount = newPlan.amount;
}
await saveSubscription(subscription);
}Canceling Subscriptions
async function cancelSubscription(subscriptionId, immediate = false) {
const subscription = await getSubscription(subscriptionId);
if (immediate) {
// Cancel immediately, no refund
subscription.status = 'canceled';
subscription.canceledAt = new Date();
} else {
// Cancel at end of billing period
subscription.status = 'canceled_at_period_end';
subscription.cancelAt = subscription.nextBillingDate;
}
await saveSubscription(subscription);
await notifyCustomer(subscription.customerId, {
type: 'subscription_canceled',
subscription: subscription
});
}Webhook Integration
Set up webhooks to handle subscription events:
app.post('/webhooks/subscriptions', async (req, res) => {
const { event, data } = req.body;
switch (event) {
case 'invoice.confirmed':
// Payment received, activate subscription
await activateSubscription(data.metadata.subscriptionId);
break;
case 'invoice.expired':
// Payment failed, mark as past due
await markSubscriptionPastDue(data.metadata.subscriptionId);
break;
case 'invoice.overpaid':
// Handle overpayment (credit to account)
await creditAccount(data.metadata.subscriptionId, data.excessAmount);
break;
}
res.status(200).json({ received: true });
});Testing Recurring Payments
Test Scenarios
- Successful Payment: Verify subscription continues
- Failed Payment: Test grace period and retry logic
- Plan Upgrade: Test proration calculations
- Plan Downgrade: Test effective date handling
- Cancellation: Test immediate vs. end-of-period
- Renewal: Test automatic invoice creation
Test Implementation
describe('Recurring Payments', () => {
it('should create invoice on billing date', async () => {
const subscription = await createTestSubscription();
const nextBilling = subscription.nextBillingDate;
// Simulate billing date
await processRecurringBilling();
const invoices = await getInvoicesForSubscription(subscription.id);
expect(invoices).toHaveLength(2); // Initial + recurring
});
it('should handle failed payment with grace period', async () => {
const subscription = await createTestSubscription();
// Simulate failed payment
await simulateFailedPayment(subscription.id);
const updated = await getSubscription(subscription.id);
expect(updated.status).toBe('past_due');
expect(updated.gracePeriodEnd).toBeDefined();
});
});Common Challenges and Solutions
Challenge 1: Customer Forgets to Pay
Solution: Implement automated reminders and grace periods
Challenge 2: Price Changes
Solution: Notify customers in advance and offer grandfathering options
Challenge 3: Proration Complexity
Solution: Use standardized proration formulas and clearly communicate to customers
Challenge 4: Time Zone Issues
Solution: Store all dates in UTC and convert for display
Conclusion
Recurring crypto payments offer significant advantages for subscription businesses. With proper implementation, you can automate billing while providing customers with a seamless payment experience.
Start implementing recurring payments with FromChain and automate your subscription billing today!
