From 42a3ee1a592c538bc0215cd7848536435ef9afae Mon Sep 17 00:00:00 2001 From: Becky Smith Date: Sat, 8 Jun 2024 22:31:25 +0100 Subject: [PATCH] Stripe webhook tests --- conftest.py | 68 +++++++++++++------ stripe_payments/emails.py | 1 + .../email/subscription_created.html | 2 +- .../email/subscription_created.txt | 2 +- stripe_payments/tests/test_stripe_views.py | 16 +++-- 5 files changed, 63 insertions(+), 26 deletions(-) diff --git a/conftest.py b/conftest.py index c244ac24..ba13f35f 100644 --- a/conftest.py +++ b/conftest.py @@ -1,3 +1,4 @@ +from datetime import datetime import pytest import os import responses @@ -8,6 +9,7 @@ from model_bakery import baker from stripe_payments.models import Invoice, Seller +from stripe_payments.tests.mock_connector import MockConnector User = get_user_model() @@ -20,6 +22,8 @@ def configured_user(): email='test@test.com', password='test' ) + user.userprofile.stripe_customer_id = "cus-1" + user.userprofile.save() yield user @@ -69,34 +73,58 @@ 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) 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_stripe_views.py b/stripe_payments/tests/test_stripe_views.py index fddb8bb5..f9b616f2 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,7 @@ import stripe from model_bakery import baker -from booking.models import Block, Booking, TicketBooking, Ticket +from booking.models import Block, Booking, TicketBooking, Ticket, Membership from ..models import Invoice, Seller, StripePaymentIntent from .mock_connector import MockConnector @@ -673,16 +673,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_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")