#mesudev - Best Practices

1 messages ยท Page 1 of 1 (latest)

harsh gazelle
#

Hello! So you're not using Stripe.js on your frontend? Is that due to a limitation or requirement on your end? We strongly recommend using Stripe.js and confirming Payment Intents client side if at all possible.

astral wedge
#

I would prefer to create and confirm the payment intent on the server-side only, and only uses stripe's js if at all needed (that is, in my opinion, if authentication is required).

#

And hello and thanks!

harsh gazelle
#

Can you tell me more about why you want to confirm the Payment Intent server-side instead of client-side?

astral wedge
#

Because I internally store data related to payment intents etc. within my system (no data that is CID or card details etc.). To be more precise, the flow is:

  • I store CC data via stripe.js (https://stripe.com/docs/js/setup_intents/confirm_setup), then store the accordingly obtained setup intent ID + payment method ID in my internal DB, and relate it to a given user ID.

  • I then plan on authorizing payments via the above-mentioned code, and only ever re-use stripe.js if ever needed to avoid authentication redirects.

  • I then capture authorized payments after the service has been delivered on the server-side only (after validating that the service has been verified on the client-side).

Given the storage of (non-risk and non-CID to remain PCI compliant) data within my internal DB, I prefer to do as much as possible on the server-side, as I will need to update the internal DB after every main change of the payment flow (payment method created, payment authorized, payment captured, payment refunded).

#

And given the internal DB system that we've setup, I want to avoid extra client-server-client roundtrips, unless they're absolutely necessary. But when a user clicks on the "pay" button, I need to verify multiple things before even starting with the authorization of the payment, so a client-side only solution is impossible. The first step of a payment authorization of my platform must be a server-side validation, and why not do it all on the server-side then, you see what I mean?

harsh gazelle
#

I totally understand the desire to do as much of this server-side as possible, as async payment flows can be challenging, but we strongly recommend confirming Payment Intents (and Setup Intents) client-side. You can place a hold on the funds with that confirmation and capture on the server when you're ready to actually move the money. An approach like that may work well for your use case: https://stripe.com/docs/payments/place-a-hold-on-a-payment-method

If you really do want to confirm server-side we have a guide for doing so, but it has significant limitations (only supports card payments, increased risk of double-charging, extra trip to the client) and is not our recommended approach: https://stripe.com/docs/payments/accept-a-payment-synchronously

astral wedge
#

What about the authentication?

#

^^

harsh gazelle
#

Oh, you're already doing separate capture, so placing a hold is something you're familiar with. ๐Ÿ™‚

astral wedge
#

3D Secure is the main problem here for me, you see?

#

yes, sorry didn't tell you

harsh gazelle
#

3D Secure is handled automatically by Stripe.js client-side when you confirm.

#

With the approach you're talking about it's broken up into several extra steps.

astral wedge
#

Well how am I supposed to get all the data needed for the payment, if not going through my server? (connected account ID, customer ID & payment method ID). Imho, I just have to go through my server for that first, and I obviously don't want to forward customer IDs, payment method IDs and connected account IDs of all the payments to the frontend. This is why I though of creating the payment intent on the server-side, and then only forwarding the client secret to the server-side if authentication is needed.

#

Like this, in the best scenario; I can do one client-server request only. And in the worst case, I do client-server-client-server, if authentication is needed. You see?

harsh gazelle
#

You would still create the Payment Intent and all of the associated things on your server, but instead of confirming the Payment Intent server-side you would pass the Payment Intent's client secret to Stripe.js and then confirm the Payment Intent there.

astral wedge
#

Yes exactly! My question is if that was correct to do it like this!

#

Because it seemed unusual to me, but actually totally makes sense. how else can the frontend know when to trigger the popup, if not with js

harsh gazelle
#

In your original message you said:

and uses 'confirm' => true to immediately confirm it.

That's the specific piece I'm saying is not a best practice. You would not confirm the Payment Intent server-side.

astral wedge
#

ooooh I see

harsh gazelle
#

This is the recommended flow:

astral wedge
#

so I do the paymentIntent call without confirm => true on the server-side, and then, ONLY IF the status of the returned payment intent object is "requires_action" and "next_action.type" of the payment intent object is "redirect_to_url", I grab the client secret, forward it to the frontend, and use stripe.js (to be precise, stripe.confirmCardPayment, using only the client secret delivered from the server, https://stripe.com/docs/js/payment_intents/confirm_card_payment) and then check in js if the confirmation was successful (promise), and forward the response back to my server. Correct?

#

Yes, but the scheme does not tell what to do here, which was my question for authentication ๐Ÿ˜„

harsh gazelle
#

The second part is correct, but the first part isn't. When you create the Payment Intent server-side it will have a status of requires_payment_method (if you haven't attached a Payment Method to it yet) or requires_confirmation (if you have). In either case usually the next step is to supply the client secret to Stripe.js so it can be confirmed client-side. This would be for every Payment Intent, every time.

astral wedge
#

I will have a payment method attached to every payment intent created on the server-side

#

Ah got it

#

So you're saying there's no way that I can fully authorize a payment intent on the server-side only, if no authentication is required?

#

meaning that it will always be client (payment triggered) -server (payment intent created WITHOUT confirmation)-client (confirm payment intent via client secret) -server (update internal payment records)?

harsh gazelle
#

You can, using the approach you described in your original message, but we do not recommend that approach.

#

Yes to your last message. ๐Ÿ™‚

astral wedge
#

humm why do you not recommend it? Will stripe.confirmCardPayment fail if the payment intent has already been created on the server-side?

harsh gazelle
#

We recommend confirming client-side for the reasons mentioned above (only supports card payments, increased risk of double-charging, extra trip to the client) which are also mentioned in the documentation I linked to.

astral wedge
#

I mean theoretically I could:

  • create AND confirm payment intent on the server-side, then check if status is "requires_action" and next_action.type is "redirect_to_url". If so, I could then retrieve the payment intent in js and trigger the authorization for it in js... If that's even possible?
harsh gazelle
#

Usually the fact that confirming server-side only supports cards and no other methods of payment is enough for most people to disregard it as a viable option.

#

What you describe is possible, but your integration is going to be a lot more complex than it needs to be if you do that.

astral wedge
#

Well the thing is that I'm only using auth and capture payments, so I can only use cards anyways ^^

#

Complex but it will save a lot of unnecessary requests, don't you agree?

harsh gazelle
#

Afterpay, Clearpay, and Klarna all support manual capture in addition to cards, and there may be more support in the future.

astral wedge
#

Yep but for my logic I actually need all setup intents (to store cc for recurring payments), payment intents, setup future usage (recurring payments with the same method), and manual capture.

#

And for this I've been told that only cards, Apple Pay and Google Pay are compatible. Is that correct? Just to double-check.

harsh gazelle
#

That's true, yes. For that set of requirements it is only cards, Apple Pay, and Google Pay currently.

astral wedge
#

Yes

harsh gazelle
#

And yes, you can build what you're talking about, we just don't recommend it. ๐Ÿ™‚

astral wedge
#

that's actually why I'm not bothered by things that only work for cards, as that's totally my case anyways.

#

But if you see the risk of double-charges yeah that sounds bad

#

Meaning that in my case, you'd always do client-server-client-server as mentioned above? Even if this means that a request is made in vain in many many cases?

#

BTW, are you a dev from Stripe? I'm crazy impressed with your support, really!

#

More than excellent

harsh gazelle
#

It's not really in vain; the only way to know authentication is required is to actually attempt the transaction, and if you do that client-side you can immediately handle it without a round trip to the server and an extra delay. Authentication isn't always required, sure, but overall it's the best balance of customer UX and keeping your integration complexity down.

#

And yep, I'm a developer at Stripe. Thank you for the kind words!

astral wedge
#

Ok I see.

#

No really. My PSP was a disaster compared to this!

#

My previous PSP *

harsh gazelle
#

I'm happy to help! ๐Ÿ™‚

astral wedge
#

okok

#

and last but not least

#

you can guarantee that in this case, authentication will always automatically occur via popup? And never ever via redirect? Like redirects can never happen in this case?

harsh gazelle
#

One thing I want to be clear about: it's okay to build what you're talking about, and it will work fine for cards as long as you prevent the double-charge scenario. It's a viable path forward, but we do recommend the client-side confirmation approach as the "happy path".

#

Unfortunately no one can guarantee that; some card issuers do not support 3D Secure correctly and may require a redirect, but it's pretty rare at this point.

astral wedge
#

uuuh

#

that's bad news, meaning that I will need to create session restorage roundtrips

harsh gazelle
#

We do have some special rules internally that redirect for some of the card issuers who require it for the payment to succeed, and that's handled by Stripe.js automatically if you confirm client-side.

#

Honestly it's rare enough that you may want to only implement it if you actually run into it.

#

But that's entirely your call.

astral wedge
#

hmm ok

#

and how can I check if confirmCardPayment requires a redirect?

#

So this means I'll have to pass a "return_url" to every call of confirmCardPayment on the client-side? Is there something like a "redirect: if_required" flag, like you do it for stripe.confirmSetup, to assure that the redirect is only done if totally needed?

harsh gazelle
#

I don't think you can detect it. I just checked the docs, and it's rare enough that we don't mention it as a separate thing. I beleive if we need to do a full redirect we open a new tab/window, so people aren't actually taken away from your site.

astral wedge
#

Hmm well then it may be better to consistently do it via redirect

#
  • confirming the payment intent on the server-side then. Sounds safer to me, no?
harsh gazelle
#

Again, it's super rare that a payment using 3D Secure would actually require a full redirect.

#

And it should be temporary; the card issuer would be expected to fix their flow, so it would be treated like a bug/unexpected downtime/etc. not at something you need to handle as a matter of course.

astral wedge
#

Also, you say it's rare enough, but you still should provide the "return_url" paramater to confirmCardPayment in every case, do I get you correctyl?

harsh gazelle
#

They would not leave your platform, Stripe.js would open a new tab/window if this was required.

#

No, you would not provide a return_url.

#

In fact you would explicitly not provide a return_url; doing so means you're telling Stripe to not let Stripe.js handle authentication.

astral wedge
#

Ah well then there is no risk whatsoever that I'll lose the session on the platform tab where the payment was triggered?

harsh gazelle
#

Correct.

astral wedge
#

Then it doesn't even matter if there will be a redirect. Guess we had a different understanding of "page redirect" then. And you have implement some kind of listener in stripe.js that closes the in-that-case opened authentication window and resolves the promise accordingly? Or how does that work?

harsh gazelle
#

Stripe.js handles all of that for you, there's nothing extra you need to do.

astral wedge
#

Meaning that my question is; all that I have to take care of is stripe.confirmCardPayment and then handle the resolving of the promise?

harsh gazelle
#

Correct, that's all you need to do client-side.

astral wedge
#

wow

#

ok, excellent

harsh gazelle
#

You should also set up a webhook endpoint and listen for payment events you're interested in, if you haven't done so yet.

astral wedge
#

my questions may seem fishy, but if you knew my previous solution .....

harsh gazelle
#

There's no guarantee the client-side code will run after authentication succeeds

astral wedge
#

you mean code a webhook endpoint as fallback for not-working stripe.js post-authentication?

#

Is not-working stripe.js post-authentication frequent ??

harsh gazelle
#

Imagine a person on a train using your site. They're prompted for 3D Secure, complete the process, and then the train goes into a tunnel before your client-side code can run. Webhooks are how you know the payment succeeded in a case like that.

#

You can replace the train scenario with spotty cell coverage, a battery dying, falling into a pool, and so on. ๐Ÿ™‚

astral wedge
#

hahahahhahahah

#

stripe is so far better than my previous solution omg

harsh gazelle
#

That's great to hear! ๐Ÿ™‚

astral wedge
#

I'm just wondering for which webhook event I should listen in this case. In this case (correct me if I'm wrong) I'd need to setup a webhook endpoint that is triggered when a payment gets authorized. When that's the case, I check if there's an according record in my DB; and if not, update the internal DB accordingly. Correct? But this means that I would run the DB query in vain many times, no?

harsh gazelle
#

Not sure what you mean about that last part, about running the DB query in vain? Can you provide more details about that?

astral wedge
harsh gazelle
#

Ah, and yes, for separate capture you probably want payment_intent.amount_capturable_updated as well.

astral wedge
#

Sure, well given your example: Client is in train, confirms the payment, gets prompted for 3D authentication, authenticates, then gets into the tunnel and loses connection. Consequence: client paid, but payment was not registered in my platform. Hence the webhook would need to check in the DB if the authorized payment has already been registered or not.

For an authentication that worked normally, that queyr will be in vain.

#

You see what I mean?

#

I thought payment_intent.succeeded represents a fully captured payment? Remember that I'm using auth and capture..?

harsh gazelle
#

Yep, you may not need to listen to that one, just payment_intent.amount_capturable_updated, but it depends on the specifics of your use case and payment flows.

#

Generally when you're doing client-side confirmation you want to have both the client-side code and the webhooks both trigger server-side confirmation of the payment success (or capture success), with the one that happens first being the "winner".

astral wedge
#

But to tell who's the winner, I'll need to do the query with the webhook

harsh gazelle
#

I need to get going, but @olive torrent should be able to help you further. ๐Ÿ™‚

astral wedge
#

Ok, thank you so much, really!

#

take care!

#

@olive torrent are you there? ๐Ÿ™‚

olive torrent
#

Yep! Getting up to speed now

astral wedge
#

first of all, you know how I can export a chat from discord to txt or whatever? Or would you just copy-paste?

#

The chat has been crazy useful so far

#

never mind, just copy-pasted it

#

So to quickly brief you about my remaining issue:

My platform uses auth and capture card payments only, and I'm currently working in the integration with stripe.

The last concern I have is authentication for the authorization of a payment. Particularly, the scenario where:

a) client clicks on "pay" on my platform
b) payment intent triggered on the server-side
c) payment intent confirmed on the client-side
d) payment authorization requires authentication, which is then automatically triggered via stripe
e) client authenicates, hence client pays
f) client loses connection

RESULT: client authorized the payment for a service of the platform, but the platform failed to register the payment. Hence, the platform will block the client from getting the service for which he actually paid, as the platforms internal DB did not register the successful authorization.

#

The solution proposed by @harsh gazelle for this was the use of webhooks. In my particular case, I wondered if I'm correct that I'd need to setup an endpoint for the payment_intent.amount_capturable_updated event, and everytime that event occurs, check via the webhook if the according payment authorization has been correctly registered within the DB. correct?

#

Or do you see a more suitable event / approach?

olive torrent
#

I think payment_intent.amount_capturable_updated is all you actually need for this use-case. If the customer loses connection/abandons the payment flow after authentication was successful, then the event will still trigger and your endpoint can then handle actually capturing the payment.

astral wedge
#

Well all I want to do in this case is update the according payment record within my internal db to "authorized"; I do not want to capture authorized payments immediately

olive torrent
#

So yeah, then that's the point at which you would update your DB to indicate the payment method was auth'd successfully.

astral wedge
#

The consequence of this is that I have to, every time a payment gets authorized, check in the internal DB if the according record has been properly to "authorized". In most of the cases, this will be a query in vain. Do you see a solution to prevent that to be done in vain?

#

Because I suppose that this webhook is a very nice safety feature, but in 90% of the cases, it will not be needed. So in many cases, the status check query triggered by the endpoint will run in vain, don't you see it like that as well?

#

And do you eventually see a better solution?

olive torrent
#

I can't think of a way around that unfortunately, since you are going to have to keep your DB synced with Stripe, there is always a possibility that it might not be up to date. And you can't rely on client-side actions to discern what state a Payment Intent or Payment Method might be in

astral wedge
#

Ok. Anyways, better consume more resources to be on the safe side. Thanks a lot!

olive torrent
#

Sure thing!

astral wedge
#

Thanks a lot, I gotta repeat your support is awesommmmmme

#

have a good one!

olive torrent
#

Likewise! ๐Ÿ™Œ

astral wedge
#

Sorry another follow-up question

#

On my platform, I store cc for later use for recurring payments, everytime a given service is processed on my platform. To do so, I use this approach here: https://stripe.com/docs/payments/save-and-reuse?platform=web

My question that I wanted to confirm: My platform uses card payments only (including Apple Pay + Google Pay), so these are the only payment methods to be stored in the above-mentioned way. In these scenarios, is it true (that is what you non-technical support stated) that this will NEVER cause a redirect if the client needs authentication?

#

I'm asking because I'm currently generating special authentication URLs with tokens as GETs etc. but if this never occurs, I can completely omit that and simply provide redirect_url: https://mydomain.com and redirect: if_required to confirmSetup and that's it

#

@olive torrent still here?

olive torrent
#

Still here! Just parsing

#

Apologies for the delay (thread juggling ๐Ÿคน )

astral wedge
#

hahahha np

olive torrent
#

My platform uses card payments only (including Apple Pay + Google Pay), so these are the only payment methods to be stored in the above-mentioned way. In these scenarios, is it true (that is what you non-technical support stated) that this will NEVER cause a redirect if the client needs authentication?
That's correct. Wallet payment methods won't redirect for authentication. They're stored in the customer's browser and are kind of implicitely assumed to be auth'd properly because of that.

astral wedge
#

True for Credit Cards and google Pay and Apple pay?

hearty grotto
#

yes

astral wedge
#

perfect!

#

thank you so much, everyone of you!

hearty grotto
#

happy to help ๐Ÿ™‚

astral wedge
#

And another thing to double-check; there's no limit on payments you can authorize at the same time on a single credit card, right?

hearty grotto
#

not sure what you mean by that

astral wedge
#

Like when you have a stored payment method, can you create multiple payment intent API calls to authorize payments before all payments that have been authorized have been captured?

#

Like I authorize a payment for service A. Can I authorize another payment for another service B on the same card, before A gets captured? And can I authorize another payment for another service C on the same card, before A and / or B get captured?

#

and so on..

hearty grotto
#

Yes it works though it's up to each bank to approve/decline payments