-
Notifications
You must be signed in to change notification settings - Fork 9
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(billing): Implement last month usage charging on subscription cancellation #296
base: main
Are you sure you want to change the base?
feat(billing): Implement last month usage charging on subscription cancellation #296
Conversation
Add STRIPE_TEST_CLOCK_ENABLED flag to control timestamp generation behavior: - In test mode: Use current time for simulation - In production: Use period end time minus buffer for accurate billing This change improves testing capabilities while maintaining proper production behavior for usage-based billing calculations.
…bscriptions Remove redundant comments and consolidate invoice handling logic: - Remove TODO comment and old conditional structure - Replace with cleaner invoice validation check - Prepare for implementing proper subscription cancellation processing The changes streamline the webhook handler code while maintaining functionality for canceled subscription scenarios.
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
… handling Split invoice processing into smaller, focused functions: - Extract finalizeAndPayInvoice into separate function - Add early return for non-canceled subscriptions - Improve code organization with clear responsibility separation This refactor enhances error handling and makes the code more maintainable by following single responsibility principle.
d79ae15
to
edfa1aa
Compare
@@ -82,33 +83,20 @@ export async function POST(req: Request) { | |||
); | |||
} | |||
await handleSubscriptionCancellation(event.data.object); | |||
await upsertSubscription(event.data.object.id); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📝 When a subscription expires and the subscription is cancelled, customer.subscription.updated
does not fire, so the above method is executed on customer.subscription.deleted
and the database is updated appropriately.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📝 Added to use the simulation function on the Stripe admin panel. We wondered whether to make it an environment variable, but decided to define it within a function as it is not a constant that is used in multiple places.
|
||
async function finalizeAndPayInvoice(invoiceId: string) { | ||
try { | ||
await stripe.invoices.finalizeInvoice(invoiceId); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📝 Update from Draft
to Open
with this method.
ref: https://support.stripe.com/questions/invoice-states?locale=en-US
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we use auto_advance
parameter here?
If we can, we would skip to call invoices.pay, invoices.send..., or so manually.
Stripe would care them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will keep you posted on the progress along the way.
I ran it with await stripe.invoices.finalizeInvoice(invoice.id, { auto_advance: true })
and the flow was as follows.
- Simulation proceeds until the end of the period and the invoice is updated from draft to open for the metered invoice.
- automatic collection of the invoice is executed one hour after the invoice is issued, so the simulation proceeds to one hour later
- Assumed to be paid at this time, but remained open previously and payment is not completed
Continue to check.
async function finalizeAndPayInvoice(invoiceId: string) { | ||
try { | ||
await stripe.invoices.finalizeInvoice(invoiceId); | ||
await stripe.invoices.pay(invoiceId); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📝 Update from Open
to Paid
with this method.
ref: https://support.stripe.com/questions/invoice-states?locale=en-US
Add upsertSubscription call after handleSubscriptionCancellation to ensure subscription data is properly synchronized in the database when a subscription is canceled. This ensures our system maintains accurate subscription state after cancellation processing.
a10f8f9
to
6ddee1e
Compare
app/webhooks/stripe/route.ts
Outdated
// TODO: This block will be removed in the other issue. | ||
if (event.data.object.billing_reason === "subscription_cycle") { | ||
await handleSubscriptionCycleInvoice(event.data.object); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📝 This block will be removed in the other issue. We plan to add user seats in other ways.
ref: #294
@satococoa review, please 🙏 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you!
Could you check my comments, please?
|
||
async function finalizeAndPayInvoice(invoiceId: string) { | ||
try { | ||
await stripe.invoices.finalizeInvoice(invoiceId); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we use auto_advance
parameter here?
If we can, we would skip to call invoices.pay, invoices.send..., or so manually.
Stripe would care them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you, but you do not have to care about this in this pull request.
Because I changed user sear reporting logic in #294.
Summary
Implement proper final billing handling when a subscription is cancelled, ensuring accurate billing for the last period of usage and maintaining data consistency.
Related issue
NONE
Changes
Testing
Other information
Billing state transitions follow Stripe's documentation: https://support.stripe.com/questions/invoice-states
Test clock simulation can be enabled for development testing
Adding the agent time charge manually is both time-consuming and expensive, so it can be done in the stripe cli as follows. As this is only a registration to stripe, the db is not updated, so the ui usage of the application remains unchanged.