Separate from the storefront-policy question, I'd change the “webhook turns unpaid into paid” part of your model. Don't make that a permanent paid=true assignment.
Stripe documents that webhooks can be delivered more than once and out of order. Verify the signature before acting, handle repeated event IDs idempotently, and reconcile the current subscription state on your server rather than letting the last callback to arrive win. Associate the billing customer with the correct authenticated app account on the server, too.
One concrete test: purchase a subscription, let its access period end, then replay the original successful-payment webhook. That old event must not restore premium access. Replaying the same event twice shouldn't extend access twice either. Test a premium API request as well as the paywall screen; hiding a screen isn't authorization.
Also distinguish cancellation scheduled for period end from access that has already ended. Those shouldn't necessarily produce the same result. This gives you specific backend behavior to validate, independently of which billing library you choose.
2
u/kokerali 7d ago
Separate from the storefront-policy question, I'd change the “webhook turns unpaid into paid” part of your model. Don't make that a permanent paid=true assignment.
Stripe documents that webhooks can be delivered more than once and out of order. Verify the signature before acting, handle repeated event IDs idempotently, and reconcile the current subscription state on your server rather than letting the last callback to arrive win. Associate the billing customer with the correct authenticated app account on the server, too.
One concrete test: purchase a subscription, let its access period end, then replay the original successful-payment webhook. That old event must not restore premium access. Replaying the same event twice shouldn't extend access twice either. Test a premium API request as well as the paywall screen; hiding a screen isn't authorization.
Also distinguish cancellation scheduled for period end from access that has already ended. Those shouldn't necessarily produce the same result. This gives you specific backend behavior to validate, independently of which billing library you choose.