You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
324 lines
10 KiB
Python
324 lines
10 KiB
Python
from datetime import timedelta
|
|
from urllib.parse import parse_qs
|
|
|
|
from django.test import TestCase, RequestFactory
|
|
from django.http import HttpResponseRedirect
|
|
from django.contrib.auth.models import User
|
|
|
|
from .models import Payment
|
|
from .backends import BitcoinBackend, PaypalBackend, StripeBackend
|
|
|
|
from decimal import Decimal
|
|
|
|
|
|
class FakeBTCRPCNew:
|
|
def getnewaddress(self, account):
|
|
return 'TEST_ADDRESS'
|
|
|
|
|
|
class FakeBTCRPCUnpaid:
|
|
def getreceivedbyaddress(self, address):
|
|
assert address == 'TEST_ADDRESS'
|
|
return Decimal('0')
|
|
|
|
|
|
class FakeBTCRPCPartial:
|
|
def getreceivedbyaddress(self, address):
|
|
assert address == 'TEST_ADDRESS'
|
|
return Decimal('0.5') * 100000000
|
|
|
|
|
|
class FakeBTCRPCPaid:
|
|
def getreceivedbyaddress(self, address):
|
|
assert address == 'TEST_ADDRESS'
|
|
return Decimal('1') * 100000000
|
|
|
|
|
|
PAYPAL_IPN_TEST = '''\
|
|
mc_gross=3.00&\
|
|
protection_eligibility=Eligible&\
|
|
address_status=confirmed&\
|
|
payer_id=LPLWNMTBWMFAY&\
|
|
tax=0.00&\
|
|
address_street=1+Main+St&\
|
|
payment_date=20%3A12%3A59+Jan+13%2C+2009+PST&\
|
|
payment_status=Completed&\
|
|
charset=windows-1252&\
|
|
address_zip=95131&\
|
|
first_name=Test&\
|
|
mc_fee=0.88&\
|
|
address_country_code=US&\
|
|
address_name=Test+User&\
|
|
notify_version=2.6&\
|
|
custom=&\
|
|
payer_status=verified&\
|
|
address_country=United+States&\
|
|
address_city=San+Jose&\
|
|
quantity=1&\
|
|
verify_sign=AtkOfCXbDm2hu0ZELryHFjY-Vb7PAUvS6nMXgysbElEn9v-1XcmSoGtf&\
|
|
payer_email=test_user@example.com&\
|
|
txn_id=61E67681CH3238416&\
|
|
payment_type=instant&\
|
|
last_name=User&\
|
|
address_state=CA&\
|
|
receiver_email=test_business@example.com&\
|
|
payment_fee=0.88&\
|
|
receiver_id=S8XGHLYDW9T3S&\
|
|
txn_type=express_checkout&\
|
|
item_name=&\
|
|
mc_currency=EUR&\
|
|
item_number=&\
|
|
residence_country=US&\
|
|
test_ipn=1&\
|
|
handling_amount=0.00&\
|
|
transaction_subject=&\
|
|
payment_gross=3.00&\
|
|
shipping=0.00'''
|
|
|
|
|
|
class BitcoinBackendTest(TestCase):
|
|
def setUp(self):
|
|
self.user = User.objects.create_user('test', 'test_user@example.com', None)
|
|
|
|
self.p = Payment.objects.create(
|
|
user=self.user, time=timedelta(days=30), backend='bitcoin',
|
|
amount=300)
|
|
|
|
def test_new(self):
|
|
backend = BitcoinBackend(dict(BITCOIN_VALUE=300, URL=''))
|
|
backend.make_rpc = FakeBTCRPCNew
|
|
|
|
backend.new_payment(self.p)
|
|
redirect = backend.new_payment(self.p)
|
|
self.assertEqual(self.p.backend_extid, 'TEST_ADDRESS')
|
|
self.assertEqual(self.p.status, 'new')
|
|
self.assertIn('btc_price', self.p.backend_data)
|
|
self.assertIn('btc_address', self.p.backend_data)
|
|
self.assertEqual(self.p.backend_data['btc_address'], 'TEST_ADDRESS')
|
|
self.assertIsInstance(redirect, HttpResponseRedirect)
|
|
self.assertEqual(redirect.url, '/payments/view/%d' % self.p.id)
|
|
self.assertEqual(self.p.status_message, "Please send 1.00000 BTC to TEST_ADDRESS")
|
|
|
|
def test_rounding(self):
|
|
""" Rounding test
|
|
300 / 300 = 1 => 1.00000 BTC
|
|
300 / 260 = Decimal('1.153846153846153846153846154') => 1.15385 BTC
|
|
"""
|
|
backend = BitcoinBackend(dict(BITCOIN_VALUE=300, URL=''))
|
|
backend.make_rpc = FakeBTCRPCNew
|
|
backend.new_payment(self.p)
|
|
self.assertEqual(self.p.status_message, "Please send 1.00000 BTC to TEST_ADDRESS")
|
|
|
|
backend = BitcoinBackend(dict(BITCOIN_VALUE=260, URL=''))
|
|
backend.make_rpc = FakeBTCRPCNew
|
|
backend.new_payment(self.p)
|
|
self.assertEqual(self.p.status_message, "Please send 1.15385 BTC to TEST_ADDRESS")
|
|
|
|
|
|
class BitcoinBackendConfirmTest(TestCase):
|
|
def setUp(self):
|
|
self.user = User.objects.create_user('test', 'test_user@example.com', None)
|
|
|
|
self.p = Payment.objects.create(
|
|
user=self.user, time=timedelta(days=30), backend='bitcoin',
|
|
amount=300)
|
|
|
|
# call new_payment
|
|
backend = BitcoinBackend(dict(BITCOIN_VALUE=300, URL=''))
|
|
backend.make_rpc = FakeBTCRPCNew
|
|
backend.new_payment(self.p)
|
|
|
|
def test_check_unpaid(self):
|
|
backend = BitcoinBackend(dict(BITCOIN_VALUE=300, URL=''))
|
|
backend.make_rpc = FakeBTCRPCUnpaid
|
|
|
|
backend.check(self.p)
|
|
self.assertEqual(self.p.status, 'new')
|
|
self.assertEqual(self.p.paid_amount, 0)
|
|
|
|
def test_check_partially_paid(self):
|
|
backend = BitcoinBackend(dict(BITCOIN_VALUE=300, URL=''))
|
|
backend.make_rpc = FakeBTCRPCPartial
|
|
backend.check(self.p)
|
|
self.assertEqual(self.p.status, 'new')
|
|
self.assertEqual(self.p.paid_amount, 150)
|
|
|
|
def test_check_paid(self):
|
|
backend = BitcoinBackend(dict(BITCOIN_VALUE=300, URL=''))
|
|
backend.make_rpc = FakeBTCRPCPaid
|
|
backend.check(self.p)
|
|
self.assertEqual(self.p.paid_amount, 300)
|
|
self.assertEqual(self.p.status, 'confirmed')
|
|
|
|
|
|
class BackendTest(TestCase):
|
|
def setUp(self):
|
|
self.user = User.objects.create_user('test', 'test_user@example.com', None)
|
|
|
|
def test_paypal(self):
|
|
# TODO: This checks the most simple and perfect payment that could
|
|
# happen, but not errors or other/invalid IPN
|
|
|
|
payment = Payment.objects.create(
|
|
user=self.user,
|
|
time=timedelta(days=30),
|
|
backend='paypal',
|
|
amount=300
|
|
)
|
|
|
|
settings = dict(
|
|
TEST=True,
|
|
TITLE='Test Title',
|
|
CURRENCY='EUR',
|
|
ADDRESS='test_business@example.com',
|
|
)
|
|
|
|
with self.settings(ROOT_URL='root'):
|
|
backend = PaypalBackend(settings)
|
|
redirect = backend.new_payment(payment)
|
|
|
|
self.assertIsInstance(redirect, HttpResponseRedirect)
|
|
|
|
host, params = redirect.url.split('?', 1)
|
|
params = parse_qs(params)
|
|
|
|
expected_notify_url = 'root/payments/callback/paypal/%d' % payment.id
|
|
expected_return_url = 'root/payments/view/%d' % payment.id
|
|
expected_cancel_url = 'root/payments/cancel/%d' % payment.id
|
|
|
|
self.assertEqual(params['cmd'][0], '_xclick')
|
|
self.assertEqual(params['notify_url'][0], expected_notify_url)
|
|
self.assertEqual(params['return'][0], expected_return_url)
|
|
self.assertEqual(params['cancel_return'][0], expected_cancel_url)
|
|
self.assertEqual(params['business'][0], 'test_business@example.com')
|
|
self.assertEqual(params['currency_code'][0], 'EUR')
|
|
self.assertEqual(params['amount'][0], '3.00')
|
|
self.assertEqual(params['item_name'][0], 'Test Title')
|
|
|
|
# Replace PaypalBackend.verify_ipn to not call the PayPal API
|
|
# we will assume the IPN is authentic
|
|
backend.verify_ipn = lambda payment, request: True
|
|
|
|
ipn_url = '/payments/callback/paypal/%d' % payment.id
|
|
ipn_request = RequestFactory().post(
|
|
ipn_url,
|
|
content_type='application/x-www-form-urlencoded',
|
|
data=PAYPAL_IPN_TEST)
|
|
r = backend.callback(payment, ipn_request)
|
|
|
|
self.assertTrue(r)
|
|
self.assertEqual(payment.status, 'confirmed')
|
|
self.assertEqual(payment.paid_amount, 300)
|
|
self.assertEqual(payment.backend_extid, '61E67681CH3238416')
|
|
|
|
def test_paypal_ipn_error(self):
|
|
payment = Payment.objects.create(
|
|
user=self.user,
|
|
time=timedelta(days=30),
|
|
backend='paypal',
|
|
amount=300
|
|
)
|
|
|
|
settings = dict(
|
|
TEST=True,
|
|
TITLE='Test Title',
|
|
CURRENCY='EUR',
|
|
ADDRESS='test_business@example.com',
|
|
)
|
|
|
|
with self.settings(ROOT_URL='root'):
|
|
backend = PaypalBackend(settings)
|
|
redirect = backend.new_payment(payment)
|
|
|
|
self.assertIsInstance(redirect, HttpResponseRedirect)
|
|
|
|
host, params = redirect.url.split('?', 1)
|
|
params = parse_qs(params)
|
|
|
|
expected_notify_url = 'root/payments/callback/paypal/%d' % payment.id
|
|
|
|
# Replace PaypalBackend.verify_ipn to not call the PayPal API
|
|
# we will assume the IPN is authentic
|
|
backend.verify_ipn = lambda payment, request: True
|
|
|
|
ipn_url = '/payments/callback/paypal/%d' % payment.id
|
|
ipn_request = RequestFactory().post(
|
|
ipn_url,
|
|
content_type='application/x-www-form-urlencoded',
|
|
data=PAYPAL_IPN_TEST)
|
|
r = backend.callback(payment, ipn_request)
|
|
|
|
self.assertTrue(r)
|
|
self.assertEqual(payment.status, 'confirmed')
|
|
self.assertEqual(payment.paid_amount, 300)
|
|
self.assertEqual(payment.backend_extid, '61E67681CH3238416')
|
|
|
|
def test_stripe(self):
|
|
payment = Payment.objects.create(
|
|
user=self.user,
|
|
time=timedelta(days=30),
|
|
backend='stripe',
|
|
amount=300
|
|
)
|
|
|
|
settings = dict(
|
|
API_KEY='test_secret_key',
|
|
PUBLIC_KEY='test_public_key',
|
|
CURRENCY='EUR',
|
|
NAME='Test Name',
|
|
)
|
|
|
|
with self.settings(ROOT_URL='root'):
|
|
backend = StripeBackend(settings)
|
|
form_html = backend.new_payment(payment)
|
|
|
|
expected_form = '''
|
|
<form action="/payments/callback/stripe/{id}" method="POST">
|
|
<script
|
|
src="https://checkout.stripe.com/checkout.js" class="stripe-button"
|
|
data-key="test_public_key"
|
|
data-image=""
|
|
data-name="Test Name"
|
|
data-currency="EUR"
|
|
data-description="30 days, 0:00:00 for test"
|
|
data-amount="300"
|
|
data-email="test_user@example.com"
|
|
data-locale="auto"
|
|
data-zip-code="true"
|
|
data-alipay="true">
|
|
</script>
|
|
</form>
|
|
'''.format(id=payment.id)
|
|
self.maxDiff = None
|
|
self.assertEqual(expected_form, form_html)
|
|
|
|
def create_charge(**kwargs):
|
|
self.assertEqual(kwargs, {
|
|
'amount': 300,
|
|
'currency': 'EUR',
|
|
'card': 'TEST_TOKEN',
|
|
'description': "1 months for test",
|
|
})
|
|
return {
|
|
'id': 'TEST_CHARGE_ID',
|
|
'refunded': False,
|
|
'paid': True,
|
|
'amount': 300,
|
|
}
|
|
|
|
# Replace the Stripe api instance
|
|
backend.stripe = type('Stripe', (object, ), {
|
|
'Charge': type('Charge', (object, ), {
|
|
'create': create_charge,
|
|
}),
|
|
'error': type('error', (object, ), {
|
|
'CardError': type('CardError', (Exception, ), {}),
|
|
}),
|
|
})
|
|
|
|
request = RequestFactory().post('', {'stripeToken': 'TEST_TOKEN'})
|
|
backend.callback(payment, request)
|
|
|
|
self.assertEqual(payment.backend_extid, 'TEST_CHARGE_ID')
|
|
|