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 @@ ...@@ -50,11 +50,11 @@
</item> </item>
<item> <item>
<key> <string>_params</string> </key> <key> <string>_params</string> </key>
<value> <string>state_change</string> </value> <value> <string>transaction_date</string> </value>
</item> </item>
<item> <item>
<key> <string>id</string> </key> <key> <string>id</string> </key>
<value> <string>PayzenEvent_registerPayzen</string> </value> <value> <string>PayzenEvent_isPaymentExpired</string> </value>
</item> </item>
</dictionary> </dictionary>
</pickle> </pickle>
......
...@@ -13,75 +13,70 @@ transaction = payzen_event.getDestinationValue() ...@@ -13,75 +13,70 @@ transaction = payzen_event.getDestinationValue()
if transaction is None: if transaction is None:
raise ValueError("Unable to find related transaction") 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')
return
isTransitionPossible = context.getPortalObject().portal_workflow.isTransitionPossible isTransitionPossible = context.getPortalObject().portal_workflow.isTransitionPossible
error_code = data_kw['errorCode'] status = data_kw['status']
if error_code == '2': answer = data_kw['answer']
transaction_date, _ = transaction.PaymentTransaction_getPayzenId() 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 # Mark on payment transaction history log that transaction was not processed yet
transaction_date, _ = transaction.PaymentTransaction_getPayzenId()
payzen_event.confirm() payzen_event.confirm()
payzen_event.acknowledge(comment='Transaction not found on payzen side.') payzen_event.acknowledge(comment='Transaction not found on payzen side.')
if int(DateTime()) - int(transaction_date) > 86400:
if context.PayzenEvent_isPaymentExpired(transaction_date):
if isTransitionPossible(transaction, 'cancel'): if isTransitionPossible(transaction, 'cancel'):
transaction.cancel(comment='Aborting unknown payzen payment.') transaction.cancel(comment='Aborting unknown payzen payment.')
else: else:
storeWorkflowComment(transaction, storeWorkflowComment(transaction,
'Error code 2 (Not found) did not changed the document state.') '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 return
elif error_code == '0': transaction_list = answer["transactions"]
transaction_code_mapping = { if len(transaction_list) != 1:
'0': 'Initial (being treated)', # Unexpected Number of Transactions
'1': 'To be validated ', payzen_event.confirm(comment='Unexpected Number of Transaction for this order')
'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)
return return
if transaction_status in mark_transaction_id_list: 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']
if transaction_status in mark_transaction_id_list:
# Mark on payment transaction history log that transaction was not processed yet # 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)) storeWorkflowComment(transaction, 'Transaction status %s did not changed the document state' % (transaction_status))
payzen_event.confirm() payzen_event.confirm()
payzen_event.acknowledge(comment='Automatic acknowledge as result of correct communication') payzen_event.acknowledge(comment='Automatic acknowledge as result of correct communication')
if isTransitionPossible(transaction, 'confirm'): if isTransitionPossible(transaction, 'confirm'):
transaction.confirm(comment='Confirmed as really saw in PayZen.') transaction.confirm(comment='Confirmed as really saw in PayZen.')
elif transaction_status in continue_transaction_id_list: elif transaction_status in continue_transaction_id_list:
# Check authAmount and authDevise and if match, stop transaction # Check authAmount and authDevise and if match, stop transaction
auth_amount = int(data_kw['authAmount']) auth_amount = int(transaction_kw['transactionDetails']['cardDetails']['authorizationResponse']['amount'])
auth_devise = data_kw['authDevise'] auth_devise = transaction_kw['transactionDetails']['cardDetails']['authorizationResponse']['currency']
transaction_amount = int((round(transaction.PaymentTransaction_getTotalPayablePrice(), 2) * -100)) transaction_amount = int((round(transaction.PaymentTransaction_getTotalPayablePrice(), 2) * -100))
if transaction_amount != auth_amount: if transaction_amount != auth_amount:
payzen_event.confirm(comment='Received amount (%r) does not match stored on transaction (%r)'% (auth_amount, transaction_amount)) payzen_event.confirm(comment='Received amount (%r) does not match stored on transaction (%r)'% (auth_amount, transaction_amount))
return return
transaction_devise = transaction.getResourceValue().Currency_getIntegrationMapping() transaction_devise = transaction.getResourceReference()
if transaction_devise != auth_devise: if transaction_devise != auth_devise:
payzen_event.confirm(comment='Received devise (%r) does not match stored on transaction (%r)'% (auth_devise, transaction_devise)) payzen_event.confirm(comment='Received devise (%r) does not match stored on transaction (%r)'% (auth_devise, transaction_devise))
return return
...@@ -100,17 +95,13 @@ elif error_code == '0': ...@@ -100,17 +95,13 @@ elif error_code == '0':
else: else:
payzen_event.confirm(comment='Expected to put transaction in stopped state, but achieved only %s state' % transaction.getSimulationState()) 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: elif transaction_status in cancel_transaction_id_list:
payzen_event.confirm() payzen_event.confirm()
payzen_event.acknowledge(comment='Refused payzen payment.') payzen_event.acknowledge(comment='Refused payzen payment.')
if isTransitionPossible(transaction, 'cancel'): if isTransitionPossible(transaction, 'cancel'):
transaction.cancel(comment='Aborting refused payzen payment.') transaction.cancel(comment='Aborting refused payzen payment.')
return return
else:
payzen_event.confirm(comment='Transaction status %r (%r) is not supported' \
% (transaction_status, transaction_status_description))
return
else: else:
# Unknown errorCode payzen_event.confirm(comment='Transaction status %r is not supported' \
payzen_event.confirm(comment='Unknown errorCode %r' % error_code) % (transaction_status))
return
...@@ -50,7 +50,15 @@ ...@@ -50,7 +50,15 @@
</item> </item>
<item> <item>
<key> <string>_params</string> </key> <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>
<item> <item>
<key> <string>id</string> </key> <key> <string>id</string> </key>
......
...@@ -5,7 +5,7 @@ from DateTime import DateTime ...@@ -5,7 +5,7 @@ from DateTime import DateTime
from Products.ERP5Type.tests.utils import createZODBPythonScript from Products.ERP5Type.tests.utils import createZODBPythonScript
import difflib import difflib
HARDCODED_PRICE = 99.6 HARDCODED_PRICE = -99.6
vads_url_cancel = 'http://example.org/cancel' vads_url_cancel = 'http://example.org/cancel'
vads_url_error = 'http://example.org/error' vads_url_error = 'http://example.org/error'
...@@ -187,6 +187,7 @@ class TestSlapOSPayzenInterfaceWorkflow(SlapOSTestCaseMixinWithAbort): ...@@ -187,6 +187,7 @@ class TestSlapOSPayzenInterfaceWorkflow(SlapOSTestCaseMixinWithAbort):
'vads_amount': str(int(HARDCODED_PRICE * -100)), 'vads_amount': str(int(HARDCODED_PRICE * -100)),
'vads_currency': 978, 'vads_currency': 978,
'vads_trans_id': transaction_id, 'vads_trans_id': transaction_id,
'vads_order_id': transaction_id,
'vads_site_id': 'foo', 'vads_site_id': 'foo',
} }
# Calculate the signature... # Calculate the signature...
...@@ -201,10 +202,10 @@ class TestSlapOSPayzenInterfaceWorkflow(SlapOSTestCaseMixinWithAbort): ...@@ -201,10 +202,10 @@ class TestSlapOSPayzenInterfaceWorkflow(SlapOSTestCaseMixinWithAbort):
'%(vads_url_return)s">\n\n\n <input type="hidden" name="vads_site_id" '\ '%(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'\ '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'\ '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="'\ '" name="vads_trans_id" value="%(vads_trans_id)s">\n\n\n '\
'hidden" name="vads_action_mode"\n value="INTERACTIVE">\n\n\n '\
'<input type="hidden" name="vads_url_success"\n value="'\ '<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'\ '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 '\ 'den" name="vads_page_action"\n value="PAYMENT">\n\n\n <input '\
'type="hidden" name="vads_trans_date"\n value="'\ 'type="hidden" name="vads_trans_date"\n value="'\
...@@ -218,7 +219,8 @@ class TestSlapOSPayzenInterfaceWorkflow(SlapOSTestCaseMixinWithAbort): ...@@ -218,7 +219,8 @@ class TestSlapOSPayzenInterfaceWorkflow(SlapOSTestCaseMixinWithAbort):
'\n <input type="hidden" name="vads_language" value="%(vads_language)s">\n\n\n <inpu'\ '\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 '\ '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_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 ' <input type="submit" value="Click to pay">\n</center>\n</form>' % data_dict
# Event message state # Event message state
...@@ -252,17 +254,17 @@ class TestSlapOSPayzenInterfaceWorkflow(SlapOSTestCaseMixinWithAbort): ...@@ -252,17 +254,17 @@ class TestSlapOSPayzenInterfaceWorkflow(SlapOSTestCaseMixinWithAbort):
_ , _ = payment.PaymentTransaction_generatePayzenId() _ , _ = payment.PaymentTransaction_generatePayzenId()
self.assertRaises(AttributeError, event.updateStatus) 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 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(arg1, expected_args[0])
self.assertEqual(arg2, expected_args[1]) self.assertEqual(arg2, expected_args[1])
return result_tuple return result_tuple
setattr(payment_service, 'soap_getInfo', mocksoad_getInfo) setattr(payment_service, 'rest_getInfo', mockrest_getInfo)
try: try:
return method_to_call() return method_to_call()
finally: finally:
del payment_service.soap_getInfo del payment_service.rest_getInfo
def _simulatePayzenEvent_processUpdate(self): def _simulatePayzenEvent_processUpdate(self):
script_name = 'PayzenEvent_processUpdate' script_name = 'PayzenEvent_processUpdate'
...@@ -293,16 +295,15 @@ portal_workflow.doActionFor(context, action='edit_action', comment='Visited by P ...@@ -293,16 +295,15 @@ portal_workflow.doActionFor(context, action='edit_action', comment='Visited by P
payment.PaymentTransaction_generatePayzenId() payment.PaymentTransaction_generatePayzenId()
mocked_data_kw = 'mocked_data_kw' mocked_data_kw = 'mocked_data_kw'
mocked_signature = 'mocked_signature'
mocked_sent_text = 'mocked_sent_text' mocked_sent_text = 'mocked_sent_text'
mocked_received_text = 'mocked_received_text' mocked_received_text = 'mocked_received_text'
self._simulatePayzenEvent_processUpdate() self._simulatePayzenEvent_processUpdate()
try: try:
self.mockSoapGetInfo( self.mockRestGetInfo(
event.updateStatus, event.updateStatus,
(transaction_date.toZone('UTC').asdatetime(), transaction_id), (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: finally:
self._dropPayzenEvent_processUpdate() self._dropPayzenEvent_processUpdate()
...@@ -311,11 +312,11 @@ portal_workflow.doActionFor(context, action='edit_action', comment='Visited by P ...@@ -311,11 +312,11 @@ portal_workflow.doActionFor(context, action='edit_action', comment='Visited by P
self.assertEqual(len(event_message_list), 2) self.assertEqual(len(event_message_list), 2)
sent_message = [x for x in event_message_list \ 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) self.assertEqual(sent_message.getTextContent(), mocked_sent_text)
received_message = [x for x in event_message_list \ 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(), self.assertEqual(received_message.getPredecessor(),
sent_message.getRelativeUrl()) sent_message.getRelativeUrl())
self.assertEqual(received_message.getTextContent(), mocked_received_text) self.assertEqual(received_message.getTextContent(), mocked_received_text)
......
...@@ -29,6 +29,7 @@ payzen_dict = { ...@@ -29,6 +29,7 @@ payzen_dict = {
'vads_amount': str(int(round((payment_transaction.PaymentTransaction_getTotalPayablePrice() * -100), 0))), '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_date': now.toZone('UTC').asdatetime().strftime('%Y%m%d%H%M%S'),
'vads_trans_id': transaction_id, 'vads_trans_id': transaction_id,
'vads_order_id': transaction_id,
'vads_language': 'en', 'vads_language': 'en',
'vads_url_cancel': vads_url_cancel, 'vads_url_cancel': vads_url_cancel,
'vads_url_error': vads_url_error, '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: ...@@ -6,20 +6,20 @@ if transaction_id is None:
raise ValueError('Transaction not registered in payzen integration tool') raise ValueError('Transaction not registered in payzen integration tool')
payment_service = payzen_event.getSourceValue(portal_type="Payzen Service") 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_date.toZone('UTC').asdatetime(),
transaction_id) transaction_id)
# SENT # SENT
sent = payzen_event.newContent( sent = payzen_event.newContent(
title='Sent SOAP', title='Sent Data',
portal_type='Payzen Event Message', portal_type='Payzen Event Message',
text_content=sent_text) text_content=sent_text)
# RECEIVED # RECEIVED
payzen_event.newContent( payzen_event.newContent(
title='Received SOAP', title='Received Data',
portal_type='Payzen Event Message', portal_type='Payzen Event Message',
text_content=received_text, text_content=received_text,
predecessor_value=sent) 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