diff --git a/conftest.py b/conftest.py
index c244ac24..f7dbc972 100644
--- a/conftest.py
+++ b/conftest.py
@@ -1,3 +1,4 @@
+from datetime import datetime
import pytest
import os
import responses
@@ -8,9 +9,11 @@
from model_bakery import baker
from stripe_payments.models import Invoice, Seller
+from stripe_payments.tests.mock_connector import MockConnector
User = get_user_model()
+
@pytest.fixture
def configured_user():
user = User.objects.create_user(
@@ -23,6 +26,20 @@ def configured_user():
yield user
+@pytest.fixture
+def configured_stripe_user():
+ user = User.objects.create_user(
+ username='stripe_customer',
+ first_name="Test",
+ last_name="User",
+ email='stripetest@test.com',
+ password='test'
+ )
+ user.userprofile.stripe_customer_id = "cus-1"
+ user.userprofile.save()
+ yield user
+
+
@pytest.fixture
def superuser():
yield User.objects.create_superuser(
@@ -69,34 +86,60 @@ def invoice(configured_user):
)
-@pytest.fixture
-def get_mock_payment_intent():
- def payment_intent(webhook_event_type=None, **params):
- defaults = {
- "id": "mock-intent-id",
- "amount": 1000,
- "description": "",
- "status": "succeeded",
- "metadata": {},
- "currency": "gbp",
- "client_secret": "secret",
- "charges": Mock(data=[{"billing_details": {"email": "stripe-payer@test.com"}}])
- }
- options = {**defaults, **params}
- if webhook_event_type == "payment_intent.payment_failed":
- options["last_payment_error"] = {'error': 'an error'}
- return Mock(**options)
- return payment_intent
+def get_mock_payment_intent(webhook_event_type=None, **params):
+ defaults = {
+ "id": "mock-intent-id",
+ "amount": 1000,
+ "description": "",
+ "status": "succeeded",
+ "metadata": {},
+ "currency": "gbp",
+ "client_secret": "secret",
+ "charges": Mock(data=[{"billing_details": {"email": "stripe-payer@test.com"}}])
+ }
+ options = {**defaults, **params}
+ if webhook_event_type == "payment_intent.payment_failed":
+ options["last_payment_error"] = {'error': 'an error'}
+ return Mock(**options)
+
+
+class MockSubscription:
+ def __init__(self, **init_dict):
+ for k, v in init_dict.items():
+ setattr(self, k, v)
+
+ def __getitem__(self, item):
+ return getattr(self, item)
+
+
+def get_mock_subscription(webhook_event_type, **params):
+ defaults = {
+ "id": "id",
+ "status": "active",
+ "items": Mock(data=[Mock(price=Mock(id="price_1234"))]), # matches the id returned by the MockStripeConnector
+ "customer": "cus-1",
+ "start_date": datetime(2024, 6, 25).timestamp(),
+ "metadata": {},
+ }
+ options = {**defaults, **params}
+ return MockSubscription(**options)
@pytest.fixture
-def get_mock_webhook_event(seller, get_mock_payment_intent):
+def get_mock_webhook_event(seller):
def mock_webhook_event(**params):
webhook_event_type = params.pop("webhook_event_type", "payment_intent.succeeded")
seller_id = params.pop("seller_id", seller.stripe_user_id)
+ if webhook_event_type in ["payment_intent.succeeded", "payment_intent.payment_failed"]:
+ object = get_mock_payment_intent(webhook_event_type, **params)
+ elif webhook_event_type == "customer.subscription.created":
+ object = get_mock_subscription(webhook_event_type, **params)
+ else:
+ object = Mock(**params)
mock_event = Mock(
account=seller_id,
- data=Mock(object=get_mock_payment_intent(webhook_event_type, **params)), type=webhook_event_type
+ data=Mock(object=object),
+ type=webhook_event_type,
)
return mock_event
return mock_webhook_event
diff --git a/stripe_payments/emails.py b/stripe_payments/emails.py
index 22bf60a3..c3229892 100644
--- a/stripe_payments/emails.py
+++ b/stripe_payments/emails.py
@@ -57,6 +57,7 @@ def send_processed_refund_emails(invoice, event_object):
user, user_membership = _get_user_from_membership(event_object)
else:
user = _get_user_from_invoice(invoice)
+ user_membership = None
ctx = {
'host': f"https://{Site.objects.get_current().domain}",
'user': user,
diff --git a/stripe_payments/templates/stripe_payments/email/subscription_created.html b/stripe_payments/templates/stripe_payments/email/subscription_created.html
index 54ff7b14..fbccd8e6 100644
--- a/stripe_payments/templates/stripe_payments/email/subscription_created.html
+++ b/stripe_payments/templates/stripe_payments/email/subscription_created.html
@@ -3,6 +3,6 @@
{% block messagecontent %}
Thank you for setting up your new membership!
Your membership: {{ user_membership.membership.name }}
-Start date: {{ user_membership.start_date|date 'd M Y' }}
+Start date: {{ user_membership.start_date|date:'d M Y' }}
View your membership details here
{% endblock %}
\ No newline at end of file
diff --git a/stripe_payments/templates/stripe_payments/email/subscription_created.txt b/stripe_payments/templates/stripe_payments/email/subscription_created.txt
index 0b7ee69a..8a582d9a 100644
--- a/stripe_payments/templates/stripe_payments/email/subscription_created.txt
+++ b/stripe_payments/templates/stripe_payments/email/subscription_created.txt
@@ -4,7 +4,7 @@
Thank you for setting up your new membership!
Your membership: {{ user_membership.membership.name }}
-Start date: {{ user_membership.start_date|date 'd M Y' }}
+Start date: {{ user_membership.start_date|date:'d M Y' }}
View your membership details:
https://{{ domain }}/memberships/
diff --git a/stripe_payments/tests/test_admin.py b/stripe_payments/tests/test_admin.py
index 57c09e30..48c21228 100644
--- a/stripe_payments/tests/test_admin.py
+++ b/stripe_payments/tests/test_admin.py
@@ -8,6 +8,7 @@
from model_bakery import baker
from stripe_payments.models import Invoice, StripePaymentIntent
+from conftest import get_mock_payment_intent
from ..admin import StripePaymentIntentAdmin, InvoiceAdmin
@@ -28,7 +29,7 @@ def test_invoice_display_no_payment_intent_or_items():
assert invoice_admin.pi(invoice) == ""
-def test_invoice_display_payment_intent(get_mock_payment_intent):
+def test_invoice_display_payment_intent():
invoice = baker.make(
Invoice, invoice_id="foo123", username="test@test.com", amount=10
)
@@ -40,7 +41,7 @@ def test_invoice_display_payment_intent(get_mock_payment_intent):
assert invoice_admin.pi(invoice) == f'mock-intent-id'
-def test_invoice_display_items(get_mock_payment_intent):
+def test_invoice_display_items():
invoice = baker.make(
Invoice, invoice_id="foo123", username="test@test.com", amount=10
)
@@ -52,7 +53,7 @@ def test_invoice_display_items(get_mock_payment_intent):
assert invoice_admin.items(invoice) == f"- {booking.event.str_no_location()}
"
-def test_payment_intent_admin_display(get_mock_payment_intent, block_gift_voucher):
+def test_payment_intent_admin_display(block_gift_voucher):
invoice = baker.make(
Invoice, invoice_id="foo123", username="test@test.com", amount=10
)
diff --git a/stripe_payments/tests/test_models.py b/stripe_payments/tests/test_models.py
index bc30ec90..34d5a3ef 100644
--- a/stripe_payments/tests/test_models.py
+++ b/stripe_payments/tests/test_models.py
@@ -7,6 +7,7 @@
from model_bakery import baker
+from conftest import get_mock_payment_intent
from booking.models import Booking, Block, TicketBooking, Ticket
from ..models import Invoice, Seller, StripePaymentIntent
@@ -141,7 +142,7 @@ def test_seller_str():
assert str(seller) == "testuser@test.com"
-def test_invoice_payment_intent_ids(get_mock_payment_intent):
+def test_invoice_payment_intent_ids():
invoice = baker.make(Invoice, invoice_id="foo123")
stripe_pi, _ = StripePaymentIntent.update_or_create_payment_intent_instance(
get_mock_payment_intent(), invoice
@@ -149,7 +150,7 @@ def test_invoice_payment_intent_ids(get_mock_payment_intent):
assert invoice.payment_intent_ids == "mock-intent-id"
-def test_create_stripe_payment_intent_instance_from_pi(get_mock_payment_intent):
+def test_create_stripe_payment_intent_instance_from_pi():
payment_intent = get_mock_payment_intent()
invoice = baker.make(Invoice, invoice_id="foo123")
assert not StripePaymentIntent.objects.exists()
@@ -167,7 +168,7 @@ def test_create_stripe_payment_intent_instance_from_pi(get_mock_payment_intent):
assert pi.seller == seller
-def test_stripe_payment_intent_str(get_mock_payment_intent):
+def test_stripe_payment_intent_str():
payment_intent = get_mock_payment_intent()
invoice = baker.make(Invoice, invoice_id="foo123", username="user@test.com")
pi, _ = StripePaymentIntent.update_or_create_payment_intent_instance(payment_intent, invoice)
diff --git a/stripe_payments/tests/test_stripe_views.py b/stripe_payments/tests/test_stripe_views.py
index fddb8bb5..e56e33db 100644
--- a/stripe_payments/tests/test_stripe_views.py
+++ b/stripe_payments/tests/test_stripe_views.py
@@ -1,5 +1,5 @@
+from datetime import datetime
from unittest.mock import patch, Mock
-import json
import pytest
from django.conf import settings
@@ -10,7 +10,8 @@
import stripe
from model_bakery import baker
-from booking.models import Block, Booking, TicketBooking, Ticket
+from conftest import get_mock_payment_intent
+from booking.models import Block, Booking, TicketBooking, Ticket, Membership
from ..models import Invoice, Seller, StripePaymentIntent
from .mock_connector import MockConnector
@@ -42,7 +43,7 @@ def test_return_with_unknown_payment_intent(mock_payment_intent_retrieve, client
@pytest.mark.usefixtures("seller", "send_all_studio_emails")
@patch("stripe_payments.views.stripe.PaymentIntent")
def test_return_with_no_matching_invoice(
- mock_payment_intent, get_mock_payment_intent, client
+ mock_payment_intent, client
):
mock_payment_intent.retrieve.return_value = get_mock_payment_intent()
resp = client.get(complete_url)
@@ -58,7 +59,7 @@ def test_return_with_no_matching_invoice(
@pytest.mark.usefixtures("seller", "send_all_studio_emails")
@patch("stripe_payments.views.stripe.PaymentIntent")
def test_return_with_matching_invoice_and_block(
- mock_payment_intent, get_mock_payment_intent, client, configured_user
+ mock_payment_intent, client, configured_user
):
assert StripePaymentIntent.objects.exists() is False
invoice = baker.make(
@@ -91,7 +92,7 @@ def test_return_with_matching_invoice_and_block(
@pytest.mark.usefixtures("seller", "send_all_studio_emails")
@patch("stripe_payments.views.stripe.PaymentIntent")
def test_return_with_matching_invoice_and_booking(
- mock_payment_intent, get_mock_payment_intent, client, configured_user
+ mock_payment_intent, client, configured_user
):
assert StripePaymentIntent.objects.exists() is False
invoice = baker.make(
@@ -124,7 +125,7 @@ def test_return_with_matching_invoice_and_booking(
@patch("stripe_payments.views.stripe.PaymentIntent")
def test_return_with_matching_invoice_and_ticket_booking(
- mock_payment_intent, get_mock_payment_intent, client, configured_user, seller
+ mock_payment_intent, client, configured_user, seller
):
assert StripePaymentIntent.objects.exists() is False
invoice = baker.make(
@@ -160,7 +161,7 @@ def test_return_with_matching_invoice_and_ticket_booking(
@pytest.mark.usefixtures("seller", "send_all_studio_emails")
@patch("stripe_payments.views.stripe.PaymentIntent")
-def test_return_with_matching_invoice_and_gift_voucher(mock_payment_intent, get_mock_payment_intent, client, configured_user, block_gift_voucher):
+def test_return_with_matching_invoice_and_gift_voucher(mock_payment_intent, client, configured_user, block_gift_voucher):
assert StripePaymentIntent.objects.exists() is False
invoice = baker.make(
Invoice, invoice_id="foo", amount=10,
@@ -199,7 +200,7 @@ def test_return_with_matching_invoice_and_gift_voucher(mock_payment_intent, get_
@pytest.mark.usefixtures("seller", "send_all_studio_emails")
@patch("stripe_payments.views.stripe.PaymentIntent")
def test_return_with_matching_invoice_and_gift_voucher_anon_user(
- mock_payment_intent, get_mock_payment_intent, client, block_gift_voucher
+ mock_payment_intent, client, block_gift_voucher
):
assert StripePaymentIntent.objects.exists() is False
invoice = baker.make(
@@ -241,7 +242,7 @@ def test_return_with_matching_invoice_and_gift_voucher_anon_user(
@pytest.mark.usefixtures("seller", "send_all_studio_emails")
@patch("stripe_payments.views.stripe.PaymentIntent")
-def test_return_with_invalid_invoice(mock_payment_intent, get_mock_payment_intent, client, configured_user):
+def test_return_with_invalid_invoice(mock_payment_intent, client, configured_user):
invoice = baker.make(
Invoice, invoice_id="", amount=10,
username=configured_user.email, stripe_payment_intent_id="mock-intent-id"
@@ -264,7 +265,7 @@ def test_return_with_invalid_invoice(mock_payment_intent, get_mock_payment_inten
@pytest.mark.usefixtures("seller", "send_all_studio_emails")
@patch("stripe_payments.views.stripe.PaymentIntent")
-def test_return_with_matching_invoice_multiple_bookingss(mock_payment_intent, get_mock_payment_intent, client, configured_user):
+def test_return_with_matching_invoice_multiple_bookingss(mock_payment_intent, client, configured_user):
invoice = baker.make(
Invoice, invoice_id="foo", amount=10,
username=configured_user.email, stripe_payment_intent_id="mock-intent-id"
@@ -292,7 +293,7 @@ def test_return_with_matching_invoice_multiple_bookingss(mock_payment_intent, ge
@pytest.mark.usefixtures("seller", "send_all_studio_emails")
@patch("stripe_payments.views.stripe.PaymentIntent")
-def test_return_with_matching_invoice_invalid_amount(mock_payment_intent, get_mock_payment_intent, client, configured_user):
+def test_return_with_matching_invoice_invalid_amount(mock_payment_intent, client, configured_user):
invoice = baker.make(
Invoice, invoice_id="foo", username=configured_user.email, amount=50,
stripe_payment_intent_id="mock-intent-id"
@@ -311,7 +312,7 @@ def test_return_with_matching_invoice_invalid_amount(mock_payment_intent, get_mo
@pytest.mark.usefixtures("seller", "send_all_studio_emails")
@patch("stripe_payments.views.stripe.PaymentIntent")
-def test_return_with_matching_invoice_invalid_signature(mock_payment_intent, get_mock_payment_intent, client, configured_user):
+def test_return_with_matching_invoice_invalid_signature(mock_payment_intent, client, configured_user):
invoice = baker.make(
Invoice, invoice_id="foo", username=configured_user.email, amount=50,
stripe_payment_intent_id="mock-intent-id"
@@ -330,7 +331,7 @@ def test_return_with_matching_invoice_invalid_signature(mock_payment_intent, get
@pytest.mark.usefixtures("seller", "send_all_studio_emails")
@patch("stripe_payments.views.stripe.PaymentIntent")
-def test_return_with_matching_invoice_block_already_processed(mock_payment_intent, get_mock_payment_intent, client, configured_user):
+def test_return_with_matching_invoice_block_already_processed(mock_payment_intent, client, configured_user):
invoice = baker.make(
Invoice, invoice_id="foo", amount=10,
username=configured_user.email, stripe_payment_intent_id="mock-intent-id",
@@ -353,7 +354,7 @@ def test_return_with_matching_invoice_block_already_processed(mock_payment_inten
@pytest.mark.usefixtures("seller", "send_all_studio_emails")
@patch("stripe_payments.views.stripe.PaymentIntent")
-def test_return_with_failed_payment_intent(mock_payment_intent, get_mock_payment_intent, client, configured_user):
+def test_return_with_failed_payment_intent(mock_payment_intent, client, configured_user):
invoice = baker.make(
Invoice, invoice_id="foo", username=configured_user.email, amount=50,
stripe_payment_intent_id="mock-intent-id"
@@ -372,7 +373,7 @@ def test_return_with_failed_payment_intent(mock_payment_intent, get_mock_payment
@pytest.mark.usefixtures("seller", "send_all_studio_emails")
@patch("stripe_payments.views.stripe.PaymentIntent")
-def test_return_with_processing_payment_intent(mock_payment_intent, get_mock_payment_intent, client, configured_user):
+def test_return_with_processing_payment_intent(mock_payment_intent, client, configured_user):
invoice = baker.make(
Invoice, invoice_id="foo", username=configured_user.email, amount=50,
stripe_payment_intent_id="mock-intent-id"
@@ -673,16 +674,24 @@ def test_webhook_deauthorized_account(
# Memberships
+@patch("booking.models.membership_models.StripeConnector", MockConnector)
@patch("stripe_payments.views.stripe.Webhook")
def test_webhook_subscription_created(
- mock_webhook, get_mock_webhook_event, client
+ mock_webhook, get_mock_webhook_event, client, configured_stripe_user
):
+ membership = baker.make(Membership, name="membership1")
+ assert not membership.user_memberships.exists()
mock_webhook.construct_event.return_value = get_mock_webhook_event(
- webhook_event_type="customer.subscription.created", metadata={}
+ webhook_event_type="customer.subscription.created",
+ start_date = datetime(2024, 6, 25).timestamp()
)
resp = client.post(webhook_url, data={}, HTTP_STRIPE_SIGNATURE="foo")
assert resp.status_code == 200
+ # email sent to user
assert len(mail.outbox) == 1
+ # membership created, with start date as first of next month
+ assert membership.user_memberships.count() == 1
+ assert membership.user_memberships.first().start_date.date() == datetime(2024, 7, 1).date()
@patch("stripe_payments.views.stripe.Webhook")