fireactjs / saas

A fully functional React web application for SaaS projects. It's built with React, Firebase and Stripe.
476 stars 177 forks source link

Billing period on invoice doesn't show monthly term length #15

Closed msacchetti closed 3 years ago

msacchetti commented 3 years ago

Hi, having a monthly subscription in stripe (as setup via your documentation) the invoice billing period doesn't show the entire month of the subscription. Is this a bug or is it supposed to show this?

... Created Time: 5/4/2021, 3:28:06 PM Billing Period: 5/4/2021, 3:28:06 PM ~ 5/4/2021, 3:28:06 PM ....

chaoming commented 3 years ago

Could you share the Firebase document object of the plan?

msacchetti commented 3 years ago

Screenshot_20210504-182903

chaoming commented 3 years ago

That looks fine. And the Stripe price object?

msacchetti commented 3 years ago

test

chaoming commented 3 years ago

It's correct to use the price_xxx ID.

The billing period data is from the Stripe Webhook. You should be able to find the request log and see what Stripe told Fireact the period was. Below is the source code in https://github.com/chaoming/fireact/blob/master/functions/index.js to record the billing period.

subscriptionCurrentPeriodStart: subscription.current_period_start, subscriptionCurrentPeriodEnd: subscription.current_period_end,

msacchetti commented 3 years ago

Stripe told the database/fireact two different dates but the billing period displays the same date for the "billing period" in the invoice.

"current_period_end": 1622838486, "current_period_start": 1620160086

chaoming commented 3 years ago

I would assume that the Firestore document of the invoice would also have those two timestamps, because the webhook doesn't transform the data from Stripe. It's still worthy to check what's recorded in the Firestore document.

The source code to display the dates are in https://github.com/chaoming/fireact/blob/master/src/pages/auth/accounts/PaymentList/index.js

I don't see a potential bug there. The whole logic is to record the unix timestamps in Firestore from the Stripe API call and display the date time with the code below.

'periodStart': (new Date(doc.data().periodStart * 1000)).toLocaleString(), 'preiodEnd': (new Date(doc.data().preiodEnd * 1000)).toLocaleString(),

msacchetti commented 3 years ago

The account has the proper start and end, the invoice under the account, period start and end are wrong. This is the base setup with no modifications to the backend code.

Edit- also should normalize the prices i.e. account =9.99, invoice = 999

I'll fix this tomorrow and try to do a PR.

Screenshot_20210504-210102~2 Screenshot_20210504-210110~2

chaoming commented 3 years ago

OK, that helps to narrow down the issue.

There are two types of webhook requests: 1) invoice and 2) customer/subscription

It seems that the customer/subscription webhook is recording the correct timestamp, but the invoice webhook has an issue.

Please check the invoice. events in the Stripe logs to see if there is any of them having the same period_start and period_end, not the current_period_start and current_period_end. If you find one, please share the request log as this might be an unexpected event or scenario which needs to be handled differently.

msacchetti commented 3 years ago

Yep that's it. The invoice.data returns:

period": { "end": 1622838486, "start": 1620160086 },

I think in the invoice modal UI could use a tweak, Change Billing Period in the UI,, to "Billing Date"

Then underneath that, add a line, "subscription period" , which prints the account level subscription start/end.

I'm going to change that in my code, I'll submit a PR for it that you can merge, if you deem it's worth a fix.

Thanks for the help! 🙌

chaoming commented 3 years ago

Sorry, I don't get what's causing the problem yet.

So the invoice.data has two different timestamps from Stripe, which means the Firestore invoice document and the UI should also show two different date time. Could you explain a bit more in details what's causing the period start and end the same?

msacchetti commented 3 years ago

This is what is happening. in the updateInvoice function,

image

And then by adding this line to the UI, you get all the info. image

chaoming commented 3 years ago

Thanks, so the invoice's timestamps are the same in the requests?

It's been working fine with my Stripe account and they are always different in the requests. I would like to understand if there is a particular invoice event type causing this to happen.

msacchetti commented 3 years ago

Its right in the docs. With a subscription product, the current back end code makes the invoice:

 periodStart: invoiceObject.period_start,
 preiodEnd: invoiceObject.period_end,

You'd want:

 periodStart: invoiceObject.lines.data.period.start,
 preiodEnd: iinvoiceObject.lines.data.period.end,

https://stripe.com/docs/api/invoices/line_item

period
hash
The period this line_item covers. For subscription line items, this is the subscription period. For prorations, this starts when the proration was calculated, and ends at the period end of the subscription. For invoice items, this is the time at which the invoice item was created, so the period start and end are the same time.

https://stripe.com/docs/api/invoices/object

period_end
timestamp
End of the usage period during which invoice items were added to this invoice.

period_start
timestamp
Start of the usage period during which invoice items were added to this invoice.
chaoming commented 3 years ago

Thanks, that makes sense now. I didn't realize the difference between the two start timestamp.

Please make a pull request for the fix. Appreciate your contribution. :-)

msacchetti commented 3 years ago

Np! Thanks for the starter code!

msacchetti commented 3 years ago

Also are you interested in a PR that moves all the auth state to a react context (AuthContext)? I have integrated the react Context API so the "userData" state is set in context, meaning anywhere in the app you can use (isSubscribed() = boolean, hasAccount() = boolean, get user() = usrrObj).

I was able to remove all the code for "check for account" logic in AppMenu etc by setting it in context.

chaoming commented 3 years ago

Could you let me have a look at the repo first?

I guess what I want to understand is that if the feature is specific for some scenario or it can be applied to the scenarios that others will run into. For example, if that's designed for one subscription per user, it won't work for others using this project.

msacchetti commented 3 years ago

Yes of course, I'll submit the PR and you can decide if it's common use case. With context, you know the users whole account record. There's no case a developer wouldn't want to know the users account list and status globally in an app like this

chaoming commented 3 years ago

Changed the solution to use Stripe Hosted Invoice URL instead of a modal in the page. The commit is fa2a2a9810c48148cf3871c1a9c873b414f987b0