Stripe Checkout - How to handle user id with webhooks - stripe-payments

I am currently adding the Stripe checkout to my application. My goal is really to have at least as code as possible.
The documentation says I need to handle at least three different webhook types:
checkout.session.completed
invoice.paid
invoice.payment_failed
I try to avoid handling any customers in Stripe and I think it is not necessary with the checkout component.
However, I still need the user id in the webhooks to update the states accordingly.
When creating the session on the backend I'll add the field client_reference_id.
For the state checkout.session.completed I get this field and everything is fine.
For invoice.paid I don't get it and I need to use subscription_data.metadata in the session to get this data.
For invoice.payment_failed I don't know how to get it or how to test it with the CLI because I can't add metadata and I cannot add the client_reference_id.
Do you have an idea what the best practice here is and how I should implement that? Or should I really handle a customer in Stripe as well?
Thanks!

There's a couple different ways you can handle this -
If also set client_reference_id in subscription_data.metadata, you can still get it from the invoice.paid and invoice.payment_failed events by retrieving the Subscription found at subscription on the Invoice (see api ref).
You could change your database to store the relationship between a Stripe Customer and a client_reference_id. When you get an invoice.paid or invoice.payment_failed event you would grab the Customer ID and the information in your database would allow you to tie it back to the correct client_reference_id.

Related

Stripe create Subscription with payment method in one go instead of two

for my subscription based product I want to have a possibility to subscribe and enter payment details at once with stripe and struggle with that with the api.
In https://stripe.com/docs/billing/subscriptions/build-subscriptions?ui=elements I see an option to create a subscription with payment_behavior='default incomplete' and then enter the details to confirm the payment intent. So far so good. However if I create the subscription like that even before the customer confirms payment details stripe already generates an invoice which is not really what I want before final confirmation by customer.
Options I see:
create setupintent, have this filled by customer via elements and then have the customer subscribe. Technically works nicely but for a sales and customer perspective is not good as it has two steps thus probably reduces conversion.
create the subscription in the background before final subscription confirmation by customer and use the clientsecret of it to pass back to browser and then have customer enter his payment data and submit that and finish the setup of subscription and payment info. Technically works - however I realize that when I create the subscription to get the clientsecret to pass to elements before the customer enters his payment data and confirms the subscription the subscription is not only created but an invoice too - which would be really, i.e. an invoice created before customers really confirms the contract
create setupintent and submit it via elements and in metadata of it add the info of product that customers wants so that when the paymentmethod gets created and I get webhook event I do the booking of the product given in metainfo. May however mean the customer gets to success page but the webhook has not notified yet and thus the customer is not really subscrubed at the point in time but gets a success message he is
same as 3 except do not pass info via metadata but via successUrl parameters which refers to and endpoint at my backend which upon being called after setupintent was setup will do the subscription and then redirect to my frontend which shows success page. That seems like a error prone workaround however.
Create a workflow which is a 2 step sign up and asks for paymentinfo, sets that up and then brings customer to a final confirmation page where the submit triggers subscription creation. Seems a bit complicated from a user flow, but so far probably the best option?
Any better options?
Cheers
Tom
ps: Interestingly enough on discord stripe support told me #2 is the way to go - find it hard to believe ...
As far as I know, there isn't a workaround for this unfortunately. It is just how Subscriptions API is designed by Stripe. You can learn more about that here where they talk about "how subscriptions work".

Retrieve a coupon associated with a checkout session during the stripe webhook named payment_intent.succeeded

I created a payment session with stripe with the AllowPromotionCodes option set to true. Once the payment is done, I would like to retrieve the associated discount coupon (if it exists) in the payment_intent.succeeded webhook. I have the impression that this is not possible and I don't understand why. If this is the case, is there a way to get around this problem?
Edit:
I think this link is part of the answer but I don't really understand what stripe proposes as a workaround.
If that's a Checkout Session, you should listen to the checkout.session.completed event and look at amount_discount for the discounted amount. Once the Checkout Session is discounted, everything on payment_intent.succeeded will be the post discounted value and doesn't contain coupon information.

How to save card details (payment method) in Stripe using backend?

The docs only covers the case where the payment method is created by the frontend (JS). But there is a risk that the user leaves the website before the frontend sends the information to the backend that the card has been added (and its ID).
In order to make a payment, I need ID of the payment method. I don't want to query Stripe's API for user cards IDs every time before making a payment, so I want to save the payment method ID in my local database. I also want to allow the user to define more payment methods and choose the default one.
Is there a reason you are using the non-recommended workflow you linked to? The most up-to-date version can be found here
Have you checked out using webhook listeners? I use them to create/update my local records.
In the workflow you reference, 3 webhook events potentially fire. First the setup_intent.created event is triggered when your server code generates the SetupIntent and its client_secret. Then, when the user fills out whatever Payment or Card element you instantiate and your frontend code calls the stripe.confirmCardSetup() (or stripe.confirmSetup() in the case of a PaymentElement), both the setup_intent.succeeded and payment_method.attached events will fire.
This last one will POST the payment_method object that was just attached to your customer back to your system. This object will have both the Payment Method ID as well as the associated Customer ID. You can use these to update your local records to map payment methods to customers in your server and avoid unnecessary API calls.

Which webhooks to look after in Stripe Checkout?

I'm implementing subscription with Stripe Checkout, but I have some questions that I couldn't get definitive answers for even from their support.
I have following scenario:
User clicks on a Subscribe button
User gets redirected to Stripe Checkout page (along with session token)
User successfully pays (and from now on is a Customer)
and here I get 14 webhook calls (each one is different event type):
checkout.session.completed
payment_method.attached
invoice.created
customer.updated
customer.subscription.created
customer.created (even though I used the same email - test mode, but still...)
invoice.finalized
invoice.updated
invoice.payment_succeeded
customer.subscription.updated
charge.succeeded
payment_intent.succeeded
payment_intent.created
invoice.updated
edit: I get all of them at once because I use Stripe CLI's listen feature and it probably shows all calls which normally wouldn't happen because they have to be defined in dashboard event by event. For instance if I create invoice in the dashboard and pay it then if no webhook is defined explicitly then my server won't know about anything
Until here it all works good. My concern is that:
if a Customer will be charged next month which webhook should I listen to? I need to differentiate between first-time Customer (because I create new account for that user) and existing Customer that I just need to note in database that this user is still active (or listen for event of subscription cancellation? subscription_schedule.canceled maybe?). One idea is to just listen to successful payment and upon that check if customer exists in database - if he does just update, if not then create account.
when I do another subscription payment using the very same email I get exactly the same webhooks (including customer.created which I think shouldn't be there) or am I missing something?
is there any possibility to double-charge a Customer by accident like I read in other Stripe implementations (see: idempotency)?
what are other things to take into account while implementing Stripe Checkout? I feel like their instructions are not reassuring that I've done everything that is considered good practice.
Flow that I want to achieve:
User successful payment (as above in points from 1 - 3). Already done!
Register new customer in database (Firebase in this case) and log him in automatically so after payment he has instant access.
As long as customer's card is charged he has access. While he cancels or card has no insufficient funds I need to have my server notified about this to downgrade access.

How to identify Stripe webhooks type?

I am using Stripe and I need to understand how to only process webhooks that are generated by Stripe behind the scenes. When my server sends something to Stripe (new subscription, new individual charge) Stripe will generate events that are sent via the webhooks I provide. Well, I don't need to process those since it will create a mess. I only want to process Stripe generated webhooks in situations like: failed charge at subscription renewal, manual modifications via the Stripe dashboard, refunds generated in the dashboard, etc).
I went through the events generated and I cannot find anything that would make a difference from those my API calls generate or those generated behind the scenes.
Is there something I am missing?
Update
- API call: event has a request id
- Stripe behind the scenes: event has null request_id
- Stripe dashboard: event has a request id (This still remains a problem)
If I discard all webhook events that are not null I will also discard Stripe dashboard events. I need to process Stripe behind the scenes and also Stripe dashboard generated events.
First of all, Stripe currently does not support identifying the incoming webhook event type. Looking in the Dashboard I indeed can see what initiated the event (API, Dashboard or Automatic) but Stripe's people said they don't support it.
However, there is a workaround. For anyone struggling with this I will describe what I did. An automatic Stripe generated event is easy to differentiate. It contains a null request field. Any other type of event will have a request id (ex: re_123h2kj18321hjk3218). The problem remains with differentiating between API and Stripe Dashboard generated requests. Therefore, the solution is to capture the request id for every request generated by the API. Whenever a webhook arrives to your server, you check for the request field NOT to be in the Storage System (DB, etc) OR that the request is null. This means that the event was generated by either the Dashboard or Automatically by Stripe (subscription renewal).
Steps:
Hook into the CurlClient provided by Stripe. Extend that class and
override the request() method. The request method returns the
response generated by Stripe servers. Capture the headers of the
response which would contain a Request-id. Store that in your
Storage (in my case a DB)
In your configuration files you need to specify that Stripe should use your own CurlClient. ApiRequestor::setHttpClient(new CurlClient()); (I've named my CurlClient too but you can name it whatever)
When a webhook arrives, you have three options to identify the type:
Automatic: if the event has a request=null
Dashboard: the request is not null and the request is not in you Storage
API: you're left with one situation. The request is not null but exists in your DB
As you can see, there's quite a lot of work for something really easy. All Stripe needs to do is provide another field in their webhook event name something like request-type with three options (api, automatic, dashboard). They already have this build but they don't allow it to be shown in the webhook event.
On the event object documentation, you'll see the request property documented. This property is set whenever the event came from an API request. Otherwise, if it's null it means there was no API request associated with it and it was what we call an Automatic event in the dashboard.
You need to discard any event where request is not null on your end!

Resources