#JeremyFlight
1 messages ยท Page 1 of 1 (latest)
๐ Thanks for reaching out
After creating the customer, you don't need to create manually the PaymentMethod and create a SetupIntent after. You should instead create a SetupIntent for the customer, and after confirming it, an authenticated PaymentMethod will be created and attached to the customer.
I suggest you to take a look at this complete guide for it.
https://stripe.com/docs/payments/save-and-reuse?platform=web#charge-saved-payment-method
For the 3DS popup, I suggest you also to let Stripe SDK handle it. When following that guide, you'll need to call stripe.confirmSetup which will handle and complete any required options, such as 3DS:
https://stripe.com/docs/js/setup_intents/confirm_setup
But we're not using the Stripe JS library at all. We collect the card details from the customer and process those via your PHP SDK. If I have to use the Stripe JS SDK instead, I have a lot of code re-writing to do!
Is your company PCI level 1 certified and audited for credit card handling and will submit a SAQ D questionnaire to Stripe every year? (https://stripe.com/docs/security/guide#validating-pci-compliance)
No. We don't store the credit card details anywhere: just collect them from an HTML form and pass them through to Stripe via your SDK / API
that's still API Direct and requires a SAQ D since your server side code touches raw card details so it needs to be developed in a PCI compliant way to protect against accidental/deliberate leaks etc. I'd suggest consulting your legal advice
taking a step back, it's almost always wrong to create SetupIntent and the immeidately create a PaymentIntent. You only use SetupIntents if you're saving a card now without charging, and will charge it some hours/days/weeks later
So if we swap to using the Stripe JS code, we can circumvent the PCI requirements?
yes; the page I linked clearly explains it really if you read that
We don't immediately create a payment intent after a setup intent. That may be how it looks in the test script ๐ But we create the setup intent and then some while later (e.g. days or weeks) we create a payment intent to process a transaction
cool
so you want to simulate that later payment getting an exemption from 3D Secure?
Yep
then you need to
a) use the appropriate 3155 test card that supports exemptions on set up (https://stripe.com/docs/testing#authentication-and-setup)
b) specifiy the PaymentIntent as merchant-initiated by passing off_session:true https://stripe.com/docs/payments/save-and-reuse?platform=web#charge-saved-payment-method
Yep - doing all of that already! Stil get status of "requires_action" back from the PaymentIntent, despite having complete the 3DS verification process
do you have an example PaymentIntent ID pi_xxx?
bump @night knoll
Try: pi_3LzjDVBZ9qUe1kQ51BBG3TXv
that PaymentIntent is not at all like the example at (b)
it's not passing off_session:true
compare with
note that setup_future_usage:off_session is not the same thing as off_session:true and they're not interchangeable, to be clear
So at the moment, I pass this to my Setup Intent:
$arrData = array('usage' => 'off_session'
,'return_url' => 'https://XXX'
,'payment_method' => $intStripePaymentMethodId
,'confirm' => true
,'customer' => $intStripeCustomerId);
And later on, when I create a PaymentIntent with hte intention of completing a transaction at that time, I pass this data:
$arrData = array('amount' => (123 * 100)
,'currency' => 'gbp'
,'customer' => $strCustomerId
,'confirm' => true
,'off_session' => true
,'payment_method' => $strPaymentMethodId
,'payment_method_types' => array('card'));
If I change the payment intent to be this:
$arrData = array('amount' => (123 * 100)
,'currency' => 'gbp'
,'customer' => $strCustomerId
,'setup_future_usage' => 'off_session'
,'payment_method' => $strPaymentMethodId
,'payment_method_types' => array('card'));
...I still end up with a Payment Intent showing a status "requires_confirmation".
Once I've completed the 3DS process with the customer, what should I subsequently pass in to my PaymentIntent to complete a transaction against their stored payment method?
Once I've completed the 3DS process with the customer, what should I subsequently pass in to my PaymentIntent to complete a transaction against their stored payment method?
the second code snippet you posted does exactly that, so you'd use that. It works fine.
not sure what the context of the third snippet is and when/why you do that, can you elaborate?
So the first screen collects payment details, connects to Stripe's API, creates a Stripe Customer, a Stripe Payment Method, attaches the Payment Method to the Customer and then creates a Stripe Setup Intent.
WHen I create hte SetupIntent, I do so with this data:
$arrData = array('usage' => 'off_session'
,'return_url' => 'https://xxx/'
,'payment_method' => $intStripePaymentMethodId
,'confirm' => true
,'customer' => $intStripeCustomerId);
The SetupIntent shows a status of "requires_action" so I use the URL from $setupIntent->next_action->redirect_to_url->url and I show that in an iframe. This is effectively the 3DS verification for the customer and they complete that online.
At a later date when I need to take money from the customer, I create a PaymentIntent and pass this data:
$arrData = array('amount' => (123 * 100)
,'currency' => 'gbp'
,'customer' => $strCustomerId
,'confirm' => true
,'off_session' => true
,'payment_method' => $strPaymentMethodId
,'payment_method_types' => array('card'));
...but I think you're suggesting that I pass this data:
$arrData = array('amount' => (123 * 100)
,'currency' => 'gbp'
,'customer' => $strCustomerId
,'setup_future_usage' => 'off_session'
,'payment_method' => $strPaymentMethodId
,'payment_method_types' => array('card'));
Either way, I don't get a complete transaction showing in my Stripe account.
, creates a Stripe Customer, a Stripe Payment Method, attaches the Payment Method to the Customer and then creates a Stripe Setup Intent.
note that's the wrong order. It should be, create Customer, CreatePaymentMethod, create the SetupIntent + confirm the SetupIntent(passingcustomerandpayment_methodandconfirm). The SI will attach the card automatically when it succeeds, you don't need to and shouldn't call the manual customer attach method.
The SetupIntent shows a status of "requires_action" so I use the URL from $setupIntent->next_action->redirect_to_url->url and I show that in an iframe. This is effectively the 3DS verification for the customer and they complete that online.
sounds good. Note it's not "for the customer", it's just for that one set up. Future payments may or may not require 3DS, depends if their bank honors the exemption for merchant-initiated-transactions. But everything you're doing there is correct yes
At a later date when I need to take money from the customer, I create a PaymentIntent and pass this data:
looks perfect
...but I think you're suggesting that I pass this data:
100% not no? I'm saying that you are passing setup_future_usage, I linked you to the request ID where you do(https://dashboard.stripe.com/test/logs/req_Wi1rqQD8VoTDiP) and I explained how that is wrong and not what's in the docs(what's in the docs is what is in your second code snippet, which is correct and is what you should use and will work fine)
Either way, I don't get a complete transaction showing in my Stripe account.
do you have an examplepi_xxxwhere you did the off session PaymentIntent using the exact code from the second snippet that is still incomplete?
Yes: pi_3M0Q2ABZ9qUe1kQ515VOheBS
https://dashboard.stripe.com/test/logs/req_DAbSHU2Nt4CXce is the creation request
you can see it passed setup_future_usage
there is no way whatsoever that PaymentIntent came from that second code snippet
so you are not running the code you think you are
This is my PHP code that runs when I create the PaymentIntent:
$arrData = array('amount' => (123 * 100)
,'currency' => 'gbp'
,'customer' => $strCustomerId
,'setup_future_usage' => 'off_session'
,'payment_method' => $strPaymentMethodId
,'payment_method_types' => array('card'));
$paymentIntent = $stripe->paymentIntents->create($arrData);
$strPaymentMethodId is the id of the payment method created previously.
?
like yes, that code is wrong
the correct code is
$arrData = array('amount' => (123 * 100)
,'currency' => 'gbp'
,'customer' => $strCustomerId
,'confirm' => true
,'off_session' => true
,'payment_method' => $strPaymentMethodId
,'payment_method_types' => array('card'));
as we've established multiple times now
so why aren't you using that? If you do, and you use the 3155 card previously set up via a SetupIntent, it will just work.
Why are you being so hostile? Is this the way Stripe Customer Support works?
just trying to help you. This is a common question and I've explained the answer earlier here over an hour ago
are the next steps clear? what can I clarify?
Yep, and we've spent over 2-weeks talking to a variety of Stripe representatives who haven't been much help at all... so perhaps a degree of patience wouldn't go amiss?
I have no way to know that, sorry you have that experience
anyway, I've unblocked you(in 10 minutes), can you make the changes and let me know what happens?
I have a PaymentIntent (ref pi_3M0Q9pBZ9qUe1kQ51cQVEdOF) which shows a status of success. ๐ This is obviously completed in the test environment though, with test card 4000002500003155. If I had completed the same process in live with a real card, am I right to think that all future transactions that we process against that customer's payment method would go through sucessfully without further 3DS intervention?
no, since it's up to the bank
if you integrate this way, we request an exemption from them https://stripe.com/guides/strong-customer-authentication#exemptions-to-strong-customer-authentication Merchant-initiated transactions (including variable subscriptions)
in most cases that would be expected to succeed
that test card simulates the case where it suceeds
you need to handle the case where it doesn't and contact the customer. Explained in the link from earlier https://stripe.com/docs/payments/save-and-reuse?platform=web#charge-saved-payment-method ; you need to contact the customer and have them come back to complete 3D Secure again
OK - and if I were to use the JS SDK instead, does that affect the chances of future transactions succeeding / failing or is the situation the same?
the integration at that level makes no difference
you should still use the SDK though so you don't have to get PCI audited and certified and for better conversion/user experience using our optimised payment components
So the bottom line is... we need to accommodate a situation where transactions fails because of 3DS requirements / complexities and in that instance, we need to get our customer back to the website to repeat the 3DS verification process... so that future transaction will go through OK until such time as the bank says "No" and we repeat the cycle once more?
that's a fair summary yes
OK. And from our point if view, understanding whether a given transaction has succeeded or not is simply a case of looking at the status of the PaymentIntent after it's confirmed. Status = success and we know we've been paid; status of anything else and we know we haven't. More specifically, if the status = requires_action, we need to repeat the 3DS verification with the customer?
no, it would be status=requires_payment_method in this case (if the off-session PaymentIntent declines because of required authentication, we don't return status=requires_action, that's only done in on-session ; it's a bit of a quirk)
OK. Thanks for your help.