Files
odoo_source/addons/payment/views/payment_templates.xml
T
Antoine Vandevenne (anv) dbefe34af5 [IMP] payment(_authorize): rework validation transactions refund flow
Before this commit, the validation flow with verification (payment of a
small amount with immediate refund) was performed with the use of
validation routes: after payment, the customer was redirected to the
validation route stored on the transaction to trigger the refund. This
implementation had an issue: if the customer never reached the
validation route, they were not refunded their validation amount. This
could happen if the customer closed the tab after paying with an
acquirer offering payments with redirection, or if the validation
payment was asynchronously confirmed through a webhook notification.

This commit gets rid of validation routes and requires acquirers to
immediately refund the validation amount when the payment is confirmed.
This way, a payment confirmation coming from a webhook can trigger the
refund too.

As the only acquirer that implements the validation with verification
flow, Authorize.net now voids validation transactions as soon as they
are authorized.

While we're at it, the logging of processing values is adapted to only
log specific rendering values if a redirect form is rendered.

task-2612977

closes odoo/odoo#74707

Related: odoo/enterprise#20060
Related: odoo/upgrade#2710
Signed-off-by: Antoine Vandevenne (anv) <AntoineVDV@users.noreply.github.com>
2021-08-04 13:03:27 +00:00

359 lines
20 KiB
XML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- Checkout form -->
<template id="checkout" name="Payment Checkout">
<!-- Variables description:
- 'acquirers' - The payment acquirers compatible with the current transaction
- 'tokens' - The payment tokens of the current partner and payment acquirers
- 'default_token_id' - The id of the token that should be pre-selected. Optional
- 'fees_by_acquirer' - The dict of transaction fees for each acquirer. Optional
- 'show_tokenize_input' - Whether the option to save the payment method is shown
- 'reference_prefix' - The custom prefix to compute the full transaction reference
- 'amount' - The amount to pay. Optional (sale_subscription)
- 'currency' - The currency of the transaction, as a `res.currency` record
- 'partner_id' - The id of the partner on behalf of whom the payment should be made
- 'access_token' - The access token used to authenticate the partner.
- 'transaction_route' - The route used to create a transaction when the user clicks Pay
- 'landing_route' - The route the user is redirected to after the transaction
- 'footer_template_id' - The template id for the submit button. Optional
- 'invoice_id' - The id of the account move being paid. Optional
-->
<form name="o_payment_checkout"
class="o_payment_form mt-3 clearfix"
t-att-data-reference-prefix="reference_prefix"
t-att-data-amount="amount"
t-att-data-currency-id="currency and currency.id"
t-att-data-partner-id="partner_id"
t-att-data-access-token="access_token"
t-att-data-transaction-route="transaction_route"
t-att-data-landing-route="landing_route"
t-att-data-allow-token-selection="True"
t-att-data-invoice-id="invoice_id">
<t t-set="acquirer_count" t-value="len(acquirers) if acquirers else 0"/>
<t t-set="token_count" t-value="len(tokens) if tokens else 0"/>
<!-- Check the radio button of the default token, if set, or of the first acquirer if
it is the only payment option -->
<t t-set="default_payment_option_id"
t-value="default_token_id if default_token_id and token_count > 0
else acquirers[0].id if acquirer_count == 1 and token_count == 0
else None"/>
<t t-set="fees_by_acquirer" t-value="fees_by_acquirer or dict()"/>
<t t-set="footer_template_id"
t-value="footer_template_id or 'payment.footer'"/>
<div class="card">
<!-- === Acquirers === -->
<t t-foreach="acquirers" t-as="acquirer">
<div name="o_payment_option_card" class="card-body o_payment_option_card">
<label>
<!-- === Radio button === -->
<!-- Only shown if linked to the only payment option -->
<input name="o_payment_radio"
type="radio"
t-att-checked="acquirer.id == default_payment_option_id"
t-att-class="'' if acquirer_count + token_count > 1 else 'd-none'"
t-att-data-payment-option-id="acquirer.id"
t-att-data-provider="acquirer.provider"
data-payment-option-type="acquirer"/>
<!-- === Acquirer name === -->
<span class="payment_option_name">
<b t-esc="acquirer.display_as or acquirer.name"/>
</span>
<!-- === "Test Mode" badge === -->
<span t-if="acquirer.state == 'test'"
class="badge-pill badge-warning ml-1">
Test Mode
</span>
<!-- === Extra fees badge === -->
<t t-if="fees_by_acquirer.get(acquirer)">
<span class="badge-pill badge-secondary ml-1">
+ <t t-esc="fees_by_acquirer.get(acquirer)"
t-options="{'widget': 'monetary', 'display_currency': currency}"/>
Fees
</span>
</t>
</label>
<!-- === Payment icon list === -->
<t t-call="payment.icon_list"/>
<!-- === Help message === -->
<div t-if="acquirer.pre_msg"
t-out="acquirer.pre_msg"
class="text-muted ml-3"/>
</div>
<!-- === Acquirer inline form === -->
<div t-attf-id="o_payment_acquirer_inline_form_{{acquirer.id}}"
name="o_payment_inline_form"
class="card-footer d-none">
<!-- === Inline form content (filled by acquirer) === -->
<t t-if="acquirer.sudo()._should_build_inline_form(is_validation=False)">
<t t-set="inline_form_xml_id"
t-value="acquirer.sudo().inline_form_view_id.xml_id"/>
<div t-if="inline_form_xml_id" class="clearfix">
<t t-call="{{inline_form_xml_id}}">
<t t-set="acquirer_id" t-value="acquirer.id"/>
</t>
</div>
</t>
<!-- === "Save my payment details" checkbox === -->
<!-- Only included if partner is known and if the choice is given -->
<t t-set="tokenization_required"
t-value="acquirer._is_tokenization_required(provider=acquirer.provider)"/>
<label t-if="show_tokenize_input and acquirer.allow_tokenization and not tokenization_required">
<input name="o_payment_save_as_token" type="checkbox"/>
Save my payment details
</label>
</div>
</t>
<!-- === Tokens === -->
<t t-foreach="tokens" t-as="token">
<div name="o_payment_option_card" class="card-body o_payment_option_card">
<label>
<!-- === Radio button === -->
<input name="o_payment_radio"
type="radio"
t-att-checked="token.id == default_payment_option_id"
t-att-data-payment-option-id="token.id"
t-att-data-provider="token.provider"
data-payment-option-type="token"/>
<!-- === Token name === -->
<span class="payment_option_name" t-esc="token.name"/>
<!-- === "V" check mark === -->
<t t-call="payment.verified_token_checkmark"/>
</label>
</div>
<!-- === Token inline form === -->
<div t-attf-id="o_payment_token_inline_form_{{token.id}}"
name="o_payment_inline_form"
class="card-footer d-none"/>
</t>
</div>
<!-- === "Pay" button === -->
<t t-call="{{footer_template_id}}">
<t t-set="label">Pay</t>
<t t-set="icon_class" t-value="'fa-lock'"/>
</t>
</form>
</template>
<!-- Manage (token create and deletion) form -->
<template id="manage" name="Payment Manage">
<!-- Variables description:
- 'acquirers' - The payment acquirers supporting tokenization
- 'tokens' - The set of payment tokens of the current partner
- 'default_token_id' - The id of the token that should be pre-selected. Optional
- 'reference_prefix' - The custom prefix to compute the full transaction reference
- 'partner_id' - The id of the partner managing the tokens
- 'access_token' - The access token used to authenticate the partner.
- 'transaction_route' - The route used to create a validation transaction
- 'assign_token_route' - The route to call to assign a token to a record. If set, it
enables the token assignation mechanisms: creation of a new
token through a refunded transaction and assignation of an
existing token
- 'landing_route' - The route the user is redirected to at then end of the flow
- 'footer_template_id' - The template id for the submit button. Optional
-->
<form name="o_payment_manage"
class="o_payment_form mt-3 clearfix"
t-att-data-reference-prefix="reference_prefix"
t-att-data-partner-id="partner_id"
t-att-data-access-token="access_token"
t-att-data-invoice-id="invoice_id"
t-att-data-transaction-route="transaction_route"
t-att-data-assign-token-route="assign_token_route"
t-att-data-landing-route="landing_route"
t-att-data-allow-token-selection="bool(assign_token_route)">
<t t-set="acquirer_count" t-value="len(acquirers) if acquirers else 0"/>
<t t-set="token_count" t-value="len(tokens) if tokens else 0"/>
<t t-set="no_selectable_token" t-value="token_count == 0 or not assign_token_route"/>
<t t-set="default_payment_option_id"
t-value="default_token_id if default_token_id and token_count > 0
else acquirers[0].id if acquirer_count == 1 and no_selectable_token
else None"/>
<t t-set="footer_template_id"
t-value="footer_template_id or 'payment.footer'"/>
<div class="card">
<!-- === Acquirers === -->
<t t-foreach="acquirers" t-as="acquirer">
<div name="o_payment_option_card" class="card-body o_payment_option_card">
<label>
<!-- === Radio button === -->
<!-- Only shown if linked to the only payment option -->
<input name="o_payment_radio"
type="radio"
t-att-checked="acquirer.id == default_payment_option_id"
t-att-class="'' if acquirer_count + token_count > 1 else 'd-none'"
t-att-data-payment-option-id="acquirer.id"
t-att-data-provider="acquirer.provider"
data-payment-option-type="acquirer"/>
<!-- === Acquirer name === -->
<span class="payment_option_name">
<b><t t-esc="acquirer.display_as or acquirer.name"/></b>
</span>
<!-- === "Test Mode" badge === -->
<span t-if="acquirer.state == 'test'"
class="badge-pill badge-warning"
style="margin-left:5px">
Test Mode
</span>
</label>
<!-- === Payment icon list === -->
<t t-call="payment.icon_list"/>
<!-- === Help message === -->
<div t-if="acquirer.pre_msg"
t-out="acquirer.pre_msg"
class="text-muted ml-3"/>
</div>
<!-- === Acquirer inline form === -->
<t t-if="acquirer.sudo()._should_build_inline_form(is_validation=True)">
<div t-attf-id="o_payment_acquirer_inline_form_{{acquirer.id}}"
name="o_payment_inline_form"
class="card-footer d-none">
<!-- === Inline form content (filled by acquirer) === -->
<t t-set="inline_form_xml_id"
t-value="acquirer.sudo().inline_form_view_id.xml_id"/>
<div t-if="inline_form_xml_id" class="clearfix">
<t t-call="{{inline_form_xml_id}}">
<t t-set="acquirer_id" t-value="acquirer.id"/>
</t>
</div>
</div>
</t>
</t>
<!-- === Tokens === -->
<t t-foreach="tokens" t-as="token">
<div name="o_payment_option_card" class="card-body o_payment_option_card">
<label>
<!-- === Radio button === -->
<!-- Only shown if 'assign_token_route' is set -->
<input name="o_payment_radio"
type="radio"
t-att-checked="token.id == default_payment_option_id"
t-att-class="'' if bool(assign_token_route) else 'd-none'"
t-att-data-payment-option-id="token.id"
t-att-data-provider="token.provider"
data-payment-option-type="token"/>
<!-- === Token name === -->
<span class="payment_option_name" t-esc="token.name"/>
<!-- === "V" check mark === -->
<t t-call="payment.verified_token_checkmark"/>
</label>
<!-- === "Delete" token button === -->
<button name="o_payment_delete_token"
class="btn btn-primary btn-sm float-right">
<i class="fa fa-trash"/> Delete
</button>
</div>
<!-- === Token inline form === -->
<div t-attf-id="o_payment_token_inline_form_{{token.id}}"
name="o_payment_inline_form"
class="card-footer d-none"/>
</t>
</div>
<!-- === "Save Payment Method" button === -->
<t t-call="{{footer_template_id}}">
<t t-set="label">Save Payment Method</t>
<t t-set="icon_class" t-value="'fa-plus-circle'"/>
</t>
</form>
</template>
<!-- Expandable payment icon list -->
<template id="icon_list" name="Payment Icon List">
<ul class="payment_icon_list float-right list-inline" data-max-icons="3">
<t t-set="icon_index" t-value="0"/>
<t t-set="MAX_ICONS" t-value="3"/>
<!-- === Icons === -->
<!-- Only shown if in the first 3 icons -->
<t t-foreach="acquirer.payment_icon_ids" t-as="icon">
<li t-attf-class="list-inline-item{{'' if (icon_index &lt; MAX_ICONS) else ' d-none'}}">
<span t-esc="icon.image_payment_form"
t-options="{'widget': 'image', 'alt-field': 'name'}"
data-toggle="tooltip"
t-att-title="icon.name"/>
</li>
<t t-set="icon_index" t-value="icon_index + 1"/>
</t>
<t t-if="icon_index >= MAX_ICONS">
<!-- === "show more" button === -->
<!-- Only displayed if too many payment icons -->
<li style="display:block;" class="list-inline-item">
<span class="float-right more_option text-info">
<a name="o_payment_icon_more"
data-toggle="tooltip"
t-att-title="', '.join([icon.name for icon in acquirer.payment_icon_ids[MAX_ICONS:]])">
show more
</a>
</span>
</li>
<!-- === "show less" button === -->
<!-- Only displayed when "show more" is clicked -->
<li style="display:block;" class="list-inline-item d-none">
<span class="float-right more_option text-info">
<a name="o_payment_icon_less">show less</a>
</span>
</li>
</t>
</ul>
</template>
<!-- Verified token checkmark -->
<template id="verified_token_checkmark" name="Payment Verified Token Checkmark">
<t t-if="0" name="payment_test_hook"/>
<t t-else="">
<i t-if="token.verified" class="fa fa-check text-success"
title="This payment method has been verified by our system."
role="img"
aria-label="Ok"/>
<i t-else="" class="fa fa-check text-muted"
title="This payment method has not been verified by our system."
role="img"
aria-label="Not verified"/>
</t>
</template>
<!-- Generic footer for payment forms -->
<template id="footer" name="Payment Footer">
<!-- Variables description:
- 'label' - The label for the submit button
- 'icon_class' - The Font Awesome icon class (e.g. 'fa-lock') for the submit button
-->
<div class="float-right mt-2">
<button name="o_payment_submit_button"
type="submit"
class="btn btn-primary btn-lg mb8 mt8"
disabled="true"
t-att-data-icon-class="icon_class">
<i t-attf-class="fa {{icon_class}}"/> <t t-esc="label"/>
</button>
</div>
</template>
<!-- Transaction status in portal -->
<template id="transaction_status">
<!-- Variables description:
- 'tx' - The transaction whose status must be displayed
-->
<div t-if="tx.state == 'pending'" class="alert alert-warning alert-dismissible">
<span t-if='tx.acquirer_id.sudo().pending_msg'
t-out="tx.acquirer_id.sudo().pending_msg"/>
<button class="close" data-dismiss="alert" title="Dismiss">×</button>
</div>
<div t-elif="tx.state == 'authorized'" class="alert alert-success alert-dismissible">
<span t-if='tx.acquirer_id.sudo().auth_msg' t-out="tx.acquirer_id.sudo().auth_msg"/>
<button class="close" data-dismiss="alert" title="Dismiss">×</button>
</div>
<div t-elif="tx.state == 'done'" class="alert alert-success alert-dismissible">
<span t-if='tx.acquirer_id.sudo().done_msg' t-out="tx.acquirer_id.sudo().done_msg"/>
<button class="close" data-dismiss="alert" title="Dismiss">×</button>
</div>
<div t-elif="tx.state == 'cancel'" class="alert alert-danger alert-dismissible">
<span t-if='tx.acquirer_id.sudo().cancel_msg' t-out="tx.acquirer_id.sudo().cancel_msg"/>
<button class="close" data-dismiss="alert" title="Dismiss">×</button>
</div>
<span t-if="tx.state_message" t-esc="tx.state_message"/>
</template>
</odoo>