Problem/Motivation

From September 2019, most ecommerce payments will have to undergo Strong Customer Authentication (SCA). A new authentication protocol is being introduced by the Card Schemes (Mastercard, Visa, etc.) called 3D Secure 2 (3DS v2).

Proposed resolution

Support the 3DS v2 protocol by introducing options that will make it easy for to comply with the SCA requirement.

Remaining tasks

Testing:

  • Capture flow
  • Auth + Capture
  • Payment refund
  • Void payment
  • Auth + Anon
  • Abanon checkout and come back with the same order
  • Test integration with Commerce Recurring

Tasks

  • Require stripe/stripe-php:~6.9
  • Ensure JavaScript error handling (#48, #53)
  • Declining authorization does nto display error message stripeErrorHandler(result);, element missing
  • Write FunctionalJavascript tests?

User interface changes

  • stripe_review checkout pane added and is required

API changes

Data model changes

  • Stripe payment method type added, to store customer ID for anonymous users

Release notes snippet

CommentFileSizeAuthor
#128 Screen Shot 2019-09-02 at 3.03.35 pm.png301.03 KBblacklabel_tom
#128 Screen Shot 2019-09-02 at 3.03.28 pm.png59.44 KBblacklabel_tom
#128 Screen Shot 2019-09-02 at 2.59.33 pm.png39.36 KBblacklabel_tom
#124 3039032-124.patch76.27 KBmglaman
#122 3039032-122.patch76.19 KBmglaman
#120 3039032-120.patch74.04 KBmglaman
#118 3039032-118.patch65.63 KBmglaman
#116 3039032-116.patch60.4 KBmglaman
#114 3039032-114.patch51.01 KBmglaman
#111 3039032-111.patch51 KBmglaman
#107 3039032-107.patch53.08 KBmglaman
#105 3039032-105.patch53.03 KBmglaman
#102 Payment-with-Recurring-card-option.png63.97 KBharika gujjula
#102 Payment-with-card-details.png77.62 KBharika gujjula
#101 3039032-101.patch53.58 KBmglaman
#94 3039032-94.patch53.26 KBmglaman
#92 3039032-92.patch48.91 KBmglaman
#85 3039032-85.patch36.07 KBmglaman
#84 Screen Shot 2019-08-12 at 4.05.32 PM.png21.49 KBmglaman
#83 3039032-83.patch33.01 KBmglaman
#82 3039032-82.patch30.85 KBmglaman
#77 3039032-77.patch31.82 KBmglaman
#76 3039032-76.patch33.42 KBmglaman
#75 3039032-75.patch36.95 KBmglaman
#70 3039032-70.patch43.97 KBMarko B
#65 3039032-65.patch43.99 KBmglaman
#63 3039032-63.patch42.85 KBmglaman
#45 interdiff-45-37.txt54.37 KBmglaman
#45 3039032-45.patch87.36 KBmglaman
#43 3039032-43.patch86.98 KBmglaman
#43 Screen Shot 2019-07-22 at 3.56.25 PM.png127.64 KBmglaman
#37 commerce_stripe-3039032-3ds-37.patch45.28 KBczigor
#34 interdiff-29-34.txt13.58 KBczigor
#34 commerce_stripe-3039032-3ds-34.patch45.33 KBczigor
#29 interdiff-25-29.txt14.52 KBczigor
#29 commerce_stripe-3039032-3ds-29.patch42.98 KBczigor
#25 interdiff-24-25.txt5.17 KBczigor
#25 commerce_stripe-3039032-3ds-25.patch38.54 KBczigor
#24 interdiff-21-24.txt6.96 KBczigor
#24 commerce_stripe-3039032-3ds-24.patch37 KBczigor
#21 commerce_stripe-3039032-3ds-21.patch36.38 KBczigor
#18 commerce_stripe-3039032-3ds-18.patch38.5 KBczigor
#16 commerce_stripe-3039032-3ds-16.patch38.4 KBczigor
#13 commerce_stripe-3039032-3ds-13.patch38.36 KBczigor
#9 commerce_stripe-3039032-3ds-9.patch30.24 KBczigor
#6 commerce_stripe-3039032-3ds-5.patch28.84 KBczigor
#4 commerce_stripe-3039032-3ds-4.patch25.03 KBczigor

Comments

adpo created an issue. See original summary.

bojanz’s picture

We are committed to fixing this in the near future.

bojanz’s picture

Version: 7.x-1.2 » 8.x-1.x-dev

Forgot to mention that the promise applies to the D8 version.

D7 already has its own issue: #2717383: Add support for 3D Secure.

czigor’s picture

Status: Active » Needs work
StatusFileSize
new25.03 KB

Uploading a patch for backup purposes.

Creates a checkout pane and adds it to the review checkout page. The 3DS2 option needs to be enabled on the payment gateway form.

It already kind of works, but it's not fully tested. I know it breaks anonymous checkout.

Terminal payments are not handled.

czigor’s picture

Status: Needs work » Needs review

The patch fixes anonymous checkout. For this a new payment method type has been added with a 'Stripe customer ID' field.

I tested the patch both with 3DS enabled and disabled, both with logged in and anonymous users and with test cards that do and do not require 3DS authentication. https://stripe.com/docs/testing#three-ds-cards Everything seems to work fine.

Added update and post_update hooks for:
- creating the new 'Stripe customer ID' field
- updating stripe payment gateways to use the new stripe payment method type.
- updating existing stripe payment methods to be of stripe bundle

czigor’s picture

StatusFileSize
new28.84 KB

The last submitted patch, 4: commerce_stripe-3039032-3ds-4.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

Status: Needs review » Needs work

The last submitted patch, 6: commerce_stripe-3039032-3ds-5.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

czigor’s picture

Status: Needs work » Needs review
StatusFileSize
new30.24 KB

Fixed all the coding standards issues, except for the namespacing ones.

Status: Needs review » Needs work

The last submitted patch, 9: commerce_stripe-3039032-3ds-9.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

czigor’s picture

Status: Needs work » Needs review

Fails only because of the full namespacing of Stripe classes.

handkerchief’s picture

Great work so far. Thx you so much for your work. This feature is very important and I look forward to using it soon.

czigor’s picture

StatusFileSize
new38.36 KB

Adding 3d2 setting to config schema.
Also adding some tests. They mostly fail, sometimes pass.

Status: Needs review » Needs work

The last submitted patch, 13: commerce_stripe-3039032-3ds-13.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

yong.gi’s picture

Thank you for the great work. The commerce_stripe-3039032-3ds-13.patch works for me with drupal/commerce_stripe:1.x-dev. in my composer based drupal installation.

However, I can't complete applying the db update. When run "drush updatedb", I got following output:

The following updates are pending:

commerce_stripe module :
Update Stripe payment methods to be of stripe bundle.

Do you wish to run all pending updates? (y/n): y
Post updating commerce_stripe [ok]
Failed: Drupal\Core\Database\InvalidQueryException: Query condition 'payment_gateway = ()' cannot be empty. in [error]
Drupal\Core\Database\Query\Condition->condition() (line 103 of core/lib/Drupal/Core/Database/Query/Condition.php).
Cache rebuild complete. [ok]
Finished performing updates. [ok]

The error is caused by the code in commerce_stripe/commerce_stripe.post_update.php.

/**
* Update Stripe payment methods to be of stripe bundle.
*/
function commerce_stripe_post_update_payment_methods() {
$gateways = PaymentGateway::loadMultiple(['plugin' => 'stripe']);
$gateway_names = array_keys($gateways);
db_update('commerce_payment_method')
->fields(['type' => 'stripe'])
->condition('payment_gateway', $gateway_names)
->execute();
}

The empty $gateway_names causes the db_update fail. Would it be better to add a check to $gateway_names?

/**
* Update Stripe payment methods to be of stripe bundle.
*/
function commerce_stripe_post_update_payment_methods() {
$gateways = PaymentGateway::loadMultiple(['plugin' => 'stripe']);
$gateway_names = array_keys($gateways);
if (!empty($gateway_names)) {
db_update('commerce_payment_method')
->fields(['type' => 'stripe'])
->condition('payment_gateway', $gateway_names)
->execute();
}
}

czigor’s picture

Status: Needs work » Needs review
StatusFileSize
new38.4 KB

Thanks for the testing, yong.gi. Added the !empty() check to the post_update hook.

Tests still fail.

Status: Needs review » Needs work

The last submitted patch, 16: commerce_stripe-3039032-3ds-16.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

czigor’s picture

Status: Needs work » Needs review
StatusFileSize
new38.5 KB

Removing the #description of the 3DS setting since it is not true.

Status: Needs review » Needs work

The last submitted patch, 18: commerce_stripe-3039032-3ds-18.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

czigor’s picture

As per https://stripe.com/docs/payments/payment-intents/quickstart

To ensure that your integration is SCA-ready, be sure to always provide the customer’s name, email, billing address, and shipping address (if available) to the stripe.handleCardPayment call.

czigor’s picture

Status: Needs work » Needs review
StatusFileSize
new36.38 KB

1. Removed the test from the patch. #howtofixtestfailures
2. We were creating 2 payment intents during a checkout while we only need 1. Fixed.
3. Failed 3DS validation broke the payment intent. Fixed.

TODOs:
1. Further testing of both 3DS and no-3DS checkouts.
2. Pass billing info to stripe.handleCardPayment if possible (see previous comment).

Status: Needs review » Needs work

The last submitted patch, 21: commerce_stripe-3039032-3ds-21.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

mglaman’s picture

Quick review.

  1. +++ b/commerce_stripe.install
    @@ -21,3 +23,18 @@ function commerce_stripe_requirements($phase) {
    +function commerce_stripe_update_8001() {
    +  $field_definition = BundleFieldDefinition::create('string')
    +    ->setName('stripe_customer_id')
    +    ->setLabel(t('Stripe customer ID'))
    +    ->setTargetEntityTypeId('commerce_payment_method')
    +    ->setTargetBundle('stripe')
    +    ->setDescription(t('The stripe customer ID for anonymous users.'));
    ...
    +  $configurable_field_manager = \Drupal::service('commerce.configurable_field_manager');
    +  $configurable_field_manager->createField($field_definition);
    

    Won't this fail unless the cached payment method type definitions are cleared? because the target bundle will not exist

  2. +++ b/commerce_stripe.module
    @@ -0,0 +1,14 @@
    +function commerce_stripe_page_attachments(array &$page) {
    +  // We need this on all pages.
    +  $page['#attached']['library'][] = 'commerce_stripe/stripe';
    +}
    

    Why do we need this on all pages?

  3. +++ b/commerce_stripe.post_update.php
    @@ -0,0 +1,34 @@
    +  $gateways = PaymentGateway::loadMultiple(['plugin' => 'stripe']);
    ...
    +  $gateways = PaymentGateway::loadMultiple(['plugin' => 'stripe']);
    

    This needs loadByProperties. This update hook will not work.

  4. +++ b/commerce_stripe.post_update.php
    @@ -0,0 +1,34 @@
    +    db_update('commerce_payment_method')
    +      ->fields(['type' => 'stripe'])
    +      ->condition('payment_gateway', $gateway_names)
    +      ->execute();
    

    db_update is deprecated.

  5. +++ b/js/commerce_stripe.review.js
    @@ -0,0 +1,92 @@
    +      let self = this;
    ...
    +        let $form = $(this).closest('form');
    +        let stripe = Stripe(drupalSettings.commerceStripe.publishableKey, {
    

    We cannot use `let` or `const` without transpiling.

  6. +++ b/src/Controller/ThreeDS.php
    @@ -0,0 +1,47 @@
    +      throw new AccessException('The order has no payment intents.');
    

    This should be an HTTP access exception. Or does Drupal core convert this to one?

  7. +++ b/src/MinorUnits.php
    @@ -0,0 +1,44 @@
    +   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
    +   *   The entity query factory.
    

    Does not match description

  8. +++ b/src/Plugin/Commerce/CheckoutPane/StripeReview.php
    @@ -0,0 +1,225 @@
    +    if (empty($this->order->get('payment_gateway')->entity)) {
    +      return FALSE;
    +    }
    +    if ($this->order->get('payment_gateway')->entity->getPluginId() !== 'stripe') {
    +      return FALSE;
    +    }
    

    We should first check `get('payment_gateway')->isEmpty()` and then assert `->entity` is `instanceof` the gateway interface.

    Then we don't need to check the plugin ID either.

  9. +++ b/src/Plugin/Commerce/CheckoutPane/StripeReview.php
    @@ -0,0 +1,225 @@
    +        $customer_id = $this->order->getCustomer()->commerce_remote_id->remote_id;
    

    This isn't right.

        /** @var \Drupal\commerce\Plugin\Field\FieldType\RemoteIdFieldItemListInterface $remote_ids */
        $remote_ids = $user->get('commerce_remote_id');
        $customer_remote_id = $remote_ids->getByProvider($this->gateway->id() . '|' . $plugin->getMode());
    
czigor’s picture

StatusFileSize
new37 KB
new6.96 KB

1. What would this cause? I tested the (post)update hooks and they work well. The field configuration does get created, even if I use a non-existent bundle name, like 'stripe2'.
2. Added a comment to explain.
3-5, 7-9: Fixed
6. What's an HTTP access exception?

czigor’s picture

Status: Needs work » Needs review
StatusFileSize
new38.54 KB
new5.17 KB

As per #20 we're also sending shipping information now. I don't think it matters much but I tested this with latest devs from everything and the patch in #3054351: Add support for the Commerce 2.14 address book.

Status: Needs review » Needs work

The last submitted patch, 25: commerce_stripe-3039032-3ds-25.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

czigor’s picture

Status: Needs work » Needs review
Rafal Lukawiecki’s picture

I do not use commerce_stripe as my company site is Ubercart and so uc_stripe based. However, I would like to offer a heads-up to the maintainers of this issue and of the module that I have been contacted by Chris Traganos (@trag-stripe) from Stripe to discuss the help that Stripe may be able to offer in order to get Drupal Stripe plug-ins SCA compliant. If you are interested in my discussion and the suggestions that Stripe made, please see comment #10 of #3040854: Implement Stripe PaymentIntents for SCA (Strong Customer Authentication).

Ideally, commerce_stripe would end up being listed by Stripe as one of their SCA compliant integrations. @AndraeRay (maintainer of uc_stripe) and I will be working with Stripe to get uc_stripe to that status. Further, while I am unsure if Ubercart can be listed as a Stripe "Partner" (see #3061293: Identify uc_stripe as a plug-in to Stripe) perhaps that is something that Commerce Guys could be interested in—unsure, as I do not know the nature of the Drupal Commerce codebase organisational maintainership.

I hope this is of some help to you. I am only sharing this in the Drupal spirit of mutual assistance and collaboration. Fingers crossed for your SCA compliance. I will post this comment on both SCA issues that commerce_stripe is tracking at the moment.

czigor’s picture

StatusFileSize
new42.98 KB
new14.52 KB

Thanks Rafal, this drew my attention to some SCA issues we have in the current patch.

Switched to using PaymentMethods instead of Cards and Sources and to using stripe.createPaymentMethod() from stripe.createToken().

Did some manual testing, these work:
1. Checkout with authenticated user having payment methods using an existing payment method.
2. Checkout with authenticated user using a new payment method.
3. Checkout with authenticated user having no payment methods using an existing payment method.
4. Checkout with anonymous user.

Status: Needs review » Needs work

The last submitted patch, 29: commerce_stripe-3039032-3ds-29.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

czigor’s picture

czigor’s picture

Status: Needs work » Needs review
audriusb’s picture

\Stripe\PaymentIntent::create, \Stripe\PaymentIntent::update, \Stripe\PaymentIntent::retrieve can throw errors, therefore surround them with try/catch. You have some retrieve surrounded but not all of them.

I can't see anywhere in code where you \Stripe\PaymentIntent::cancel. If payment intent is not used, cancel it, don't leave it hanging in Stripe dashboard.

Also add idempotency_key to \Stripe\PaymentIntent::create

czigor’s picture

StatusFileSize
new45.33 KB
new13.58 KB

Thanks audriusb!

Added the try-catch blocks and the idempotency keys. As per the payment intent cancellation, I'm not sure where I should do that unless we create a cronjob for it to clean them up.

Status: Needs review » Needs work

The last submitted patch, 34: commerce_stripe-3039032-3ds-34.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

audriusb’s picture

Yeah, cronjob sounds good. Couple more points from our experience:
1. Add description to \Stripe\PaymentIntent::create. It helps users to see what payment is for when viewing transactions list in the Stripe dashboard. Without description, all they see is payment intent ids.
2. I see you have support for Stripe webhooks. This one has it's own unique twists:
2.1 If Stripe account has any connected Stripe accounts, then all connected account's webhooks are fired against main account as well, therefore you get unrelated requests. To mitigate that, add some unique data to \Stripe\PaymentIntent::create metadata and check for it in the webhook endpoint. Check for livemode=true, too.
2.2 Couple times Stripe stopped sending webhooks for 10 minutes straight but was accepting payments. Users ended up paying but not receiving goods. To mitigate this, find where Stripe returns success in javascript and fire ajax call to server to double check the payment status and complete the order manually. This way you have double check for completing orders. Make sure 2nd try to complete order doesnt throw error. Usually webhook still completes first and manual attempt is 2nd.

czigor’s picture

Status: Needs work » Needs review
StatusFileSize
new45.28 KB

1. Added the order_id to the description.
2. Not sure what you mean by webhooks. I don't think we use them. We retrieve the payment intent by intent id (which is stored on the order) when capturing the payment, so there's no way we would try to handle payment intents we should not.

Status: Needs review » Needs work

The last submitted patch, 37: commerce_stripe-3039032-3ds-37.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

czigor’s picture

Status: Needs work » Needs review

For handling unused payment intents in a cronjob let's open a different issue. I'm not sure if it should not go to a custom module instead and this patch is big enough already.

mglaman’s picture

Assigned: Unassigned » mglaman

Reviewing today

mglaman’s picture

Here is an initial review of the code, I need to go through the comments once again

  1. +++ b/commerce_stripe.install
    @@ -21,3 +23,18 @@ function commerce_stripe_requirements($phase) {
    +/**
    + * Create the stripe_customer_id payment method field.
    + */
    +function commerce_stripe_update_8001() {
    

    Do we need to clear the payment method type definitions and bundle info prior to running this so that the field has a valid destination to install to?

  2. +++ b/commerce_stripe.post_update.php
    @@ -0,0 +1,41 @@
    +function commerce_stripe_post_update_payment_method_type() {
    +  $gateways = PaymentGateway::loadMultiple();
    +  foreach ($gateways as $gateway) {
    +    if ($gateway->getPluginId() === 'stripe') {
    +      $config = $gateway->getPluginConfiguration();
    

    We can perform an entity query against the plugin ID.

  3. +++ b/commerce_stripe.post_update.php
    @@ -0,0 +1,41 @@
    +  $gateways = PaymentGateway::loadMultiple();
    +  $gateway_names = [];
    +  foreach ($gateways as $gateway) {
    +    if ($gateway->getPluginId() === 'stripe') {
    +      $gateway_names[] = $gateway->id();
    

    We could use an entity query with conditions to load the IDs.

  4. +++ b/js/commerce_stripe.review.js
    @@ -0,0 +1,91 @@
    +                $.get("/admin/commerce-stripe/post-failed-3ds/" + drupalSettings.commerceStripe.orderId  + "/" + result.error.payment_intent.id);
    

    We need to use Drupal.url to ensure proper prefix of the path.

  5. +++ b/src/Controller/ThreeDS.php
    @@ -0,0 +1,53 @@
    +        return ['#markup' => $this->t('Payment intent updated.')];
    ...
    +    return ['#markup' => $this->t('The payment intent does not belong to this order.')];
    

    s/markup/plain_text ?

  6. +++ b/src/Plugin/Commerce/CheckoutPane/StripeReview.php
    @@ -0,0 +1,261 @@
    +  public function isVisible() {
    +    $gateway = $this->order->get('payment_gateway');
    +    if ($gateway->isEmpty() || !($gateway->entity->getPlugin() instanceof StripeInterface)) {
    +      return FALSE;
    ...
    +    if (!$stripe_plugin->getConfiguration()['enable_3ds2'] || !empty($form_values)) {
    

    Should visibility check if 3DS2 is enabled?

  7. +++ b/src/Plugin/Commerce/PaymentGateway/Stripe.php
    @@ -178,46 +186,81 @@ class Stripe extends OnsitePaymentGatewayBase implements StripeInterface {
    +      // Stub for payment through the admin UI. Does not work.
    +      // @todo Fix it.
    +      if (!isset($remote_id)) {
    

    We should open up a follow up issue and add link here

  8. +++ b/src/Plugin/Commerce/PaymentGateway/Stripe.php
    @@ -431,14 +511,13 @@ class Stripe extends OnsitePaymentGatewayBase implements StripeInterface {
    +      'amex' => 'amex',
    +      'diners' => 'dinersclub',
    +      'discover' => 'discover',
    +      'jcb' => 'jcb',
    +      'mastercard' => 'mastercard',
    +      'visa' => 'visa',
    

    Did the library change causing a break here? should we reflect it in the composer.json?

mglaman’s picture

Status: Needs review » Needs work

I received an error immediately after going back and having to re-enter card information

Keys for idempotent requests can only be used with the same parameters they were first used with. Try using a key other than 'cust-demo+1563826315@commercekickstart.com' if you meant to execute a different request.

Idempotency keys are meant to reply a request if there's a 500 response. PHP isn't concurrent and we have no ways to retry a request (easily) before the request ends.

Per https://stripe.com/docs/api/idempotent_requests, lets use UUIDs for the keys.

], ['idempotency_key' => \Drupal::getContainer()->get('uuid')->generate()]);

Another easy way to reproduce w/ last patch: 🤦‍♂️forget to select shipping and have the form re-render on validation failures.

---

JavaScript error due to incorrect detach

Uncaught ReferenceError: $form is not defined
    at Object.detach (commerce_stripe.review.js?v=8.7.0-dev:87)
    at drupal.js?v=8.7.0-dev:42
    at Array.forEach (<anonymous>)
    at Object.Drupal.detachBehaviors (drupal.js?v=8.7.0-dev:39)
    at Drupal.AjaxCommands.insert (ajax.js?v=8.7.0-dev:532)
    at Drupal.Ajax.success (ajax.js?pv28lp:155)
    at HTMLScriptElement.bigPipeProcessPlaceholderReplacement (big_pipe.js?v=8.7.0-dev:38)
    at Function.each (jquery.min.js?v=3.2.1:2)
    at r.fn.init.each (jquery.min.js?v=3.2.1:2)
    at bigPipeProcessDocument (big_pipe.js?v=8.7.0-dev:52)
	detach	@	commerce_stripe.review.js?v=8.7.0-dev:87
(anonymous)	@	drupal.js?v=8.7.0-dev:42
Drupal.detachBehaviors	@	drupal.js?v=8.7.0-dev:39
insert	@	ajax.js?v=8.7.0-dev:532
Drupal.Ajax.success	@	ajax.js?pv28lp:155
bigPipeProcessPlaceholderReplacement	@	big_pipe.js?v=8.7.0-dev:38
each	@	jquery.min.js?v=3.2.1:2
each	@	jquery.min.js?v=3.2.1:2
bigPipeProcessDocument	@	big_pipe.js?v=8.7.0-dev:52
(anonymous)	@	big_pipe.js?v=8.7.0-dev:66
setTimeout (async)		
Drupal.throwError	@	drupal.js?v=8.7.0-dev:12
(anonymous)	@	drupal.js?v=8.7.0-dev:44
Drupal.detachBehaviors	@	drupal.js?v=8.7.0-dev:39
insert	@	ajax.js?v=8.7.0-dev:532
Drupal.Ajax.success	@	ajax.js?pv28lp:155
bigPipeProcessPlaceholderReplacement	@	big_pipe.js?v=8.7.0-dev:38
each	@	jquery.min.js?v=3.2.1:2
each	@	jquery.min.js?v=3.2.1:2
bigPipeProcessDocument	@	big_pipe.js?v=8.7.0-dev:52
(anonymous)	@	big_pipe.js?v=8.7.0-dev:66
setTimeout (async)		
bigPipeProcess	@	big_pipe.js?v=8.7.0-dev:65
(anonymous)	@	big_pipe.js?v=8.7.0-dev:72
(anonymous)	@	big_pipe.js?v=8.7.0-dev:80
mglaman’s picture

Status: Needs work » Needs review
StatusFileSize
new127.64 KB
new86.98 KB

This patch got me to testability. It needs works. But it uses UUID's for the idempotency key. I also added an ES6 -> JS setup so we can manage the JavaScript according to core's Prettier standards and ensure we support IE11.

Status: Needs review » Needs work

The last submitted patch, 43: 3039032-43.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

mglaman’s picture

Status: Needs work » Needs review
StatusFileSize
new87.36 KB
new54.37 KB

Here's a fix for some JS. Also attaching an interdiff from #37

Status: Needs review » Needs work

The last submitted patch, 45: 3039032-45.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

Marko B’s picture

Composer.json should be changed to "stripe/stripe-php": "~6.9" otherwise versions below produce
Error: Class 'Stripe\PaymentIntent' not found
can be seen that this class is first introduced in https://github.com/stripe/stripe-php/tree/v6.9.0/lib

Other then that I tried latest patch on vanilla commerce install, works nice. No problems with sandbox account.
Important note is that regular test cards wont trigger 3DS (like often used 4242424242424242)
There is a special section for 3DS cards that need to be used so that this functionality is triggered.
https://stripe.com/docs/testing#three-ds-cards

manish palawat’s picture

I received an error after enter wrong details for cards like

4242 4242 4242 4242
02/42
123

Drupal\Core\Database\DatabaseExceptionWrapper : SQLSTATE[22003]: Numeric value out of range: 1264 Out of range value for column 'expires' at row 1: INSERT INTO {commerce_payment_method} (type, uuid, payment_gateway, payment_gateway_mode, uid, remote_id, billing_profile__target_id, billing_profile__target_revision_id, reusable, is_default, expires, created, changed) VALUES (:db_insert_placeholder_0, :db_insert_placeholder_1, :db_insert_placeholder_2, :db_insert_placeholder_3, :db_insert_placeholder_4, :db_insert_placeholder_5, :db_insert_placeholder_6, :db_insert_placeholder_7, :db_insert_placeholder_8, :db_insert_placeholder_9, :db_insert_placeholder_10, :db_insert_placeholder_11, :db_insert_placeholder_12); Array ( [:db_insert_placeholder_0] => stripe [:db_insert_placeholder_1] => b297c08c-9e51-4fef-88dc-5398fd293236 [:db_insert_placeholder_2] => stripe [:db_insert_placeholder_3] => test [:db_insert_placeholder_4] => 234 [:db_insert_placeholder_5] => pm_1EzOe6EHyNH8KK0xpM28PGQH [:db_insert_placeholder_6] => 250 [:db_insert_placeholder_7] => 249 [:db_insert_placeholder_8] => 1 [:db_insert_placeholder_9] => [:db_insert_placeholder_10] => 2308780799 [:db_insert_placeholder_11] => 1563890817 [:db_insert_placeholder_12] => 1563890817 ) dans Drupal\Core\Entity\Sql\SqlContentEntityStorage->doSaveFieldItems() (ligne 978 de httpdocs/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php).

I think Patch does not handling these error messages . It should be thrown error by javascript when typing.

Also Sometimes got error anytime when click on payment button

Notice : Undefined index: #parents dans Drupal\Core\Form\FormState->getError()

Marko B’s picture

There is an error with inline validation but it happens on wider range, like if you enter 01 / 74 you will get "Your card's expiration year is invalid." so this needs to match range with data storage.

manish palawat’s picture

Yes , if I enter past and wider range Month/Year it works fine (shows error) but when enter 02/42 should be handle with drupal itself . It should be in try catch function. What should i do in that case ?

Marko B’s picture

Yeah, should be fixed :)

mglaman’s picture

Yes , if I enter past and wider range Month/Year it works fine (shows error) but when enter 02/42 should be handle with drupal itself . It should be in try catch function. What should i do in that case ?

This is a different issue than 3DSv2, if you could open that.

darrenwh’s picture

I'm just having a play with this. I have applied the patch, updated the stripe-php lib and using the latest version of the module 8.x-1.0-rc.

I get this exception on submitting payment details:

The website encountered an unexpected error. Please try again later.</br></br><em class="placeholder">InvalidArgumentException</em>: $payment_details must contain the stripe_payment_method_id key. in <em class="placeholder">Drupal\commerce_stripe\Plugin\Commerce\PaymentGateway\Stripe-&gt;createPaymentMethod()</em> (line <em class="placeholder">391</em> of <em class="placeholder">modules/contrib/commerce_stripe/src/Plugin/Commerce/PaymentGateway/Stripe.php</em>). <pre class="backtrace">Drupal\commerce_payment\PluginForm\PaymentMethodAddForm-&gt;submitConfigurationForm(Array, Object) (Line: 100)
Drupal\commerce_stripe\PluginForm\Stripe\PaymentMethodAddForm-&gt;submitConfigurationForm(Array, Object) (Line: 169)
Drupal\commerce_payment\Plugin\Commerce\InlineForm\PaymentGatewayForm-&gt;submitInlineForm(Array, Object) (Line: 162)
Drupal\commerce\Plugin\Commerce\InlineForm\InlineFormBase::runSubmit(Array, Object)
call_user_func_array(Array, Array) (Line: 130)
Drupal\commerce\Element\CommerceElementTrait::doExecuteSubmitHandlers(Array, Object) (Line: 123)
Drupal\commerce\Element\CommerceElementTrait::doExecuteSubmitHandlers(Array, Object) (Line: 123)
Drupal\commerce\Element\CommerceElementTrait::doExecuteSubmitHandlers(Array, Object) (Line: 107)
Drupal\commerce\Element\CommerceElementTrait::executeElementSubmitHandlers(Array, Object)
call_user_func_array(Array, Array) (Line: 82)
Drupal\Core\Form\FormValidator-&gt;executeValidateHandlers(Array, Object) (Line: 275)
Drupal\Core\Form\FormValidator-&gt;doValidateForm(Array, Object, &#039;commerce_checkout_flow_multistep_default&#039;) (Line: 118)
Drupal\Core\Form\FormValidator-&gt;validateForm(&#039;commerce_checkout_flow_multistep_default&#039;, Array, Object) (Line: 576)
Drupal\Core\Form\FormBuilder-&gt;processForm(&#039;commerce_checkout_flow_multistep_default&#039;, Array, Object) (Line: 319)
Drupal\Core\Form\FormBuilder-&gt;buildForm(&#039;commerce_checkout_flow_multistep_default&#039;, Object) (Line: 217)
Drupal\Core\Form\FormBuilder-&gt;getForm(Object, &#039;order_information&#039;) (Line: 94)
Drupal\commerce_checkout\Controller\CheckoutController-&gt;formPage(Object)
call_user_func_array(Array, Array) (Line: 123)
Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber-&gt;Drupal\Core\EventSubscriber\{closure}() (Line: 582)
Drupal\Core\Render\Renderer-&gt;executeInRenderContext(Object, Object) (Line: 124)
Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber-&gt;wrapControllerExecutionInRenderContext(Array, Array) (Line: 97)
Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber-&gt;Drupal\Core\EventSubscriber\{closure}() (Line: 151)
Symfony\Component\HttpKernel\HttpKernel-&gt;handleRaw(Object, 1) (Line: 68)
Symfony\Component\HttpKernel\HttpKernel-&gt;handle(Object, 1, 1) (Line: 57)
Drupal\Core\StackMiddleware\Session-&gt;handle(Object, 1, 1) (Line: 47)
Drupal\Core\StackMiddleware\KernelPreHandle-&gt;handle(Object, 1, 1) (Line: 106)
Drupal\page_cache\StackMiddleware\PageCache-&gt;pass(Object, 1, 1) (Line: 85)
Drupal\page_cache\StackMiddleware\PageCache-&gt;handle(Object, 1, 1) (Line: 47)
Drupal\Core\StackMiddleware\ReverseProxyMiddleware-&gt;handle(Object, 1, 1) (Line: 52)
Drupal\Core\StackMiddleware\NegotiationMiddleware-&gt;handle(Object, 1, 1) (Line: 23)
Stack\StackedHttpKernel-&gt;handle(Object, 1, 1) (Line: 693)
Drupal\Core\DrupalKernel-&gt;handle(Object) (Line: 19)
</pre>

This is using this card 4000000000003220 from here https://stripe.com/docs/testing#three-ds-cards

I previously tested a payment without the patch and it worked fine, I'm just running a vanilla build on docksal.

mglaman’s picture

Assigned: mglaman » Unassigned

Forgot to unassign.

darrenwh’s picture

Please disregard my comment https://www.drupal.org/project/commerce_stripe/issues/3039032#comment-13... looks as if the patch need to be built against the development version.

Is there a timescale for getting this resolved and to have a stable release before September, it would be beneficial to have this ready at least 2 weeks before then in order to fully test it.

I feel that if its not ready there is a potential for clients to lose revenue revenue due to rejected payments, (or will this not be so, will sites continue to take payments reverting back to version 1?)

Thanks

amelio’s picture

I tested the patch in #45 against a fresh Drupal 8 install. As darrenwh mentioned, I used the dev version of the module: composer require 'drupal/commerce_stripe:1.x-dev'

I enabled commerce_stripe and related required modules, created a Store and a product. I was able to complete payment & checkout using cards that both required or did not require 3D Secure 2.

I didn't have to change the version of stripe-php in composer.json as Marko B said - I left it as "stripe/stripe-php": "~6.0" and it worked fine. The version it actually installed is 6.40.0.

I also tested the patch on a client site. The client's Stripe account is using an old version of the API by default, so I had to force using the latest version. Wherever \Stripe\Stripe::setApiKey is called in Stripe.php, I added this: \Stripe\Stripe::setApiVersion("2019-05-16");
- see https://stripe.com/docs/api/versioning

However, it doesn't work when 'Enable 3D Secure 2.0' is checked. The first problem is that it never brings up the 3D Secure authentication popup, so the card is declined if this is required. I think this is probably a problem with our particular checkout as we have a lot of custom stuff going on.

However, I also get this error if I attempt to checkout when logged out:
InvalidArgumentException: Sorry, we cannot create a payment method without an email. in Drupal\commerce_stripe\Plugin\Commerce\PaymentGateway\Stripe->doCreatePaymentMethod() (line 473 of /var/www/docroot/modules/contrib/commerce_stripe/src/Plugin/Commerce/PaymentGateway/Stripe.php).

It seems that it's expecting an email field in contact_information, which doesn't exist (we do capture email address but it's in a different checkout pane). Again, this is probably an issue on our side, but does anyone know how I could make sure this field is available?

Another question: is the new Stripe Review checkout pane required for 3DS2?

audriusb’s picture

@darrenwh don't quote me on this but I believe 14th September is the hard deadline after which banks will be required to authenticate transactions. So you need to get Stripe SCA running before that.

Stripe held and recorded a conference call about SCA couple months back. I strongly suggest to watch it, at the end there is a QA session that might answer all your questions.
https://www.youtube.com/watch?v=WWJ4VAgqRUM

mglaman’s picture

Assigned: Unassigned » mglaman
Issue tags: +Needs issue summary update

I'm going to try and triage the issue summary to state where we are and what needs to be done. There's a lot of comments.

mglaman’s picture

Issue summary: View changes
mglaman’s picture

Issue summary: View changes
mglaman’s picture

Issue summary: View changes
mglaman’s picture

Issue summary: View changes
mglaman’s picture

Issue summary: View changes
Issue tags: -Needs issue summary update
StatusFileSize
new42.85 KB

The biggest remaining issue is what to do if the user declines authentication? Right now it stays on review and

mglaman’s picture

Found out why errors do not display! The JavaScript theme method is defined in the form library, which is not loaded.

mglaman’s picture

Status: Needs work » Needs review
StatusFileSize
new43.99 KB

This moves the messaging to its own library. We just need to decide if it should redirect the user back to the order information pane or not.

Status: Needs review » Needs work

The last submitted patch, 65: 3039032-65.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

mglaman’s picture

  1. +++ b/src/Plugin/Commerce/PaymentGateway/Stripe.php
    @@ -178,46 +186,83 @@ class Stripe extends OnsitePaymentGatewayBase implements StripeInterface {
    +        $intent = \Stripe\PaymentIntent::create($intent_array, ['idempotency_key' => \Drupal::getContainer()->get('uuid')->generate()]);
    

    Inject

  2. +++ b/src/Plugin/Commerce/PaymentGateway/Stripe.php
    @@ -282,11 +354,12 @@ class Stripe extends OnsitePaymentGatewayBase implements StripeInterface {
    +      $refund = \Stripe\Refund::create($data, ['idempotency_key' => \Drupal::getContainer()->get('uuid')->generate()]);
    

    Inject

  3. +++ b/src/Plugin/Commerce/PaymentGateway/Stripe.php
    @@ -373,52 +442,65 @@ class Stripe extends OnsitePaymentGatewayBase implements StripeInterface {
    +          throw new \InvalidArgumentException('Sorry, we cannot create a payment method without an email.');
    

    Will this type of exception go uncaught and break the site? Is there a Payment API friendly exception

  4. +++ b/src/Plugin/Commerce/PaymentMethodType/Stripe.php
    @@ -0,0 +1,31 @@
    +    $fields['stripe_customer_id'] = BundleFieldDefinition::create('string')
    +      ->setLabel(t('Stripe customer ID'))
    +      ->setDescription(t('The stripe customer ID for anonymous users.'));
    +
    

    In Square we combined the remote instrument token with another identifier using a |

    Should we be doing that over making a new payment method type and bundle field?

  5. +++ b/src/PluginForm/Stripe/PaymentMethodAddForm.php
    @@ -84,10 +87,19 @@ class PaymentMethodAddForm extends BasePaymentMethodAddForm {
    +  /**
    +   * {@inheritdoc}
    +   */
    +  public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
    +    $this->submitCreditCardForm($form['payment_details'], $form_state);
    +    parent::submitConfigurationForm($form, $form_state);
    +  }
    +
    

    This ends up calling the submit credit card form method twice?

    Ah, no. because we have a custom bundle.

jons’s picture

Assigned: mglaman » Unassigned

Thanks for update @mglaman! Not sure I can make sensible responses to your questions - but happy to test final result

bojanz’s picture

Needs a reroll now that #3073323: Mark stripe/stripe-php as ^6.36 and #3073327: Cannot set id on this object. have landed.

Anyone who needs to test the patch before that can revert the last two commits, then apply the patch.

Marko B’s picture

StatusFileSize
new43.97 KB

Patch re-rolled

amelio’s picture

Have been doing some more testing with this. The error I was getting above about the missing email wasn't resolved just by enabling the Contact Information pane (which we weren't using before) - I also had to amend submitCreditCardForm() as it assumes that the form values are in the structure [contact_information][email]. In our case the panes are inside a parent wrapper so this didn't work. Is it possible to retrieve the email in a more generic way - can something like $order->getEmail() be used?

Another problem is that the current JS doesn't work with a single-page checkout form. In commerce_stripe.form.js, the creation of the payment method is triggered by form submit. However, for a single-page checkout we want to trigger it when the user clicks the 'next' button on the Payment section, not when they submit the whole form. stripePaymentMethodHandler() also submits the form after setting the payment method id. I know that a single-page checkout isn't the normal use-case, but is there some way to alter the triggers for the payment method handler?

I'm able to get past these errors by altering the module code, and the payment method id is created successfully. However when I then submit the form, payment fails, with these errors in the logs:

The payment method you provided has already been attached to a customer.

Invalid parameters were supplied to Stripe's API.

It seems like it's trying to reuse the payment method token when it's already been saved against a customer. Has anyone come across this?

amelio’s picture

Also, a couple of minor notes:

  1. comerce_stripe.form.js line 44: #stripe_token is referenced, don't believe this is needed any more?
  2. StripeReview.php line 184: References the ThreeDS controller, which has been removed
mglaman’s picture

Assigned: Unassigned » mglaman

@amelio:

Another problem is that the current JS doesn't work with a single-page checkout form.

We're going to focus on the conventional out of the box flow to start, can you help test on a vanilla install? Then we can help support more customized checkout flows.

I'll be working on this today.

mglaman’s picture

Issue summary: View changes

We can't use Charge objects anymore.

mglaman’s picture

StatusFileSize
new36.95 KB

This simplifies the patch by not introducing a new payment method type and removes any upgrade path.

mglaman’s picture

Issue summary: View changes
StatusFileSize
new33.42 KB

Another go. This ensures orders only use one payment intent.

mglaman’s picture

Assigned: mglaman » Unassigned
Status: Needs work » Needs review
StatusFileSize
new31.82 KB

Okay, ready for review.

My concern:

Testing all of the test cards as anonymous + authenticated users. Testing both authentication approvals and failures. Abandoning checkout with re-enter, Abandon and adding items to cart and re-enter.

I think there is a bug I missed, but out of time for the day. We need an `updatePaymentIntent` method which allows updating the price and payment method, etc, on an intent

    if (empty($intent)) {
      $payment_process_pane = $this->checkoutFlow->getPane('payment_process');
      assert($payment_process_pane instanceof CheckoutPaneInterface);
      $capture = $payment_process_pane->getConfiguration()['capture'];
      $intent = $stripe_plugin->createPaymentIntent($this->order, $capture);
    }

The above would fail if the order total changed, as the intent is no longer correct.

--

For amelio:

* No longer exceptions on missing email
* Payment intents are created in the \Drupal\commerce_stripe\Plugin\Commerce\PaymentGateway\StripeInterface::createPaymentIntent method
* Custom checkout flows will, for now, need to replicate parts of \Drupal\commerce_stripe\Plugin\Commerce\CheckoutPane\StripeReview::buildPaneForm

For 3DSv2 to work we must create a payment intent as a "contract" of the price. Then the user agrees/authenticates in the modal and that is what captures/authorizes the payment. Nothing in the PaymentProcess pane.

Collins405’s picture

Hey guys, is the plan to finish Drupal 8 and backport to Drupal 7?

I can see there are 2,889 Drupal 7 sites using this module, and only 700 using the Drupal 8 version. Do we know if anyone is working on Drupal 7?

mglaman’s picture

@Collins405: we're doing it in 8.x first and then backporting. See #2717383: Add support for 3D Secure. I recently talked to torgosPizza and he's in the middle of a move

bojanz’s picture

@Collins405
Drupal 7 and Drupal 8 versions are developed by different people and do not share code. The D7 maintainer (torgosPizza) might look at this code for inspiration, but the D7 issue is not blocked on the D8 one, or vice-versa.

mglaman’s picture

Assigned: Unassigned » mglaman
Status: Needs review » Needs work
  1. +++ b/src/Plugin/Commerce/PaymentGateway/Stripe.php
    @@ -181,47 +190,34 @@ class Stripe extends OnsitePaymentGatewayBase implements StripeInterface {
    -      $result = \Stripe\Charge::create($transaction_data);
    -      ErrorHelper::handleErrors($result);
    +      $intent = \Stripe\PaymentIntent::retrieve($intent_id);
    

    We need to verify the intent has a charge object.

  2. +++ b/src/Plugin/Commerce/PaymentGateway/Stripe.php
    @@ -345,18 +367,55 @@ class Stripe extends OnsitePaymentGatewayBase implements StripeInterface {
    +      $intent = \Stripe\PaymentIntent::create($intent_array);
    +      $order->setData('stripe_intent', $intent->id)->save();
    

    The intent is never returned.

mglaman’s picture

Status: Needs work » Needs review
StatusFileSize
new30.85 KB

- Payment intent secret is null randomly on the first load of the review pane.
- StripeReview processes the intent on form submission.
- Need to verify that the payment intent has a charge attached.

mglaman’s picture

StatusFileSize
new33.01 KB

This enhances how authentication failures are handled.

However, payment intent amounts are out of sync. If I apply a coupon on the Review page, I am still charged the full amount. If I abandon checkout and come back, the payment amount will persist.

mglaman’s picture

Status: Needs review » Needs work
StatusFileSize
new21.49 KB

AJAX operations break the current process as the ID for the button changes to be unique for each request.

JavaScript debugging

mglaman’s picture

Assigned: mglaman » Unassigned
Status: Needs work » Needs review
StatusFileSize
new36.07 KB

This patch:

- Supports ensuring the PaymentIntent amount is sync'd with the order, which is critical as this is the amount charged once authentication is verified
- Fixes the mounting of the Stripe scripts due to dynamic IDs on AJAX.

mglaman’s picture

  1. +++ b/src/EventSubscriber/OrderEventsSubscriber.php
    @@ -0,0 +1,117 @@
    +        $stop = null;
    ...
    +        $stop = null;
    +        // noop.
    

    I forgot some debugging lines while testing the payment amount sync.

  2. +++ b/src/EventSubscriber/OrderEventsSubscriber.php
    @@ -0,0 +1,117 @@
    +  private function toMinorUnits(Price $amount) {
    +    $currency_storage = $this->entityTypeManager->getStorage('commerce_currency');
    

    This needs a `@todo` to remove once the commerce issue is fixed to make this a public method on the payment gateway.

  3. +++ b/src/Plugin/Commerce/CheckoutPane/StripeReview.php
    @@ -0,0 +1,219 @@
    +use Drupal\commerce_stripe\MinorUnitsInterface;
    

    Does not exist anymore.

  4. +++ b/src/Plugin/Commerce/CheckoutPane/StripeReview.php
    @@ -0,0 +1,219 @@
    +use Drupal\Core\Url;
    

    Not used.

  5. +++ b/src/Plugin/Commerce/CheckoutPane/StripeReview.php
    @@ -0,0 +1,219 @@
    +  /**
    +   * The inline form manager.
    +   *
    +   * @var \Drupal\commerce\InlineFormManager
    +   */
    +  protected $inlineFormManager;
    ...
    +   * @param \Drupal\commerce\InlineFormManager $inline_form_manager
    +   *   The inline form manager.
    ...
    +    $this->inlineFormManager = $inline_form_manager;
    

    Inline form manager is not used.

  6. +++ b/src/Plugin/Commerce/CheckoutPane/StripeReview.php
    @@ -0,0 +1,219 @@
    +  /**
    +   * The logger.
    +   *
    +   * @var \Psr\Log\LoggerInterface
    +   */
    +  protected $logger;
    ...
    +    $this->logger = $logger;
    

    Logger is not used.

  7. +++ b/src/Plugin/Commerce/CheckoutPane/StripeReview.php
    @@ -0,0 +1,219 @@
    +      '#description' => $this->t('The html id of the button in the review checkout step.'),
    

    "The HTML ID of the button that represents Pay and Complete"

  8. +++ b/src/Plugin/Commerce/PaymentGateway/Stripe.php
    @@ -181,47 +184,45 @@ class Stripe extends OnsitePaymentGatewayBase implements StripeInterface {
    +        throw new HardDeclineException('No charge to capture.');
    

    Better message

  9. +++ b/src/Plugin/Commerce/PaymentGateway/Stripe.php
    @@ -181,47 +184,45 @@ class Stripe extends OnsitePaymentGatewayBase implements StripeInterface {
    +      // Add metadata and extra transaction data where required.
    +      $event = new TransactionDataEvent($payment);
    +      $this->eventDispatcher->dispatch(StripeEvents::TRANSACTION_DATA, $event);
    ...
    +      // Update the transaction data from additional information added through
    +      // the event.
    

    This no longer influences the transaction data. That event should have the methods deprecated.

harika gujjula’s picture

+++ b/src/EventSubscriber/OrderEventsSubscriber.php
@@ -0,0 +1,117 @@
+    if ($gateway->isEmpty()) {

One more safe check i have used on this patch is adding the below condition.

if (($gateway->isEmpty()) || (!isset($gateway->entity)))

mglaman’s picture

Assigned: Unassigned » mglaman

Technically ->entity is set, it's just a null value. The check I prefer is that the value returned from ->entity matches the expected entity interface. It's mixed throughout the patch, I will update. Thanks for the suggestion, @harika gujjula

I'll be finishing this up today along with reviewing SetupIntent for recurring payments.

darrenwh’s picture

I've just updated a current site that will now go through QA:

Updated commerce to 8.x-2.13

Updated stripe-php to 6.36.0

Updated commerce_stripe to 8.0-dev

Added latest patch #85

Pointed it at testing servers and all went fine, had a notification about 3D Secure 2 and payment completed.

So working well so far, will await results from QA

darrenwh’s picture

mglaman’s picture

Assigned: mglaman » Unassigned

Here is a new patch! It's big! It's got tests! It's cleaned up!

I have added automated test coverage for payment intent creation, order total sync w/ intents, and payment creation. This fixes my code nits and phpcs.

Next is reviewing reusable payment methods and ensuring they work.

mglaman’s picture

StatusFileSize
new48.91 KB
mglaman’s picture

Issue summary: View changes

Reviewing https://stripe.com/docs/api/setup_intents/confirm, it looks like any and all calls to payment methods need to be wrapped in setup intents, so that future charges will not fail due to the customer unable to perform authentication client-side.

mglaman’s picture

StatusFileSize
new53.26 KB

This patch adds reusable payment method support. If the user is authenticated, this will provide a SetupIntent for the payment method so that it can be authenticated for recurring charges.

I think we may need a follow up for those who use an anonymous -> authenticated flow with recurring. However, that is difficult as the payment intent cannot be made from a payment method generated through setup intents unless it is attached to a customer.

The provided PaymentMethod is already attached to another object. You cannot reuse PaymentMethods without attaching them to a Customer object first.

I think this is an edge case for anonymous recurring signups. And is hard to test without real-world examples.

mglaman’s picture

Issue summary: View changes
darrenwh’s picture

Doing some testing and I'm seeing this error :

Message Error: Class 'Stripe\SetupIntent' not found in Drupal\commerce_stripe\PluginForm\Stripe\PaymentMethodAddForm->buildCreditCardForm() (line 30 of /var/www.current/docroot/modules/contrib/commerce_stripe/src/PluginForm/Stripe/PaymentMethodAddForm.php) #0 /var/www.current/docroot/modules/contrib/commerce/modules/payment/src/PluginForm/PaymentMethodAddForm.php(100): Drupal\commerce_stripe\PluginForm\Stripe\PaymentMethodAddForm->buildCreditCardForm(Array, Object(Drupal\Core\Form\FormState)) #1 /var/www.current/docroot/modules/contrib/commerce_stripe/src/PluginForm/Stripe/PaymentMethodAddForm.php(109): Drupal\commerce_payment\PluginForm\PaymentMethodAddForm->buildConfigurationForm(Array, Object(Drupal\Core\Form\FormState)) #2 /var/www.current/docroot/modules/contrib/commerce/modules/payment/src/Plugin/Commerce/InlineForm/PaymentGatewayForm.php(108): Drupal\commerce_stripe\PluginForm\Stripe\PaymentMethodAddForm->buildConfigurationForm(Array, Object(Drupal\Core\Form\FormState)) #3 /var/www.current/docroot/modules/contrib/commerce/modules/payment/src/Plugin/Commerce/CheckoutPane/PaymentInformation.php(251): Drupal\commerce_payment\Plugin\Commerce\InlineForm\PaymentGatewayForm->buildInlineForm(Array, Object(Drupal\Core\Form\FormState)) #4 /var/www.current/docroot/modules/contrib/commerce/modules/payment/src/Plugin/Commerce/CheckoutPane/PaymentInformation.php(207): Drupal\commerce_payment\Plugin\Commerce\CheckoutPane\PaymentInformation->buildPaymentMethodForm(Array, Object(Drupal\Core\Form\FormState), Object(Drupal\commerce_payment\PaymentOption)) #5 /var/www.current/docroot/modules/contrib/commerce/modules/checkout/src/Plugin/Commerce/CheckoutFlow/CheckoutFlowWithPanesBase.php(558): Drupal\commerce_payment\Plugin\Commerce\CheckoutPane\PaymentInformation->buildPaneForm(Array, Object(Drupal\Core\Form\FormState), Array) #6 [internal function]: Drupal\commerce_checkout\Plugin\Commerce\CheckoutFlow\CheckoutFlowWithPanesBase->buildForm(Array, Object(Drupal\Core\Form\FormState), 'order_informati...') #7 /var/www.current/docroot/core/lib/Drupal/Core/Form/FormBuilder.php(519): call_user_func_array(Array, Array) #8 /var/www.current/docroot/core/lib/Drupal/Core/Form/FormBuilder.php(276): Drupal\Core\Form\FormBuilder->retrieveForm('commerce_checko...', Object(Drupal\Core\Form\FormState)) #9 /var/www.current/docroot/core/lib/Drupal/Core/Form/FormBuilder.php(217): Drupal\Core\Form\FormBuilder->buildForm('commerce_checko...', Object(Drupal\Core\Form\FormState)) #10 /var/www.current/docroot/modules/contrib/commerce/modules/checkout/src/Controller/CheckoutController.php(94): Drupal\Core\Form\FormBuilder->getForm(Object(Drupal\odl_checkout\Plugin\Commerce\CheckoutFlow\OdlMultistepDefault), 'order_informati...') #11 [internal function]: Drupal\commerce_checkout\Controller\CheckoutController->formPage(Object(Drupal\Core\Routing\RouteMatch)) #12 /var/www.current/docroot/core/lib/Drupal/Core/EventSubscriber/EarlyRenderingControllerWrapperSubscriber.php(123): call_user_func_array(Array, Array) #13 /var/www.current/docroot/core/lib/Drupal/Core/Render/Renderer.php(582): Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->Drupal\Core\EventSubscriber\{closure}() #14 /var/www.current/docroot/core/lib/Drupal/Core/EventSubscriber/EarlyRenderingControllerWrapperSubscriber.php(124): Drupal\Core\Render\Renderer->executeInRenderContext(Object(Drupal\Core\Render\RenderContext), Object(Closure)) #15 /var/www.current/docroot/core/lib/Drupal/Core/EventSubscriber/EarlyRenderingControllerWrapperSubscriber.php(97): Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->wrapControllerExecutionInRenderContext(Array, Array) #16 /var/www.current/vendor/symfony/http-kernel/HttpKernel.php(151): Drupal\Core\EventSubscriber\EarlyRenderingControllerWrapperSubscriber->Drupal\Core\EventSubscriber\{closure}() #17 /var/www.current/vendor/symfony/http-kernel/HttpKernel.php(68): Symfony\Component\HttpKernel\HttpKernel->handleRaw(Object(Symfony\Component\HttpFoundation\Request), 1) #18 /var/www.current/docroot/core/lib/Drupal/Core/StackMiddleware/Session.php(57): Symfony\Component\HttpKernel\HttpKernel->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true) #19 /var/www.current/docroot/core/lib/Drupal/Core/StackMiddleware/KernelPreHandle.php(47): Drupal\Core\StackMiddleware\Session->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true) #20 /var/www.current/docroot/core/modules/page_cache/src/StackMiddleware/PageCache.php(106): Drupal\Core\StackMiddleware\KernelPreHandle->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true) #21 /var/www.current/docroot/core/modules/page_cache/src/StackMiddleware/PageCache.php(85): Drupal\page_cache\StackMiddleware\PageCache->pass(Object(Symfony\Component\HttpFoundation\Request), 1, true) #22 /var/www.current/docroot/core/lib/Drupal/Core/StackMiddleware/ReverseProxyMiddleware.php(47): Drupal\page_cache\StackMiddleware\PageCache->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true) #23 /var/www.current/docroot/core/lib/Drupal/Core/StackMiddleware/NegotiationMiddleware.php(52): Drupal\Core\StackMiddleware\ReverseProxyMiddleware->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true) #24 /var/www.current/vendor/stack/builder/src/Stack/StackedHttpKernel.php(23): Drupal\Core\StackMiddleware\NegotiationMiddleware->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true) #25 /var/www.current/docroot/core/lib/Drupal/Core/DrupalKernel.php(693): Stack\StackedHttpKernel->handle(Object(Symfony\Component\HttpFoundation\Request), 1, true) #26 /var/www.current/docroot/index.php(19): Drupal\Core\DrupalKernel->handle(Object(Symfony\Component\HttpFoundation\Request)) #27 {main}.

darrenwh’s picture

Status: Needs review » Needs work
mglaman’s picture

Status: Needs work » Needs review

Please update your Stripe library SDK.

EDIT:

composer update stripe/stripe

mglaman’s picture

Status: Needs review » Needs work

Ah this is on SetupIntents! Sorry. 6.36 was for PaymentIntent.

6.40 added SetupIntent https://github.com/stripe/stripe-php/releases/tag/v6.40.0

darrenwh’s picture

I added "stripe/stripe-php": "^6.41" as a requirement in composer.json and it works now local SDK is now 6.43.0

mglaman’s picture

Status: Needs work » Needs review
StatusFileSize
new53.58 KB

Bumped stripe/stripe-php SDK to the version which includes SetupIntent.

harika gujjula’s picture

Tested the following cases on 8.x-1.x with the latest patch.

Commerce Version: 8.x-2.11

1. Initial Payment or Payment by entering the card details is failed with Any Test card(3D supported, 3D Required)
- Drupal Error Set message With Invalid Payment Details.
Logs:
InvalidArgumentException: $payment_details must contain the stripe_payment_method_id key. in Drupal\commerce_stripe\Plugin\Commerce\PaymentGateway\Stripe->createPaymentMethod() (line 357 of /Users/harika/Sites/bb_dev/docroot/modules/contrib/commerce_stripe/src/Plugin/Commerce/PaymentGateway/Stripe.php).

Invalid parameters were supplied to Stripe's API.

2. Recurring Payment Selection with the existing card details:
- Works Fine.

3. Payment Refund for successful paid order.
- Works Fine.
(Ref: I have attached the screenshot).

mglaman’s picture

@harika gujjula 🥶 that's not good on #1. Can you try with Commerce 2.13 just to check. However it seems to be more linked to JavaScript bypassing clientside validation and we need better server-side handling.

nicolas bouteille’s picture

Hello

I would like to report some positive testings of mine from this morning.

On a very recent Drupal 8 website which is currently on D8.7.5
I am using the Drupal Composer project.
I have installed Drupal Commerce (all submodules except Payment Examples) version 8.x-2.13
I have created a Store working for France in euros and French VAT, and one test product.

First I installed Commerce Stripe 1.0-rc2, created a new Stripe account and made a simple test.
Payment was working fine with regular VISA test card 4242424242424242
https://stripe.com/docs/testing

However, when I tried with the test card that requires 3DS 2.0 : 4000000000003220
It failed. I got errors on both Drupal Commerce and Stripe, saying the bank had refused payment requiring authentication.
And I could see the /charges API has been used.

I then removed Commerce Stripe from composer so that it would also removed old stripe-php library and then required Commerce Stripe 1.x-dev after adding the latest patch (#101) in composer.json so that it gets applied.

I tested again with the 3DS 2.0 test card from Stripe. This time I could see Stripe’s JS pop up from 3DS 2.0 and it worked! And I could see Payment Intents API had been used.

Only problem I noticed is that in Stripe Dashboard, I no longer have information about the « Client ». With 1.0-RC2, even though I checkout as Guest, I can see "First Name Last Name" as the Client. And in Stripe’s payment metadata, I now only see the order_id whereas on rc2 I would see order_id and store_id.

Thank you very much for the hard work!

mglaman’s picture

StatusFileSize
new53.03 KB

Use \Drupal\commerce_payment\Exception\InvalidRequestException for better handling if stripe_payment_method_id is missing (client side prevents the form from submitting without this.

Only problem I noticed is that in Stripe Dashboard, I no longer have information about the « Client ». With 1.0-RC2, even though I checkout as Guest, I can see "First Name Last Name" as the Client. And in Stripe’s payment metadata, I now only see the order_id whereas on rc2 I would see order_id and store_id.

Added back the store_id to the payment intent metadata.

+++ b/src/Plugin/Commerce/PaymentGateway/Stripe.php
@@ -181,47 +185,48 @@ class Stripe extends OnsitePaymentGatewayBase implements StripeInterface {
-        'store_id' => $payment->getOrder()->getStoreId(),

+++ b/src/PluginForm/Stripe/PaymentMethodAddForm.php
@@ -103,8 +127,8 @@ class PaymentMethodAddForm extends BasePaymentMethodAddForm {
-    $element['address']['widget'][0]['address']['given_name']['#attributes']['data-stripe'] = 'given_name';
-    $element['address']['widget'][0]['address']['family_name']['#attributes']['data-stripe'] = 'family_name';

These were throwing warnings that they didn't exist. Maybe that's why the name disappeared? The name is always available for authenticated users.

Status: Needs review » Needs work

The last submitted patch, 105: 3039032-105.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

mglaman’s picture

Status: Needs work » Needs review
StatusFileSize
new53.08 KB

Fix mocked order.

nicolas bouteille’s picture

I might have found a use case where 3DS pop up shows up twice.
In short : if I register during checkout, 3DS pop up shows up before and after Review step.
FYI I am using Stripe 3DS 2.0 Testing Card : 4000000000003220

Steps to reproduce :
I have disabled "Allow Guest checkout" and enabled "Allow Register during checkout" in the Checkout Process settings.
So now my first step of the Checkout process is either Login or Register.
When I register, the next step is asking for Paiement information (Card + Billing Address).
When I validate this step, Stripe's 3DS 2.0 pop up shows up and I think it should not.
Indeed, the payment still has not been made which is normal I guess since the next step is Review.
When I validate the Review step, the payment is made and Stripe's 3DS pop up again, which is normal I guess.

If I disabled Register during checkout and enable Checkout as Guest again, 3DS pop up does not show up twice. It only shows up after Review, when the payment is actually made. So it seems the problem only occurs when I register during checkout. Just to make sure, I also tested with Login during checkout and the problem does not occur either.

bojanz’s picture

This patch has come a long way! Here's an initial review.

+/**
+ * Subscribers to order events for the Stripe integration.
+ */
+class OrderEventsSubscriber implements EventSubscriberInterface, DestructableInterface {

We should provide a real docblock here, detailing what the subscriber around intents, and why it's needed (why it can't be done in the checkout pane or the gateway).

+  private function toMinorUnits(Price $amount) {

This should be protected. We don't do private methods.

+/**
+ * Provides functionality for finishing a Stripe checkout.
+ *
+ * @CommerceCheckoutPane(
+ *   id = "stripe_review",
+ *   label = @Translation("Stripe review"),
+ *   default_step = "review",
+ *   wrapper_element = "container",
+ * )
+ */

I would also like this docblock to be extended, to clarify what the actual output of the pane is.

+    if ($this->moduleHandler->moduleExists('commerce_shipping') && !$this->order->get('shipments')->isEmpty()) {
+      $pane_form['#attached']['drupalSettings']['commerceStripe']['shipping'] = $this->order->get('shipments')->entity->getShippingProfile()->get('address')->first()->toArray();
+    }

We can make this generic by using the 2.14 code:

  $profiles = $this->order->collectProfiles();
  if (isset($profiles['shipping'])) {
    $pane_form['#attached']['drupalSettings']['commerceStripe']['shipping'] = $profiles['shipping']->get('address')->first()->toArray();
  }

Also allows us to stop injecting moduleHandler.

diff --git a/src/Event/TransactionDataEvent.php b/src/Event/TransactionDataEvent.php
index e2fb321..e5ea464 100644
--- a/src/Event/TransactionDataEvent.php
+++ b/src/Event/TransactionDataEvent.php
@@ -51,6 +51,8 @@ class TransactionDataEvent extends Event {
    *
    * @return array
    *   The transaction data.
+   *
+   * @deprecated Transaction data is no longer influenced by this event.
    */
   public function getTransactionData() {
     return $this->transactionData;
@@ -63,6 +65,8 @@ class TransactionDataEvent extends Event {
    *   The transaction data.
    *
    * @return $this
+   *
+   * @deprecated Transaction data is no longer influenced by this event.
    */
   public function setTransactionData(array $transaction_data) {
     $this->transactionData = $transaction_data;

Is this true? I see the event used in createPayment.

     $payment_method->card_number = $remote_payment_method['last4'];
     $payment_method->card_exp_month = $remote_payment_method['exp_month'];
     $payment_method->card_exp_year = $remote_payment_method['exp_year'];
-    $remote_id = $remote_payment_method['id'];
     $expires = CreditCard::calculateExpirationTimestamp($remote_payment_method['exp_month'], $remote_payment_method['exp_year']);
-    $payment_method->setRemoteId($remote_id);
     $payment_method->setExpiresTime($expires);
     $payment_method->save();

What's going on here? When will the remote ID be set?

mglaman’s picture

Is this true? I see the event used in createPayment.

Only the transaction data is deprecated, not the event itself. At this point, it is too late to modify any of the transaction data as it would invalidate the payment intent to which the user has agreed upon and been charged.

We would need to refactor that event to work without the payment entity and instead receive an order object from \Drupal\commerce_stripe\Plugin\Commerce\PaymentGateway\Stripe::createPaymentIntent.

What's going on here? When will the remote ID be set?

The remote ID is set in doCreatePayment method. The remote ID needs to contain the payment method ID and customer ID.

$payment_method->setRemoteId($stripe_payment_method_id . '|' . $customer_id);

Actually. I think this is an artifact from earlier versions of the patch where anonymous customers were having Stripe customer objects created and this section can be reverted back to the way it once was. There is no need for REMOTE_PAYMENT_METHOD_ID|REMOTE_CUSTOM_ID anymore, I do not think.

mglaman’s picture

Assigned: Unassigned » mglaman
StatusFileSize
new51 KB

Here is a patch to address #109.

I'll be reviewing #108. This sounds like the SetupIntent is triggering 3DS for adding your card as a stored payment method. Then you're receiving the second 3DS for confirming the charge. But, it is weird it only occurs when you register during checkout.

nicolas bouteille’s picture

This sounds like the SetupIntent is triggering 3DS for adding your card as a stored payment method.

Indeed I noticed that the card is stored in the available payment methods on the user profile. Good point!

Then you're receiving the second 3DS for confirming the charge.

Is it normal though to have two 3DS validation so close one from the other? Maybe this is only because we're testing? But in real life an exemption would be requested?

But, it is weird it only occurs when you register during checkout.

For anonymous user it makes sense since the card won't be stored. For existing user, I think I might have only tested this with a user that had already stored the card. We need to test this with an existing user that does not have his/her credit card stored yet.

Status: Needs review » Needs work

The last submitted patch, 111: 3039032-111.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

mglaman’s picture

Status: Needs work » Needs review
StatusFileSize
new51.01 KB

@Nicolas Bouteille thanks for the reply. I was afraid of this in #94 when we switched to SetupIntent. I was hoping it wouldn't cause two triggers. I'll test this today.

(patch attached to fix tests, renamed a class but forgot about services.yml, whoops!)

nicolas bouteille’s picture

FYI: I had a side question related to 3DS 2.0 so I opened a separate issue: #3077741: PSD2 / SCA / 3D Secure 2 : what's going to happen on September 14th?
Thanks!

mglaman’s picture

StatusFileSize
new60.4 KB

Adds a FunctionalJavascript test for checkout. Fixes a bug I made in #111 where the remote ID became null.

Status: Needs review » Needs work

The last submitted patch, 116: 3039032-116.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

mglaman’s picture

Status: Needs work » Needs review
StatusFileSize
new65.63 KB

This adds a lot more testing. It also fixes the double modal! The problem is running SetupIntent when we're also doing a PaymentIntent. We only need SetupIntent when a PaymentIntent will not follow to capture authorization.

Status: Needs review » Needs work

The last submitted patch, 118: 3039032-118.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

mglaman’s picture

Status: Needs work » Needs review
StatusFileSize
new74.04 KB

Marking that we need 2.x-dev, I think that'll fix the errors on checkout.

Adding more tests for setup intents for payment methods created outside of checkout. It is seeming redundant as future charges still require authentication for the amount. With or without setup intent the payment methods can be used and require approval at the end of checkout.

Status: Needs review » Needs work

The last submitted patch, 120: 3039032-120.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

mglaman’s picture

Assigned: mglaman » Unassigned
Status: Needs work » Needs review
StatusFileSize
new76.19 KB

I've fixed up the test asserts for better timing. They should be faster and more reliable. The fail in #120 was due to bad timing.

We're creating setup intents when a payment method is added off-checkout. It seems it won't solve recurring. But it's part of the correct flow.

I want to see what DrupalCI says and then we just commit this to -dev.

Status: Needs review » Needs work

The last submitted patch, 122: 3039032-122.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

mglaman’s picture

Status: Needs work » Needs review
StatusFileSize
new76.27 KB

This should fix the error. I got on IRC and chatted with the Stripe folks to clear up some concerns.

mglaman’s picture

Status: Needs review » Reviewed & tested by the community

Updating issue credits before commit. Crediting those who helped do testing.

  • mglaman committed e09aa72 on 8.x-1.x
    Issue #3039032 by mglaman, czigor, Marko B, harika gujjula, darrenwh,...
mglaman’s picture

Status: Reviewed & tested by the community » Fixed

🚀 COMMITTED! 🎉

Thank you, everyone!

blacklabel_tom’s picture

Hi,

I wanted to check the process of entering the 3D secure challenge. Currently I enter the credit card details, then the form submits and I see an error about processing the payment:

Error

I can see in the logs that there is an error about an empty payment intent ID `Could not determine which URL to request: Stripe\PaymentIntent instance has invalid ID: `

On reload the payment method is saved and clicking the 'Pay and complete purchase' button presents the 3D secure challenge.

Payment method

3D Secure

Looking at `drupal/web/modules/contrib/commerce_stripe/js/commerce_stripe.form.js` I can't see an AJAX call that I would expect to see to check if the card requires 3D secure or not.

I may be missing a setup step here as I've upgraded an existing install instead of creating one from scratch.

I'm using the latest dev of commerce_stripe and commerce 2.1.4.

Any help is appreciated!

Cheers

Tom

bojanz’s picture

@blacklabel_tom
The current issue is fixed, you will need to open a new one with your comment.

Marko B’s picture

Seems there is some leftover in JS script commerce_stripe.form.js around line 120

            stripe.handleCardSetup(drupalSettings.commerceStripe.clientSecret, self.cardNumber).then(function (result) {
debugger;
              if (result.error) {

this debugger; seems odd thing, looks like leftover?

mglaman’s picture

Can you open a follow up issue? That did sneek in.

bojanz’s picture

nicolas bouteille’s picture

@blacklabel_tom : (#128) I encountered a similar problem after removing patch #101 and updating to newest dev version. In the end, because of composer.lock my composer update command had not actually retrieved the latest dev version of commerce_stripe. I had to composer remove commerce_stripe and require it again to actually get the newest dev version (commit 5fb79fd).

dunebl’s picture

I have updated today my stripe module:
composer update drupal/commerce_stripe 1.x-dev
And I have made a payment (<30€) with my mastercard on my site.
Unfortunately, when going to the stripe dashboard and looking at my payment, I have the following warning:
This payment didn’t use an SCA-ready product. Unless you update your integration, payments like this are at risk of being declined.
Am I missing something?
Should I clear cache and retry a new payment?

dunebl’s picture

I could solve #135 by re-installing the commerce_paiement module
This step allows me to remove the error message in the report/status page: The Client Secret field needs to be installed

On my Dev server everything runs fine, but on my production server, I had to recreate a table (paiement_client_secret) as I got another sql error message during the un-installation of commerce_paiement (table not found)

If you have the same problem, check also that you have the last versions of:

-drupal/commerce
-stripe/stripe-php
-drupal/profile =>>!!!!important
-and of course drupal/commerce_stripe

After that, I could get SCA compliant paiements.

Thank you guys!!!

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.

darrenwh’s picture

@blacklabel_tom re #128 I added the patches on this page https://www.drupal.org/node/3079830

remaye’s picture

Sorry to reopen this issue.
It is just to have a summary of what is needed at this time to make 3D secure 2 working.

I have updated to Drupal 8.9.11 and Commerce 8.x-2.21
By now, what are the correct versions of Commerce related modules ? Are last stable or dev versions required ?
I'm using of course Commerce_stripe (last dev version) and also Commerce_shipping (8.x-2.0-rc2) and Profile last dev version and strpe/stripe-php v7.67.0.
Is there also any patch requiered for some modules ?

And also how to see and be sure on local site that 3D secure is ok before to make it live ?

Thanks for help !

audriusb’s picture

Connect Stripe to sandbox and use test card details https://stripe.com/docs/testing

krzysztof domański’s picture

TransactionData methods were removed as part of the 3D authentication issue.

-    // Update the transaction data from additional information added through
-    // the event.
-    $transaction_data += $event->getTransactionData();
-    $transaction_data['metadata'] += $event->getMetadata();

Methods were useful for:
#3170903: Allow customising the payment intent
#3121771: Fix TransactionData event, include description and initial metadata