source: main/kofacustom.nigeria/trunk/src/kofacustom/nigeria/interswitch/tests.py @ 17497

Last change on this file since 17497 was 17231, checked in by Henrik Bettermann, 23 months ago

Use Mac validation.

  • Property svn:keywords set to Id
File size: 21.2 KB
RevLine 
[11640]1## $Id: tests.py 17231 2022-12-18 09:24:18Z henrik $
2##
3## Copyright (C) 2011 Uli Fouquet & Henrik Bettermann
4## This program is free software; you can redistribute it and/or modify
5## it under the terms of the GNU General Public License as published by
6## the Free Software Foundation; either version 2 of the License, or
7## (at your option) any later version.
8##
9## This program is distributed in the hope that it will be useful,
10## but WITHOUT ANY WARRANTY; without even the implied warranty of
11## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12## GNU General Public License for more details.
13##
14## You should have received a copy of the GNU General Public License
15## along with this program; if not, write to the Free Software
16## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17##
18import os
[16487]19import random
[12973]20from datetime import datetime, timedelta, date
[11640]21from zope.component import createObject, getUtility
22from zope.catalog.interfaces import ICatalog
[16484]23from xml.dom.minidom import parseString
[11640]24from hurry.workflow.interfaces import IWorkflowState
25from waeup.kofa.students.tests.test_browser import StudentsFullSetup
26from waeup.kofa.applicants.tests.test_browser import ApplicantsFullSetup
27from waeup.kofa.configuration import SessionConfiguration
[13386]28from waeup.kofa.students.payments import StudentOnlinePayment
[16484]29from waeup.kofa.browser.tests.test_pdf import samples_dir
30from kofacustom.nigeria.interswitch.helpers import (
[16487]31    query_interswitch, get_SOAP_response_paydirect, SOAP_post,
[17221]32    create_paydirect_booking,
33    confirm_transaction
[16487]34    )
[11640]35from kofacustom.nigeria.testing import FunctionalLayer
36
[13386]37# Also run tests that send requests to external servers?
38#   If you enable this, please make sure the external services
39#   do exist really and are not bothered by being spammed by a test programme.
[17221]40EXTERNAL_TESTS_1 = False
41EXTERNAL_TESTS_2 = False
42EXTERNAL_TESTS_3 = True
[13386]43
[16484]44PAYDIRECT_HOST = 'sandbox.interswitchng.com'
45PAYDIRECT_URL = '/bookonhold/bookonhold.asmx'
46MERCHANT_ID = '6033'
47
48def external_test_1(func):
49    if not EXTERNAL_TESTS_1:
[13386]50        myself = __file__
51        if myself.endswith('.pyc'):
[14737]52            myself = myself[:-1]
[13386]53        print "WARNING: external tests are skipped!"
54        print "WARNING: edit %s to enable them." % myself
55        return
56    return func
57
[16484]58def external_test_2(func):
59    if not EXTERNAL_TESTS_2:
60        myself = __file__
61        if myself.endswith('.pyc'):
62            myself = myself[:-1]
63        print "WARNING: external tests are skipped!"
64        print "WARNING: edit %s to enable them." % myself
65        return
66    return func
67
[17221]68def external_test_3(func):
69    if not EXTERNAL_TESTS_3:
70        myself = __file__
71        if myself.endswith('.pyc'):
72            myself = myself[:-1]
73        print "WARNING: external tests are skipped!"
74        print "WARNING: edit %s to enable them." % myself
75        return
76    return func
77
[11640]78class InterswitchTestsStudents(StudentsFullSetup):
79    """Tests for the Interswitch payment gateway.
80    """
81
82    layer = FunctionalLayer
83
84    def setUp(self):
85        super(InterswitchTestsStudents, self).setUp()
86        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
87        self.browser.open(self.payments_path)
88        IWorkflowState(self.student).setState('cleared')
89        self.student.nationality = u'NG'
90        self.browser.open(self.payments_path + '/addop')
91        self.browser.getControl(name="form.p_category").value = ['schoolfee']
92        self.browser.getControl("Create ticket").click()
93        self.assertMatches('...ticket created...',
94                           self.browser.contents)
[16378]95        self.browser.open(self.payments_path)
[11640]96        ctrl = self.browser.getControl(name='val_id')
97        self.value = ctrl.options[0]
98        self.browser.getLink(self.value).click()
99        self.assertMatches('...Amount Authorized...',
100                           self.browser.contents)
[13386]101        self.assertTrue('<span>40000.0</span>', self.browser.contents)
[11640]102        self.payment_url = self.browser.url
[12973]103        self.payment = self.student['payments'][self.value]
[11640]104
105    def test_interswitch_form(self):
106        # Manager can access InterswitchForm
[16484]107        self.browser.getLink("Pay via Interswitch CollegePAY", index=0).click()
[11640]108        self.assertMatches('...<input type="hidden" name="pay_item_id" />...',
109                           self.browser.contents)
110        self.assertMatches('...Total Amount Authorized:...',
111                           self.browser.contents)
112        self.assertEqual(self.student.current_mode, 'ug_ft')
113        self.assertMatches(
[11767]114            '...<input type="hidden" name="amount" value="4000000" />...',
[11640]115            self.browser.contents)
116
117        # Create school fee ticket for returning students. Payment is made
118        # for next session.
119        current_payment_key = self.student['payments'].keys()[0]
120        self.certificate.study_mode = u'ug_pt'
121        IWorkflowState(self.student).setState('returning')
122        configuration = createObject('waeup.SessionConfiguration')
123        configuration.academic_session = 2005
124        self.app['configuration'].addSessionConfiguration(configuration)
125        self.browser.open(self.payments_path + '/addop')
126        self.browser.getControl(name="form.p_category").value = ['schoolfee']
127        self.browser.getControl("Create ticket").click()
[16378]128        self.browser.open(self.payments_path)
[11640]129        ctrl = self.browser.getControl(name='val_id')
130        value = ctrl.options[1]
131        self.assertEqual(self.student['payments'][value].provider_amt, 0.0)
132        self.assertEqual(self.student['payments'][value].gateway_amt, 0.0)
133        self.browser.getLink(value).click()
[16484]134        self.browser.getLink("Pay via Interswitch CollegePAY", index=0).click()
[11640]135        # Split amounts have been set.
136        self.assertEqual(self.student['payments'][value].provider_amt, 0.0)
[15755]137        self.assertEqual(self.student['payments'][value].gateway_amt, 300.0)
[11640]138        self.assertMatches('...<input type="hidden" name="pay_item_id" />...',
139                           self.browser.contents)
[15755]140        self.assertTrue(
141            '<input type="hidden" name="amount" value="2030000" />' in
[11640]142            self.browser.contents)
143
[12973]144    def test_interswitch_form_ticket_expired(self):
145        # Manager can access InterswitchForm
[16484]146        self.browser.getLink("Pay via Interswitch CollegePAY", index=0).click()
[12973]147        self.assertMatches('...<input type="hidden" name="pay_item_id" />...',
148                           self.browser.contents)
149        self.assertMatches('...Total Amount Authorized:...',
150                           self.browser.contents)
151        self.assertEqual(self.student.current_mode, 'ug_ft')
[15755]152        self.assertTrue(
153            '<input type="hidden" name="amount" value="4030000" />' in
[12973]154            self.browser.contents)
[13015]155        delta = timedelta(days=8)
[12973]156        self.payment.creation_date -= delta
157        self.browser.open(self.payment_url)
[16484]158        self.browser.getLink("Pay via Interswitch CollegePAY", index=0).click()
[12973]159        self.assertMatches(
160            '...This payment ticket is too old. Please create a new ticket...',
161            self.browser.contents)
[13015]162        delta = timedelta(days=2)
[12973]163        self.payment.creation_date += delta
164        self.browser.open(self.payment_url)
[16484]165        self.browser.getLink("Pay via Interswitch CollegePAY").click()
[12973]166        self.assertMatches('...Total Amount Authorized:...',
167                           self.browser.contents)
168
[13477]169    def test_interswitch_form_ticket_expired_tz(self):
170        # The form copes with timezones when calculating expirements.
171        # We should not have TZ data in timestamps processed, but it looks
172        # like we get some with imports :-/
[16910]173        #self.browser.getLink("Pay via Interswitch CollegePAY", index=0).click()
174        delta = timedelta(days=9) # In European summertime, 8 days is not enough
[13477]175        self.payment.creation_date -= delta
176        from pytz import timezone
177        self.payment.creation_date = timezone("Europe/Berlin").localize(
178            self.payment.creation_date)
179        self.browser.open(self.payment_url)
[16484]180        self.browser.getLink("Pay via Interswitch CollegePAY", index=0).click()
[13477]181        self.assertMatches(
182            '...This payment ticket is too old. Please create a new ticket...',
183            self.browser.contents)
184        delta = timedelta(days=2)
185        self.payment.creation_date += delta
186        self.browser.open(self.payment_url)
[16484]187        self.browser.getLink("Pay via Interswitch CollegePAY").click()
[13477]188        self.assertMatches('...Total Amount Authorized:...',
189                           self.browser.contents)
190
[16484]191    @external_test_1
[13387]192    def test_query_interswitch_SOAP(self):
[13386]193        host = 'webpay.interswitchng.com'
194        url = '/paydirect/services/TransactionQueryWs.asmx'
195        https = True
[13387]196        mac = None
[13386]197        product_id = '5845' # AAUE regular
198        payment = StudentOnlinePayment()
199        payment.p_id ='p4465649308559'
200        payment.amount_auth = 60250.0
201        success, msg, log = query_interswitch(
[16115]202            payment, product_id, host, url, https, mac, False)
203        self.assertEqual('Successful callback received.', msg)
[13386]204        self.assertTrue(success)
205        self.assertTrue(
206            '00:Approved Successful:6025000:3154:p4465649308559:'
207            'ZIB|WEB|ABAL|3-11-2015|021336:000457580882' in log)
208
[16484]209    @external_test_1
[13387]210    def test_query_interswitch_JSON(self):
211        host = 'webpay.interswitchng.com'
212        url = '/paydirect/api/v1/gettransaction.json'
213        mac = '9718FA00B0F5070B388A9896ADCED9B2FB02D30F71E12E68BDADC63F6852A3496FF97D8A0F9DA9F753B911A49BB09BB87B55FD02046BD325C74C46C0123CF023'
214        https = True
215        product_id = '5845' # AAUE regular
216        payment = StudentOnlinePayment()
217        payment.p_id ='p4465649308559'
218        payment.amount_auth = 1.0
219        success, msg, log = query_interswitch(
[16115]220            payment, product_id, host, url, https, mac, False)
[13387]221        self.assertFalse(success)
222        self.assertTrue('Unsuccessful callback:' in msg)
223        self.assertTrue('Amount Inconsistency' in log)
224        payment.amount_auth = 60250.0
225        success, msg, log = query_interswitch(
[16115]226            payment, product_id, host, url, https, mac, False)
[13387]227        self.assertEqual('Successful callback received', msg)
228        self.assertTrue(success)
229        self.assertTrue(
230            "{u'SplitAccounts': [], "
231            "u'MerchantReference': u'p4465649308559', "
232            "u'PaymentReference': u'ZIB|WEB|ABAL|3-11-2015|021336', "
233            "u'TransactionDate': u'2015-11-03T16:40:54.487', "
234            "u'RetrievalReferenceNumber': u'000457580882', "
235            "u'ResponseDescription': u'Approved Successful', "
236            "u'Amount': 6025000, "
237            "u'CardNumber': u'3154', "
238            "u'ResponseCode': u'00', "
239            "u'LeadBankCbnCode': None, "
240            "u'LeadBankName': None}" in log)
241
[16484]242# PAYDirect tests
243
244    @external_test_2
[16487]245    def test_SOAP_paydirect(self):
246        payment = createObject('waeup.StudentOnlinePayment')
247        payment.p_category = u'schoolfee'
248        payment.p_session = self.student.current_session
249        payment.p_item = u'My Certificate'
250        payment.p_id = 'p' + str(random.randint(10000000000, 90000000000))
251        payment.amount_auth = 1000.0
[16587]252        item_code = '01'
[16487]253        self.student['payments'][payment.p_id] = payment
254        result_xml = create_paydirect_booking(
[16587]255            MERCHANT_ID, payment, item_code, PAYDIRECT_HOST, PAYDIRECT_URL, True)
[16484]256        result_xml = get_SOAP_response_paydirect(
[16487]257            MERCHANT_ID, payment.p_id, PAYDIRECT_HOST, PAYDIRECT_URL, True)
[16484]258        doc=parseString(result_xml)
259        status=doc.getElementsByTagName('PaymentStatus')[0].firstChild.data
260        amount=doc.getElementsByTagName('Amount')[0].firstChild
261        self.assertEqual(status, 'Pending')
262        self.assertEqual(amount, None)
263        p_id = 'p5812734587097'
264        result_xml = get_SOAP_response_paydirect(MERCHANT_ID, p_id, PAYDIRECT_HOST, PAYDIRECT_URL, True)
265        doc=parseString(result_xml)
266        self.assertEqual(doc.getElementsByTagName('PaymentStatus'), [])
267
268    @external_test_2
[16488]269    def test_paydirect_pending(self):
[16484]270        # Manager can access InterswitchForm
271        self.browser.getLink("Pay via Interswitch PAYDirect").click()
272        self.assertMatches('...Total Amount Authorized:...',
273                           self.browser.contents)
274        self.assertEqual(self.student.current_mode, 'ug_ft')
275        # Create school fee ticket for returning students. Payment is made
276        # for next session.
277        current_payment_key = self.student['payments'].keys()[0]
278        self.certificate.study_mode = u'ug_pt'
279        IWorkflowState(self.student).setState('returning')
280        configuration = createObject('waeup.SessionConfiguration')
281        configuration.academic_session = 2005
282        self.app['configuration'].addSessionConfiguration(configuration)
283        self.browser.open(self.payments_path + '/addop')
284        self.browser.getControl(name="form.p_category").value = ['schoolfee']
285        self.browser.getControl("Create ticket").click()
286        self.browser.open(self.payments_path)
287        ctrl = self.browser.getControl(name='val_id')
288        value = ctrl.options[1]
289        self.assertEqual(self.student['payments'][value].provider_amt, 0.0)
290        self.assertEqual(self.student['payments'][value].gateway_amt, 0.0)
291        self.browser.getLink(value).click()
292        self.browser.getLink("Pay via Interswitch PAYDirect").click()
293        # Split amounts have been set.
294        self.assertEqual(self.student['payments'][value].provider_amt, 0.0)
295        self.assertEqual(self.student['payments'][value].gateway_amt, 300.0)
296        ref_number = '%s%s' % (MERCHANT_ID,value[1:])
297        self.assertTrue(ref_number in self.browser.contents)
[16514]298        self.assertEqual(self.student['payments'][value].r_pay_reference, None)
[16484]299        self.browser.getControl("Requery").click()
300        self.assertTrue('pending' in self.browser.contents)
[16514]301        # Reference number has been saved
302        self.assertEqual(self.student['payments'][value].r_pay_reference, ref_number)
[16484]303        # Students can download reference number slip
304        self.browser.getLink("Pay via Interswitch PAYDirect").click()
305        self.browser.getLink("Download reference number slip").click()
306        self.assertEqual(self.browser.headers['Status'], '200 Ok')
307        self.assertEqual(self.browser.headers['Content-Type'], 'application/pdf')
308        path = os.path.join(samples_dir(), 'refnumberslip.pdf')
309        open(path, 'wb').write(self.browser.contents)
310        print "Sample PDF refnumberslip.pdf written to %s" % path
311
[16488]312    @external_test_2
313    def test_paydirect_completed(self):
314        payment = createObject('waeup.StudentOnlinePayment')
315        payment.p_category = u'schoolfee'
316        payment.p_session = self.student.current_session
317        payment.p_item = u'My Certificate'
[16528]318        # p6214352306153 has been manually set completed by Interswitch
[16488]319        payment.p_id = u'p6214352306153'
[16528]320        payment.amount_auth = 10.0 # 300 will be added
[16488]321        self.student['payments'][payment.p_id] = payment
322        result_xml = get_SOAP_response_paydirect(
323            MERCHANT_ID, payment.p_id, PAYDIRECT_HOST, PAYDIRECT_URL, True)
324        doc=parseString(result_xml)
325        status=doc.getElementsByTagName('PaymentStatus')[0].firstChild.data
326        refnumber=doc.getElementsByTagName('ReferenceNumber')[0].firstChild.data
327        amount=float(doc.getElementsByTagName('Amount')[0].firstChild.data)
328        self.assertEqual(status, 'Completed')
[16528]329        self.assertEqual(amount, 1000)
[16488]330        self.assertEqual(refnumber, '60336214352306153')
331        # Now we login and check what will happen with the student after payment
332        self.browser.open(self.payments_path)
333        self.browser.getLink(payment.p_id).click()
334        self.browser.getLink("Pay via Interswitch PAYDirect").click()
335        # 300 has been added
[16528]336        self.assertEqual(payment.amount_auth, 310)
337        self.assertEqual(payment.net_amt, 10)
[16587]338
339        # Unfortunately the payment 60336214352306153 was done without surcharge.
340        # Therefore we have to deduct 300 again in this test.
341        payment.amount_auth = 10.0
342
[16488]343        # Reference number has been saved
344        self.browser.getControl("Requery").click()
345        self.assertEqual(
346            self.student['payments'][payment.p_id].r_pay_reference, '60336214352306153')
347        # Payment is made
348        self.assertEqual(
349            self.student['payments'][payment.p_id].p_state, 'paid')
350        # Rubbish has been stored too
351        self.assertTrue(
352            self.student['payments'][payment.p_id].r_desc.startswith('Channel Name: Bank Branc'))
353        logfile = os.path.join(
354            self.app['datacenter'].storage, 'logs', 'students.log')
355        logcontent = open(logfile).read()
356        self.assertTrue(
357            'zope.mgr - kofacustom.nigeria.interswitch.paydirectbrowser.PAYDirectPageStudent '
358            '- K1000000 - valid callback for schoolfee payment %s: Completed' % payment.p_id in logcontent)
359        self.assertTrue(
360            'zope.mgr - K1000000 - First school fee payment made' in logcontent)
361        self.assertTrue(
362            'zope.mgr - kofacustom.nigeria.interswitch.paydirectbrowser.PAYDirectPageStudent '
363            '- K1000000 - successful schoolfee payment: %s' % payment.p_id in logcontent)
364
[11640]365class InterswitchTestsApplicants(ApplicantsFullSetup):
366    """Tests for the Interswitch payment gateway.
367    """
368
369    layer = FunctionalLayer
370
371    def setUp(self):
372        super(InterswitchTestsApplicants, self).setUp()
373        configuration = SessionConfiguration()
374        configuration.academic_session = datetime.now().year - 2
375        self.app['configuration'].addSessionConfiguration(configuration)
376        self.browser.addHeader('Authorization', 'Basic mgr:mgrpw')
377        self.browser.open(self.manage_path)
378        #IWorkflowState(self.student).setState('started')
379        super(InterswitchTestsApplicants, self).fill_correct_values()
380        self.applicantscontainer.application_fee = 1000.0
381        self.browser.getControl(name="form.nationality").value = ['NG']
382        self.browser.getControl(name="transition").value = ['start']
383        self.browser.getControl("Save").click()
384        self.browser.getControl("Add online").click()
385        self.assertMatches('...ticket created...',
386                           self.browser.contents)
[12973]387        self.payment = self.applicant.values()[0]
[11640]388        self.payment_url = self.browser.url
389
390    def test_interswitch_form(self):
391        self.assertMatches('...Amount Authorized...',
392                           self.browser.contents)
393        self.assertMatches(
394            '...<span>1000.0</span>...',
395            self.browser.contents)
396        # Manager can access InterswitchForm
[16484]397        self.browser.getLink("Pay via Interswitch CollegePAY", index=0).click()
[11640]398        self.assertMatches('...Total Amount Authorized:...',
399                           self.browser.contents)
400        self.assertMatches(
[11767]401            '...<input type="hidden" name="amount" value="100000" />...',
[11640]402            self.browser.contents)
[13015]403        delta = timedelta(days=8)
[12973]404        self.payment.creation_date -= delta
405        self.browser.open(self.payment_url)
[15702]406        self.browser.getLink("Pay via Interswitch", index=0).click()
[12973]407        self.assertMatches(
408            '...This payment ticket is too old. Please create a new ticket...',
409            self.browser.contents)
[13015]410        delta = timedelta(days=2)
[12973]411        self.payment.creation_date += delta
[15702]412        self.browser.getLink("Pay via Interswitch", index=0).click()
[12973]413        self.assertMatches('...Total Amount Authorized:...',
414                           self.browser.contents)
[17221]415
416# WebCheckout tests
417
418
419    # https://webpay.interswitchng.com/collections/api/v1/gettransaction.json?merchantcode=MX76823&transactionreference=p6709347986663&amount=100
420
[17231]421    mac = "uS6U18pC6GFKCpJoZH6J6jlOmR81FSrHkjBRpMaaydNQtywuG0hdB02J56MCqLV8rmzeAkhiaQR2nNcX3EPJePl5ppidImeKSzdunhddQh61UGpZPiS2CxMdAem8ueA1"
422
[17221]423    @external_test_3
424    def test_confirm_transaction(self):
425        host = 'webpay.interswitchng.com'
426        url = '/collections/api/v1/gettransaction.json'
427        https = True
428        merchant_code = 'MX76823'
429        payment = StudentOnlinePayment()
430        payment.p_id ='p4465649308559'
[17231]431        payment.amount_auth = 100000.0
[17221]432        success, msg, log = confirm_transaction(
[17231]433            payment, merchant_code, host, url, https, self.mac)
[17221]434        self.assertFalse(success)
435        self.assertTrue('Unsuccessful callback:' in msg)
436        self.assertTrue('Transaction not Found' in log)
437
438        payment.p_id ='p6709347986663'
439        payment.amount_auth = 1.0
440        success, msg, log = confirm_transaction(
[17231]441            payment, merchant_code, host, url, https, self.mac)
[17221]442        self.assertTrue('Amount Inconsistency' in log)
443        payment.amount_auth = 100.0
444        success, msg, log = confirm_transaction(
[17231]445            payment, merchant_code, host, url, https, self.mac)
[17221]446        self.assertEqual('Successful callback received', msg)
447        self.assertTrue(success)
448        self.assertTrue(
449            "{u'SplitAccounts': [], "
450            "u'RemittanceAmount': 0, "
451            "u'MerchantReference': u'p6709347986663', "
452            "u'PaymentReference': u'FBN|WEB|MX76823|13-12-2022|935097929|608001', "
453            "u'TransactionDate': u'2022-12-13T01:34:21', "
454            "u'RetrievalReferenceNumber': u'814212374638', "
455            "u'ResponseDescription': u'Approved by Financial Institution', "
456            "u'Amount': 10000, "
457            "u'CardNumber': u'', "
458            "u'ResponseCode': u'00', "
459            "u'BankCode': u'011'}" in log)
460
Note: See TracBrowser for help on using the repository browser.