Problem/Motivation

Some payment methods are only available to customers in specific countries; for example, in the past we’ve had multi-national stores where they used one gateway for France and another for Germany. Or, certain options (like GoCardless & ByJuno) may only be available to customers [with a billing address] in a specific country (i.e. UK / Switzerland).

In order to support them, we need to be able to use the billing address inline condition on payment gateways to determine payment method availability. This is complicated by the fact that an order will not necessarily have a billing address until the payment information pane is at least partially submitted.

Steps to reproduce

These two issues both describe the scenarios to reproduce.

Proposed resolution

Settings

Add a setting to the payment information checkout pane that moves billing address collection above the payment method radios. In the settings summary, it should read:

”Billing address collection determined by payment gateway.” or ”Billing address is always collected.”

The checkbox default to unchecked to preserve current functionality and should be labeled, “Collect a billing address for all payment methods.” with a description that reads, “The billing address fields will be moved above the payment method radio options.”

Checkout

We need some way to detect that a billing address has been supplied (in 1.x we used onchange on all of the embedded customer profile fields and triggered a refresh when all required fields had a value). Once the billing address is entered, we need to refresh the list of available payment methods - this means saving the profile, assigning it to the order, and re-building the pane form to reflect new options.

We need to ensure the form accommodates the three types of address entry: new address entry, reuse of a shipping address, and selection of an address from the addressbook. It isn’t clear to me at what point we’ll need to actually write information to the database based on those two alternate address sources, but at this point … presumably the shipping address would have been saved, if applicable, due to the “Recalculate shipping” button … but it also appears that the addressbook widget uses its own logic to determine a default address vs. writing a default address reference to the database for the order.

Remaining tasks

User interface changes

Admin

Payment information checkout pane will now have the following options

  • Collect billing information
    The option will collect billing profile before filtering and presenting payment gateway options
  • Require billing information
    Hide payment gateways until an address is entered
  • Auto Rebuild
    Auto rebuild payment options when a new address is entered

Customer

API changes

Noen identified

Data model changes

None identified

CommentFileSizeAuthor
#43 commerce--country-dependant-payment-integration-3170412-43.patch24.55 KBayalon
#40 commerce-Billing_address_before_payment-3170412-40.patch23.53 KBayalon
#39 commerce-Billing_address_before_payment-3170412-39.patch24.97 KBayalon
#33 commerce-Billing_address_before_payment-3170412-33.diff25 KBdwkitchen
#29 interdiff_28_29.txt5.07 KBkhiminrm
#29 commerce-Billing_address_before_payment-3170412-29.patch43.18 KBkhiminrm
#28 interdiff_27_28.txt3.2 KBkhiminrm
#28 commerce-Billing_address_before_payment-3170412-28.patch43.6 KBkhiminrm
#27 interdiff_26_27.txt4.97 KBkhiminrm
#27 commerce-Billing_address_before_payment-3170412-27.patch41.22 KBkhiminrm
#26 interdiff_25_26.txt3.22 KBkhiminrm
#26 commerce-Billing_address_before_payment-3170412-26.patch41.83 KBkhiminrm
#25 interdiff_22_25.txt3.17 KBkhiminrm
#25 commerce-Billing_address_before_payment-3170412-25.patch41.51 KBkhiminrm
#23 interdiff_21_22.txt15.01 KBkhiminrm
#22 commerce-Billing_address_before_payment-3170412-22.patch39.3 KBkhiminrm
#21 commerce-Billing_address_before_payment-3170412-20.patch33.99 KBkhiminrm
#20 commerce_core-billing-information-patch-3170412-20.patch31.99 KBerom
#20 commerce_core-billing-information-patch-3170412-20.patch31.99 KBerom
#19 commerce-Billing_address_before_payment-3170412-19.patch33.68 KBayalon
#17 interdiff_16-17.txt609 bytesdwkitchen
#17 commerce-Billing_address_before_payment-3170412-17.patch33.68 KBdwkitchen
#16 interdiff_15-16.txt620 bytesdwkitchen
#16 commerce-Billing_address_before_payment-3170412-16.patch33.34 KBdwkitchen
#15 interdiff_14-15.txt5.62 KBdwkitchen
#15 commerce-Billing_address_before_payment-3170412-15.patch33.39 KBdwkitchen
#14 interdiff_13-14.txt692 bytesdwkitchen
#14 commerce-Billing_address_before_payment-3170412-14.patch31.96 KBdwkitchen
#13 interdiff_11-13.txt2.47 KBdwkitchen
#13 commerce-Billing_address_before_payment-3170412-13.patch32.01 KBdwkitchen
#11 interdiff_9-11.txt3.41 KBdwkitchen
#11 commerce-Billing_address_before_payment-3170412-11.patch32.29 KBdwkitchen
#10 commerce-Billing_address_before_payment-3170412-10.patch31.88 KBdwkitchen
#9 interdiff_4-9.txt2.57 KBdwkitchen
#9 commerce-Billing_address_before_payment-3170412-9.patch31.99 KBdwkitchen
#4 interdiff_3-4.txt12.69 KBdwkitchen
#4 commerce-Billing_address_before_payment-3170412-4.patch31.93 KBdwkitchen
#3 commerce-Billing_address_before_payment-3170412-3.patch29.17 KBdwkitchen

Comments

dwkitchen created an issue. See original summary.

jsacksick’s picture

I think adding a setting to the payment information pane is the right approach. An alternative approach would have been to allow collecting the billing information via the billing information pane which is unset by the payment module... But this change is probably invasive and confusing.

Regarding

We need to ensure the form accommodates the three types of address entry: new address entry, reuse of a shipping address, and selection of an address from the addressbook. It isn’t clear to me at what point we’ll need to actually write information to the database based on those two alternate address sources, but at this point … presumably the shipping address would have been saved, if applicable, due to the “Recalculate shipping” button … but it also appears that the addressbook widget uses its own logic to determine a default address vs. writing a default address reference to the database for the order.

I'm wondering if we could avoid saving the profile (We could try making this work by creating a temporary billing profile that we could set on the order so that the OrderBillingAddress condition can be properly evaluated.

dwkitchen’s picture

Issue summary: View changes
StatusFileSize
new29.17 KB

Here is my first progress patch.

What I have done:

  • Split PaymentOptionsBuild to be able to getStoredOptions and getNewOptions
  • Added configuration options to PaymentInformation checkout pane
  • Added notice to Billing Information Condition to tell admin to enable option
  • Inserted Billing Information in checkout pane based on configuration, and removed from Payment Information
  • Copy Order Billing Information to Payment Method if Payment Method collects billing information
  • Added re-calculate button and JS based on shipping

Known issues:

  • JS recalculation doesn't work
  • Hiding payment options until address set not built
dwkitchen’s picture

Improvements since last patch:

  • "My billing address is the same as my shipping address" with Commerce Shipping
  • Entering a new address with existing addresses in the address book

Known issues:

  • JS recalculation doesn't work
  • Hiding payment options until address set not built

JS Rebuilding of Payment Gateways is based on #2849756: Auto-recalculate shipping when the address changes and they can be tested together.

dwkitchen’s picture

Status: Active » Needs review

The last submitted patch, 3: commerce-Billing_address_before_payment-3170412-3.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

Status: Needs review » Needs work

The last submitted patch, 4: commerce-Billing_address_before_payment-3170412-4.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

jsacksick’s picture

I don't really like the fact that we're duplicating the logic that currently lives in ProfileFieldCopy (in PaymentInformation), is that necessary? Can't we avoid that?

dwkitchen’s picture

StatusFileSize
new31.99 KB
new2.57 KB

Thanks to @rszrama for being a 🐥 I figured out where my JS issue was - the wrapper was two wide and it was checking all the required information in the payment method form was entered before refreshing the pane. Now limited to billing information in the payment pane.

@jsacksick I agree, but we have ended up with a few situations where core commerce reference shipping. I'm thinking that this is a work in progress - it is now at a state that will work for limiting the payment methods based on Billing Address.

I think there are two options:

Move ProfileFieldCopy functionality into core.
This has the potential to help with #3163551: Shipping same as billing as it could just build the option to copy from any profile before the current pane.

Replace payment pane rather than alter.
In the same way that commerce_payment replaces the billing information pane, commerce_shipping should replace the pane provided be commerce_payment. That way the profile can be set at the right time in the flow.

This is much a WIP that needs more thought before it goes in.

dwkitchen’s picture

Removing debug messages

dwkitchen’s picture

StatusFileSize
new32.29 KB
new3.41 KB

Fixing a couple of extra issues I found.

  1. If the payment gateway doesn't collet billing or the option is not set there won't be a profile to validate
  2. Don't unset the billing profile in the add for if we are not collecting it at the start
jsacksick’s picture

Issue tags: +Needs tests
dwkitchen’s picture

Improve handling if shipping profile on different checkout page or has been skipped because order doesn't have shippable item.

dwkitchen’s picture

StatusFileSize
new31.96 KB
new692 bytes

Found an issue on checkouts where Billing Address is loaded from order; in this case we don't need to save to billing profile - unlike when we are copying the profile to the order.

dwkitchen’s picture

Two further issues found:

If an anon user is checking out there is no address to the country limited Payment Gateway is hidden, so set the country code to the same as the store for starting. This could do with a further improvement to mirror what would normally be filled in when a user is adding an address.

Improved the setting of the billing profile on the payment method when it is being collected first as was sometimes double saving and getting a UUID validation error.

dwkitchen’s picture

I tiny update for client request

dwkitchen’s picture

Catch all case was not included, the billing address was only set on the order if PaymentGateway implemented:

  • SupportsCreatingPaymentMethodsInterface
  • SupportsStoredPaymentMethodsInterface
dwkitchen’s picture

ayalon’s picture

Rerolled version for Commerce Core 2.25

erom’s picture

tried this patch for commerce 2.28

khiminrm’s picture

Rerolled to last 2.x-dev

khiminrm’s picture

Updated last patch. Improved code. Fixed auto recalculate.

khiminrm’s picture

StatusFileSize
new15.01 KB
khiminrm’s picture

Fixed case when we select not default value from existing payment methods and then reload the checkout step with payment information pane. The address should be updated to default payment method profile's address in that case.

khiminrm’s picture

khiminrm’s picture

Fixed billing profile when we select Add payment method.

khiminrm’s picture

Fixed refresh total when adding new first method with new billing address. But comparing created profiles with the patch and Commerce 2.x without the patch, there is difference - when using clean Commerce without the patch it will create 3 profile entities - 1st for payment method's billing profile, 2nd for address book, 3rd - for order billing profile. In case with patch there are only two created - order and payment method reference to the same billing profile.

khiminrm’s picture

Fixed profiles when we create new payment method with new billing profile.

khiminrm’s picture

Refactored code to fix error in console when we tried to use existing payment method and submit the checkout form.

dwkitchen’s picture

Status: Needs work » Reviewed & tested by the community

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 29: commerce-Billing_address_before_payment-3170412-29.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

avpaderno’s picture

Assigned: dwkitchen » Unassigned
dwkitchen’s picture

Refactored to now require #3184254: Separate stored payment methods from new in the Payment Checkout Pane to be applied first

Improved JS based on Shipping to detect required fields for each address format.

Refresh order total as well for recalculating tax and fees on address change.

mglaman’s picture

+++ b/modules/payment/commerce_payment.libraries.yml
@@ -24,3 +24,13 @@ offsite_redirect:
+    - core/jquery.core

This library doesn't exist. Is it supposed to be once? Also make sure it's not using the deprecated jQuery once over the new @once library.

mglaman’s picture

  1. +++ b/modules/payment/js/payment-checkout.js
    @@ -0,0 +1,71 @@
    +      const waitForAjaxComplete = (element) => {
    +        setTimeout(() => {
    ...
    +      let canRecalculate = true;
    

    You're using ES5 syntax, this will break browsers still supported by Drupal core. Drupal Commerce doesn't have the es5 transpile setup from Drupal core like Cart Flyout does.

  2. +++ b/modules/payment/src/Plugin/Commerce/CheckoutPane/PaymentInformation.php
    @@ -151,8 +221,94 @@ class PaymentInformation extends CheckoutPaneBase {
    +        if (isset($user_input['copy_fields'])) {
    +          $enabled = (bool) $user_input['copy_fields']['enable'];
    +        }
    

    $user_input['copy_fields']['enable'] ?? false

    Removes if and defaults to false.

  3. +++ b/modules/payment/src/Plugin/Commerce/CheckoutPane/PaymentInformation.php
    @@ -151,8 +221,94 @@ class PaymentInformation extends CheckoutPaneBase {
    +      // @todo If the customer has not entered an address, base the selection on the store.
    

    TODO

  4. +++ b/modules/payment/src/Plugin/Commerce/CheckoutPane/PaymentInformation.php
    @@ -216,17 +372,27 @@ class PaymentInformation extends CheckoutPaneBase {
    -          'callback' => [static::class, 'ajaxRefresh'],
    +          'callback' => [get_class($this), 'ajaxRefreshForm'],
    

    static::class is the same as get_class.

jsacksick’s picture

This patch is way too big as is... I believe the JS was initially copied from commerce_shipping which has been rewritten since to not rely on JQuery since core itself is moving away from JQuery.

I noticed several coding standard issues, and I really don't like the fact that core now has an implicit dependency on commerce_shipping...

We're using a form display that's added by commerce_shipping, and we're getting the shipping profile from the form state. We need to figure out a better way as I wouldn't feel comfortable committing anything close to this approach.

dwkitchen’s picture

Issue summary: View changes

Added a task to update the JS inline with the changes that were made in #3254957: Remove the JQuery dependency for the JS logic in checkout

Based on my comment in #9

Move ProfileFieldCopy functionality into core.
This has the potential to help with #3163551: Shipping same as billing

Let's create a new issue for that, and then we can reduce the complexity of the patch and remove the dependency on shipping.

It will also offer a simplification for Commerce Shipping so it no longer needs to alter the payment information pane.

ayalon’s picture

This patch brings me always big pain when I need to update drupal commerce.

I tested the new patch with the reworked javascript from #33 and I think it completely bricks the checkout because the 'wrapper' is wrong.

Wrong:

      $element['#attached']['drupalSettings']['commercePayment'] = [
        'wrapper' => '#' . $element['billing_information']['#attributes']['data-drupal-selector'],
        'paneWrapper' => '#' . $element['#attributes']['data-drupal-selector'],
        'recalculateButtonSelector' => '[data-drupal-selector="' . $recalculate_button_selector . '"]',
      ];

Correct:

      $element['#attached']['drupalSettings']['commercePayment'] = [
        'wrapper' => '#' . $element['#attributes']['data-drupal-selector'],
        'paneWrapper' => '#' . $element['#attributes']['data-drupal-selector'],
        'recalculateButtonSelector' => '[data-drupal-selector="' . $recalculate_button_selector . '"]',
      ];

In the js file, the recalculateButtonSelector is outside if the billing_information. On change, the next button is disabled and never released because the recalculateButtonSelector is not found / triggered.

ayalon’s picture

Attached you can find an updated patch.

This patch only can be applied if with patch 12 from #3184254-12: Separate stored payment methods from new in the Payment Checkout Pane was applied before.

https://www.drupal.org/files/issues/2022-01-06/3184254-separate-stored-payment-methods-from-new-12.diff

I tried to make it compatible with the latest patch that got commited to Commece 3.0.x but I failed.

Maybe @dwkitchen can jump in and help to make this patch compatible with the one, that got finally commited here:
https://www.drupal.org/files/issues/2022-01-31/3184254-15.patch

ayalon’s picture

It turned out, that the patch is not working with free orders.

Attached a fixed version supporting free (0 Value) orders.

avpaderno’s picture

Status: Needs work » Needs review
jsacksick’s picture

Status: Needs review » Needs work

We're not going to move forward with this patch as is...

We need to break this issue into multiple. The JS / auto rebuild should be handled separately and this patch should be just about the changes to the Payment information pane itself.

ayalon’s picture

@jsacksick

Unfortunatly we are stuck with this patch. Centarro developed the Byjuno Payment integration (namely dwkitchen). This patch is needed to make the module work at all.

I rerolled the patch again for Commerce 2.31. I don't think that we can split the JS functionality away, because it's an essential part of the idea of the patch. Attached you can find a rerolled patch.

Usecase:

  1. If you have a checkout without shipment, you need to enter your payment address details.
  2. Based on the country you select, the list of the available payment method is automatically refreshed / updated.
  3. If we remove the JS part, the patch will no longer work, because it solved the auto-refresh.

I don't know how to proceed with this patch. It was based on Centarros work, and we should be able to somehow bring this functionality into commerce. Maybe you can make a proposal, how we should proceed with this?

ayalon’s picture

Status: Needs work » Closed (won't fix)

As discussed with @jsacksick we will move the custom code to a custom PaymentInformation checkout pane and no longer rely on this patch.

damienmckenna’s picture

Status: Closed (won't fix) » Needs work

Cross-posting from #3110842: The billing address condition should not be shown for payment gateways.

There are tangible business reasons why this is necessary.

In Commerce 1 on Drupal 7 it is possible to limit one payment method so it can only be used in one country, and other payment methods so they can be used in all countries except for that one. We've done that for years on a non-profit org's site and it works reliably.

A D7 rule that allows a certain billing method to only be available outside of the US


Per #42 it sounds like the work needs to be split into several pieces. Were the necessary issues created? I don't see any open issues regarding this use case besides #2950877, which hasn't been touched in almost three years. What are the next steps here?

agoradesign’s picture

In most of our e-commerce projects, we are providing a custom billing information pane and putting the payment method selection in a dedicated payment step. This way it is easy to restrict a payment gateway to a single country, for example

jsacksick’s picture

Version: 8.x-2.x-dev » 3.0.x-dev

If this is ever implemented, this should go into 3.x.