From 8a6e859c2bc964693a1fb809f3df7ac027e04b78 Mon Sep 17 00:00:00 2001
From: Ajay javiya
Date: Thu, 24 Jul 2014 12:58:18 +0530
Subject: [PATCH] [ADD] New payment acquirer Authorize.net.
---
addons/payment/models/res_config.py | 3 +
addons/payment/views/res_config_view.xml | 4 +
addons/payment_authorize/__init__.py | 23 +++
addons/payment_authorize/__openerp__.py | 18 ++
.../payment_authorize/controllers/__init__.py | 3 +
addons/payment_authorize/controllers/main.py | 32 ++++
addons/payment_authorize/data/authorize.xml | 18 ++
addons/payment_authorize/models/__init__.py | 3 +
addons/payment_authorize/models/authorize.py | 165 +++++++++++++++++
.../static/description/icon.png | Bin 0 -> 6281 bytes
.../static/src/img/authorize_icon.png | Bin 0 -> 2966 bytes
addons/payment_authorize/tests/__init__.py | 3 +
.../payment_authorize/tests/test_authorize.py | 173 ++++++++++++++++++
addons/payment_authorize/views/authorize.xml | 42 +++++
.../views/payment_acquirer.xml | 33 ++++
.../views/payment_authorize_template.xml | 10 +
16 files changed, 530 insertions(+)
create mode 100644 addons/payment_authorize/__init__.py
create mode 100644 addons/payment_authorize/__openerp__.py
create mode 100644 addons/payment_authorize/controllers/__init__.py
create mode 100644 addons/payment_authorize/controllers/main.py
create mode 100644 addons/payment_authorize/data/authorize.xml
create mode 100644 addons/payment_authorize/models/__init__.py
create mode 100644 addons/payment_authorize/models/authorize.py
create mode 100644 addons/payment_authorize/static/description/icon.png
create mode 100644 addons/payment_authorize/static/src/img/authorize_icon.png
create mode 100644 addons/payment_authorize/tests/__init__.py
create mode 100644 addons/payment_authorize/tests/test_authorize.py
create mode 100644 addons/payment_authorize/views/authorize.xml
create mode 100644 addons/payment_authorize/views/payment_acquirer.xml
create mode 100644 addons/payment_authorize/views/payment_authorize_template.xml
diff --git a/addons/payment/models/res_config.py b/addons/payment/models/res_config.py
index a74e595b52e..b64b698c83d 100644
--- a/addons/payment/models/res_config.py
+++ b/addons/payment/models/res_config.py
@@ -19,4 +19,7 @@ class AccountPaymentConfig(osv.TransientModel):
'module_payment_buckaroo': fields.boolean(
'Manage Payments Using Buckaroo',
help='-It installs the module payment_buckaroo.'),
+ 'module_payment_authorize': fields.boolean(
+ 'Manage Payments Using Authorize.Net',
+ help='-It installs the module payment_authorize.'),
}
diff --git a/addons/payment/views/res_config_view.xml b/addons/payment/views/res_config_view.xml
index e4e6fc96652..8f5e5f31321 100644
--- a/addons/payment/views/res_config_view.xml
+++ b/addons/payment/views/res_config_view.xml
@@ -24,6 +24,10 @@
+
+
+
+
diff --git a/addons/payment_authorize/__init__.py b/addons/payment_authorize/__init__.py
new file mode 100644
index 00000000000..9332521b0ee
--- /dev/null
+++ b/addons/payment_authorize/__init__.py
@@ -0,0 +1,23 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# Odoo, Open Source Management Solution
+# Copyright (C) 2014-TODAY Odoo SA ().
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+#
+##############################################################################
+
+import models
+import controllers
diff --git a/addons/payment_authorize/__openerp__.py b/addons/payment_authorize/__openerp__.py
new file mode 100644
index 00000000000..57bf2c7e711
--- /dev/null
+++ b/addons/payment_authorize/__openerp__.py
@@ -0,0 +1,18 @@
+# -*- coding: utf-8 -*-
+
+{
+ 'name': 'Authorize.Net Payment Acquirer',
+ 'category': 'Hidden',
+ 'summary': 'Payment Acquirer: Authorize.net Implementation',
+ 'version': '1.0',
+ 'description': """Authorize.Net Payment Acquirer""",
+ 'author': 'Odoo SA',
+ 'depends': ['payment'],
+ 'data': [
+ 'views/authorize.xml',
+ 'views/payment_acquirer.xml',
+ 'data/authorize.xml',
+ 'views/payment_authorize_template.xml',
+ ],
+ 'installable': True,
+}
diff --git a/addons/payment_authorize/controllers/__init__.py b/addons/payment_authorize/controllers/__init__.py
new file mode 100644
index 00000000000..bbd183e955b
--- /dev/null
+++ b/addons/payment_authorize/controllers/__init__.py
@@ -0,0 +1,3 @@
+# -*- coding: utf-8 -*-
+
+import main
diff --git a/addons/payment_authorize/controllers/main.py b/addons/payment_authorize/controllers/main.py
new file mode 100644
index 00000000000..1279c23ce26
--- /dev/null
+++ b/addons/payment_authorize/controllers/main.py
@@ -0,0 +1,32 @@
+# -*- coding: utf-8 -*-
+import pprint
+import logging
+import urlparse
+
+from openerp import http
+from openerp.http import request
+
+_logger = logging.getLogger(__name__)
+
+
+class AuthorizeController(http.Controller):
+ _return_url = '/payment/authorize/return/'
+ _cancel_url = '/payment/authorize/cancel/'
+
+ @http.route([
+ '/payment/authorize/return/',
+ '/payment/authorize/cancel/',
+ ], type='http', auth='public')
+ def authorize_form_feedback(self, **post):
+ _logger.info('Authorize: entering form_feedback with post data %s', pprint.pformat(post))
+ return_url = '/'
+ if post:
+ request.env['payment.transaction'].sudo().form_feedback(post, 'authorize')
+ return_url = post.pop('return_url', '/')
+ base_url = request.env['ir.config_parameter'].get_param('web.base.url')
+ # Authorize.Net is expecting a response to the POST sent by their server.
+ # This response is in the form of a URL that Authorize.Net will pass on to the
+ # client's browser to redirect them to the desired location need javascript.
+ return request.render('payment_authorize.payment_authorize_redirect', {
+ 'return_url': '%s' % urlparse.urljoin(base_url, return_url)
+ })
diff --git a/addons/payment_authorize/data/authorize.xml b/addons/payment_authorize/data/authorize.xml
new file mode 100644
index 00000000000..d1d38106252
--- /dev/null
+++ b/addons/payment_authorize/data/authorize.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+ Authorize.Net
+ authorize
+
+
+ test
+ You will be redirected to the Authorize website after clicking on the payment button.
]]>
+ dummy
+ dummy
+
+
+
+
diff --git a/addons/payment_authorize/models/__init__.py b/addons/payment_authorize/models/__init__.py
new file mode 100644
index 00000000000..92e55eeff24
--- /dev/null
+++ b/addons/payment_authorize/models/__init__.py
@@ -0,0 +1,3 @@
+# -*- coding: utf-8 -*-
+
+import authorize
diff --git a/addons/payment_authorize/models/authorize.py b/addons/payment_authorize/models/authorize.py
new file mode 100644
index 00000000000..2708daa37a6
--- /dev/null
+++ b/addons/payment_authorize/models/authorize.py
@@ -0,0 +1,165 @@
+# -*- coding: utf-'8' "-*-"
+
+import hashlib
+import hmac
+import logging
+import time
+import urlparse
+
+from openerp import api, fields, models
+from openerp.addons.payment.models.payment_acquirer import ValidationError
+from openerp.addons.payment_authorize.controllers.main import AuthorizeController
+from openerp.tools.float_utils import float_compare
+
+_logger = logging.getLogger(__name__)
+
+
+class PaymentAcquirerAuthorize(models.Model):
+ _inherit = 'payment.acquirer'
+
+ def _get_authorize_urls(self, environment):
+ """ Authorize URLs """
+ if environment == 'prod':
+ return {'authorize_form_url': 'https://secure.authorize.net/gateway/transact.dll'}
+ else:
+ return {'authorize_form_url': 'https://test.authorize.net/gateway/transact.dll'}
+
+ @api.model
+ def _get_providers(self):
+ providers = super(PaymentAcquirerAuthorize, self)._get_providers()
+ providers.append(['authorize', 'Authorize.Net'])
+ return providers
+
+ authorize_login = fields.Char(string='API Login Id', required_if_provider='authorize')
+ authorize_transaction_key = fields.Char(string='API Transaction Key', required_if_provider='authorize')
+
+ def _authorize_generate_hashing(self, values):
+ data = '^'.join([
+ values['x_login'],
+ values['x_fp_sequence'],
+ values['x_fp_timestamp'],
+ values['x_amount'],
+ values['x_currency_code']])
+ return hmac.new(str(values['x_trans_key']), data, hashlib.md5).hexdigest()
+
+ @api.multi
+ def authorize_form_generate_values(self, partner_values, tx_values):
+ self.ensure_one()
+ base_url = self.env['ir.config_parameter'].get_param('web.base.url')
+ authorize_tx_values = dict(tx_values)
+ temp_authorize_tx_values = {
+ 'x_login': self.authorize_login,
+ 'x_trans_key': self.authorize_transaction_key,
+ 'x_amount': str(tx_values['amount']),
+ 'x_show_form': 'PAYMENT_FORM',
+ 'x_type': 'AUTH_CAPTURE',
+ 'x_method': 'CC',
+ 'x_fp_sequence': '%s%s' % (self.id, int(time.time())),
+ 'x_version': '3.1',
+ 'x_relay_response': 'TRUE',
+ 'x_fp_timestamp': str(int(time.time())),
+ 'x_relay_url': '%s' % urlparse.urljoin(base_url, AuthorizeController._return_url),
+ 'x_cancel_url': '%s' % urlparse.urljoin(base_url, AuthorizeController._cancel_url),
+ 'x_currency_code': tx_values['currency'] and tx_values['currency'].name or '',
+ 'address': partner_values['address'],
+ 'city': partner_values['city'],
+ 'country': partner_values['country'] and partner_values['country'].name or '',
+ 'email': partner_values['email'],
+ 'zip': partner_values['zip'],
+ 'first_name': partner_values['first_name'],
+ 'last_name': partner_values['last_name'],
+ 'phone': partner_values['phone'],
+ 'state': partner_values.get('state') and partner_values['state'].name or '',
+ }
+ temp_authorize_tx_values['returndata'] = authorize_tx_values.pop('return_url', '')
+ temp_authorize_tx_values['x_fp_hash'] = self._authorize_generate_hashing(temp_authorize_tx_values)
+ authorize_tx_values.update(temp_authorize_tx_values)
+ return partner_values, authorize_tx_values
+
+ @api.multi
+ def authorize_get_form_action_url(self):
+ self.ensure_one()
+ return self._get_authorize_urls(self.environment)['authorize_form_url']
+
+
+class TxAuthorize(models.Model):
+ _inherit = 'payment.transaction'
+
+ authorize_txnid = fields.Char(string='Transaction ID')
+
+ _authorize_valid_tx_status = 1
+ _authorize_pending_tx_status = 4
+ _authorize_cancel_tx_status = 2
+
+ # --------------------------------------------------
+ # FORM RELATED METHODS
+ # --------------------------------------------------
+
+ @api.model
+ def _authorize_form_get_tx_from_data(self, data):
+ """ Given a data dict coming from authorize, verify it and find the related
+ transaction record. """
+ reference, trans_id, fingerprint = data.get('x_invoice_num'), data.get('x_trans_id'), data.get('x_MD5_Hash')
+ if not reference or not trans_id or not fingerprint:
+ error_msg = 'Authorize: received data with missing reference (%s) or trans_id (%s) or fingerprint (%s)' % (reference, trans_id, fingerprint)
+ _logger.error(error_msg)
+ raise ValidationError(error_msg)
+ tx = self.search([('reference', '=', reference)])
+ if not tx or len(tx) > 1:
+ error_msg = 'Authorize: received data for reference %s' % (reference)
+ if not tx:
+ error_msg += '; no order found'
+ else:
+ error_msg += '; multiple order found'
+ _logger.error(error_msg)
+ raise ValidationError(error_msg)
+ return tx[0]
+
+ @api.model
+ def _authorize_form_get_invalid_parameters(self, tx, data):
+ invalid_parameters = []
+
+ if tx.authorize_txnid and data.get('x_trans_id') != tx.authorize_txnid:
+ invalid_parameters.append(('Transaction Id', data.get('x_trans_id'), tx.authorize_txnid))
+ # check what is buyed
+ if float_compare(float(data.get('x_amount', '0.0')), tx.amount, 2) != 0:
+ invalid_parameters.append(('Amount', data.get('x_amount'), '%.2f' % tx.amount))
+ return invalid_parameters
+
+ @api.model
+ def _authorize_form_validate(self, tx, data):
+ if tx.state == 'done':
+ _logger.warning('Authorize: trying to validate an already validated tx (ref %s)' % tx.reference)
+ return True
+ status_code = int(data.get('x_response_code', '0'))
+ if status_code == self._authorize_valid_tx_status:
+ tx.write({
+ 'state': 'done',
+ 'authorize_txnid': data.get('x_trans_id'),
+ 'acquirer_reference': data['x_invoice_num'],
+ })
+ return True
+ elif status_code == self._authorize_pending_tx_status:
+ tx.write({
+ 'state': 'pending',
+ 'authorize_txnid': data.get('x_trans_id'),
+ 'acquirer_reference': data['x_invoice_num'],
+ })
+ return True
+ elif status_code == self._authorize_cancel_tx_status:
+ tx.write({
+ 'state': 'cancel',
+ 'authorize_txnid': data.get('x_trans_id'),
+ 'acquirer_reference': data['x_invoice_num'],
+ })
+ return True
+ else:
+ error = data.get('x_response_reason_text')
+ _logger.info(error)
+ tx.write({
+ 'state': 'error',
+ 'state_message': error,
+ 'authorize_txnid': data.get('x_trans_id'),
+ 'acquirer_reference': data['x_invoice_num'],
+ })
+ return False
diff --git a/addons/payment_authorize/static/description/icon.png b/addons/payment_authorize/static/description/icon.png
new file mode 100644
index 0000000000000000000000000000000000000000..c2bab4f7a07daccbbd2ec348788d6e9ce437db9e
GIT binary patch
literal 6281
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
literal 0
HcmV?d00001
diff --git a/addons/payment_authorize/tests/__init__.py b/addons/payment_authorize/tests/__init__.py
new file mode 100644
index 00000000000..7c3bcfce559
--- /dev/null
+++ b/addons/payment_authorize/tests/__init__.py
@@ -0,0 +1,3 @@
+# -*- coding: utf-8 -*-
+
+from openerp.addons.payment_authorize.tests import test_authorize
diff --git a/addons/payment_authorize/tests/test_authorize.py b/addons/payment_authorize/tests/test_authorize.py
new file mode 100644
index 00000000000..f49aefaee40
--- /dev/null
+++ b/addons/payment_authorize/tests/test_authorize.py
@@ -0,0 +1,173 @@
+# -*- coding: utf-8 -*-
+
+import hashlib
+import hmac
+import time
+import urlparse
+from lxml import objectify
+
+import openerp
+from openerp.addons.payment.models.payment_acquirer import ValidationError
+from openerp.addons.payment.tests.common import PaymentAcquirerCommon
+from openerp.addons.payment_authorize.controllers.main import AuthorizeController
+from openerp.tools import mute_logger
+
+
+@openerp.tests.common.at_install(True)
+@openerp.tests.common.post_install(True)
+class AuthorizeCommon(PaymentAcquirerCommon):
+
+ def setUp(self):
+ super(AuthorizeCommon, self).setUp()
+ self.base_url = self.env['ir.config_parameter'].get_param('web.base.url')
+ # authorize only support USD in test environment
+ self.currency_usd = self.env['res.currency'].search([('name', '=', 'USD')], limit=1)[0]
+ # get the authorize account
+ model, self.authorize_id = self.env['ir.model.data'].get_object_reference('payment_authorize', 'payment_acquirer_authorize')
+
+
+@openerp.tests.common.at_install(True)
+@openerp.tests.common.post_install(True)
+class AuthorizeForm(AuthorizeCommon):
+
+ def _authorize_generate_hashing(self, values):
+ data = '^'.join([
+ values['x_login'],
+ values['x_fp_sequence'],
+ values['x_fp_timestamp'],
+ values['x_amount'],
+ ]) + '^'
+ return hmac.new(str(values['x_trans_key']), data, hashlib.md5).hexdigest()
+
+ def test_10_Authorize_form_render(self):
+ authorize = self.env['payment.acquirer'].browse(self.authorize_id)
+ self.assertEqual(authorize.environment, 'test', 'test without test environment')
+
+ # ----------------------------------------
+ # Test: button direct rendering
+ # ----------------------------------------
+ form_values = {
+ 'x_login': authorize.authorize_login,
+ 'x_trans_key': authorize.authorize_transaction_key,
+ 'x_amount': '320.0',
+ 'x_show_form': 'PAYMENT_FORM',
+ 'x_type': 'AUTH_CAPTURE',
+ 'x_method': 'CC',
+ 'x_fp_sequence': '%s%s' % (authorize.id, int(time.time())),
+ 'x_version': '3.1',
+ 'x_relay_response': 'TRUE',
+ 'x_fp_timestamp': str(int(time.time())),
+ 'x_relay_url': '%s' % urlparse.urljoin(self.base_url, AuthorizeController._return_url),
+ 'x_cancel_url': '%s' % urlparse.urljoin(self.base_url, AuthorizeController._cancel_url),
+ 'return_url': None,
+ 'x_currency_code': 'USD',
+ 'x_invoice_num': 'SO004',
+ 'x_first_name': 'Buyer',
+ 'x_last_name': 'Norbert',
+ 'x_address': 'Huge Street 2/543',
+ 'x_city': 'Sin City',
+ 'x_zip': '1000',
+ 'x_country': 'Belgium',
+ 'x_phone': '0032 12 34 56 78',
+ 'x_email': 'norbert.buyer@example.com',
+ 'x_state': None,
+ }
+
+ form_values['x_fp_hash'] = self._authorize_generate_hashing(form_values)
+ # render the button
+ cr, uid, context = self.env.cr, self.env.uid, {}
+ res = self.payment_acquirer.render(
+ cr, uid, self.authorize_id, 'SO004', 320.0, self.currency_usd.id,
+ partner_id=None, partner_values=self.buyer_values, context=context)
+ # check form result
+ tree = objectify.fromstring(res)
+ self.assertEqual(tree.get('action'), 'https://test.authorize.net/gateway/transact.dll', 'Authorize: wrong form POST url')
+ for form_input in tree.input:
+ # Generated and received 'x_fp_hash' are always different so skeep it.
+ if form_input.get('name') in ['submit', 'x_fp_hash']:
+ continue
+ self.assertEqual(
+ form_input.get('value'),
+ form_values[form_input.get('name')],
+ 'Authorize: wrong value for input %s: received %s instead of %s' % (form_input.get('name'), form_input.get('value'), form_values[form_input.get('name')])
+ )
+
+ @mute_logger('openerp.addons.payment_authorize.models.authorize', 'ValidationError')
+ def test_20_authorize_form_management(self):
+ cr, uid, context = self.env.cr, self.env.uid, {}
+ # be sure not to do stupid thing
+ authorize = self.env['payment.acquirer'].browse(self.authorize_id)
+ self.assertEqual(authorize.environment, 'test', 'test without test environment')
+
+ # typical data posted by authorize after client has successfully paid
+ authorize_post_data = {
+ 'return_url': u'/shop/payment/validate',
+ 'x_MD5_Hash': u'7934485E1C105940BE854208D10FAB4F',
+ 'x_account_number': u'XXXX0027',
+ 'x_address': u'Huge Street 2/543',
+ 'x_amount': u'320.00',
+ 'x_auth_code': u'E4W7IU',
+ 'x_avs_code': u'Y',
+ 'x_card_type': u'Visa',
+ 'x_cavv_response': u'2',
+ 'x_city': u'Sun City',
+ 'x_company': u'',
+ 'x_country': u'Belgium',
+ 'x_cust_id': u'',
+ 'x_cvv2_resp_code': u'',
+ 'x_description': u'',
+ 'x_duty': u'0.00',
+ 'x_email': u'norbert.buyer@exampl',
+ 'x_fax': u'',
+ 'x_first_name': u'Norbert',
+ 'x_freight': u'0.00',
+ 'x_invoice_num': u'SO004',
+ 'x_last_name': u'Buyer',
+ 'x_method': u'CC',
+ 'x_phone': u'0032 12 34 56 78',
+ 'x_po_num': u'',
+ 'x_response_code': u'1',
+ 'x_response_reason_code': u'1',
+ 'x_response_reason_text': u'This transaction has been approved.',
+ 'x_ship_to_address': u'Huge Street 2/543',
+ 'x_ship_to_city': u'Sun City',
+ 'x_ship_to_company': u'',
+ 'x_ship_to_country': u'Belgium',
+ 'x_ship_to_first_name': u'Norbert',
+ 'x_ship_to_last_name': u'Buyer',
+ 'x_ship_to_state': u'',
+ 'x_ship_to_zip': u'1000',
+ 'x_state': u'',
+ 'x_tax': u'0.00',
+ 'x_tax_exempt': u'FALSE',
+ 'x_test_request': u'false',
+ 'x_trans_id': u'2217460311',
+ 'x_type': u'auth_capture',
+ 'x_zip': u'1000'
+ }
+
+ # should raise error about unknown tx
+ with self.assertRaises(ValidationError):
+ self.payment_transaction.form_feedback(cr, uid, authorize_post_data, 'authorize', context=context)
+
+ tx = self.env['payment.transaction'].create({
+ 'amount': 320.0,
+ 'acquirer_id': self.authorize_id,
+ 'currency_id': self.currency_usd.id,
+ 'reference': 'SO004',
+ 'partner_name': 'Norbert Buyer',
+ 'partner_country_id': self.country_france_id})
+ # validate it
+ self.payment_transaction.form_feedback(cr, uid, authorize_post_data, 'authorize', context=context)
+ # check state
+ self.assertEqual(tx.state, 'done', 'Authorize: validation did not put tx into done state')
+ self.assertEqual(tx.authorize_txnid, authorize_post_data.get('x_trans_id'), 'Authorize: validation did not update tx payid')
+
+ # reset tx
+ tx.write({'state': 'draft', 'date_validate': False, 'authorize_txnid': False})
+
+ # simulate an error
+ authorize_post_data['x_response_code'] = u'3'
+ self.payment_transaction.form_feedback(cr, uid, authorize_post_data, 'authorize', context=context)
+ # check state
+ self.assertEqual(tx.state, 'error', 'Authorize: erroneous validation did not put tx into error state')
diff --git a/addons/payment_authorize/views/authorize.xml b/addons/payment_authorize/views/authorize.xml
new file mode 100644
index 00000000000..7285ccb860a
--- /dev/null
+++ b/addons/payment_authorize/views/authorize.xml
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/addons/payment_authorize/views/payment_acquirer.xml b/addons/payment_authorize/views/payment_acquirer.xml
new file mode 100644
index 00000000000..a370f9f7a2e
--- /dev/null
+++ b/addons/payment_authorize/views/payment_acquirer.xml
@@ -0,0 +1,33 @@
+
+
+
+
+ acquirer.form.authorize
+ payment.acquirer
+
+
+
+
+
+
+
+
+
+
+
+
+ acquirer.transaction.form.authorize
+ payment.transaction
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/addons/payment_authorize/views/payment_authorize_template.xml b/addons/payment_authorize/views/payment_authorize_template.xml
new file mode 100644
index 00000000000..58b1ee85e67
--- /dev/null
+++ b/addons/payment_authorize/views/payment_authorize_template.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+