diff options
| author | stephanchrst <stephanchrst@gmail.com> | 2022-05-10 21:51:50 +0700 |
|---|---|---|
| committer | stephanchrst <stephanchrst@gmail.com> | 2022-05-10 21:51:50 +0700 |
| commit | 3751379f1e9a4c215fb6eb898b4ccc67659b9ace (patch) | |
| tree | a44932296ef4a9b71d5f010906253d8c53727726 /addons/payment_alipay/models | |
| parent | 0a15094050bfde69a06d6eff798e9a8ddf2b8c21 (diff) | |
initial commit 2
Diffstat (limited to 'addons/payment_alipay/models')
| -rw-r--r-- | addons/payment_alipay/models/__init__.py | 4 | ||||
| -rw-r--r-- | addons/payment_alipay/models/payment.py | 226 |
2 files changed, 230 insertions, 0 deletions
diff --git a/addons/payment_alipay/models/__init__.py b/addons/payment_alipay/models/__init__.py new file mode 100644 index 00000000..2ec5b9cd --- /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 00000000..326f0ddc --- /dev/null +++ b/addons/payment_alipay/models/payment.py @@ -0,0 +1,226 @@ +# -*- 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') + ], ondelete={'alipay': 'set default'}) + 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' + + 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.sudo().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 + + 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() + + def _get_alipay_tx_values(self, values): + base_url = self.env['ir.config_parameter'].sudo().get_param('web.base.url') + + 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), + '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 + + def alipay_form_generate_values(self, values): + values.update(self._get_alipay_tx_values(values)) + return values + + def alipay_get_form_action_url(self): + self.ensure_one() + environment = 'prod' if self.state == 'enabled' else 'test' + return self._get_alipay_urls(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 + + 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 + + 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 + + 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(date=date_validate) + self._set_transaction_done() + 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_message=data.get('close_reason', '')) + self._set_transaction_cancel() + 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_message=error) + self._set_transaction_error() + return self.write(res) |
