## $Id: test_paypal.py 12741 2015-03-12 05:29:43Z uli $ ## ## Copyright (C) 2014 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 grok import decimal import os import re import shutil import tempfile import unittest import paypalrestsdk from zope.component import ( getGlobalSiteManager, queryUtility, ) from zope.component.hooks import setSite, clearSite from zope.i18nmessageid.message import Message as i18nMessage from zope.interface import implements from zope.interface.verify import verifyObject, verifyClass from zope.schema.interfaces import IVocabularyTokenized, ITerm from waeup.ikoba.interfaces import IPayPalConfig from waeup.ikoba.payments.interfaces import ( IPaymentGatewayService, IPayer, IPayee, IPaymentItem, ) from waeup.ikoba.payments.paypal import ( get_paypal_config_file_path, parse_paypal_config, get_access_token, configure_sdk, get_payment, Payer, PayerInfo, ShippingAddress, ADDRESS_TYPES_VOCAB, IShippingAddress, Address, IAddress, to_dict, CreditCard, CREDIT_CARD_TYPES_VOCAB, CREDIT_CARD_STATUS_VOCAB, ICreditCard, ICreditCardToken, CreditCardToken, FundingInstrument, AmountDetails, IAmount, Amount, IItem, Item, STOCK_KEEPING_UNITS_VOCAB, IItemList, ItemList, IPaymentOptions, PaymentOptions, ITransaction, Transaction, PAYMENT_OPTION_METHODS_VOCAB, PayPalCreditCardService, PayPalRegularPaymentService, ) from waeup.ikoba.testing import ( FunctionalLayer, FunctionalTestCase, ) # # PayPal test config # EXTERNAL_PAYPAL_TESTS = False # # End of PayPal test config # def external_paypal_test(func): """A decorator that can block test functions. """ if not EXTERNAL_PAYPAL_TESTS: myself = __file__ if myself.endswith('.pyc'): myself = myself[:-2] print "WARNING: external paypal tests are skipped!" print "WARNING: edit %s to enable them." % myself return return func class HelperTests(unittest.TestCase): def setUp(self): self.workdir = tempfile.mkdtemp() def tearDown(self): shutil.rmtree(self.workdir) # unregister any remaining utils registered during tests util = queryUtility(IPayPalConfig) if util is not None: getGlobalSiteManager().unregisterUtility(util, IPayPalConfig) def test_get_paypal_config_file_path(self): # we can get a config file path (if registered) path = os.path.join(self.workdir, 'sample.cfg') getGlobalSiteManager().registerUtility( {'path': path, }, IPayPalConfig, '') result = get_paypal_config_file_path() assert result == path def test_get_paypal_config_file_path_no_file(self): # without IPayPalConfig util set, we get `None` result = get_paypal_config_file_path() assert result is None def test_parse_paypal_config(self): # we can parse paypal configs path = os.path.join(self.workdir, 'sample.cfg') open(path, 'w').write( "[rest-client]\n" "id = myid\n" "secret = mysecret\n" "mode = live\n") result = parse_paypal_config(path) assert result['client_id'] == u'myid' assert result['client_secret'] == u'mysecret' assert result['mode'] == 'live' def test_parse_paypal_config_defaults(self): # we have fallback values (defaults) in place path = os.path.join(self.workdir, 'sample.cfg') open(path, 'w').write( "[rest-client]\n" ) result = parse_paypal_config(path) assert result['client_id'] is None assert result['client_secret'] is None assert result['mode'] == "sandbox" def test_configure_sdk(self): # we can configure the paypal sdk path = os.path.join(self.workdir, 'sample.cfg') open(path, 'w').write( "[rest-client]\n" "id = my-special-id\n" ) result = configure_sdk(path) assert result['client_id'] == 'my-special-id' assert result['client_secret'] is None assert result['mode'] == "sandbox" assert paypalrestsdk.api.__api__.mode == 'sandbox' assert paypalrestsdk.api.__api__.client_id == 'my-special-id' def test_get_payment_invalid_intent(self): # only 'sale' is currently allowed self.assertRaises( ValueError, get_payment, intent='invalid') self.assertRaises( ValueError, get_payment, intent='order') def test_to_dict(self): # we can turn objects into dicts class C1(object): a = 1 b = "somestring" c = u"unicodestring" d = None obj = C1() self.assertEqual( to_dict(obj), {'a': u"1", 'b': u"somestring", 'c': u"unicodestring", } ) def test_to_dict_map(self): # we can map attribute names class C1(object): a = 1 b = "somestring" obj = C1() self.assertEqual( to_dict(obj, name_map={'a': 'replaced_a'}), {'replaced_a': u"1", 'b': u"somestring", }) def test_to_dict_lists(self): # to_dict can handle lists class C1(object): a = 1 def to_dict(self): return to_dict(self) obj1 = C1() obj2 = C1() obj2.a = 2 obj3 = C1() obj3.a = 3 obj1.foo = [obj2, obj3] self.assertEqual( to_dict(obj1), { 'a': u'1', 'foo': [ {'a': u'2'}, {'a': u'3'}, ] } ) def test_to_dict_decimals(self): # decimals are converted to numbers with 2 decimals class C1(object): a = decimal.Decimal("0.1") self.assertEqual( to_dict(C1()), { 'a': u"0.10", } ) class PayerTests(unittest.TestCase): def test_create(self): # we can create payer objects payer = Payer(payment_method='paypal') assert payer.payment_method == 'paypal' assert payer.funding_instruments == [] assert payer.payer_info is None assert payer.status is None def test_init_invalid_payment_meth(self): # we must provide a valid payment method payer = Payer(payment_method='paypal') assert payer is not None payer = Payer(payment_method='credit_card') assert payer is not None self.assertRaises( ValueError, Payer, payment_method='invalid') def test_init_invalid_payer_state(self): # only certain values are allowed as payer states payer = Payer(payment_method='paypal', status='VERIFIED') assert payer is not None payer = Payer(payment_method='paypal', status='UNVERIFIED') assert payer is not None self.assertRaises( ValueError, Payer, payment_method='paypal', status='InVaLiD') class PayerInfoTests(unittest.TestCase): def test_create(self): # we can create payer infos info = PayerInfo() assert info.email == '' assert info.first_name == '' assert info.last_name == '' assert info.payer_id == '' assert info.phone == '' assert info.shipping_address is None assert info.tax_id_type == '' assert info.tax_id == '' def test_init_invalid_tax_id_type(self): # onyl certain tax id types are allowed info = PayerInfo(tax_id_type='BR_CPF') assert info is not None info = PayerInfo(tax_id_type='BR_CNPJ') assert info is not None self.assertRaises( ValueError, PayerInfo, tax_id_type='INVALID_TYPE') class CreditCardTests(unittest.TestCase): def test_iface(self): # we fullfill any interface contracts credit_card = CreditCard( number=u"12345678", credit_card_type="visa", expire_month=4, expire_year=2012, ) verifyClass(ICreditCard, CreditCard) verifyObject(ICreditCard, credit_card) def test_create(self): # we can create CreditCard objects credit_card = CreditCard( number=u"12345678", credit_card_type="visa", expire_month=4, expire_year=2012, ) assert credit_card.paypal_id is None assert credit_card.external_customer_id is not None assert credit_card.number == u"12345678" assert credit_card.credit_card_type == "visa" assert credit_card.expire_month == 4 assert credit_card.expire_year == 2012 assert credit_card.cvv2 is None assert credit_card.first_name is None assert credit_card.last_name is None assert credit_card.billing_address is None assert credit_card.state is None assert credit_card.paypal_valid_until is None def test_external_customer_id_given(self): # we do not override given curstomer ids credit_card = CreditCard( number=u"12345678", credit_card_type="visa", expire_month=4, expire_year=2012, external_customer_id=u'MySpecialPayerId', ) assert credit_card.external_customer_id == u'MySpecialPayerId' def test_external_customer_id_not_given(self): # in case no customer id is given, we generate one credit_card = CreditCard( number=u"12345678", credit_card_type="visa", expire_month=4, expire_year=2012, ) # our customer ids contain a leading 'PAYER_' and 32 hex digits assert re.match( 'PAYER_[0-9a-f]{32}$', credit_card.external_customer_id) def test_number_is_checked(self): # we do not accept invalid numbers self.assertRaises( ValueError, CreditCard, number=u"not-a-number", credit_card_type="visa", expire_month=4, expire_year=2012, ) def test_to_str(self): # we can turn CreditCard objects into dicts. addr = Address( line1=u"52 N Main ST", city=u"Johnstown", state=u"OH", postal_code=u"43210", country_code=u"US") credit_card = CreditCard( credit_card_type=u"visa", external_customer_id=u"PAYER_0123456789012345678901", number=u"4417119669820331", expire_month=11, expire_year=2018, cvv2=u"874", first_name=u"Joe", last_name=u"Shopper", billing_address=addr) self.assertEqual( credit_card.to_dict(), { "type": u"visa", "number": u"4417119669820331", "external_customer_id": u"PAYER_0123456789012345678901", "expire_month": u"11", "expire_year": u"2018", "cvv2": u"874", "first_name": u"Joe", "last_name": u"Shopper", "billing_address": { "line1": u"52 N Main ST", "city": u"Johnstown", "state": u"OH", "postal_code": u"43210", "country_code": u"US"} } ) class CreditCardTokenTests(unittest.TestCase): def test_iface(self): # we fullfill any interface contracts token = CreditCardToken( credit_card_id=u"12345678", ) verifyClass(ICreditCardToken, CreditCardToken) verifyObject(ICreditCardToken, token) def test_create(self): # we can create CreditCardToken objects token = CreditCardToken( credit_card_id=u"12345678", ) assert token.credit_card_id == u"12345678" assert token.external_customer_id is None assert token.credit_card_type is None assert token.expire_month is None assert token.expire_year is None assert token.last4 is None def test_customer_id_given(self): # we do not override given customer ids token = CreditCardToken( credit_card_id=u"12345678", external_customer_id=u'MySpecialPayerId', ) assert token.external_customer_id == u'MySpecialPayerId' def test_to_str(self): # we can turn CreditCardToken objects into dicts. token = CreditCardToken( credit_card_type=u"visa", external_customer_id=u"PAYER_0123456789012345678901", credit_card_id=u"12345678", last4="8901", expire_month=11, expire_year=2018, ) self.assertEqual( token.to_dict(), { "credit_card_id": u"12345678", "external_customer_id": u"PAYER_0123456789012345678901", "last4": u"8901", "type": u"visa", "expire_month": u"11", "expire_year": u"2018", } ) class FundingInstrumentTests(unittest.TestCase): def test_create(self): # we can create FundingInstrument objects token = CreditCardToken( credit_card_id=u"12345678", ) instr = FundingInstrument(credit_card_token=token) assert instr.credit_card_token is token def test_require_credit_card_or_token(self): # we require a credit card object or a token. credit_card = CreditCard( number=u"12345678", credit_card_type="visa", expire_month=4, expire_year=2012, ) token = CreditCardToken( credit_card_id=u"12345678", ) self.assertRaises( ValueError, FundingInstrument, credit_card=credit_card, credit_card_token=token ) self.assertRaises( ValueError, FundingInstrument ) def test_to_dict(self): # we can turn Funding instruments into dicts token = CreditCardToken( credit_card_type=u"visa", external_customer_id=u"PAYER_0123456789012345678901", credit_card_id=u"12345678", last4="8901", expire_month=11, expire_year=2018, ) instr = FundingInstrument(credit_card_token=token) result = instr.to_dict() self.assertEqual( result, { "credit_card_token": { "credit_card_id": u"12345678", "external_customer_id": u"PAYER_0123456789012345678901", "last4": u"8901", "type": u"visa", "expire_month": u"11", "expire_year": u"2018", } } ) class AddressTests(unittest.TestCase): def test_iface(self): # we fullfill any interface contracts addr = Address( line1=u'Address Line 1', city=u'Somecity', country_code=u'AT', ) verifyClass(IAddress, Address) verifyObject(IAddress, addr) def test_create(self): # we can create addresses addr = Address( line1=u'Honey Street 1', city=u'Beartown', country_code=u'GB', ) assert addr.line1 == u'Honey Street 1' assert addr.line2 is None assert addr.city == u'Beartown' assert addr.country_code == u'GB' assert addr.postal_code is None assert addr.state is None assert addr.phone is None def test_to_dict(self): # we can turn addresses into dicts addr = Address( line1=u'Honey Street 1', city=u'Beartown', country_code=u'GB', ) self.assertEqual( addr.to_dict(), { 'line1': u'Honey Street 1', 'city': u'Beartown', 'country_code': u'GB', } ) addr.line2 = u"Behind little tree" self.assertEqual( addr.to_dict(), { 'line1': u'Honey Street 1', 'line2': u'Behind little tree', 'city': u'Beartown', 'country_code': u'GB', } ) class ShippingAddressTests(unittest.TestCase): def test_iface(self): # we fullfill any interface contracts addr = ShippingAddress( recipient_name=u'Foo Bar', line1=u'Address Line 1', city=u'Somecity', country_code=u'AT', ) verifyClass(IShippingAddress, ShippingAddress) verifyObject(IShippingAddress, addr) def test_create(self): # we can create shipping addresses addr = ShippingAddress( recipient_name=u'Rob Receiver', line1=u'Honey Street 1', city=u'Beartown', country_code=u'GB', ) assert addr.recipient_name == u'Rob Receiver' assert addr.type == u'residential' assert addr.line1 == u'Honey Street 1' assert addr.line2 is None assert addr.city == u'Beartown' assert addr.country_code == u'GB' assert addr.postal_code is None assert addr.state is None assert addr.phone is None def test_to_dict(self): # we can turn shipping addresses into dicts addr = ShippingAddress( recipient_name=u'Rob Receiver', line1=u'Honey Street 1', city=u'Beartown', country_code=u'GB', ) self.assertEqual( addr.to_dict(), { 'recipient_name': u'Rob Receiver', 'type': u'residential', 'line1': u'Honey Street 1', 'city': u'Beartown', 'country_code': u'GB', } ) addr.line2 = u"Behind little tree" self.assertEqual( addr.to_dict(), { 'recipient_name': u'Rob Receiver', 'type': u'residential', 'line1': u'Honey Street 1', 'line2': u'Behind little tree', 'city': u'Beartown', 'country_code': u'GB', } ) class AmountDetailsTests(unittest.TestCase): def test_create(self): # we can create AmountDetail objects details = AmountDetails() assert details.shipping is None assert details.subtotal is None assert details.tax is None assert details.fee is None assert details.handling_fee is None assert details.insurance is None assert details.shipping_discount is None def test_to_dict(self): # we can turn AmountDetails into a dict details = AmountDetails( shipping=decimal.Decimal("0.10"), tax=decimal.Decimal("0.30"), fee=decimal.Decimal("0.40"), handling_fee=decimal.Decimal("0.50"), insurance=decimal.Decimal("0.60"), shipping_discount=decimal.Decimal("0.70") ) self.assertEqual( details.to_dict(), { 'shipping': u"0.10", 'subtotal': u"1.20", 'tax': u"0.30", 'fee': u"0.40", 'handling_fee': u"0.50", 'insurance': u"0.60", 'shipping_discount': u"0.70" } ) def test_subtotal_all_none(self): # if all items are none, also subtotal is none details = AmountDetails( shipping=None, tax=None, fee=None, handling_fee=None, insurance=None, shipping_discount=None, ) assert details.subtotal is None details.shipping_discount = decimal.Decimal("1.00") assert details.subtotal == decimal.Decimal("-1.00") def test_subtotal_sum(self): # subtotal sums up correctly details = AmountDetails( shipping=decimal.Decimal("0.05"), tax=decimal.Decimal("0.40"), fee=decimal.Decimal("3.00"), handling_fee=decimal.Decimal("20.00"), insurance=decimal.Decimal("100.00"), shipping_discount=None ) self.assertEqual(details.subtotal, decimal.Decimal("123.45")) details.shipping_discount = decimal.Decimal("0.00") self.assertEqual(details.subtotal, decimal.Decimal("123.45")) details.shipping_discount = decimal.Decimal("23.45") self.assertEqual(details.subtotal, decimal.Decimal("100.00")) class AmountTests(unittest.TestCase): def test_iface(self): # we fullfill any interface contracts. amount = Amount() verifyClass(IAmount, Amount) verifyObject(IAmount, amount) def test_create(self): # we can create amount objects details = AmountDetails( shipping=decimal.Decimal("0.05"), tax=decimal.Decimal("0.40"), fee=decimal.Decimal("3.00"), handling_fee=decimal.Decimal("20.00"), insurance=decimal.Decimal("100.00"), shipping_discount=None ) amount = Amount( total=decimal.Decimal("12.12"), currency="USD", details=details ) assert amount.total == decimal.Decimal("12.12") assert amount.currency == "USD" assert amount.details is details def test_to_dict(self): # we can turn Amount objects into dicts self.maxDiff = None details = AmountDetails( shipping=decimal.Decimal("0.05"), tax=decimal.Decimal("0.40"), fee=decimal.Decimal("3.00"), handling_fee=decimal.Decimal("20.00"), insurance=decimal.Decimal("100.00"), shipping_discount=None ) amount = Amount( total=decimal.Decimal("12.12"), currency="USD", details=details ) self.assertEqual( amount.to_dict(), { 'total': u'12.12', 'currency': u'USD', 'details': { 'shipping': u'0.05', 'subtotal': u'123.45', 'tax': u'0.40', 'fee': u'3.00', 'handling_fee': u'20.00', 'insurance': u'100.00', } } ) class ItemTests(unittest.TestCase): def test_iface(self): # we fullfill all interface contracts item = Item(name=u"Splendid Item") verifyClass(IItem, Item) verifyObject(IItem, item) def test_create(self): # we can create Item objects item = Item( quantity=3, name=u"Splendid Thing", price=decimal.Decimal("1.1"), currency="USD", sku="pcs", description=u"Soo splendid!", tax=decimal.Decimal("0.1"), ) assert item.quantity == 3 assert item.name == u"Splendid Thing" assert item.price == decimal.Decimal("1.1") assert item.currency == "USD" assert item.sku == "pcs" assert item.description == u"Soo splendid!" assert item.tax == decimal.Decimal("0.1") def test_to_dict(self): # we can turn Item objects into dicts item = Item( quantity=3, name=u"Splendid Thing", price=decimal.Decimal("1.1"), currency="USD", sku="pcs", description=u"Soo splendid!", tax=decimal.Decimal("0.1"), ) self.assertEqual( item.to_dict(), { "quantity": u"3", "name": u"Splendid Thing", "price": u"1.10", "currency": u"USD", "sku": u"pcs", "description": u"Soo splendid!", "tax": u"0.10" } ) class ItemListTests(unittest.TestCase): def test_iface(self): # we fullfill all interface contracts item_list = ItemList() verifyClass(IItemList, ItemList) verifyObject(IItemList, item_list) def test_create_minimal(self): # we can create ItemLists with a minimum of params item_list = ItemList() assert item_list.shipping_address is None assert item_list.items == [] def test_create(self): # we can create ItemLists item1 = Item( name=u"Splendid Thing", ) item2 = Item( name=u"Other Splendid Thing", ) addr = ShippingAddress( recipient_name=u'Rob Receiver', line1=u'Honey Street 1', city=u'Beartown', country_code=u'GB', ) item_list = ItemList( shipping_address=addr, items=[item1, item2]) assert item_list.shipping_address is addr assert item_list.items == [item1, item2] def test_to_dict(self): # we can turn ITemLists into dicts item = Item( quantity=3, name=u"Splendid Thing", price=decimal.Decimal("1.1"), currency="USD", sku="pcs", description=u"Soo splendid!", tax=decimal.Decimal("0.1"), ) addr = ShippingAddress( recipient_name=u'Rob Receiver', line1=u'Honey Street 1', city=u'Beartown', country_code=u'GB', ) item_list = ItemList(items=[item, ], shipping_address=addr) self.assertEqual( item_list.to_dict(), { "items": [ { "quantity": u"3", "name": u"Splendid Thing", "price": u"1.10", "currency": u"USD", "sku": u"pcs", "description": u"Soo splendid!", "tax": u"0.10" } ], "shipping_address": { 'recipient_name': u'Rob Receiver', 'type': u'residential', 'line1': u'Honey Street 1', 'city': u'Beartown', 'country_code': u'GB', } } ) class PaymentOptionsTests(unittest.TestCase): def test_iface(self): # we fullfill all interface contracts opts = PaymentOptions() verifyClass(IPaymentOptions, PaymentOptions) verifyObject(IPaymentOptions, opts) def test_create(self): # we can create PaymentOptions objects opts = PaymentOptions() assert opts.allowed_payment_method is None def test_allowed_payment_method_checked_in_init(self): # any value apart from None, INSTANT... is rejected in __init__ self.assertRaises( ValueError, PaymentOptions, allowed_payment_method='NoTvAlID') def test_to_dict(self): # we can turn PaymentOptions into dicts opts = PaymentOptions( allowed_payment_method="INSTANT_FUNDING_SOURCE") self.assertEqual( opts.to_dict(), { 'allowed_payment_method': "INSTANT_FUNDING_SOURCE", } ) class TransactionTests(unittest.TestCase): def test_iface(self): # we fullfill all interface contracts amount = Amount() transaction = Transaction(amount=amount) verifyClass(ITransaction, Transaction) verifyObject(ITransaction, transaction) def test_create(self): # we can create transacions amount = Amount() transaction = Transaction(amount=amount) assert transaction.amount is amount assert transaction.description is None assert transaction.item_list is None assert transaction.related_resources == [] assert transaction.invoice_number is None assert transaction.custom is None assert transaction.soft_descriptor is None assert transaction.payment_options is None def test_to_dict(self): # we can turn Transaction objects into dicts transaction = Transaction( amount=Amount(), description=u"My description", item_list=ItemList(), related_resources=[], invoice_number=u"12345", custom=u"Some custom remark", soft_descriptor=u"softdescriptor?", payment_options=PaymentOptions( allowed_payment_method="INSTANT_FUNDING_SOURCE"), ) self.assertEqual( transaction.to_dict(), { 'amount': {'currency': u'USD', 'total': u'0.00'}, 'custom': u'Some custom remark', 'description': u'My description', 'invoice_number': u'12345', 'item_list': { 'items': [] }, 'payment_options': { 'allowed_payment_method': u'INSTANT_FUNDING_SOURCE' }, 'related_resources': [], 'soft_descriptor': u'softdescriptor?' } ) class AddressTypesVocabTests(unittest.TestCase): def test_address_types_vocab_tokenized(self): # we can get a countries source suitable for forms etc. verifyObject(IVocabularyTokenized, ADDRESS_TYPES_VOCAB) def test_address_types_vocab_i18nized(self): # vocab titles are i18nized result = ADDRESS_TYPES_VOCAB.getTerm('residential') assert ITerm.providedBy(result) self.assertEqual(result.title, u'residential') assert isinstance(result.title, i18nMessage) def test_address_types_vocab_tokens_are_string(self): # vocab tokens are simple strings result = ADDRESS_TYPES_VOCAB.getTerm('residential') assert ITerm.providedBy(result) assert result.token == result.value assert result.value == 'residential' assert isinstance(result.token, str) assert isinstance(result.value, str) class CreditCardTypesVocabTests(unittest.TestCase): def test_credit_card_types_vocab_tokenized(self): # we can get a countries source suitable for forms etc. verifyObject(IVocabularyTokenized, CREDIT_CARD_TYPES_VOCAB) def test_credit_cards_types_vocab_i18nized(self): # vocab titles are i18nized result = CREDIT_CARD_TYPES_VOCAB.getTerm('visa') assert ITerm.providedBy(result) self.assertEqual(result.title, u'visa') assert isinstance(result.title, i18nMessage) def test_credit_cards_types_vocab_tokens_are_string(self): # vocab tokens are simple strings result = CREDIT_CARD_TYPES_VOCAB.getTerm('visa') assert ITerm.providedBy(result) assert result.token == result.value assert result.value == 'visa' assert isinstance(result.token, str) assert isinstance(result.value, str) class CreditcardstatusVocabTests(unittest.TestCase): def test_credit_card_status_vocab_tokenized(self): # we can get a countries source suitable for forms etc. verifyObject(IVocabularyTokenized, CREDIT_CARD_STATUS_VOCAB) def test_credit_cards_status_vocab_i18nized(self): # vocab titles are i18nized result = CREDIT_CARD_STATUS_VOCAB.getTerm('ok') assert ITerm.providedBy(result) self.assertEqual(result.title, u'ok') assert isinstance(result.title, i18nMessage) def test_credit_cards_status_vocab_tokens_are_string(self): # vocab tokens are simple strings result = CREDIT_CARD_STATUS_VOCAB.getTerm('expired') assert ITerm.providedBy(result) assert result.token == result.value assert result.value == 'expired' assert isinstance(result.token, str) assert isinstance(result.value, str) class StockKeepingUnitsVocabTests(unittest.TestCase): def test_sku_vocab_tokenized(self): # we can get a tokenzed vocab for stock keeping units verifyObject(IVocabularyTokenized, STOCK_KEEPING_UNITS_VOCAB) def test_sku_vocab_i18nized(self): # vocab titles are i18nized result = STOCK_KEEPING_UNITS_VOCAB.getTerm('pcs') assert ITerm.providedBy(result) self.assertEqual(result.title, u'pieces') assert isinstance(result.title, i18nMessage) def test_sku_vocab_tokens_are_string(self): # vocab tokens are simple strings result = STOCK_KEEPING_UNITS_VOCAB.getTerm('pcs') assert ITerm.providedBy(result) assert result.token == result.value assert result.value == 'pcs' assert isinstance(result.token, str) assert isinstance(result.value, str) class PaymentOptionMethodsVocabTests(unittest.TestCase): def test_payment_option_methods_vocab_tokenized(self): # we can get a countries source suitable for forms etc. verifyObject(IVocabularyTokenized, PAYMENT_OPTION_METHODS_VOCAB) def test_payment_option_methods_vocab_i18nized(self): # vocab titles are i18nized result = PAYMENT_OPTION_METHODS_VOCAB.getTerm('INSTANT_FUNDING_SOURCE') assert ITerm.providedBy(result) self.assertEqual(result.title, u'INSTANT_FUNDING_SOURCE') assert isinstance(result.title, i18nMessage) def test_payment_option_methods_vocab_tokens_are_string(self): # vocab tokens are simple strings result = PAYMENT_OPTION_METHODS_VOCAB.getTerm('INSTANT_FUNDING_SOURCE') assert ITerm.providedBy(result) assert result.token == result.value assert result.value == 'INSTANT_FUNDING_SOURCE' assert isinstance(result.token, str) assert isinstance(result.value, str) class FakePayer(object): implements(IPayer) payer_id = 'PAYER-123' first_name = u'Joe' last_name = u'Shopper' class FakePayee(object): implements(IPayee) payee_id = 'PAYEE-456' class FakePaymentItem(object): implements(IPaymentItem) title = u'Fake Item' item_id = 'BILL-123456' amount = decimal.Decimal("12.10") currency = 'EUR' # Make sure all fake classes (and objects made of them) are up-to-date verifyClass(IPayer, FakePayer) verifyObject(IPayer, FakePayer()) verifyClass(IPayee, FakePayee) verifyObject(IPayee, FakePayee()) verifyClass(IPaymentItem, FakePaymentItem) verifyObject(IPaymentItem, FakePaymentItem()) class PayPalCreditCardServiceTests(FunctionalTestCase): layer = FunctionalLayer def tearDown(self): super(PayPalCreditCardServiceTests, self).tearDown() clearSite() def create_site(self): # create a simple site in root. class MyApp(grok.Application, grok.Container): pass site = MyApp() self.getRootFolder()['app'] = site setSite(site) return site def get_credit_card(self): # get a valid credit card credit_card = CreditCard( credit_card_type=u"visa", external_customer_id=u"PAYER_0123456789012345678901", number=u"4417119669820331", expire_month=11, expire_year=2018, cvv2=u"874", first_name=u"Joe", last_name=u"Shopper", ) return credit_card def test_iface(self): # we fullfill all interface contracts service = PayPalCreditCardService() verifyClass(IPaymentGatewayService, PayPalCreditCardService) verifyObject(IPaymentGatewayService, service) def test_get_credit_card_no_site(self): # we get simply None if no site is registered service = PayPalCreditCardService() assert service.get_credit_card('not-a-valid-payer-id') is None def test_get_credit_card_site_not_a_container(self): # we get simply None if site is not a container site = grok.Application() # does not provide IContainer self.getRootFolder()['app'] = site setSite(site) service = PayPalCreditCardService() assert service.get_credit_card('not-a-valid-payer-id') is None def test_get_credit_card_no_container(self): # we get simply None if no 'creditcards' container is in site self.create_site() service = PayPalCreditCardService() assert service.get_credit_card('not-a-valid-payer-id') is None def test_get_credit_card(self): # we can get a credit card, if one was stored class MyCard(object): myid = 'CARD1' def __eq__(self, obj): return self.myid == obj.myid site = self.create_site() site['creditcards'] = grok.Container() card = MyCard() card.myid = 'CHANGED CARD ID' site['creditcards'][u'CARD1'] = card service = PayPalCreditCardService() assert service.get_credit_card(u'CARD1') == card assert service.get_credit_card(u'CARD2') is None @external_paypal_test def test_store_credit_card(self): # we can (and must) store credit card data online. site = self.create_site() assert 'creditcards' not in site service = PayPalCreditCardService() credit_card = self.get_credit_card() result = service.store_credit_card(credit_card) # a missing creditcards container is created on-the-fly assert 'creditcards' in site assert ICreditCardToken.providedBy(result) assert result.external_customer_id in site['creditcards'] assert site['creditcards'][result.external_customer_id] == result @external_paypal_test def test_store_credit_card_invalid(self): # an exception is raised with invalid credit cards. self.create_site() service = PayPalCreditCardService() credit_card = CreditCard( number=u"12345678", credit_card_type="visa", expire_month=4, expire_year=2012, ) self.assertRaises( IOError, service.store_credit_card, credit_card) def test_create_payment_no_credictard(self): # trying to create a payment without credit card raises an exception service = PayPalCreditCardService() exception = None try: service.create_payment( payer=FakePayer(), payment_item=FakePaymentItem(), payee=FakePayee() ) except (ValueError, ) as err: exception = err assert exception.message == 'Payer PAYER-123 has no credit card.' @external_paypal_test def test_create_payment(self): # we can actually create payments service = PayPalCreditCardService() self.create_site() credit_card = self.get_credit_card() result = service.store_credit_card(credit_card) payer = FakePayer() payer.payer_id = result.external_customer_id payment_item = FakePaymentItem() payment = service.create_payment( payer=payer, payment_item=payment_item) result = payment.create() assert result is True class FunctionalPaypalTests(FunctionalTestCase): layer = FunctionalLayer @external_paypal_test def test_get_access_token(self): # we can get an access token result = get_access_token() assert isinstance(result, unicode) @external_paypal_test def test_get_payment(self): # we can construct (paypal-)payment objects payment = get_payment() assert isinstance(payment, paypalrestsdk.Payment) result = payment.create() assert result is True def test_paypal_services_registered(self): # the PayPal gateway services are all registered creditcard_service = queryUtility( IPaymentGatewayService, name=u'paypal_creditcard') assert creditcard_service is not None assert isinstance(creditcard_service, PayPalCreditCardService) paypal_regular_service = queryUtility( IPaymentGatewayService, name=u'paypal_regular') assert paypal_regular_service is not None assert isinstance(paypal_regular_service, PayPalRegularPaymentService)