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.
- #3110842: The billing address condition should not be shown for payment gateways
- #3035976: Keep billing information as a separate pane instead of replacing it with the payment information pane
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
AJAX refresh of payment optionError Message for payment option no longer availableHide payment options until address selected- Apply JS updates based on #3254957: Remove the JQuery dependency for the JS logic in checkout
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
Comments
Comment #2
jsacksick commentedI 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
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
OrderBillingAddresscondition can be properly evaluated.Comment #3
dwkitchen commentedHere is my first progress patch.
What I have done:
PaymentOptionsBuildto be able togetStoredOptionsandgetNewOptionsKnown issues:
Comment #4
dwkitchen commentedImprovements since last patch:
Known issues:
JS Rebuilding of Payment Gateways is based on #2849756: Auto-recalculate shipping when the address changes and they can be tested together.
Comment #5
dwkitchen commentedComment #8
jsacksick commentedI 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?
Comment #9
dwkitchen commentedThanks 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.
Comment #10
dwkitchen commentedRemoving debug messages
Comment #11
dwkitchen commentedFixing a couple of extra issues I found.
Comment #12
jsacksick commentedComment #13
dwkitchen commentedImprove handling if shipping profile on different checkout page or has been skipped because order doesn't have shippable item.
Comment #14
dwkitchen commentedFound 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.
Comment #15
dwkitchen commentedTwo 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.
Comment #16
dwkitchen commentedI tiny update for client request
Comment #17
dwkitchen commentedCatch all case was not included, the billing address was only set on the order if
PaymentGatewayimplemented:SupportsCreatingPaymentMethodsInterfaceSupportsStoredPaymentMethodsInterfaceComment #18
dwkitchen commentedThis issue will now depend on #3184254: Separate stored payment methods from new in the Payment Checkout Pane the separation of saved and new payment methods.
Comment #19
ayalon commentedRerolled version for Commerce Core 2.25
Comment #20
erom commentedtried this patch for commerce 2.28
Comment #21
khiminrm commentedRerolled to last 2.x-dev
Comment #22
khiminrm commentedUpdated last patch. Improved code. Fixed auto recalculate.
Comment #23
khiminrm commentedComment #24
khiminrm commentedFixed 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.
Comment #25
khiminrm commentedComment #26
khiminrm commentedFixed billing profile when we select Add payment method.
Comment #27
khiminrm commentedFixed 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.
Comment #28
khiminrm commentedFixed profiles when we create new payment method with new billing profile.
Comment #29
khiminrm commentedRefactored code to fix error in console when we tried to use existing payment method and submit the checkout form.
Comment #30
dwkitchen commentedComment #32
avpadernoComment #33
dwkitchen commentedRefactored 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.
Comment #34
mglamanThis 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.
Comment #35
mglamanYou'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.
$user_input['copy_fields']['enable'] ?? falseRemoves if and defaults to false.
TODO
static::classis the same as get_class.Comment #36
jsacksick commentedThis 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.
Comment #37
dwkitchen commentedAdded 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
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.
Comment #38
ayalon commentedThis 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:
Correct:
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.
Comment #39
ayalon commentedAttached 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
Comment #40
ayalon commentedIt turned out, that the patch is not working with free orders.
Attached a fixed version supporting free (0 Value) orders.
Comment #41
avpadernoComment #42
jsacksick commentedWe'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.
Comment #43
ayalon commented@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:
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?
Comment #44
ayalon commentedAs discussed with @jsacksick we will move the custom code to a custom PaymentInformation checkout pane and no longer rely on this patch.
Comment #45
damienmckennaCross-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.
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?
Comment #46
agoradesign commentedIn 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
Comment #47
jsacksick commentedIf this is ever implemented, this should go into 3.x.