## $Id$ ## ## Copyright (C) 2022 Uli Fouquet & Henrik Bettermann ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 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 General Public License for more details. ## ## You should have received a copy of the GNU General Public License ## along with this program; if not, write to the Free Software ## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ## import datetime import mock import os import unittest from zope.component import queryUtility, createObject from waeup.kofa.applicants.tests.test_browser import ApplicantsFullSetup from waeup.kofa.browser.tests.test_browser import UniversitySetup from waeup.kofa.configuration import SessionConfiguration from kofacustom.nigeria.paypal.config import VALID_PAYPAL_EMAILS from kofacustom.nigeria.paypal.interfaces import IPaypalConfig from kofacustom.nigeria.paypal.ipn_listener import VERIFY_URL_TEST, verify_notification from kofacustom.nigeria.paypal import Amount, PurchaseUnit, module_activated from kofacustom.nigeria.paypal.rest import ( get_auth_token, create_order, capture_order) from kofacustom.nigeria.testing import FunctionalLayer RUN_EXTERNAL_TESTS = False class MockResponse(object): def __init__(self, *args, **kw): self.text = "VERIFIED" self.json_data = None def raise_for_status(self): pass def json(self): return self.json_data class PaypalIPNListenerTests(unittest.TestCase): def setUp(self): pass def tearDown(self): pass @mock.patch("requests.post", side_effect=MockResponse) def test_verify_transaction(self, post): self.assertEqual(verify_notification("foo=bar&bar=baz"), False) self.assertTrue(post.called) valid_email = VALID_PAYPAL_EMAILS[0] self.assertEqual( verify_notification("foo=bar&receiver_email=%s" % valid_email), True ) class PaypalConfigTests(unittest.TestCase): layer = FunctionalLayer def test_paypal_config_util_available(self): config = queryUtility(IPaypalConfig) self.assertTrue(config is not None) def mocked_requests_post(*args, **kw): response = MockResponse() response.status_code = 401 if args[0].endswith("v1/oauth2/token"): response.status_code = 200 response.json_data = { "scope": "https://uri.paypal.com/services/invoicing https://uri.paypal.com/services/vault/payment-tokens/read https://uri.paypal.com/services/disputes/read-buyer https://uri.paypal.com/services/payments/realtimepayment https://uri.paypal.com/services/disputes/update-seller https://uri.paypal.com/services/payments/payment/authcapture openid https://uri.paypal.com/services/disputes/read-seller Braintree:Vault https://uri.paypal.com/services/payments/refund https://api.paypal.com/v1/vault/credit-card https://uri.paypal.com/services/billing-agreements https://api.paypal.com/v1/payments/.* https://uri.paypal.com/payments/payouts https://uri.paypal.com/services/vault/payment-tokens/readwrite https://api.paypal.com/v1/vault/credit-card/.* https://uri.paypal.com/services/shipping/trackers/readwrite https://uri.paypal.com/services/subscriptions https://uri.paypal.com/services/applications/webhooks", "access_token": "A21AAL21A1ohAg6u75tLLi3LSshFmONDD8JC_AOd0Bkmc2cloeeSZIofamBbFXEYHvq7C5ViKJeSDq9PMfoV6shmfRhVyvDIg", "token_type": "Bearer", "app_id": "APP-80W284485P519543T", "expires_in": 32400, "nonce": "2022-12-14T08:05:21ZF5NvAXAkc46HOplYDYqHnBBPJDZJbwd4e3zDgaI5Vo0", } if args[0].endswith("/v2/checkout/orders"): response.status_code = 200 response.json_data = { "id": "2LP804900J8837429", "status": "PAYER_ACTION_REQUIRED", "payment_source": {"paypal": {}}, "links": [ { "href": "https://api.sandbox.paypal.com/v2/checkout/orders/2LP804900J8837429", "rel": "self", "method": "GET", }, { "href": "https://www.sandbox.paypal.com/checkoutnow?token=2LP804900J8837429", "rel": "payer-action", "method": "GET", }, ], } if args[0].endswith("/capture"): response.status_code = 201 response.json_data = { "id":"2LP804900J8837429", "status":"COMPLETED", "payment_source":{ "paypal":{ "email_address":"sb-90spn23140065@personal.example.com", "account_id":"3LP3QX8H9YC3G", "name":{ "given_name": "John", "surname":"Doe" }, "address":{"country_code":"DE"}}}, "purchase_units":[ { "reference_id":"0", "payments":{ "captures":[ { "id":"3JF41209TB864721F", "status":"COMPLETED", "amount":{ "currency_code":"USD", "value":"3.23"}, "final_capture":True, "seller_protection":{ "status":"ELIGIBLE", "dispute_categories":[ "ITEM_NOT_RECEIVED", "UNAUTHORIZED_TRANSACTION"] }, "seller_receivable_breakdown":{ "gross_amount":{ "currency_code":"USD", "value":"3.23"}, "paypal_fee":{ "currency_code":"USD", "value":"0.36"}, "net_amount":{ "currency_code":"USD", "value":"2.87"} }, "custom_id":"my-custom-id", "links":[ { "href":"https://api.sandbox.paypal.com/v2/payments/captures/3JF41209TB864721F", "rel":"self","method":"GET" }, { "href":"https://api.sandbox.paypal.com/v2/payments/captures/3JF41209TB864721F/refund", "rel":"refund","method":"POST" }, { "href":"https://api.sandbox.paypal.com/v2/checkout/orders/2LP804900J8837429", "rel":"up", "method":"GET" } ], "create_time":"2022-12-17T16:34:12Z", "update_time":"2022-12-17T16:34:12Z" } ] } } ], "payer":{ "name":{ "given_name":"John", "surname":"Doe" }, "email_address":"sb-90spn23140065@personal.example.com", "payer_id":"3LP3QX8H9YC3G","address":{ "country_code":"DE" } }, "links":[ { "href":"https://api.sandbox.paypal.com/v2/checkout/orders/2LP804900J8837429", "rel":"self", "method":"GET" } ] } return response sample_units = [PurchaseUnit(description="Test Good", amount=Amount(value="3.23"))] class PaypalRESTClientTests(unittest.TestCase): layer = FunctionalLayer # we need the IPaypalConfig data @unittest.skipUnless(RUN_EXTERNAL_TESTS, "Live paypal tests skipped") def test_get_auth_token(self): result = get_auth_token() self.assertTrue(isinstance(result, basestring)) @unittest.skipUnless(RUN_EXTERNAL_TESTS, "Live paypal tests skipped") def test_create_order_live(self): token, url, request_id, data = create_order( sample_units, "http://fake-return-url", "http://fake-cancel-url" ) self.assertTrue(isinstance(token, basestring)) self.assertTrue("paypal.com/checkoutnow?token=" in url) self.order_id = token @mock.patch("requests.post", side_effect=mocked_requests_post) def test_create_order(self, mock_post): token, url, request_id, data = create_order( sample_units, "http://fake-return-url", "http://fake-cancel-url" ) self.assertEqual(len(mock_post.call_args_list), 2) self.assertEqual(token, "2LP804900J8837429") @mock.patch("requests.post", side_effect=mocked_requests_post) def test_capture_order(self, mock_post): result = capture_order("2LP804900J8837429") self.assertEqual(len(mock_post.call_args_list), 2) self.assertEqual(result["status"], "COMPLETED") class PaypalBaseObjectsTests(unittest.TestCase): def test_amount(self): # we can create Amount objects amt = Amount() self.assertEqual(amt.value, "0.00") self.assertEqual(amt.currency_code, "USD") def test_amount_json(self): # we can get a JSON representation of amounts amt = Amount() self.assertEqual(amt.json(), {"value": "0.00", "currency_code": "USD"}) def test_purchase_unit(self): # we can create PurchaseItem instances item = PurchaseUnit() self.assertTrue(isinstance(item.amount, Amount)) self.assertEqual(item.custom_id, None) self.assertEqual(item.description, "") def test_purchase_unit_json(self): # we can get PurchaseItem instances as JSON item = PurchaseUnit() self.assertEqual( item.json(), { "amount": {"value": "0.00", "currency_code": "USD"}, "description": "", }, ) class PaypalHelperTests(UniversitySetup): layer = FunctionalLayer # we need the IPaypalConfig data def test_module_activated(self): session_conf1 = SessionConfiguration() session_conf2 = SessionConfiguration() curr_year = datetime.datetime.now().year session_conf1.academic_session = curr_year - 2 session_conf2.academic_session = curr_year session_conf1.paypal_enabled = True session_conf2.paypal_enabled = True # session_conf2 yet not active... self.app['configuration'].addSessionConfiguration(session_conf1) self.assertFalse(module_activated(2000, None)) self.assertFalse(module_activated(curr_year, None)) self.assertTrue(module_activated(curr_year - 2, None)) payment = createObject('waeup.StudentOnlinePayment') payment.r_company = 'unknown' payment.p_currency = 'USD' self.assertFalse(module_activated(curr_year - 2, payment)) payment.r_company = 'paypal' self.assertTrue(module_activated(curr_year - 2, payment)) self.assertFalse(module_activated(curr_year, payment)) # activate also session_conf2 self.app['configuration'].addSessionConfiguration(session_conf2) self.assertTrue(module_activated(curr_year - 1, payment)) self.assertTrue(module_activated(curr_year, payment)) class PaypalTestsApplicants(ApplicantsFullSetup): """Tests for the Paypal payment gateway. """ layer = FunctionalLayer def setUp(self): super(PaypalTestsApplicants, self).setUp() configuration = SessionConfiguration() configuration.academic_session = datetime.datetime.now().year - 2 configuration.paypal_enabled = True self.app['configuration'].addSessionConfiguration(configuration) self.browser.addHeader('Authorization', 'Basic mgr:mgrpw') self.browser.open(self.manage_path) #IWorkflowState(self.student).setState('started') super(PaypalTestsApplicants, self).fill_correct_values() self.applicantscontainer.application_fee = 10.0 self.browser.getControl(name="form.nationality").value = ['NG'] self.browser.getControl(name="transition").value = ['start'] self.browser.getControl("Save").click() self.browser.getControl("Add online").click() self.assertMatches('...ticket created...', self.browser.contents) self.payment = self.applicant.values()[0] self.payment_url = self.browser.url #@unittest.skipUnless(RUN_EXTERNAL_TESTS, "Live paypal tests skipped") #def test_applicant_paypal_form(self): # import pdb; pdb.set_trace() # self.browser.getLink("Pay via Paypal").click() # logfile = os.path.join( # self.app['datacenter'].storage, 'logs', 'applicants.log') # logcontent = open(logfile).read() # self.assertTrue(self.payment.r_pay_reference in logcontent)