Fix #571 -- Partial payments and refunds

This commit is contained in:
Raphael Michel
2018-06-26 12:09:36 +02:00
parent 8e7af49206
commit 18a378976b
115 changed files with 6026 additions and 1598 deletions

View File

@@ -5,9 +5,9 @@ from decimal import Decimal
import pytest
from django.test import RequestFactory
from django.utils.timezone import now
from stripe import APIConnectionError, CardError, StripeError
from stripe.error import APIConnectionError, CardError, StripeError
from pretix.base.models import Event, Order, Organizer
from pretix.base.models import Event, Order, OrderRefund, Organizer
from pretix.base.payment import PaymentException
from pretix.plugins.stripe.payment import StripeCC
@@ -23,7 +23,7 @@ def env():
code='FOOBAR', event=event, email='dummy@dummy.test',
status=Order.STATUS_PENDING,
datetime=now(), expires=now() + timedelta(days=10),
total=Decimal('13.37'), payment_provider='banktransfer'
total=Decimal('13.37')
)
return event, o1
@@ -39,11 +39,16 @@ def factory():
return RequestFactory()
class MockedRefunds():
pass
class MockedCharge():
def __init__(self):
self.status = ''
self.paid = False
self.id = 'ch_123345345'
self.refunds = MockedRefunds()
def refresh(self):
pass
@@ -72,7 +77,10 @@ def test_perform_success(env, factory, monkeypatch):
req.session = {}
prov.checkout_prepare(req, {})
assert 'payment_stripe_token' in req.session
prov.payment_perform(req, order)
payment = order.payments.create(
provider='stripe_cc', amount=order.total
)
prov.execute_payment(req, payment)
order.refresh_from_db()
assert order.status == Order.STATUS_PAID
@@ -102,7 +110,10 @@ def test_perform_success_zero_decimal_currency(env, factory, monkeypatch):
req.session = {}
prov.checkout_prepare(req, {})
assert 'payment_stripe_token' in req.session
prov.payment_perform(req, order)
payment = order.payments.create(
provider='stripe_cc', amount=order.total
)
prov.execute_payment(req, payment)
order.refresh_from_db()
assert order.status == Order.STATUS_PAID
@@ -125,7 +136,10 @@ def test_perform_card_error(env, factory, monkeypatch):
prov.checkout_prepare(req, {})
assert 'payment_stripe_token' in req.session
with pytest.raises(PaymentException):
prov.payment_perform(req, order)
payment = order.payments.create(
provider='stripe_cc', amount=order.total
)
prov.execute_payment(req, payment)
order.refresh_from_db()
assert order.status == Order.STATUS_PENDING
@@ -148,7 +162,10 @@ def test_perform_stripe_error(env, factory, monkeypatch):
prov.checkout_prepare(req, {})
assert 'payment_stripe_token' in req.session
with pytest.raises(PaymentException):
prov.payment_perform(req, order)
payment = order.payments.create(
provider='stripe_cc', amount=order.total
)
prov.execute_payment(req, payment)
order.refresh_from_db()
assert order.status == Order.STATUS_PENDING
@@ -175,7 +192,10 @@ def test_perform_failed(env, factory, monkeypatch):
prov.checkout_prepare(req, {})
assert 'payment_stripe_token' in req.session
with pytest.raises(PaymentException):
prov.payment_perform(req, order)
payment = order.payments.create(
provider='stripe_cc', amount=order.total
)
prov.execute_payment(req, payment)
order.refresh_from_db()
assert order.status == Order.STATUS_PENDING
@@ -185,26 +205,26 @@ def test_refund_success(env, factory, monkeypatch):
event, order = env
def charge_retr(*args, **kwargs):
def refund_create():
def refund_create(amount):
pass
c = MockedCharge()
c.refunds = MockedCharge()
c.refunds.create = refund_create
return c
monkeypatch.setattr("stripe.Charge.retrieve", charge_retr)
order.status = Order.STATUS_PAID
order.payment_info = json.dumps({
p = order.payments.create(provider='stripe_cc', amount=order.total, info=json.dumps({
'id': 'ch_123345345'
})
}))
order.save()
prov = StripeCC(event)
req = factory.post('/', data={'auto_refund': 'auto'})
req.user = None
prov.order_control_refund_perform(req, order)
order.refresh_from_db()
assert order.status == Order.STATUS_REFUNDED
refund = order.refunds.create(
provider='stripe_cc', amount=order.total, payment=p,
)
prov.execute_refund(refund)
refund.refresh_from_db()
assert refund.state == OrderRefund.REFUND_STATE_DONE
@pytest.mark.django_db
@@ -212,23 +232,24 @@ def test_refund_unavailable(env, factory, monkeypatch):
event, order = env
def charge_retr(*args, **kwargs):
def refund_create():
def refund_create(amount):
raise APIConnectionError(message='Foo')
c = MockedCharge()
c.refunds = object()
c.refunds.create = refund_create()
c.refunds.create = refund_create
return c
monkeypatch.setattr("stripe.Charge.retrieve", charge_retr)
order.status = Order.STATUS_PAID
order.payment_info = json.dumps({
p = order.payments.create(provider='stripe_cc', amount=order.total, info=json.dumps({
'id': 'ch_123345345'
})
}))
order.save()
prov = StripeCC(event)
req = factory.get('/')
req.user = None
prov.order_control_refund_perform(req, order)
order.refresh_from_db()
assert order.status == Order.STATUS_PAID
refund = order.refunds.create(
provider='stripe_cc', amount=order.total, payment=p
)
with pytest.raises(PaymentException):
prov.execute_refund(refund)
refund.refresh_from_db()
assert refund.state != OrderRefund.REFUND_STATE_DONE

View File

@@ -6,7 +6,7 @@ import pytest
from django.utils.timezone import now
from pretix.base.models import (
Event, Order, Organizer, RequiredAction, Team, User,
Event, Order, OrderPayment, OrderRefund, Organizer, Team, User,
)
from pretix.plugins.stripe.models import ReferencedStripeObject
@@ -26,7 +26,7 @@ def env():
code='FOOBAR', event=event, email='dummy@dummy.test',
status=Order.STATUS_PAID,
datetime=now(), expires=now() + timedelta(days=10),
total=Decimal('13.37'), payment_provider='stripe'
total=Decimal('13.37'),
)
return event, o1
@@ -129,7 +129,7 @@ def test_webhook_all_good(env, client, monkeypatch):
@pytest.mark.django_db
def test_webhook_mark_paid(env, client, monkeypatch):
def test_webhook_mark_paid_without_reference_and_payment(env, client, monkeypatch):
order = env[1]
order.status = Order.STATUS_PENDING
order.save()
@@ -164,6 +164,13 @@ def test_webhook_mark_paid(env, client, monkeypatch):
@pytest.mark.django_db
def test_webhook_partial_refund(env, client, monkeypatch):
charge = get_test_charge(env[1])
payment = env[1].payments.create(
provider='stripe', amount=env[1].total, info=json.dumps(charge)
)
ReferencedStripeObject.objects.create(order=env[1], reference="ch_18TY6GGGWE2Ias8TZHanef25",
payment=payment)
charge['refunds'] = {
"object": "list",
"data": [
@@ -209,13 +216,10 @@ def test_webhook_partial_refund(env, client, monkeypatch):
order.refresh_from_db()
assert order.status == Order.STATUS_PAID
ra = RequiredAction.objects.get(action_type="pretix.plugins.stripe.refund")
client.login(username='dummy@dummy.dummy', password='dummy')
client.post('/control/event/dummy/dummy/stripe/refund/{}/'.format(ra.pk))
order = env[1]
order.refresh_from_db()
assert order.status == Order.STATUS_REFUNDED
ra = order.refunds.first()
assert ra.state == OrderRefund.REFUND_STATE_EXTERNAL
assert ra.source == 'external'
assert ra.amount == Decimal('123.00')
@pytest.mark.django_db
@@ -227,6 +231,48 @@ def test_webhook_global(env, client, monkeypatch):
charge = get_test_charge(env[1])
monkeypatch.setattr("stripe.Charge.retrieve", lambda *args, **kwargs: charge)
payment = order.payments.create(
provider='stripe', amount=order.total, info=json.dumps(charge), state=OrderPayment.PAYMENT_STATE_CREATED
)
ReferencedStripeObject.objects.create(order=order, reference="ch_18TY6GGGWE2Ias8TZHanef25",
payment=payment)
client.post('/_stripe/webhook/', json.dumps(
{
"id": "evt_18otImGGWE2Ias8TUyVRDB1G",
"object": "event",
"api_version": "2016-03-07",
"created": 1472729052,
"data": {
"object": {
"id": "ch_18TY6GGGWE2Ias8TZHanef25",
"object": "charge",
# Rest of object is ignored anway
}
},
"livemode": True,
"pending_webhooks": 1,
"request": "req_977XOWC8zk51Z9",
"type": "charge.succeeded"
}
), content_type='application_json')
order.refresh_from_db()
assert order.status == Order.STATUS_PAID
@pytest.mark.django_db
def test_webhook_global_legacy_reference(env, client, monkeypatch):
order = env[1]
order.status = Order.STATUS_PENDING
order.save()
charge = get_test_charge(env[1])
monkeypatch.setattr("stripe.Charge.retrieve", lambda *args, **kwargs: charge)
payment = order.payments.create(
provider='stripe', amount=order.total, info=json.dumps(charge), state=OrderPayment.PAYMENT_STATE_CREATED
)
ReferencedStripeObject.objects.create(order=order, reference="ch_18TY6GGGWE2Ias8TZHanef25")
client.post('/_stripe/webhook/', json.dumps(
@@ -251,3 +297,4 @@ def test_webhook_global(env, client, monkeypatch):
order.refresh_from_db()
assert order.status == Order.STATUS_PAID
assert list(order.payments.all()) == [payment]