How do you match a Stripe Checkout to a charge.succeeded webhook? - stripe-payments

We are using the Stripe Checkout and setting our metadata when we create the Session. This allows us to catch the checkout.session.completed webhook and that webhook contains the metadata.
However, we also use Sources, like WeChat, and these are not handled by the Stripe Checkout. To use WeChat, we create a Source and when the user confirms via the QR code, we charge it.
We want to use the charge.succeeded webhook as a central place to fulfil orders, regardless of where they come.
The issue is that the charge.succeeded webhook generated from a successful Stripe Checkout does not contain any of the Session metadata we set, so we can't link it to a Checkout Session and hence have no idea what it relates to
The only way we can see how to do this is:
Stripe Checkout
Catch the checkout.session.completed webhook and use the included metadata to fulfil the order. For the customer details, we can call the Payment Intent
WeChat (And other sources)
Catch the charge.succeeded webhook which contains both the customer data and the Source, which contains the metadata.

The Charge object will have a payment_intent and you can list CheckoutSessions by that PaymentIntent's id.
Alternatively, you can also set the metadata directly on the PaymentIntent when creating the CheckoutSession.

Related

Stripe - Get risk score from checkout.session.completed event

I have a webhook for checkout.session.completed. There is no information about risk score in the object I receive. How can I get the risk evaluation information for my customers' payments?
I can see the risk score from Stripe dashboard, but I also need it in my database.
The risk_score field is set on the Charge object associated with the payment. This object is not directly available on the Checkout Session object or events.
In order to retrieve these details, you'd need to make an additional API request in your webhook handler. My recommendation would be to retrieve the associated Payment Intent using the pi_xxx ID from the payment_intent field on your checkout.session.completed event, and expand the latest_charge field. The response will include the full Charge object, including the required outcome.risk_score field.
This will look something like this using the stripe-node library:
const paymentIntent = await stripe.paymentIntents.retrieve('pi_xxx', {
expand: ['latest_charge']
})

Passing Metadata While Generating Stripe Payment Link

I want to pass a few app specific credentials with stripe url for checkout.
I tried something like the following,
const paymentLink = await stripe.paymentLinks.create({
line_items: [{price: "price_1XXXXK", quantity: 1}],
metadata: {
author_id: "author_id",
server_id: "server_id",
},
});
But in payments tab, the metadata is still empty.
I can only see those metadata in generated payment link section. But I can't find any relations or references about the link that was used to make the purchase on the payment response data.
So is there any better way to add metadata to the checkout while creating a payment link, instead of adding metadata just for the generated link?
Passing the metadata parameter when creating a Payment Link will only persist on the generated Checkout Session object, and not the underlying payment:
Metadata associated with this Payment Link will automatically be copied to checkout sessions created by this payment link.
But I can't find any relations or references about the link that was used to make the purchase on the payment response data.
To reconcile the Payment Intent object with the associated Payment Link that was the origin of the payment:
List all Checkout Sessions, passing the payment_intent parameter.
Expand the payment_link field on the returned Checkout Session object.
The Create method for Checkout Sessions supports these parameters:
payment_intent_data
metadata passed this way will be available in both payment_intent.succeeded and charge.succeeded events.
setup_intent_data
subscription_data
That let you pass data to the underlying payment_intent/setup_intent/subscription objects and it supports metadata.
So you'd be able to pass metadata to, say, a PaymentIntent this way.
See: https://support.stripe.com/questions/using-metadata-with-checkout-sessions
In passing / rant: most people will need to use metadata so as to get an "easy" way to know which products are in the order, because out of the box the Stripe API does NOT tell you, in an event, which product was ordered 🙄 (except for Payment Links).

Stripe Checkout - How to handle user id with webhooks

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.

Stripe APi paymentIntent and sessions object

I am having trouble finding out the difference between payment Intent and a session.
Assuming I a customer logs into a page and goes to domain.com/register how can I create session and check if customer has already visited page by using customer email address to get the customer object?
What are the difference between paymentIntent and session and how do they help? I see that session is created on Checkout but not when accepting one time payments.
Current I create a payment intent and it works find but my 'url' has no session
PaymentIntents
A PaymentIntent is an API object in Stripe's API that create encapsulates a lifecycle of a one-time payment.
You typically create your own form on your webpage when using PaymentIntents, create a PaymentIntent using the Stripe API, then confirm it using the cardElement from Stripe Elements (the frontend UI elements for collecting card details).
Checkout
Checkout is a full page "hosted UI" that creates its own PaymentIntent and provides all the UI that a customer needs to enter their card details and takes a payment. It also supports many other payment methods automatically without you having to manually add support for each one.
So a CheckoutSession under the hood uses a Subscription or a PaymentIntent object, depending on whether it was used in subscription or payment mode.

payment intent is null in a subscription checkout session

I am integrating stripe using php by following this tutorial:
https://phppot.com/php/manage-recurring-payments-using-stripe-billing-in-php/
(my website has some subscription plans and redirects to the stripe checkout form ) . however, for the last step, I decided not to use webhooks, I chose to store the info like in this tutorial https://www.codexworld.com/stripe-checkout-payment-gateway-integration-php/ (I know it is not for subscription but I just use the success.php code from this tutorial to collect the customer info and payment intent details).
I tested it, gone throught the stripe checkout form , and on success I printed the checkout session object and noticed that the payment_intent field of that object is empty ! so i cannot load the payment intent object and get its info although the payment is successfully made and it is showing on the dashboard . any idea why ??
EDIT :
According to the documenttaion of a checkout session (https://stripe.com/docs/api/checkout/sessions/object), the payment_intent field stores the ID of the PaymentIntent for Checkout Sessions in payment mode. In my case I have a subscription mode not a payment one. however,if I still want to get the $intent->status , can I use the payment_status field of the session object $checkout_session->payment_status?
And if subcription payments really don't have paymentintents , then why did the payment appear in the payments section on the dashboard?
Based on the mode you passed in, either one of payment_intent, subscription or setup_intent will be populated, the rest will be null.
When a Subscription is made, your user is invoiced so it is considered a payment and will appear in the Payments dashboard. You can retrieve the Subscription and access the latest_invoice field to obtain the Invoice object. The Invoice object contains the payment_intent field. This is likely what you're looking for.
Using webhook would simplify the process, since you could listen to invoice.payment_suceeded to retrieve the PaymentIntent ID.

Resources