Skip to content

Commit

Permalink
Stripe webhook tests
Browse files Browse the repository at this point in the history
  • Loading branch information
rebkwok committed Jun 9, 2024
1 parent 4029a62 commit 43ea005
Show file tree
Hide file tree
Showing 7 changed files with 100 additions and 45 deletions.
83 changes: 63 additions & 20 deletions conftest.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from datetime import datetime
import pytest
import os
import responses
Expand All @@ -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(
Expand All @@ -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='[email protected]',
password='test'
)
user.userprofile.stripe_customer_id = "cus-1"
user.userprofile.save()
yield user


@pytest.fixture
def superuser():
yield User.objects.create_superuser(
Expand Down Expand Up @@ -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": "[email protected]"}}])
}
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": "[email protected]"}}])
}
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
Expand Down
1 change: 1 addition & 0 deletions stripe_payments/emails.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@
{% block messagecontent %}
<p>Thank you for setting up your new membership!</p>
<p><strong>Your membership:</strong> {{ user_membership.membership.name }}</p>
<p><strong>Start date:</strong> {{ user_membership.start_date|date 'd M Y' }}</p>
<p><strong>Start date:</strong> {{ user_membership.start_date|date:'d M Y' }}</p>
<p>View your membership details <a href="https://{{ domain }}/memberships/">here</a></p>
{% endblock %}
Original file line number Diff line number Diff line change
Expand Up @@ -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/
Expand Down
7 changes: 4 additions & 3 deletions stripe_payments/tests/test_admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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="[email protected]", amount=10
)
Expand All @@ -40,7 +41,7 @@ def test_invoice_display_payment_intent(get_mock_payment_intent):
assert invoice_admin.pi(invoice) == f'<a href="{pi_admin_url}">mock-intent-id</a>'


def test_invoice_display_items(get_mock_payment_intent):
def test_invoice_display_items():
invoice = baker.make(
Invoice, invoice_id="foo123", username="[email protected]", amount=10
)
Expand All @@ -52,7 +53,7 @@ def test_invoice_display_items(get_mock_payment_intent):
assert invoice_admin.items(invoice) == f"<ul><li>{booking.event.str_no_location()}</li></ul>"


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="[email protected]", amount=10
)
Expand Down
7 changes: 4 additions & 3 deletions stripe_payments/tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -141,15 +142,15 @@ def test_seller_str():
assert str(seller) == "[email protected]"


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
)
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()
Expand All @@ -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="[email protected]")
pi, _ = StripePaymentIntent.update_or_create_payment_intent_instance(payment_intent, invoice)
Expand Down
43 changes: 26 additions & 17 deletions stripe_payments/tests/test_stripe_views.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from datetime import datetime
from unittest.mock import patch, Mock
import json
import pytest

from django.conf import settings
Expand All @@ -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

Expand Down Expand Up @@ -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)
Expand All @@ -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(
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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"
Expand All @@ -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"
Expand Down Expand Up @@ -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"
Expand All @@ -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"
Expand All @@ -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",
Expand All @@ -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"
Expand All @@ -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"
Expand Down Expand Up @@ -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")
Expand Down

0 comments on commit 43ea005

Please sign in to comment.