[IMP] payment(_adyen): support multiple payment forms on the same page
This commit addresses an issue with the checkout form displayed on the portal page of the Subscriptions app: when only one payment provider was available for checkout, and it required an inline form to be displayed, the latter would not be shown, and customers were able to hit the "Pay" button, resulting in a client error. The problem was that Subscriptions now simultaneously displays two payment forms on the same page, one for checkout and one for managing payment methods, which was not supported by the payment engine. closes odoo/odoo#120482 X-original-commit: 91d701e34a25d2ceada1679f36a9cdbb96c6c019 Signed-off-by: Antoine Vandevenne (anv) <anv@odoo.com>
This commit is contained in:
@@ -19,6 +19,7 @@
|
||||
* @constructor
|
||||
*/
|
||||
init: function () {
|
||||
this.formType = 'checkout';
|
||||
const preventDoubleClick = handlerMethod => {
|
||||
return debounce(handlerMethod, 500, true);
|
||||
};
|
||||
@@ -62,9 +63,8 @@
|
||||
|
||||
// Update the tx context with the value of the "Save my payment details" checkbox
|
||||
if (flow !== 'token') {
|
||||
const $tokenizeCheckbox = this.$(
|
||||
`#o_payment_provider_inline_form_${paymentOptionId}` // Only match provider radios
|
||||
).find('input[name="o_payment_save_as_token"]');
|
||||
const $inlineForm = this._getInlineFormFromRadio(checkedRadio);
|
||||
const $tokenizeCheckbox = $inlineForm.find('input[name="o_payment_save_as_token"]');
|
||||
this.txContext.tokenizationRequested = $tokenizeCheckbox.length === 1
|
||||
&& $tokenizeCheckbox[0].checked;
|
||||
} else {
|
||||
|
||||
@@ -24,6 +24,7 @@ import { debounce } from "@web/core/utils/timing";
|
||||
* @constructor
|
||||
*/
|
||||
init: function () {
|
||||
this.formType = 'manage';
|
||||
const preventDoubleClick = handlerMethod => {
|
||||
return debounce(handlerMethod, 500, true);
|
||||
};
|
||||
@@ -136,7 +137,7 @@ import { debounce } from "@web/core/utils/timing";
|
||||
`input[name="o_payment_radio"][data-payment-option-id="${tokenId}"]` +
|
||||
`[data-payment-option-type="token"]`
|
||||
).closest('div[name="o_payment_option_card"]');
|
||||
$tokenCard.siblings(`#o_payment_token_inline_form_${tokenId}`).remove();
|
||||
$tokenCard.siblings(`#o_payment_token_inline_manage_form_${tokenId}`).remove();
|
||||
$tokenCard.remove();
|
||||
this._disableButton(false);
|
||||
}).guardedCatch(error => {
|
||||
|
||||
@@ -116,9 +116,7 @@
|
||||
|
||||
// Append error to inline form and center the page on the error
|
||||
const checkedRadio = $checkedRadios[0];
|
||||
const paymentOptionId = this._getPaymentOptionIdFromRadio(checkedRadio);
|
||||
const formType = $(checkedRadio).data('payment-option-type');
|
||||
const $inlineForm = this.$(`#o_payment_${formType}_inline_form_${paymentOptionId}`);
|
||||
const $inlineForm = this._getInlineFormFromRadio(checkedRadio);
|
||||
$inlineForm.removeClass('d-none'); // Show the inline form even if it was empty
|
||||
$inlineForm.append(errorHtml).find('div[name="o_payment_error"]')[0]
|
||||
.scrollIntoView({behavior: 'smooth', block: 'center'});
|
||||
@@ -146,8 +144,7 @@
|
||||
|
||||
// Prepare the inline form of the selected payment option and display it if not empty
|
||||
this._prepareInlineForm(provider, paymentOptionId, flow);
|
||||
const formType = $(radio).data('payment-option-type');
|
||||
const $inlineForm = this.$(`#o_payment_${formType}_inline_form_${paymentOptionId}`);
|
||||
const $inlineForm = this._getInlineFormFromRadio(radio);
|
||||
if ($inlineForm.children().length > 0) {
|
||||
$inlineForm.removeClass('d-none');
|
||||
}
|
||||
@@ -198,6 +195,22 @@
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Find and return the inline form of the selected payment option.
|
||||
*
|
||||
* @private
|
||||
* @param {HTMLInputElement} radio - The radio button linked to the payment option.
|
||||
* @return {jQuery} The inline form of the selected payment option.
|
||||
*/
|
||||
_getInlineFormFromRadio: function (radio) {
|
||||
const paymentOptionId = this._getPaymentOptionIdFromRadio(radio);
|
||||
const paymentOptionType = $(radio).data('payment-option-type');
|
||||
const $inlineForm = this.$(
|
||||
`#o_payment_${paymentOptionType}_inline_${this.formType}_form_${paymentOptionId}`
|
||||
);
|
||||
return $inlineForm;
|
||||
},
|
||||
|
||||
/**
|
||||
* Determine and return the online payment flow of the selected payment option.
|
||||
*
|
||||
@@ -247,23 +260,23 @@
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove the error in the provider form.
|
||||
* Remove the error in the inline form of the current widget.
|
||||
*
|
||||
* @private
|
||||
* @return {jQuery} The removed error
|
||||
*/
|
||||
_hideError() {
|
||||
return $('div[name="o_payment_error"]').remove();
|
||||
return this.$('div[name="o_payment_error"]').remove();
|
||||
},
|
||||
|
||||
/**
|
||||
* Collapse all inline forms.
|
||||
* Collapse all inline forms of the current widget.
|
||||
*
|
||||
* @private
|
||||
* @return {undefined}.
|
||||
*/
|
||||
_hideInlineForms() {
|
||||
return $('[name="o_payment_inline_form"]').addClass('d-none');
|
||||
return this.$('[name="o_payment_inline_form"]').addClass('d-none');
|
||||
},
|
||||
|
||||
/**
|
||||
|
||||
@@ -97,7 +97,7 @@
|
||||
</label>
|
||||
</div>
|
||||
<!-- === Token inline form === -->
|
||||
<div t-attf-id="o_payment_token_inline_form_{{token.id}}"
|
||||
<div t-attf-id="o_payment_token_inline_checkout_form_{{token.id}}"
|
||||
name="o_payment_inline_form"
|
||||
class="card-footer d-none">
|
||||
<t t-set="token_inline_form_xml_id"
|
||||
@@ -105,6 +105,7 @@
|
||||
<!-- === Inline form content (filled by provider) === -->
|
||||
<div t-if="token_inline_form_xml_id" class="clearfix">
|
||||
<t t-call="{{token_inline_form_xml_id}}">
|
||||
<t t-set="form_type" t-value="'checkout'"/>
|
||||
<t t-set="token" t-value="token"/>
|
||||
</t>
|
||||
</div>
|
||||
@@ -158,7 +159,7 @@
|
||||
class="text-muted ms-3"/>
|
||||
</div>
|
||||
<!-- === Provider inline form === -->
|
||||
<div t-attf-id="o_payment_provider_inline_form_{{provider.id}}"
|
||||
<div t-attf-id="o_payment_provider_inline_checkout_form_{{provider.id}}"
|
||||
name="o_payment_inline_form"
|
||||
class="card-footer px-3 d-none">
|
||||
<t t-if="provider.sudo()._should_build_inline_form(is_validation=False)">
|
||||
@@ -167,6 +168,7 @@
|
||||
<!-- === Inline form content (filled by provider) === -->
|
||||
<div t-if="inline_form_xml_id" class="clearfix">
|
||||
<t t-call="{{inline_form_xml_id}}">
|
||||
<t t-set="form_type" t-value="'checkout'"/>
|
||||
<t t-set="provider_id" t-value="provider.id"/>
|
||||
</t>
|
||||
</div>
|
||||
@@ -264,7 +266,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<!-- === Token inline form === -->
|
||||
<div t-attf-id="o_payment_token_inline_form_{{token.id}}"
|
||||
<div t-attf-id="o_payment_token_inline_manage_form_{{token.id}}"
|
||||
name="o_payment_inline_form"
|
||||
class="card-footer d-none">
|
||||
<t t-set="token_inline_form_xml_id"
|
||||
@@ -272,6 +274,7 @@
|
||||
<!-- === Inline form content (filled by provider) === -->
|
||||
<div t-if="token_inline_form_xml_id" class="clearfix">
|
||||
<t t-call="{{token_inline_form_xml_id}}">
|
||||
<t t-set="form_type" t-value="'manage'"/>
|
||||
<t t-set="token" t-value="token"/>
|
||||
</t>
|
||||
</div>
|
||||
@@ -318,7 +321,7 @@
|
||||
</div>
|
||||
<!-- === Provider inline form === -->
|
||||
<t t-if="provider.sudo()._should_build_inline_form(is_validation=True)">
|
||||
<div t-attf-id="o_payment_provider_inline_form_{{provider.id}}"
|
||||
<div t-attf-id="o_payment_provider_inline_manage_form_{{provider.id}}"
|
||||
name="o_payment_inline_form"
|
||||
class="card-footer d-none">
|
||||
<t t-set="inline_form_xml_id"
|
||||
@@ -326,6 +329,7 @@
|
||||
<!-- === Inline form content (filled by provider) === -->
|
||||
<div t-if="inline_form_xml_id" class="clearfix">
|
||||
<t t-call="{{inline_form_xml_id}}">
|
||||
<t t-set="form_type" t-value="'manage'"/>
|
||||
<t t-set="provider_id" t-value="provider.id"/>
|
||||
</t>
|
||||
</div>
|
||||
|
||||
@@ -173,7 +173,7 @@
|
||||
showPayButton: false,
|
||||
setStatusAutomatically: true,
|
||||
},
|
||||
).mount(`#o_adyen_dropin_container_${paymentOptionId}`);
|
||||
).mount(`#o_adyen_${this.formType}_dropin_container_${paymentOptionId}`);
|
||||
this.adyenDropin.providerId = paymentOptionId;
|
||||
});
|
||||
}).guardedCatch((error) => {
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
<odoo>
|
||||
|
||||
<template id="inline_form">
|
||||
<div t-attf-id="o_adyen_dropin_container_{{provider_id}}" class="o_adyen_dropin"/>
|
||||
<div t-attf-id="o_adyen_{{form_type}}_dropin_container_{{provider_id}}"
|
||||
class="o_adyen_dropin"
|
||||
/>
|
||||
</template>
|
||||
|
||||
</odoo>
|
||||
|
||||
Reference in New Issue
Block a user