diff --git a/addons/payment/data/payment_acquirer_data.xml b/addons/payment/data/payment_acquirer_data.xml index be5d404adee..b9040c22757 100644 --- a/addons/payment/data/payment_acquirer_data.xml +++ b/addons/payment/data/payment_acquirer_data.xml @@ -265,4 +265,21 @@ ref("payment.payment_icon_cc_codensa_easy_credit")])]'/> + + Alipay + + + + + + Alipay is the most popular online payment platform in China. Chinese consumers can buy online using their Alipay eWallet. + + + Online Payment + Payment Status Tracking + + + + + diff --git a/addons/payment/data/payment_icon_data.xml b/addons/payment/data/payment_icon_data.xml index ded11d7dee9..a4613d3e3d9 100644 --- a/addons/payment/data/payment_icon_data.xml +++ b/addons/payment/data/payment_icon_data.xml @@ -54,4 +54,14 @@ Codensa Easy Credit + + + Western Union + + + + + WebMoney + + \ No newline at end of file diff --git a/addons/payment/static/img/webmoney.png b/addons/payment/static/img/webmoney.png new file mode 100644 index 00000000000..2e4e591669c Binary files /dev/null and b/addons/payment/static/img/webmoney.png differ diff --git a/addons/payment/static/img/western_union.png b/addons/payment/static/img/western_union.png new file mode 100644 index 00000000000..02969bd5624 Binary files /dev/null and b/addons/payment/static/img/western_union.png differ diff --git a/addons/payment_alipay/__init__.py b/addons/payment_alipay/__init__.py new file mode 100644 index 00000000000..7d34c7c054a --- /dev/null +++ b/addons/payment_alipay/__init__.py @@ -0,0 +1,5 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from . import controllers +from . import models diff --git a/addons/payment_alipay/__manifest__.py b/addons/payment_alipay/__manifest__.py new file mode 100644 index 00000000000..1228c13920d --- /dev/null +++ b/addons/payment_alipay/__manifest__.py @@ -0,0 +1,15 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +{ + 'name': 'Alipay Payment Acquirer', + 'category': 'Accounting', + 'summary': 'Payment Acquirer: Alipay Implementation', + 'description': """Alipay Payment Acquirer""", + 'depends': ['payment'], + 'data': [ + 'views/alipay_views.xml', + 'views/payment_alipay_templates.xml', + 'data/payment_acquirer_data.xml', + ], +} diff --git a/addons/payment_alipay/controllers/__init__.py b/addons/payment_alipay/controllers/__init__.py new file mode 100644 index 00000000000..5d4b25db9c0 --- /dev/null +++ b/addons/payment_alipay/controllers/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from . import main diff --git a/addons/payment_alipay/controllers/main.py b/addons/payment_alipay/controllers/main.py new file mode 100644 index 00000000000..3f1b4e1e40c --- /dev/null +++ b/addons/payment_alipay/controllers/main.py @@ -0,0 +1,63 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +import logging +import pprint +import requests +import werkzeug + +from odoo import http +from odoo.http import request + +_logger = logging.getLogger(__name__) + + +class AlipayController(http.Controller): + _notify_url = '/payment/alipay/notify' + _return_url = '/payment/alipay/return' + + def _alipay_validate_data(self, **post): + resp = post.get('trade_status') + if resp: + if resp in ['TRADE_FINISHED', 'TRADE_SUCCESS']: + _logger.info('Alipay: validated data') + elif resp == 'TRADE_CLOSED': + _logger.warning('Alipay: payment refunded to user and closed the transaction') + else: + _logger.warning('Alipay: unrecognized alipay answer, received %s instead of TRADE_FINISHED/TRADE_SUCCESS and TRADE_CLOSED' % (post['trade_status'])) + if post.get('out_trade_no') and post.get('trade_no'): + post['reference'] = request.env['payment.transaction'].sudo().search([('reference', '=', post['out_trade_no'])]).reference + return request.env['payment.transaction'].sudo().form_feedback(post, 'alipay') + return False + + def _alipay_validate_notification(self, **post): + if post.get('out_trade_no'): + alipay = request.env['payment.transaction'].sudo().search([('reference', '=', post.get('out_trade_no'))]).acquirer_id + else: + alipay = request.env['payment.acquirer'].sudo().search([('provider', '=', 'alipay')]) + val = { + 'service': 'notify_verify', + 'partner': alipay.alipay_merchant_partner_id, + 'notify_id': post['notify_id'] + } + response = requests.post(alipay._get_alipay_urls(alipay.environment), val) + response.raise_for_status() + _logger.info('Validate alipay Notification %s' % response.text) + # After program is executed, the page must print “success” (without quote). If not, Alipay server would keep re-sending notification, until over 24 hour 22 minutes Generally, there are 8 notifications within 25 hours (Frequency: 2m,10m,15m,1h,2h,6h,15h) + if response.text == 'true': + self._alipay_validate_data(**post) + return 'success' + return "" + + @http.route('/payment/alipay/return', type='http', auth="none", methods=['GET', 'POST']) + def alipay_return(self, redirect_url=False, **post): + """ Alipay return """ + _logger.info('Beginning Alipay form_feedback with post data %s', pprint.pformat(post)) + self._alipay_validate_data(**post) + return werkzeug.utils.redirect(redirect_url or "/") + + @http.route('/payment/alipay/notify', type='http', auth='none', methods=['POST'], csrf=False) + def alipay_notify(self, **post): + """ Alipay Notify """ + _logger.info('Beginning Alipay notification form_feedback with post data %s', pprint.pformat(post)) + return self._alipay_validate_notification(**post) diff --git a/addons/payment_alipay/data/payment_acquirer_data.xml b/addons/payment_alipay/data/payment_acquirer_data.xml new file mode 100644 index 00000000000..a608788e5fa --- /dev/null +++ b/addons/payment_alipay/data/payment_acquirer_data.xml @@ -0,0 +1,18 @@ + + + + Alipay + alipay + + + test + + + You will be redirected to the Alipay website after clicking on the payment button. + + + dummy + dummy + dummy + + diff --git a/addons/payment_alipay/models/__init__.py b/addons/payment_alipay/models/__init__.py new file mode 100644 index 00000000000..2ec5b9cd2f6 --- /dev/null +++ b/addons/payment_alipay/models/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from . import payment diff --git a/addons/payment_alipay/models/payment.py b/addons/payment_alipay/models/payment.py new file mode 100644 index 00000000000..7f133b4d31f --- /dev/null +++ b/addons/payment_alipay/models/payment.py @@ -0,0 +1,228 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +import logging + +from hashlib import md5 +from werkzeug import urls + +from odoo import api, fields, models, _ +from odoo.tools.float_utils import float_compare +from odoo.addons.payment_alipay.controllers.main import AlipayController +from odoo.addons.payment.models.payment_acquirer import ValidationError + +_logger = logging.getLogger(__name__) + + +class PaymentAcquirer(models.Model): + _inherit = 'payment.acquirer' + + provider = fields.Selection(selection_add=[('alipay', 'Alipay')]) + alipay_payment_method = fields.Selection([ + ('express_checkout', 'Express Checkout (only for Chinese Merchant)'), + ('standard_checkout', 'Cross-border'), + ], string='Account', default='express_checkout', + help=" * Cross-border: For the Overseas seller \n * Express Checkout: For the Chinese Seller") + alipay_merchant_partner_id = fields.Char( + string='Merchant Partner ID', required_if_provider='alipay', groups='base.group_user', + help='The Merchant Partner ID is used to ensure communications coming from Alipay are valid and secured.') + alipay_md5_signature_key = fields.Char( + string='MD5 Signature Key', required_if_provider='alipay', groups='base.group_user', + help="The MD5 private key is the 32-byte string which is composed of English letters and numbers.") + alipay_seller_email = fields.Char(string='Alipay Seller Email', groups='base.group_user') + + def _get_feature_support(self): + res = super(PaymentAcquirer, self)._get_feature_support() + res['fees'].append('alipay') + return res + + @api.model + def _get_alipay_urls(self, environment): + """ Alipay URLS """ + if environment == 'prod': + return 'https://mapi.alipay.com/gateway.do' + return 'https://openapi.alipaydev.com/gateway.do' + + @api.multi + def alipay_compute_fees(self, amount, currency_id, country_id): + """ Compute alipay fees. + + :param float amount: the amount to pay + :param integer country_id: an ID of a res.country, or None. This is + the customer's country, to be compared to + the acquirer company country. + :return float fees: computed fees + """ + fees = 0.0 + if self.fees_active: + country = self.env['res.country'].browse(country_id) + if country and self.company_id.country_id.id == country.id: + percentage = self.fees_dom_var + fixed = self.fees_dom_fixed + else: + percentage = self.fees_int_var + fixed = self.fees_int_fixed + fees = (percentage / 100.0 * amount + fixed) / (1 - percentage / 100.0) + return fees + + @api.multi + def _build_sign(self, val): + # Rearrange parameters in the data set alphabetically + data_to_sign = sorted(val.items()) + # Exclude parameters that should not be signed + data_to_sign = ["{}={}".format(k, v) for k, v in data_to_sign if k not in ['sign', 'sign_type', 'reference']] + # And connect rearranged parameters with & + data_string = '&'.join(data_to_sign) + data_string += self.alipay_md5_signature_key + return md5(data_string.encode('utf-8')).hexdigest() + + @api.multi + def _get_alipay_tx_values(self, values): + base_url = self.env['ir.config_parameter'].sudo().get_param('web.base.url') + + tx = self.env['payment.transaction'].search([('reference', '=', values.get('reference'))], limit=1) + alipay_tx_values = ({ + '_input_charset': 'utf-8', + 'notify_url': urls.url_join(base_url, AlipayController._notify_url), + 'out_trade_no': values.get('reference'), + 'partner': self.alipay_merchant_partner_id, + 'return_url': urls.url_join(base_url, AlipayController._return_url) + '?' + urls.url_encode({'redirect_url': values.get('return_url')}), + 'subject': values.get('reference'), + 'total_fee': values.get('amount') + values.get('fees'), + }) + if self.alipay_payment_method == 'standard_checkout': + alipay_tx_values.update({ + 'service': 'create_forex_trade', + 'product_code': 'NEW_OVERSEAS_SELLER', + 'currency': values.get('currency').name, + }) + else: + alipay_tx_values.update({ + 'service': 'create_direct_pay_by_user', + 'payment_type': 1, + 'seller_email': self.alipay_seller_email, + }) + sign = self._build_sign(alipay_tx_values) + alipay_tx_values.update({ + 'sign_type': 'MD5', + 'sign': sign, + }) + return alipay_tx_values + + @api.multi + def alipay_form_generate_values(self, values): + values.update(self._get_alipay_tx_values(values)) + return values + + @api.multi + def alipay_get_form_action_url(self): + return self._get_alipay_urls(self.environment) + + +class PaymentTransaction(models.Model): + _inherit = 'payment.transaction' + + def _check_alipay_configuration(self, vals): + acquirer_id = int(vals.get('acquirer_id')) + acquirer = self.env['payment.acquirer'].sudo().browse(acquirer_id) + if acquirer and acquirer.provider == 'alipay' and acquirer.alipay_payment_method == 'express_checkout': + currency_id = int(vals.get('currency_id')) + if currency_id: + currency = self.env['res.currency'].sudo().browse(currency_id) + if currency and currency.name != 'CNY': + _logger.info("Only CNY currency is allowed for Alipay Express Checkout") + raise ValidationError(_(""" + Only transactions in Chinese Yuan (CNY) are allowed for Alipay Express Checkout.\n + If you wish to use another currency than CNY for your transactions, switch your + configuration to a Cross-border account on the Alipay payment acquirer in Odoo. + """)) + return True + + @api.model + def write(self, vals): + if vals.get('currency_id') or vals.get('acquirer_id'): + for payment in self: + check_vals = { + 'acquirer_id': vals.get('acquirer_id', payment.acquirer_id.id), + 'currency_id': vals.get('currency_id', payment.currency_id.id) + } + payment._check_alipay_configuration(check_vals) + return super(PaymentTransaction, self).write(vals) + + @api.model + def create(self, vals): + self._check_alipay_configuration(vals) + return super(PaymentTransaction, self).create(vals) + + # -------------------------------------------------- + # FORM RELATED METHODS + # -------------------------------------------------- + + @api.model + def _alipay_form_get_tx_from_data(self, data): + reference, txn_id, sign = data.get('reference'), data.get('trade_no'), data.get('sign') + if not reference or not txn_id: + _logger.info('Alipay: received data with missing reference (%s) or txn_id (%s)' % (reference, txn_id)) + raise ValidationError(_('Alipay: received data with missing reference (%s) or txn_id (%s)') % (reference, txn_id)) + + txs = self.env['payment.transaction'].search([('reference', '=', reference)]) + if not txs or len(txs) > 1: + error_msg = _('Alipay: received data for reference %s') % (reference) + logger_msg = 'Alipay: received data for reference %s' % (reference) + if not txs: + error_msg += _('; no order found') + logger_msg += '; no order found' + else: + error_msg += _('; multiple order found') + logger_msg += '; multiple order found' + _logger.info(logger_msg) + raise ValidationError(error_msg) + + # verify sign + sign_check = txs.acquirer_id._build_sign(data) + if sign != sign_check: + _logger.info('Alipay: invalid sign, received %s, computed %s, for data %s' % (sign, sign_check, data)) + raise ValidationError(_('Alipay: invalid sign, received %s, computed %s, for data %s') % (sign, sign_check, data)) + + return txs + + @api.multi + def _alipay_form_get_invalid_parameters(self, data): + invalid_parameters = [] + + if float_compare(float(data.get('total_fee', '0.0')), (self.amount + self.fees), 2) != 0: + invalid_parameters.append(('total_fee', data.get('total_fee'), '%.2f' % (self.amount + self.fees))) # mc_gross is amount + fees + if self.acquirer_id.alipay_payment_method == 'standard_checkout': + if data.get('currency') != self.currency_id.name: + invalid_parameters.append(('currency', data.get('currency'), self.currency_id.name)) + else: + if data.get('seller_email') != self.acquirer_id.alipay_seller_email: + invalid_parameters.append(('seller_email', data.get('seller_email'), self.acquirer_id.alipay_seller_email)) + return invalid_parameters + + @api.multi + def _alipay_form_validate(self, data): + if self.state in ['done']: + _logger.info('Alipay: trying to validate an already validated tx (ref %s)', self.reference) + return True + + status = data.get('trade_status') + res = { + 'acquirer_reference': data.get('trade_no'), + } + if status in ['TRADE_FINISHED', 'TRADE_SUCCESS']: + _logger.info('Validated Alipay payment for tx %s: set as done' % (self.reference)) + date_validate = fields.Datetime.now() + res.update(state='done', date=date_validate) + self.write(res) + self.execute_callback() + return True + elif status == 'TRADE_CLOSED': + _logger.info('Received notification for Alipay payment %s: set as Canceled' % (self.reference)) + res.update(state='cancel', state_message=data.get('close_reason', '')) + return self.write(res) + else: + error = 'Received unrecognized status for Alipay payment %s: %s, set as error' % (self.reference, status) + _logger.info(error) + res.update(state='error', state_message=error) + return self.write(res) diff --git a/addons/payment_alipay/static/description/icon.png b/addons/payment_alipay/static/description/icon.png new file mode 100644 index 00000000000..0aeddb17290 Binary files /dev/null and b/addons/payment_alipay/static/description/icon.png differ diff --git a/addons/payment_alipay/tests/__init__.py b/addons/payment_alipay/tests/__init__.py new file mode 100644 index 00000000000..bf53571ae82 --- /dev/null +++ b/addons/payment_alipay/tests/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from . import test_alipay diff --git a/addons/payment_alipay/tests/test_alipay.py b/addons/payment_alipay/tests/test_alipay.py new file mode 100644 index 00000000000..b735861b9c2 --- /dev/null +++ b/addons/payment_alipay/tests/test_alipay.py @@ -0,0 +1,195 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from werkzeug import urls +from lxml import objectify + +import odoo + +from odoo.tools import mute_logger +from odoo.addons.payment.models.payment_acquirer import ValidationError +from odoo.addons.payment.tests.common import PaymentAcquirerCommon +from odoo.addons.payment_alipay.controllers.main import AlipayController + +@odoo.tests.tagged('post_install', '-at_install', 'external', '-standard') +class AlipayTest(PaymentAcquirerCommon): + + def setUp(self): + super(AlipayTest, self).setUp() + + self.currency_yuan = self.env['res.currency'].search([('name', '=', 'CNY'), + '|', + ('active', '=', True), + ('active', '=', False)], limit=1) + self.alipay = self.env.ref('payment.payment_acquirer_alipay') + + def test_10_alipay_form_render(self): + base_url = self.env['ir.config_parameter'].get_param('web.base.url') + self.assertEqual(self.alipay.environment, 'test', 'test without test environment') + + # ---------------------------------------- + # Test: button direct rendering + # ---------------------------------------- + + # render the button + res = self.alipay.render( + 'test_ref0', 0.01, self.currency_euro.id, + values=self.buyer_values) + + form_values = { + '_input_charset': 'utf-8', + 'notify_url': urls.url_join(base_url, AlipayController._notify_url), + 'out_trade_no': 'SO12345-1', + 'partner': self.alipay.alipay_merchant_partner_id, + 'return_url': urls.url_join(base_url, AlipayController._return_url) + '?' + urls.url_encode({'redirect_url': '/payment/process'}), + 'subject': 'test_ref0', + 'total_fee': '0.01', + } + + if self.alipay.alipay_payment_method == 'standard_checkout': + form_values.update({ + 'service': 'create_forex_trade', + 'currency': 'EUR', + 'product_code': 'NEW_OVERSEAS_SELLER', + }) + else: + form_values.update({ + 'payment_type': '1', + 'seller_email': self.alipay.alipay_seller_email, + 'service': 'create_direct_pay_by_user' + }) + sign = self.alipay._build_sign(form_values) + + form_values.update({'sign': sign, 'sign_type': 'MD5'}) + # check form result + tree = objectify.fromstring(res) + + data_set = tree.xpath("//input[@name='data_set']") + self.assertEqual(len(data_set), 1, 'Alipay: Found %d "data_set" input instead of 1' % len(data_set)) + self.assertEqual(data_set[0].get('data-action-url'), 'https://openapi.alipaydev.com/gateway.do', 'alipay: wrong form POST url') + for form_input in tree.input: + if form_input.get('name') in ['submit', 'data_set', 'sign', 'out_trade_no']: + continue + self.assertEqual(form_input.get('value'), form_values[form_input.get('name')], 'alipay: wrong value for input %s: received %s instead of %s' % (form_input.get('name'), form_input.get('value'), form_values[form_input.get('name')])) + + def test_11_alipay_form_with_fees(self): + self.assertEqual(self.alipay.environment, 'test', 'test without test environment') + + # update acquirer: compute fees + self.alipay.write({ + 'fees_active': True, + 'fees_dom_fixed': 1.0, + 'fees_dom_var': 0.35, + 'fees_int_fixed': 1.5, + 'fees_int_var': 0.50, + }) + + # render the button + res = self.alipay.render( + 'test_ref0', 12.50, self.currency_euro.id, + values=self.buyer_values) + + tree = objectify.fromstring(res) + + data_set = tree.xpath("//input[@name='data_set']") + self.assertEqual(len(data_set), 1, 'alipay: Found %d "data_set" input instead of 1' % len(data_set)) + self.assertEqual(data_set[0].get('data-action-url'), 'https://openapi.alipaydev.com/gateway.do', 'alipay: wrong form POST url') + for form_input in tree.input: + if form_input.get('name') in ['total_fee']: + self.assertEqual(form_input.get('value'), '14.07', 'alipay: wrong computed fees') # total amount = amount + fees + + @mute_logger('odoo.addons.payment_alipay.models.payment', 'ValidationError') + def test_20_alipay_form_management(self): + self.alipay.alipay_payment_method = 'standard_checkout' + self._test_20_alipay_form_management() + self.alipay.alipay_payment_method = 'express_checkout' + self._test_20_alipay_form_management() + + def _test_20_alipay_form_management(self): + self.assertEqual(self.alipay.environment, 'test', 'test without test environment') + + # typical data posted by alipay after client has successfully paid + alipay_post_data = { + 'trade_no': '2017112321001003690200384552', + 'reference': 'test_ref_' + self.alipay.alipay_payment_method, + 'total_fee': 1.95, + 'trade_status': 'TRADE_CLOSED', + } + + if self.alipay.alipay_payment_method == 'express_checkout': + alipay_post_data.update({ + 'seller_email': self.alipay.alipay_seller_email, + }) + else: + alipay_post_data.update({ + 'currency': 'EUR', + }) + + alipay_post_data['sign'] = self.alipay._build_sign(alipay_post_data) + # should raise error about unknown tx + with self.assertRaises(ValidationError): + self.env['payment.transaction'].form_feedback(alipay_post_data, 'alipay') + + if self.alipay.alipay_payment_method == 'express_checkout': + currency = self.currency_yuan + else: + currency = self.currency_euro + + # create tx + tx = self.env['payment.transaction'].create({ + 'amount': 1.95, + 'acquirer_id': self.alipay.id, + 'currency_id': currency.id, + 'reference': 'test_ref_' + self.alipay.alipay_payment_method, + 'partner_name': 'Norbert Buyer', + 'partner_country_id': self.country_france.id + }) + + # validate tx + tx.form_feedback(alipay_post_data, 'alipay') + # check tx + self.assertEqual(tx.state, 'cancel', 'alipay: wrong state after receiving a valid pending notification') + self.assertEqual(tx.acquirer_reference, '2017112321001003690200384552', 'alipay: wrong txn_id after receiving a valid pending notification') + + # update tx + tx.write({'acquirer_reference': False}) + + # update notification from alipay should not go through since it has already been set as 'done' + if self.alipay.alipay_payment_method == 'standard_checkout': + alipay_post_data['trade_status'] = 'TRADE_FINISHED' + else: + alipay_post_data['trade_status'] = 'TRADE_SUCCESS' + alipay_post_data['sign'] = self.alipay._build_sign(alipay_post_data) + # validate tx + tx.form_feedback(alipay_post_data, 'alipay') + # check tx + self.assertEqual(tx.acquirer_reference, '2017112321001003690200384552', 'alipay: notification should not go throught since it has already been validated') + + # this time it should go through since the transaction is not validated yet + tx.write({'state': 'draft', 'acquirer_reference': False}) + tx.form_feedback(alipay_post_data, 'alipay') + self.assertEqual(tx.state, 'done', 'alipay: wrong state after receiving a valid pending notification') + self.assertEqual(tx.acquirer_reference, '2017112321001003690200384552', 'alipay: wrong txn_id after receiving a valid pending notification') + + @mute_logger('odoo.addons.payment_alipay.models.payment', 'ValidationError') + def test_30_alipay_bad_configuration(self): + self.alipay.alipay_payment_method = 'express_checkout' + + # should raise error since `express_checkout` must only be used with CNY currency + with self.assertRaises(ValidationError): + # create tx + tx = self.env['payment.transaction'].create({ + 'acquirer_id': self.alipay.id, + 'amount': 4, + 'currency_id': self.currency_euro.id, + 'reference': 'test_ref_2', + 'partner_country_id': self.country_france.id + }) + + tx = self.env['payment.transaction'].create({ + 'acquirer_id': self.alipay.id, + 'amount': 4, + 'currency_id': self.currency_yuan.id, + 'reference': 'test_ref_2', + 'partner_country_id': self.country_france.id + }) \ No newline at end of file diff --git a/addons/payment_alipay/views/alipay_views.xml b/addons/payment_alipay/views/alipay_views.xml new file mode 100644 index 00000000000..3289f4c775c --- /dev/null +++ b/addons/payment_alipay/views/alipay_views.xml @@ -0,0 +1,18 @@ + + + + payment.acquirer.view.form.inherit.payment.alipay + payment.acquirer + + + + + + + + + + + + + diff --git a/addons/payment_alipay/views/payment_alipay_templates.xml b/addons/payment_alipay/views/payment_alipay_templates.xml new file mode 100644 index 00000000000..0f672861074 --- /dev/null +++ b/addons/payment_alipay/views/payment_alipay_templates.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + +
+ Alipay is the most popular online payment platform in China. Chinese consumers can buy online using their Alipay eWallet. +
+ You will be redirected to the Alipay website after clicking on the payment button. +