Use Checkout Sessions to sell subscription products with a secure, hosted checkout. Pass your subscription product in product_cart and redirect customers to the returned checkout_url.
Mixed Checkout: You can combine subscription products with one-time products in the same checkout session. This enables use cases like setup fees with subscriptions, hardware bundles with SaaS, and more. See the Checkout Sessions guide for examples.
When integrating subscriptions, you’ll receive webhooks to track the subscription lifecycle. These webhooks help you manage subscription states and payment scenarios effectively.To set up your webhook endpoint, please follow our Detailed Integration Guide.
The following webhook events track subscription status changes:
subscription.active - Subscription is successfully activated.
subscription.updated - Subscription object was updated (fires on any field change).
subscription.on_hold - Subscription is put on hold due to failed renewal.
subscription.failed - Subscription creation failed during mandate creation.
subscription.renewed - Subscription is renewed for the next billing period.
For reliable subscription lifecycle management, we recommend tracking these subscription events.
Use subscription.updated to get real-time notifications about any subscription changes, keeping your application state in sync without polling the API.
For immediate billing (0 trial days): Expect within 2-10 minutes
For trial days: whenever that ends
subscription.renewed - Indicates payment deduction and renewal for next cycle. (Basically, whenever payment gets deducted for subscription products, you will get subscription.renewed webhook along with payment.succeeded)
Payment Failure Scenarios
Subscription Failure
subscription.failed - Subscription creation failed due to failure to create a mandate.
payment.failed - Indicates failed payment.
Subscription On Hold
subscription.on_hold - Subscription is put on hold due to failed renewal payment or failed plan change charge.
When a subscription goes on hold, it will not renew automatically until the payment method is updated.
Best Practice: To simplify implementation, we recommend primarily tracking subscription events for managing the subscription lifecycle.
// Webhook handlerapp.post('/webhooks/dodo', async (req, res) => { const event = req.body; if (event.type === 'subscription.on_hold') { const subscription = event.data; // Update subscription status in your database await updateSubscriptionStatus(subscription.subscription_id, 'on_hold'); // Notify customer to update payment method await sendEmailToCustomer(subscription.customer_id, { subject: 'Payment Required - Subscription On Hold', message: 'Your subscription is on hold. Please update your payment method to continue service.' }); } res.json({ received: true });});
2
Update payment method
客户准备好更新付款方式时,请调用更新付款方式 API:
// Update with new payment methodconst response = await client.subscriptions.updatePaymentMethod(subscriptionId, { type: 'new', return_url: 'https://example.com/return'});// For on_hold subscriptions, a charge is automatically createdif (response.payment_id) { console.log('Charge created for remaining dues:', response.payment_id); // Redirect customer to response.payment_link to complete payment}
if (event.type === 'payment.succeeded') { const payment = event.data; // Check if this payment is for an on_hold subscription if (payment.subscription_id) { // Wait for subscription.active webhook to confirm reactivation }}if (event.type === 'subscription.active') { const subscription = event.data; // Update subscription status in your database await updateSubscriptionStatus(subscription.subscription_id, 'active'); // Restore customer access await restoreCustomerAccess(subscription.customer_id); // Notify customer of successful reactivation await sendEmailToCustomer(subscription.customer_id, { subject: 'Subscription Reactivated', message: 'Your subscription has been reactivated successfully.' });}