I really like the answer given by colinm, and I want to expand on both the why and how, because I think it's a great answer but it could be fleshed out in a little more detail. Interestingly I think all three answers could be combined here, like, I wouldn't do just one of these things, I would do all three.
I think the most sustainable practice here is to replicate the relevant data locally, in your own database. This has several advantages, notably:
- Performance - There is no need to send a request to an external box. This will make your scripts execute much faster.
- Reduced Server Load - When you query the Stripe API, they typically send back large objects. I've had Stripe return objects taking up about 40k in memory, when I only need a single value, such as an integer. But, depending on what language you're using, merely loading the file for the Stripe API can be a much bigger burder. For example, in PHP, jsut loading the libraries and doing a single call will make a script take up an additional 1MB RAM, and scripts that require the response from an external server will typically stay in memory much longer than a typical script. If you're allowing end users (and not just admins) to load scripts that make calls to the Stripe API, this problem is magnified. Best practice is to not ever allow users to trigger interaction with the Stripe API any more than absolutely necessary (i.e. only when they are doing something Stripe needs to know about.)
- Uptime - If Stripe has any downtime, or if there is any trouble with connectivity between your server and stripe, your information is still available and there is no visible service interruption to your users.
- Features - This is what you care about here, and what is ultimately driving you to need a local database. You can structure the data how you want, which includes possibilities like putting indices on whatever fields you want so that you can quickly and efficiently return whatever information you want in a query. You are not limited by what Stripe allows you to do with their API.
- Extensibility - If you ever want to change payment processors, you can seamlessly switch over to a new one without users even noticing, because you have abstracted your own database and records of invoicing from your payment processor. In fact, if you want, you could even have multiple payment processors at the same time, displayed to your users in a unified invoicing system. Stripe is pretty fantastic right now in a lot of ways, but who knows where the company is going in the long-run. Sometimes once-excellent companies and their products fall from grace. Maybe a better competitor will come along. And there are already a few payment methods that it doesn't support but some of its competitors do: if you end up expanding into a market where one of those payment methods is preferred, you might need another payment processor.
The more you think about it, the more you realize that it's not only best practice to store this information locally, it's a huge benefit, and it's a bit of a risk not to do it.
How to get this started?
First, create in your own database, the relevant tables and structure for the events you want to store.
Using webhooks, whenever a relevant event happens, capture the event, extract the relevant data from it, and store it locally in your database. For invoicing, you will need to capture any event that could potentially reflect a change in the invoice.
You do not, however, have to store all or even most of the data in each event. Stripe stores a huge amount of data in each event, and in practice you need very little of it. I can't say exactly what you need for your use; this depends on what information you want to store, which will depend both on what you want to present to your users, and whether you want to store any additional information behind-the-scenes for your own internal use and recordkeeping.
How to catch-up if you haven't done this from the start?
In this case, you're going to need to iterate through all the records and extract the relevant information.
This is where the answers from bobmarksie and urban_raccoons come in. They provide two suggestions of two completely different ways to do it, bobmarksie suggests iterating through receipts and urban_raccoons suggests doing it by invoices. The solution by bobmarksie gives details using Java, but you can do it in any language.
Regardless of how you carry out the details, I recommend combining both approaches: iterate through all invoices, and all recepits and link them up in your database. Now you'll have a complete, up-to-date listing, and you can rely on webhooks to update your local database in the long-run.