Commit 40fa9ff0 authored by Rafael Monnerat's avatar Rafael Monnerat

Payzen rest

See merge request nexedi/slapos.core!274
parents eb35deda 79de2138
"""
This script was introduced for backward compatibility on migration and for
introduce custom delays on what configures the expiration dates.
"""
from DateTime import DateTime
return int(DateTime()) - int(transaction_date) > 86400
......@@ -50,11 +50,11 @@
</item>
<item>
<key> <string>_params</string> </key>
<value> <string>state_change</string> </value>
<value> <string>transaction_date</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>PayzenEvent_registerPayzen</string> </value>
<value> <string>PayzenEvent_isPaymentExpired</string> </value>
</item>
</dictionary>
</pickle>
......
......@@ -13,104 +13,95 @@ transaction = payzen_event.getDestinationValue()
if transaction is None:
raise ValueError("Unable to find related transaction")
assert signature in (True, False)
if signature is False:
# signature is wrong, bye bye
payzen_event.confirm(comment='Signature does not match')
isTransitionPossible = context.getPortalObject().portal_workflow.isTransitionPossible
status = data_kw['status']
answer = data_kw['answer']
if status != "SUCCESS":
error_code = answer["error_code"]
if error_code == "PSP_010":
# Transaction Not Found
# Mark on payment transaction history log that transaction was not processed yet
transaction_date, _ = transaction.PaymentTransaction_getPayzenId()
payzen_event.confirm()
payzen_event.acknowledge(comment='Transaction not found on payzen side.')
if context.PayzenEvent_isPaymentExpired(transaction_date):
if isTransitionPossible(transaction, 'cancel'):
transaction.cancel(comment='Aborting unknown payzen payment.')
else:
storeWorkflowComment(transaction,
'Error code PSP_010 (Not found) did not changed the document state.')
return
else:
# Unknown errorCode
payzen_event.confirm(comment='Unknown errorCode %r' % error_code)
return
transaction_list = answer["transactions"]
if len(transaction_list) != 1:
# Unexpected Number of Transactions
payzen_event.confirm(comment='Unexpected Number of Transaction for this order')
return
isTransitionPossible = context.getPortalObject().portal_workflow.isTransitionPossible
transaction_kw = transaction_list[0]
# See Full Status list at https://payzen.io/en-EN/rest/V4.0/api/kb/status_reference.html
mark_transaction_id_list = [
'AUTHORISED_TO_VALIDATE',
'WAITING_AUTHORISATION',
'WAITING_AUTHORISATION_TO_VALIDATE',
]
continue_transaction_id_list = ['AUTHORISED', 'CAPTURED', 'PARTIALLY_AUTHORISED']
cancel_transaction_id_list = ['REFUSED']
transaction_status = transaction_kw['detailedStatus']
error_code = data_kw['errorCode']
if error_code == '2':
transaction_date, _ = transaction.PaymentTransaction_getPayzenId()
if transaction_status in mark_transaction_id_list:
# Mark on payment transaction history log that transaction was not processed yet
storeWorkflowComment(transaction, 'Transaction status %s did not changed the document state' % (transaction_status))
payzen_event.confirm()
payzen_event.acknowledge(comment='Transaction not found on payzen side.')
if int(DateTime()) - int(transaction_date) > 86400:
if isTransitionPossible(transaction, 'cancel'):
transaction.cancel(comment='Aborting unknown payzen payment.')
else:
storeWorkflowComment(transaction,
'Error code 2 (Not found) did not changed the document state.')
return
payzen_event.acknowledge(comment='Automatic acknowledge as result of correct communication')
if isTransitionPossible(transaction, 'confirm'):
transaction.confirm(comment='Confirmed as really saw in PayZen.')
elif transaction_status in continue_transaction_id_list:
# Check authAmount and authDevise and if match, stop transaction
auth_amount = int(transaction_kw['transactionDetails']['cardDetails']['authorizationResponse']['amount'])
auth_devise = transaction_kw['transactionDetails']['cardDetails']['authorizationResponse']['currency']
transaction_amount = int((round(transaction.PaymentTransaction_getTotalPayablePrice(), 2) * -100))
if transaction_amount != auth_amount:
payzen_event.confirm(comment='Received amount (%r) does not match stored on transaction (%r)'% (auth_amount, transaction_amount))
return
elif error_code == '0':
transaction_code_mapping = {
'0': 'Initial (being treated)',
'1': 'To be validated ',
'2': 'To be forced - Contact issuer',
'3': 'To be validated and authorized',
'4': 'Waiting for submission',
'5': 'Waiting for authorization',
'6': 'Submitted',
'7': 'Expired',
'8': 'Refused',
'9': 'Cancelled',
'10': 'Waiting',
'11': 'Being submitted',
'12': 'Being authorized',
'13': 'Failed',
}
mark_transaction_id_list = ['0', '1', '3', '5', '10', '11', '12']
continue_transaction_id_list = ['4', '6']
cancel_transaction_id_list = ['8']
transaction_status = data_kw['transactionStatus']
transaction_status_description = transaction_code_mapping.get(transaction_status, None)
if transaction_status_description is None:
payzen_event.confirm(comment='Unknown transactionStatus %r' % transaction_status)
transaction_devise = transaction.getResourceReference()
if transaction_devise != auth_devise:
payzen_event.confirm(comment='Received devise (%r) does not match stored on transaction (%r)'% (auth_devise, transaction_devise))
return
if transaction_status in mark_transaction_id_list:
# Mark on payment transaction history log that transaction was not processed yet
storeWorkflowComment(transaction, 'Transaction status %s (%s) did not changed the document state' % (transaction_status, transaction_status_description))
payzen_event.confirm()
payzen_event.acknowledge(comment='Automatic acknowledge as result of correct communication')
if isTransitionPossible(transaction, 'confirm'):
transaction.confirm(comment='Confirmed as really saw in PayZen.')
elif transaction_status in continue_transaction_id_list:
# Check authAmount and authDevise and if match, stop transaction
auth_amount = int(data_kw['authAmount'])
auth_devise = data_kw['authDevise']
transaction_amount = int((round(transaction.PaymentTransaction_getTotalPayablePrice(), 2) * -100))
if transaction_amount != auth_amount:
payzen_event.confirm(comment='Received amount (%r) does not match stored on transaction (%r)'% (auth_amount, transaction_amount))
return
transaction_devise = transaction.getResourceValue().Currency_getIntegrationMapping()
if transaction_devise != auth_devise:
payzen_event.confirm(comment='Received devise (%r) does not match stored on transaction (%r)'% (auth_devise, transaction_devise))
return
comment = 'PayZen considered as paid.'
if isTransitionPossible(transaction, 'confirm'):
transaction.confirm(comment=comment)
if isTransitionPossible(transaction, 'start'):
transaction.start(comment=comment)
if isTransitionPossible(transaction, 'stop'):
transaction.stop(comment=comment)
if transaction.getSimulationState() == 'stopped':
payzen_event.confirm()
payzen_event.acknowledge(comment='Automatic acknowledge as result of correct communication')
else:
payzen_event.confirm(comment='Expected to put transaction in stopped state, but achieved only %s state' % transaction.getSimulationState())
comment = 'PayZen considered as paid.'
if isTransitionPossible(transaction, 'confirm'):
transaction.confirm(comment=comment)
if isTransitionPossible(transaction, 'start'):
transaction.start(comment=comment)
if isTransitionPossible(transaction, 'stop'):
transaction.stop(comment=comment)
elif transaction_status in cancel_transaction_id_list:
if transaction.getSimulationState() == 'stopped':
payzen_event.confirm()
payzen_event.acknowledge(comment='Refused payzen payment.')
if isTransitionPossible(transaction, 'cancel'):
transaction.cancel(comment='Aborting refused payzen payment.')
return
payzen_event.acknowledge(comment='Automatic acknowledge as result of correct communication')
else:
payzen_event.confirm(comment='Transaction status %r (%r) is not supported' \
% (transaction_status, transaction_status_description))
return
payzen_event.confirm(comment='Expected to put transaction in stopped state, but achieved only %s state' % transaction.getSimulationState())
elif transaction_status in cancel_transaction_id_list:
payzen_event.confirm()
payzen_event.acknowledge(comment='Refused payzen payment.')
if isTransitionPossible(transaction, 'cancel'):
transaction.cancel(comment='Aborting refused payzen payment.')
return
else:
# Unknown errorCode
payzen_event.confirm(comment='Unknown errorCode %r' % error_code)
payzen_event.confirm(comment='Transaction status %r is not supported' \
% (transaction_status))
return
......@@ -50,7 +50,15 @@
</item>
<item>
<key> <string>_params</string> </key>
<value> <string>data_kw, signature, REQUEST=None</string> </value>
<value> <string>data_kw, REQUEST=None</string> </value>
</item>
<item>
<key> <string>_proxy_roles</string> </key>
<value>
<tuple>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>id</string> </key>
......
......@@ -5,7 +5,7 @@ from DateTime import DateTime
from Products.ERP5Type.tests.utils import createZODBPythonScript
import difflib
HARDCODED_PRICE = 99.6
HARDCODED_PRICE = -99.6
vads_url_cancel = 'http://example.org/cancel'
vads_url_error = 'http://example.org/error'
......@@ -187,6 +187,7 @@ class TestSlapOSPayzenInterfaceWorkflow(SlapOSTestCaseMixinWithAbort):
'vads_amount': str(int(HARDCODED_PRICE * -100)),
'vads_currency': 978,
'vads_trans_id': transaction_id,
'vads_order_id': transaction_id,
'vads_site_id': 'foo',
}
# Calculate the signature...
......@@ -201,10 +202,10 @@ class TestSlapOSPayzenInterfaceWorkflow(SlapOSTestCaseMixinWithAbort):
'%(vads_url_return)s">\n\n\n <input type="hidden" name="vads_site_id" '\
'value="%(vads_site_id)s">\n\n\n <input type="hidden" name="vads_url_e'\
'rror"\n value="%(vads_url_error)s">\n\n\n <input type="hidden'\
'" name="vads_trans_id" value="%(vads_trans_id)s">\n\n\n <input type="'\
'hidden" name="vads_action_mode"\n value="INTERACTIVE">\n\n\n '\
'" name="vads_trans_id" value="%(vads_trans_id)s">\n\n\n '\
'<input type="hidden" name="vads_url_success"\n value="'\
'%(vads_url_success)s">\n\n\n <input type="hidden" name="vads_url_refe'\
'%(vads_url_success)s">\n\n\n <input type="hidden" name="vads_order_id'\
'" value="%(vads_trans_id)s">\n\n\n <input type="hidden" name="vads_url_refe'\
'rral"\n value="%(vads_url_referral)s">\n\n\n <input type="hid'\
'den" name="vads_page_action"\n value="PAYMENT">\n\n\n <input '\
'type="hidden" name="vads_trans_date"\n value="'\
......@@ -218,7 +219,8 @@ class TestSlapOSPayzenInterfaceWorkflow(SlapOSTestCaseMixinWithAbort):
'\n <input type="hidden" name="vads_language" value="%(vads_language)s">\n\n\n <inpu'\
't type="hidden" name="vads_currency" value="%(vads_currency)s">\n\n\n '\
' <input type="hidden" name="vads_amount" value="%(vads_amount)s">\n\n\n'\
' <input type="hidden" name="vads_version" value="V2">\n\n<center>\n '\
' <input type="hidden" name="vads_version" value="V2">\n\n\n <input type="'\
'hidden" name="vads_action_mode"\n value="INTERACTIVE">\n\n<center>\n '\
' <input type="submit" value="Click to pay">\n</center>\n</form>' % data_dict
# Event message state
......@@ -252,17 +254,17 @@ class TestSlapOSPayzenInterfaceWorkflow(SlapOSTestCaseMixinWithAbort):
_ , _ = payment.PaymentTransaction_generatePayzenId()
self.assertRaises(AttributeError, event.updateStatus)
def mockSoapGetInfo(self, method_to_call, expected_args, result_tuple):
def mockRestGetInfo(self, method_to_call, expected_args, result_tuple):
payment_service = self.portal.portal_secure_payments.slapos_payzen_test
def mocksoad_getInfo(arg1, arg2):
def mockrest_getInfo(arg1, arg2):
self.assertEqual(arg1, expected_args[0])
self.assertEqual(arg2, expected_args[1])
return result_tuple
setattr(payment_service, 'soap_getInfo', mocksoad_getInfo)
setattr(payment_service, 'rest_getInfo', mockrest_getInfo)
try:
return method_to_call()
finally:
del payment_service.soap_getInfo
del payment_service.rest_getInfo
def _simulatePayzenEvent_processUpdate(self):
script_name = 'PayzenEvent_processUpdate'
......@@ -293,16 +295,15 @@ portal_workflow.doActionFor(context, action='edit_action', comment='Visited by P
payment.PaymentTransaction_generatePayzenId()
mocked_data_kw = 'mocked_data_kw'
mocked_signature = 'mocked_signature'
mocked_sent_text = 'mocked_sent_text'
mocked_received_text = 'mocked_received_text'
self._simulatePayzenEvent_processUpdate()
try:
self.mockSoapGetInfo(
self.mockRestGetInfo(
event.updateStatus,
(transaction_date.toZone('UTC').asdatetime(), transaction_id),
(mocked_data_kw, mocked_signature, mocked_sent_text, mocked_received_text),
(mocked_data_kw, mocked_sent_text, mocked_received_text),
)
finally:
self._dropPayzenEvent_processUpdate()
......@@ -311,11 +312,11 @@ portal_workflow.doActionFor(context, action='edit_action', comment='Visited by P
self.assertEqual(len(event_message_list), 2)
sent_message = [x for x in event_message_list \
if x.getTitle() == 'Sent SOAP'][0]
if x.getTitle() == 'Sent Data'][0]
self.assertEqual(sent_message.getTextContent(), mocked_sent_text)
received_message = [x for x in event_message_list \
if x.getTitle() == 'Received SOAP'][0]
if x.getTitle() == 'Received Data'][0]
self.assertEqual(received_message.getPredecessor(),
sent_message.getRelativeUrl())
self.assertEqual(received_message.getTextContent(), mocked_received_text)
......
......@@ -29,6 +29,7 @@ payzen_dict = {
'vads_amount': str(int(round((payment_transaction.PaymentTransaction_getTotalPayablePrice() * -100), 0))),
'vads_trans_date': now.toZone('UTC').asdatetime().strftime('%Y%m%d%H%M%S'),
'vads_trans_id': transaction_id,
'vads_order_id': transaction_id,
'vads_language': 'en',
'vads_url_cancel': vads_url_cancel,
'vads_url_error': vads_url_error,
......
"""Registers current transaction in payment
In order to not transmit sensitive information the registration is done by looking the newest
payzen related transaction for destination_section and doing its duplicate"""
from DateTime import DateTime
payzen_event = state_change['object']
transaction = payzen_event.getDestinationValue()
payment_service = payzen_event.getSourceValue(portal_type="Payzen Service")
previous_id = transaction.PaymentTransaction_getPreviousPayzenId()
if previous_id is None:
payzen_event.confirm(comment='No previous id found')
return
transaction_date, transaction_id = transaction.PaymentTransaction_generatePayzenId()
if transaction_id is None:
raise ValueError('Transaction already mapped in integration tool.')
# do causality mapping in integration_site between transaction.getRelativeUrl and today + transaction_id
payzen_dict = {}
payzen_dict.update(
devise=transaction.getResourceValue().Currency_getIntegrationMapping(),
amount=str(int(round((transaction.PaymentTransaction_getTotalPayablePrice() * -100), 0))),
presentationDate=transaction.getStartDate().toZone('UTC').asdatetime(),
newTransactionId=transaction_id,
transmissionDate=transaction_date.asdatetime(),
transactionId=previous_id
)
data_kw, signature, sent_text, received_text = payment_service.soap_duplicate(**payzen_dict)
# SENT
sent = payzen_event.newContent(title='Sent SOAP',
portal_type='Payzen Event Message',
text_content=sent_text)
# RECEIVED
payzen_event.newContent(title='Received SOAP',
text_content=received_text,
predecessor_value=sent,
portal_type='Payzen Event Message')
context.PayzenEvent_processUpdate(state_change, data_kw, signature)
......@@ -6,20 +6,20 @@ if transaction_id is None:
raise ValueError('Transaction not registered in payzen integration tool')
payment_service = payzen_event.getSourceValue(portal_type="Payzen Service")
data_kw, signature, sent_text, received_text = payment_service.soap_getInfo(
data_kw, sent_text, received_text = payment_service.rest_getInfo(
transaction_date.toZone('UTC').asdatetime(),
transaction_id)
# SENT
sent = payzen_event.newContent(
title='Sent SOAP',
title='Sent Data',
portal_type='Payzen Event Message',
text_content=sent_text)
# RECEIVED
payzen_event.newContent(
title='Received SOAP',
title='Received Data',
portal_type='Payzen Event Message',
text_content=received_text,
predecessor_value=sent)
payzen_event.PayzenEvent_processUpdate(data_kw, signature)
payzen_event.PayzenEvent_processUpdate(data_kw)
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="TransitionDefinition" module="Products.DCWorkflow.Transitions"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>actbox_category</string> </key>
<value> <string>workflow</string> </value>
</item>
<item>
<key> <string>actbox_icon</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>actbox_name</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>actbox_url</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>after_script_name</string> </key>
<value> <string>PayzenEvent_registerPayzen</string> </value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>guard</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>register_payzen</string> </value>
</item>
<item>
<key> <string>new_state_id</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>script_name</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>trigger_type</string> </key>
<value> <int>2</int> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment