Issue - Go to Accounting / Customers / Invoices - Pick any invoice not paid - Action: Generate a Payment Link - Open the link - Refresh the page having Javascript disabled - Click the Pay Now button Traceback Cause If JS is not loaded, the values in the form fields are not bound as expected. It can also happen with slow connections and fast click on the button at the loading. Before the JS is entirely loaded (as it is lazy-loaded). Solution Button disabled by default, wait the page to be loaded and then activate the button OPW-2255760 closes odoo/odoo#54128 X-original-commit: 0e7071f249e69c5cca519ec66120530eb0f97153 Signed-off-by: Jason Van Malder (jvm) <jvm@odoo.com>
185 lines
12 KiB
XML
185 lines
12 KiB
XML
<?xml version="1.0" encoding="utf-8"?>
|
|
<odoo>
|
|
<template id="assets_backend" name="Payment Assets Backend" inherit_id="web.assets_backend">
|
|
<xpath expr="." position="inside">
|
|
<link rel="stylesheet" type="text/scss" href="/payment/static/src/scss/payment_acquirer.scss"/>
|
|
</xpath>
|
|
</template>
|
|
|
|
<template id="payment_tokens_list" name="Payment Tokens list">
|
|
<!--
|
|
Variables description:
|
|
- 'submit_txt' the text displayed inside the submit button
|
|
- 'submit_class' the css classes to style the submit button
|
|
- 'icon_class' font awesome class (e.g. 'fa-trash', 'fa-lock')
|
|
- 'form_action' the URI to the page that will handle the form values given for server2server
|
|
- 'pms' the tokens
|
|
- 'checked_pm_id' the payment token that should be checked (for radio buttons)
|
|
- 'mode' can take two values, either 'payment' or 'manage'. 'manage' displays the add a new card and delete buttons. 'payment'
|
|
display a form that is used to pay and send the information to the form action url.
|
|
- 'acquirers' the list of both server2server and form payment acquirers
|
|
- 'verify_validity' if we need to verify if the payment method is valid when adding a new one
|
|
- 'prepare_tx_url' the url of the route which will handle the creation of a transaction for a form base payment (handles if the transaction is form or form_save)
|
|
- 'show_manage_btn' if True, a button is added in the footer to manage payment methods
|
|
-->
|
|
<form t-if="pms or acquirers" method="post" class="o_payment_form mt-3 clearfix"
|
|
t-att-action="form_action if form_action else '#'"
|
|
t-att-data-success-url="success_url or ''"
|
|
t-att-data-error-url="error_url or ''"
|
|
t-att-data-access-token="access_token or ''"
|
|
t-att-data-partner-id="partner_id"
|
|
t-att-data-callback-method="callback_method or ''"
|
|
t-att-data-order-id="order_id or ''"
|
|
t-att-data-mode="mode">
|
|
<input type="hidden" name="csrf_token" t-att-value="request.csrf_token()"/>
|
|
<input type="hidden" t-if="prepare_tx_url" name="prepare_tx_url" t-att-value="prepare_tx_url"/>
|
|
<input type="hidden" t-if="order_id" name="order_id" t-att-value="order_id"/>
|
|
<!-- s2s form submission -->
|
|
<input type="hidden" t-if="access_token" name="access_token" t-att-value="access_token"/>
|
|
<input type="hidden" t-if="success_url" name="success_url" t-att-value="success_url"/>
|
|
<input type="hidden" t-if="error_url" name="error_url" t-att-value="error_url"/>
|
|
<input type="hidden" t-if="callback_method" name="callback_method" t-att-value="callback_method"/>
|
|
|
|
<div class="card">
|
|
<t t-set="acquirers_count" t-value="len(acquirers) if acquirers else 0"/>
|
|
<t t-set="pms_count" t-value="len(pms) if pms else 0"/>
|
|
<t t-set="MAX_BRAND_LINE" t-value="3"/>
|
|
<t t-foreach="acquirers" t-as="acq">
|
|
<div class="card-body o_payment_acquirer_select">
|
|
<label>
|
|
<t t-if="acq.payment_flow == 'form'">
|
|
<input type="radio" t-att-data-acquirer-id="acq.id"
|
|
t-att-data-form-payment="true"
|
|
t-att-data-provider="acq.provider"
|
|
t-att-class="'d-none' if (acquirers_count==1 and pms_count==0) else ''"
|
|
name="pm_id" t-attf-value="form_{{acq.id}}"
|
|
t-att-checked="acquirers_count==1 and pms_count==0 or acquirers[0] == acq"/>
|
|
</t>
|
|
<t t-else="acq.payment_flow == 's2s'">
|
|
<input type="radio" t-att-data-acquirer-id="acq.id"
|
|
t-att-data-s2s-payment="true"
|
|
t-att-data-provider="acq.provider"
|
|
name="pm_id" t-attf-value="new_{{acq.id}}"
|
|
t-att-class="'d-none' if (acquirers_count==1 and pms_count==0) else ''"
|
|
t-att-checked="acquirers_count==1 and pms_count==0 or acquirers[0] == acq"/>
|
|
</t>
|
|
<span class="payment_option_name">
|
|
<t t-esc="acq.display_as or acq.name"/>
|
|
<div t-if="acq.state == 'test'" class="badge-pill badge-warning float-right" style="margin-left:5px">
|
|
Test Mode
|
|
</div>
|
|
</span>
|
|
<t t-if="acq_extra_fees and acq_extra_fees.get(acq)">
|
|
<span class="badge badge-pill badge-secondary"> + <t t-esc="acq_extra_fees[acq]" t-options='{"widget": "monetary", "display_currency": acq_extra_fees["currency_id"]}'/> Fee </span>
|
|
</t>
|
|
<t t-elif="acq.fees_active">
|
|
<small class="text-muted">(Some fees may apply)</small>
|
|
</t>
|
|
</label>
|
|
<ul class="float-right list-inline payment_icon_list">
|
|
<t t-set="i" t-value="0"/>
|
|
<t t-foreach="acq.payment_icon_ids" t-as="pm_icon">
|
|
<li t-attf-class="list-inline-item#{'' if (i < MAX_BRAND_LINE) else ' d-none'}">
|
|
<span t-field="pm_icon.image_payment_form"
|
|
t-options='{"widget": "image", "alt-field": "name"}'/>
|
|
</li>
|
|
<li t-if="i==MAX_BRAND_LINE" style="display:block;" class="list-inline-item">
|
|
<span class="float-right more_option text-info">
|
|
<a href="#" class="o_payment_form_pay_icon_more" data-toggle="tooltip" t-att-title="', '.join([opt.name for opt in acq.payment_icon_ids[MAX_BRAND_LINE:]])">and more</a>
|
|
</span>
|
|
</li>
|
|
<t t-set="i" t-value="i+1"/>
|
|
</t>
|
|
</ul>
|
|
<div t-raw="acq.pre_msg" class="text-muted ml-3"/>
|
|
</div>
|
|
<t t-if="acq.payment_flow == 'form'">
|
|
<div t-attf-id="o_payment_form_acq_{{acq.id}}"
|
|
t-attf-class="d-none {{'card-footer' if acq.save_token == 'ask' else ''}}">
|
|
<label t-if="acq.save_token == 'ask'">
|
|
<input type="checkbox" name="o_payment_form_save_token" data-remove-me=""/>
|
|
Save my payment data
|
|
</label>
|
|
<t t-if="acq.save_token == 'always'">
|
|
<input type="checkbox" name="o_payment_form_save_token" checked="'checked'" class="o_hidden" data-remove-me=""/>
|
|
</t>
|
|
</div>
|
|
</t>
|
|
<t t-else="acq.payment_flow == 's2s'">
|
|
<div t-attf-id="o_payment_add_token_acq_{{acq.id}}"
|
|
t-attf-class="card-footer {{'d-none' if(acquirers_count > 1 and pms_count==0 and acquirers[0]!=acq) else 'd-none' if pms_count >0 else ''}}">
|
|
<div class="clearfix">
|
|
<input type="hidden" t-if="(verify_validity==True or mode == 'manage') and acq.check_validity" name="verify_validity" t-att-value="acq.check_validity"/>
|
|
<t t-call="{{acq.sudo().get_s2s_form_xml_id()}}">
|
|
<t t-set="id" t-value="acq.id"/>
|
|
<t t-set="partner_id" t-value="partner_id"/>
|
|
<t t-if="not return_url" t-set="return_url" t-value="''"/>
|
|
</t>
|
|
</div>
|
|
</div>
|
|
</t>
|
|
</t>
|
|
<t t-foreach="pms" t-as="pm">
|
|
<t t-if="not verify_validity or (pm.acquirer_id.check_validity and pm.verified) or not pm.acquirer_id.check_validity">
|
|
<div class="card-body o_payment_acquirer_select">
|
|
<label>
|
|
<input t-if="mode == 'payment'" type="radio" name="pm_id" t-att-value="pm.id" t-att-checked="checked_pm_id == pm.id"/>
|
|
<span class="payment_option_name" t-esc="pm.name"/>
|
|
<t t-if="pm.verified">
|
|
<i class="fa fa-check text-success" title="This payment method is verified by our system." role="img" aria-label="Ok"></i>
|
|
</t>
|
|
<t t-else="">
|
|
<i class="fa fa-check text-muted" title="This payment method has not been verified by our system." role="img" aria-label="Not verified"></i>
|
|
</t>
|
|
</label>
|
|
<button t-if="mode == 'manage'" name="delete_pm" t-att-value="pm.id" class="btn btn-primary btn-sm float-right">
|
|
<i class="fa fa-trash"></i> Delete
|
|
</button>
|
|
</div>
|
|
</t>
|
|
</t>
|
|
</div>
|
|
<div t-if='back_button_txt' class="float-left mt-2">
|
|
<a role="button" t-att-href="back_button_link or '#'" t-att-class="back_button_class or 'btn btn-lg btn-secondary'">
|
|
<i t-if="back_button_icon_class" t-attf-class="fa {{back_button_icon_class}}"/>
|
|
<t t-esc="back_button_txt"/>
|
|
</a>
|
|
</div>
|
|
<div class="float-right mt-2">
|
|
<button t-if="mode == 'payment'" id="o_payment_form_pay" type="submit" t-att-class="submit_class if submit_class else 'btn btn-primary btn-lg mb8 mt8'" disabled="true">
|
|
<t t-if="submit_txt">
|
|
<i t-if="icon_class and not icon_right" t-attf-class="fa {{icon_class}}"/>
|
|
<t t-esc="submit_txt"/>
|
|
<i t-if="icon_class and icon_right" t-attf-class="fa {{icon_class}}"/>
|
|
</t>
|
|
<t t-else="">
|
|
<i class="fa fa-lock"/> Pay
|
|
</t>
|
|
</button>
|
|
<t t-if="show_manage_btn">
|
|
<a class="btn btn-link mb8 mt8" href="/my/payment_method">Manage your payment methods</a>
|
|
</t>
|
|
<button t-if="mode == 'manage' and list(filter(lambda x: x.payment_flow == 's2s', acquirers))" type="submit" id="o_payment_form_add_pm" class="btn btn-primary btn-lg mb8 mt8">
|
|
<i class="fa fa-plus-circle"/> Add new card
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</template>
|
|
|
|
<template id="payment_process_page" name="Payment processing page">
|
|
<t t-call="portal.frontend_layout">
|
|
<div class="wrap">
|
|
<div class="container o_website_payment">
|
|
<div class="o_payment_processing">
|
|
<div class="row">
|
|
<div class="o_payment_processing_content col-sm-6 col-sm-offset-3">
|
|
<!-- The content here is generated in JS -->
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</t>
|
|
</template>
|
|
</odoo>
|