summaryrefslogtreecommitdiff
path: root/addons/payment_alipay/models
diff options
context:
space:
mode:
authorstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
committerstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
commit3751379f1e9a4c215fb6eb898b4ccc67659b9ace (patch)
treea44932296ef4a9b71d5f010906253d8c53727726 /addons/payment_alipay/models
parent0a15094050bfde69a06d6eff798e9a8ddf2b8c21 (diff)
initial commit 2
Diffstat (limited to 'addons/payment_alipay/models')
-rw-r--r--addons/payment_alipay/models/__init__.py4
-rw-r--r--addons/payment_alipay/models/payment.py226
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)