The previous commit introduced some changes for SCA/PSD2 but some things were missing; most notably, registering payment cards in a s2s flow used the old Charge API that is not SCA-ready. This commit uses the SetupIntent API instead, allowing to register the card prior to payment. The global flow is described in the Stripe documentation in more details: https://stripe.com/docs/payments/cards/saving-cards Unfortunately, given the complete flow of payments, it is not possible to use the PaymentIntents API since this would require having the reference of the transaction before it gets created (the TX is created after the form submission, but the token must already exist at that point). Since this is meant to be backported, this changes are kept minimal in this feature branch. The PaymentIntents API would have been better since registering a card during a payment makes it more likely that future off-session transactions will not require an authentication step. We'll have to live with that for now; it's difficult to know exactly how this will affect customers before PSD2 goes into effect on september 14. Another branch might completely change the flow soon-ish to a more asynchroneous model, although it may miss the v13 freeze date -_-
179 lines
12 KiB
XML
179 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-esc="acq.name"/>
|
|
<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 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'">
|
|
<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>
|