Authorize.Net is phasing out the MD5 based hash use for transaction response verification in favor of the SHA-512 based hash utilizing a Signature Key. Instead of hashing with md5 the transaction key, it is now required to hash the signature key (binary format) with SHA-512. Support for MD5 will be dropped the 7th March 2019 for sandbox environment and the 28th March 2019 for production environment, initially planned for the 14th. Note that as of February 11, 2019 authorize removed the ability to configure or update MD5 Hash setting in the Merchant Interface. Merchants who had this setting configured have been emailed/contacted. opw-1943030 Usefull links: https://developer.authorize.net/support/hash_upgrade/ https://support.authorize.net/s/article/What-is-a-Signature-Key https://support.authorize.net/s/article/MD5-Hash-End-of-Life-Signature-Key-Replacement https://support.authorize.net/s/article/Do-I-need-to-upgrade-my-transaction-fingerprint-from-HMAC-MD5-to-HMAC-SHA512-and-how closes odoo/odoo#31642 Signed-off-by: Romain Derie (rde) <rde@odoo.com>
262 lines
11 KiB
Python
262 lines
11 KiB
Python
# -*- coding: utf-8 -*-
|
|
|
|
import time
|
|
import urlparse
|
|
import unittest
|
|
from lxml import objectify
|
|
|
|
import odoo
|
|
from odoo.addons.payment.models.payment_acquirer import ValidationError
|
|
from odoo.addons.payment.tests.common import PaymentAcquirerCommon
|
|
from odoo.addons.payment_authorize.controllers.main import AuthorizeController
|
|
from odoo.tools import mute_logger
|
|
|
|
|
|
@odoo.tests.common.at_install(True)
|
|
@odoo.tests.common.post_install(True)
|
|
class AuthorizeCommon(PaymentAcquirerCommon):
|
|
|
|
def setUp(self):
|
|
super(AuthorizeCommon, self).setUp()
|
|
# authorize only support USD in test environment
|
|
self.currency_usd = self.env['res.currency'].search([('name', '=', 'USD')], limit=1)[0]
|
|
# get the authorize account
|
|
self.authorize = self.env.ref('payment.payment_acquirer_authorize')
|
|
# Be sure to be in 'capture' mode
|
|
self.authorize.auto_confirm = 'confirm_so'
|
|
|
|
|
|
@odoo.tests.common.at_install(True)
|
|
@odoo.tests.common.post_install(True)
|
|
class AuthorizeForm(AuthorizeCommon):
|
|
|
|
def test_10_Authorize_form_render(self):
|
|
self.assertEqual(self.authorize.environment, 'test', 'test without test environment')
|
|
|
|
# ----------------------------------------
|
|
# Test: button direct rendering
|
|
# ----------------------------------------
|
|
base_url = self.env['ir.config_parameter'].get_param('web.base.url')
|
|
form_values = {
|
|
'x_login': self.authorize.authorize_login,
|
|
'x_trans_key': self.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' % (self.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(base_url, AuthorizeController._return_url),
|
|
'x_cancel_url': '%s' % urlparse.urljoin(base_url, AuthorizeController._cancel_url),
|
|
'return_url': None,
|
|
'x_currency_code': 'USD',
|
|
'x_invoice_num': 'SO004',
|
|
'x_first_name': 'Norbert',
|
|
'x_last_name': 'Buyer',
|
|
'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,
|
|
'x_ship_to_first_name': 'Norbert',
|
|
'x_ship_to_last_name': 'Buyer',
|
|
'x_ship_to_address': 'Huge Street 2/543',
|
|
'x_ship_to_city': 'Sin City',
|
|
'x_ship_to_zip': '1000',
|
|
'x_ship_to_country': 'Belgium',
|
|
'x_ship_to_phone': '0032 12 34 56 78',
|
|
'x_ship_to_email': 'norbert.buyer@example.com',
|
|
'x_ship_to_state': None,
|
|
}
|
|
|
|
form_values['x_fp_hash'] = self.env['payment.acquirer']._authorize_generate_hashing(form_values)
|
|
# render the button
|
|
res = self.authorize.render('SO004', 320.0, self.currency_usd.id, values=self.buyer_values)
|
|
# 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 el in tree.iterfind('input'):
|
|
values = el.values()
|
|
if values[1] in ['submit', 'x_fp_hash', 'return_url', 'x_state', 'x_ship_to_state']:
|
|
continue
|
|
self.assertEqual(
|
|
unicode(values[2], "utf-8"),
|
|
form_values[values[1]],
|
|
'Authorize: wrong value for input %s: received %s instead of %s' % (values[1], values[2], form_values[values[1]])
|
|
)
|
|
|
|
@mute_logger('odoo.addons.payment_authorize.models.payment', 'ValidationError')
|
|
def test_20_authorize_form_management(self):
|
|
# be sure not to do stupid thing
|
|
self.assertEqual(self.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 will be empty starting the 28th March 2019
|
|
'x_MD5_Hash': u'7934485E1C105940BE854208D10FAB4F',
|
|
'x_SHA2_Hash': u'7D3AC844BE8CA3F649AB885A90D22CFE35B850338EC91D1A5ADD819A85FF948A3D777334A18CDE36821DC8F2B42A6E1950C1FF96B52B60F23201483A656195FB',
|
|
'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@example.com',
|
|
'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.env['payment.transaction'].form_feedback(authorize_post_data, 'authorize')
|
|
|
|
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.env['payment.transaction'].form_feedback(authorize_post_data, 'authorize')
|
|
# check state
|
|
self.assertEqual(tx.state, 'done', 'Authorize: validation did not put tx into done state')
|
|
self.assertEqual(tx.acquirer_reference, authorize_post_data.get('x_trans_id'), 'Authorize: validation did not update tx payid')
|
|
|
|
# reset tx
|
|
tx.write({'state': 'draft', 'date_validate': False, 'acquirer_reference': False})
|
|
|
|
# simulate an error
|
|
authorize_post_data['x_response_code'] = u'3'
|
|
self.env['payment.transaction'].form_feedback(authorize_post_data, 'authorize')
|
|
# check state
|
|
self.assertEqual(tx.state, 'error', 'Authorize: erroneous validation did not put tx into error state')
|
|
|
|
@unittest.skip("Authorize s2s test disabled: We do not want to overload Authorize.net with runbot's requests")
|
|
def test_30_authorize_s2s(self):
|
|
# be sure not to do stupid thing
|
|
authorize = self.authorize
|
|
self.assertEqual(authorize.environment, 'test', 'test without test environment')
|
|
|
|
# add credential
|
|
# FIXME: put this test in master-nightly on odoo/odoo + create sandbox account
|
|
authorize.write({
|
|
'authorize_transaction_key': '',
|
|
'authorize_login': '',
|
|
})
|
|
self.assertTrue(authorize.authorize_test_credentials, 'Authorize.net: s2s authentication failed')
|
|
|
|
# create payment meethod
|
|
payment_token = self.env['payment.token'].create({
|
|
'acquirer_id': authorize.id,
|
|
'partner_id': self.buyer_id,
|
|
'cc_number': '4111 1111 1111 1111',
|
|
'cc_expiry': '02 / 26',
|
|
'cc_brand': 'visa',
|
|
'cc_cvc': '111',
|
|
'cc_holder_name': 'test',
|
|
})
|
|
|
|
# create normal s2s transaction
|
|
transaction = self.env['payment.transaction'].create({
|
|
'amount': 500,
|
|
'acquirer_id': authorize.id,
|
|
'type': 'server2server',
|
|
'currency_id': self.currency_usd.id,
|
|
'reference': 'test_ref_%s' % int(time.time()),
|
|
'payment_token_id': payment_token.id,
|
|
'partner_id': self.buyer_id,
|
|
|
|
})
|
|
transaction.authorize_s2s_do_transaction()
|
|
self.assertEqual(transaction.state, 'done',)
|
|
|
|
# switch to 'authorize only'
|
|
# create authorize only s2s transaction & capture it
|
|
self.authorize.auto_confirm = 'authorize'
|
|
transaction = self.env['payment.transaction'].create({
|
|
'amount': 500,
|
|
'acquirer_id': authorize.id,
|
|
'type': 'server2server',
|
|
'currency_id': self.currency_usd.id,
|
|
'reference': 'test_%s' % int(time.time()),
|
|
'payment_token_id': payment_token.id,
|
|
'partner_id': self.buyer_id,
|
|
|
|
})
|
|
transaction.authorize_s2s_do_transaction()
|
|
self.assertEqual(transaction.state, 'authorized')
|
|
transaction.action_capture()
|
|
self.assertEqual(transaction.state, 'done')
|
|
|
|
# create authorize only s2s transaction & void it
|
|
self.authorize.auto_confirm = 'authorize'
|
|
transaction = self.env['payment.transaction'].create({
|
|
'amount': 500,
|
|
'acquirer_id': authorize.id,
|
|
'type': 'server2server',
|
|
'currency_id': self.currency_usd.id,
|
|
'reference': 'test_%s' % int(time.time()),
|
|
'payment_token_id': payment_token.id,
|
|
'partner_id': self.buyer_id,
|
|
|
|
})
|
|
transaction.authorize_s2s_do_transaction()
|
|
self.assertEqual(transaction.state, 'authorized')
|
|
transaction.action_void()
|
|
self.assertEqual(transaction.state, 'cancel')
|
|
|
|
# try charging an unexisting profile
|
|
ghost_payment_token = payment_token.copy()
|
|
ghost_payment_token.authorize_profile = '99999999999'
|
|
# create normal s2s transaction
|
|
transaction = self.env['payment.transaction'].create({
|
|
'amount': 500,
|
|
'acquirer_id': authorize.id,
|
|
'type': 'server2server',
|
|
'currency_id': self.currency_usd.id,
|
|
'reference': 'test_ref_%s' % int(time.time()),
|
|
'payment_token_id': ghost_payment_token.id,
|
|
'partner_id': self.buyer_id,
|
|
|
|
})
|
|
transaction.authorize_s2s_do_transaction()
|
|
self.assertEqual(transaction.state, 'error')
|