This article is the second one of my 'HowTo:Stripe' series. Set of articles will be helpful for those of managers or developers, who are planning to use Stripe as a payment system or those of you, who already using it. I won't compare Stripe with analogs (let's just say, BrainTree is also a great fit for this purpose), but rather planning to provide a detailed instruction on how to implement a ready-and-working one-time checkout or subscription system with billing cycle of recurrent payments.
If you are already have answers on questions from the previous article, you are all-set to begin.
In Stripe game, any payment begins with creating Customer and Checkout Session for this Customer in Stripe (here the details how to do it). The moment we create those Entities in Stripe, we need to create our
StripeCheckoutSession accordingly with necessary data, that Stripe gave to us (identifiers, dates, so on).
StripeCustomer: I recommend to save only necessary data here (for example, stripe customer id, createdAtStripe datetime and customer email) and link it One-to-One with your User identity via userId field.
StripeCheckoutSession: the best here is to save such data, as stripe session identifier, created and updated datetime and stripe customer identifier (Many-to-One with Customer here). In case you have a Subscription type checkout, there are two more fields, that are necessary here:
subscriptionId (stripe subscription identifier, null at this moment) and
product identifier (your 'subscription' identifier).
Important to know, that Session is always should be linked with your 'product' specification in order to control and respond to any stripe changes and save this 'product identifier' in
StripeSubscription later on accordingly with Stripe Checkout Process and webhooks. Those 'Customer-CheckoutSession' actions should be handled by one
CreateStripeCheckoutSession query. So, you can return 'ready-to-pay' Session object to the Web Client in response on
CreateStripeCheckoutSession query. Note, that if you do not want to create a customer for every checkout session (every payment), you need to pass it to Stripe at the moment you create a CheckoutSession in Stripe. Our
StripeCustomer linked with User comes in handy here: you can just look by your user identity identifier among all customers in your
StripeCustomer table and use it after that.
One should not forget to setup 'keys': Stripe gives you public and private keys. The first one you are going to use in frontend queries and the second one should be set in any API query to Stripe from backend. You can find your keys at your project 'Dashboard' main page.
Now the client can proceed with payment and actually pay for your product in a one-time-checkout or subscribe to the billing cycle.
The client is going to be redirected to the
success_url, that you set up when initiated the Checkout Session, in case of successful checkout and to
cancel_url page otherwise. If you have the one-time-checkout, than I have good news for you: it's almost done. You need to update the status of your checkout session in order to know, that everything paid in
redirectToCheckout event handler and that's all.
Meanwhile, with 'Subscription' type of payment, we are not done here. During the payment and redirect process, you need to receive and handle all necessary webhooks in order to show 'paid' invoices and 'active' subscription in his success page.
It means, that you are going to need a full cycle of recurrent payments. So, you have to operate with the next types of event entities:
All those entities have required and not required fields. Some of entity fields can be absent during specific event, but filled out in other. For example,
invoice.created or any other
invoice event, which happens before
subscription.created and so on. All event fields you can find in specific StripeEventInputType article (TBD)
So, in order to receive all important events, you need to listen to at least the following event list:
You can setup whatever stack you have as listener and create a
Webhook point at your dashboard. Do not forget, that you should always listen to webhooks. Important to notice, that here you should check signature, that Stripe gave you as 'Signing Secret' on Webhhoks page. Do it for every query, that you receive.
All events, that come from Stripe, go directly to this Webhook point. Here you can respond directly or format it depending on the event type and send to your API to handle. Here, not dependently on event type, I recommend to save any of it as
StripeEvent and respond to event accordingly with it's type only after that. Having it that way, you can always find all events, that you received from Stripe at any time by 'customerId' in your
StripeEvent table or you can setup a view in your admin panel as well.
Here we come to the most not obvious and crazy thing, that I have not expected from a fancy world-wide-known system. Stripe send all webhooks asynchronously. So you can never know you can never know what comes first: subscription, invoice or checkout session webhook.
Why is that not convenient at all? At first, all data, that can be linked with is 'checkout session' for specific payment and 'customer' for user in general.
But the thing is 'checkout.completed' webhook you can receive at any time: after
subscription.created. More to that, you can give
invoice.created and so on with invoices. More to it, you can get 'subscription' at the end of webhooks list. In that way, you firstly getting an invoice, that you can save, but you can't attach it to subscription, because there are none at the moment and you can't attach it to your 'product' (e.g. subscription plan), because you can't identify the specific product in your DB with paid invoice. Sure thing, in order to deal with asynchronous Stripe events we got all cases handled. I am going to describe it in detail in the next article in this series. In a nutshell, the most important thing is to save and link Stripe subscription with invoices, checkout session with subscription and subscription with your product. Every event should be handled by different Handler, for example, it can look in the following way:
HandleStripeChargeWebhookSrevice HandleStripeInvoiceWebhookService HandleStripeCheckoutWebhookService
Useful Stripe Docs, that comes in handy:
In the next article I am going to specify ways to handle 'asynchronous' webhooks and how each event may look like.
Keep your code clean and yourself safe and sound.