Skip to content

Commit 81820ac

Browse files
committed
Add test for stripe webhook checkout session completed
1 parent 1ee5166 commit 81820ac

File tree

3 files changed

+340
-28
lines changed

3 files changed

+340
-28
lines changed
+231
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,231 @@
1+
{
2+
"allowed_source_types": [
3+
"card"
4+
],
5+
"amount": 3000,
6+
"amount_capturable": 0,
7+
"amount_details": {
8+
"tip": {}
9+
},
10+
"amount_received": 3000,
11+
"application": null,
12+
"application_fee_amount": null,
13+
"automatic_payment_methods": null,
14+
"canceled_at": null,
15+
"cancellation_reason": null,
16+
"capture_method": "automatic",
17+
"charges": {
18+
"data": [
19+
{
20+
"amount": 3000,
21+
"amount_captured": 3000,
22+
"amount_refunded": 0,
23+
"application": null,
24+
"application_fee": null,
25+
"application_fee_amount": null,
26+
"balance_transaction": "txn_2MXkIN4ao19wAZWp0wQIDyAD",
27+
"billing_details": {
28+
"address": {
29+
"city": null,
30+
"country": null,
31+
"line1": null,
32+
"line2": null,
33+
"postal_code": null,
34+
"state": null
35+
},
36+
"email": null,
37+
"name": "[email protected]",
38+
"phone": null
39+
},
40+
"calculated_statement_descriptor": "DJANGOPROJECT.COM",
41+
"captured": true,
42+
"card": {
43+
"address_city": null,
44+
"address_country": null,
45+
"address_line1": null,
46+
"address_line1_check": null,
47+
"address_line2": null,
48+
"address_state": null,
49+
"address_zip": null,
50+
"address_zip_check": null,
51+
"brand": "Visa",
52+
"country": "US",
53+
"customer": "cus_3MXPY5pvYMWTBf",
54+
"cvc_check": null,
55+
"dynamic_last4": null,
56+
"exp_month": 12,
57+
"exp_year": 2034,
58+
"fingerprint": "xygwMO5bYmv4EmZm",
59+
"funding": "credit",
60+
"id": "card_6Mf8W5TmoG71AP",
61+
"last4": "4242",
62+
"metadata": {},
63+
"name": "[email protected]",
64+
"object": "card",
65+
"tokenization_method": null,
66+
"wallet": null
67+
},
68+
"created": 1675511560,
69+
"currency": "usd",
70+
"customer": "cus_6Mf8HUzoOM5r06",
71+
"description": "Invoice 94026E9-0072",
72+
"destination": null,
73+
"dispute": null,
74+
"disputed": false,
75+
"failure_balance_transaction": null,
76+
"failure_code": null,
77+
"failure_message": null,
78+
"fraud_details": {},
79+
"id": "ch_2MXkIN4ao19wAZWp0yIS2t5T",
80+
"invoice": "in_0MXjHW4ao19wAZWpwzZjD8Ie",
81+
"livemode": false,
82+
"metadata": {},
83+
"object": "charge",
84+
"on_behalf_of": null,
85+
"order": null,
86+
"outcome": {
87+
"network_advice_code": null,
88+
"network_decline_code": null,
89+
"network_status": "approved_by_network",
90+
"reason": null,
91+
"risk_level": "normal",
92+
"risk_score": 6,
93+
"seller_message": "Payment complete.",
94+
"type": "authorized"
95+
},
96+
"paid": true,
97+
"payment_intent": "pi_2MXkIN4ao19wAZWp0atVBWd2",
98+
"payment_method": "card_6Mf8W5TmoG71AP",
99+
"payment_method_details": {
100+
"card": {
101+
"amount_authorized": 3000,
102+
"authorization_code": null,
103+
"brand": "visa",
104+
"checks": {
105+
"address_line1_check": null,
106+
"address_postal_code_check": null,
107+
"cvc_check": null
108+
},
109+
"country": "US",
110+
"exp_month": 12,
111+
"exp_year": 2034,
112+
"extended_authorization": {
113+
"status": "disabled"
114+
},
115+
"fingerprint": "xygwMO5bYmv4EmZm",
116+
"funding": "credit",
117+
"incremental_authorization": {
118+
"status": "unavailable"
119+
},
120+
"installments": null,
121+
"last4": "4242",
122+
"mandate": null,
123+
"multicapture": {
124+
"status": "unavailable"
125+
},
126+
"network": "visa",
127+
"network_token": {
128+
"used": false
129+
},
130+
"overcapture": {
131+
"maximum_amount_capturable": 3000,
132+
"status": "unavailable"
133+
},
134+
"three_d_secure": null,
135+
"wallet": null
136+
},
137+
"type": "card"
138+
},
139+
"receipt_email": "[email protected]",
140+
"receipt_number": null,
141+
"receipt_url": "https://pay.stripe.com/receipts/invoices/CAcaGwoZYWNjdF8xV01QNGFvMTl3QVpXcGlNb3d0QijtvP65BjIG7quOkp-6OiwWZhwfLUVCB3PgWg7lw1O_qxdCsOOzqKj3Gn7Z16NF5qMYuHEtHxhOJa6nJA?s=ap",
142+
"refunded": false,
143+
"refunds": {
144+
"data": [],
145+
"has_more": false,
146+
"object": "list",
147+
"total_count": 0,
148+
"url": "/v1/charges/ch_2MXkIN4ao19wAZWp0yIS2t5T/refunds"
149+
},
150+
"review": null,
151+
"shipping": null,
152+
"source": {
153+
"address_city": null,
154+
"address_country": null,
155+
"address_line1": null,
156+
"address_line1_check": null,
157+
"address_line2": null,
158+
"address_state": null,
159+
"address_zip": null,
160+
"address_zip_check": null,
161+
"brand": "Visa",
162+
"country": "US",
163+
"customer": "cus_6Mf8HUzoOM5r06",
164+
"cvc_check": null,
165+
"dynamic_last4": null,
166+
"exp_month": 12,
167+
"exp_year": 2034,
168+
"fingerprint": "xygwMO5bYmv4EmZm",
169+
"funding": "credit",
170+
"id": "card_6Mf8W5TmoG71AP",
171+
"last4": "4242",
172+
"metadata": {},
173+
"name": "[email protected]",
174+
"object": "card",
175+
"tokenization_method": null,
176+
"wallet": null
177+
},
178+
"source_transfer": null,
179+
"statement_descriptor": null,
180+
"statement_descriptor_suffix": null,
181+
"status": "paid",
182+
"transfer_data": null,
183+
"transfer_group": null
184+
}
185+
],
186+
"has_more": false,
187+
"object": "list",
188+
"total_count": 1,
189+
"url": "/v1/charges?payment_intent=pi_2MXkIN4ao19wAZWp0atVBWd2"
190+
},
191+
"client_secret": "pi_2MXkIN4ao19wAZWp0atVBWd2_secret_WX2PK2jXLxklt3dHzrTMNpVn2",
192+
"confirmation_method": "automatic",
193+
"created": 1675511559,
194+
"currency": "usd",
195+
"customer": "cus_6Mf8HUzoOM5r06",
196+
"description": "Invoice 94026E9-0072",
197+
"id": "pi_2MXkIN4ao19wAZWp0atVBWd2",
198+
"invoice": "in_0MXjHW4ao19wAZWpwzZjD8Ie",
199+
"last_payment_error": null,
200+
"latest_charge": "ch_2MXkIN4ao19wAZWp0yIS2t5T",
201+
"livemode": false,
202+
"metadata": {},
203+
"next_action": null,
204+
"next_source_action": null,
205+
"object": "payment_intent",
206+
"on_behalf_of": null,
207+
"payment_method": null,
208+
"payment_method_configuration_details": null,
209+
"payment_method_options": {
210+
"card": {
211+
"installments": null,
212+
"mandate_options": null,
213+
"network": null,
214+
"request_three_d_secure": "automatic"
215+
}
216+
},
217+
"payment_method_types": [
218+
"card"
219+
],
220+
"processing": null,
221+
"receipt_email": "[email protected]",
222+
"review": null,
223+
"setup_future_usage": null,
224+
"shipping": null,
225+
"source": "card_6Mf8W5TmoG71AP",
226+
"statement_descriptor": null,
227+
"statement_descriptor_suffix": null,
228+
"status": "succeeded",
229+
"transfer_data": null,
230+
"transfer_group": null
231+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
{
2+
"id": "evt_103MXP2kbIgHtIBFeIFqPxp7",
3+
"object": "event",
4+
"api_version": "2015-01-11",
5+
"created": 1683928544,
6+
"data": {
7+
"object": {
8+
"id": "cs_test_a11YYufWQzNY63zpQ6QSNRQhkUpVph4WRmzW0zWJO2znZKdVujZ0N0S22u",
9+
"object": "checkout.session",
10+
"after_expiration": null,
11+
"allow_promotion_codes": null,
12+
"amount_subtotal": 2198,
13+
"amount_total": 2198,
14+
"automatic_tax": {
15+
"enabled": false,
16+
"liability": null,
17+
"status": null
18+
},
19+
"billing_address_collection": null,
20+
"cancel_url": null,
21+
"client_reference_id": null,
22+
"consent": null,
23+
"consent_collection": null,
24+
"created": 1679600215,
25+
"currency": "usd",
26+
"custom_fields": [],
27+
"custom_text": {
28+
"shipping_address": null,
29+
"submit": null
30+
},
31+
"customer": "cus_3MXPY5pvYMWTBf",
32+
"customer_creation": "if_required",
33+
"customer_details": null,
34+
"customer_email": "[email protected]",
35+
"expires_at": 1679686615,
36+
"invoice": null,
37+
"invoice_creation": {
38+
"enabled": false,
39+
"invoice_data": {
40+
"account_tax_ids": null,
41+
"custom_fields": null,
42+
"description": null,
43+
"footer": null,
44+
"issuer": null,
45+
"metadata": {},
46+
"rendering_options": null
47+
}
48+
},
49+
"livemode": false,
50+
"locale": null,
51+
"metadata": {},
52+
"mode": "payment",
53+
"payment_intent": null,
54+
"payment_link": null,
55+
"payment_method_collection": "always",
56+
"payment_method_options": {},
57+
"payment_method_types": [
58+
"card"
59+
],
60+
"payment_status": "unpaid",
61+
"phone_number_collection": {
62+
"enabled": false
63+
},
64+
"recovered_from": null,
65+
"setup_intent": null,
66+
"shipping_address_collection": null,
67+
"shipping_cost": null,
68+
"shipping_details": null,
69+
"shipping_options": [],
70+
"status": "open",
71+
"submit_type": null,
72+
"subscription": null,
73+
"success_url": "https://example.com/success",
74+
"total_details": {
75+
"amount_discount": 0,
76+
"amount_shipping": 0,
77+
"amount_tax": 0
78+
},
79+
"url": "https://checkout.stripe.com/c/pay/cs_test_a11YYufWQzNY63zpQ6QSNRQhkUpVph4WRmzW0zWJO2znZKdVujZ0N0S22u#fidkdWxOYHwnPyd1blpxYHZxWjA0SDdPUW5JbmFMck1wMmx9N2BLZjFEfGRUNWhqTmJ%2FM2F8bUA2SDRySkFdUV81T1BSV0YxcWJcTUJcYW5rSzN3dzBLPUE0TzRKTTxzNFBjPWZEX1NKSkxpNTVjRjN8VHE0YicpJ2N3amhWYHdzYHcnP3F3cGApJ2lkfGpwcVF8dWAnPyd2bGtiaWBabHFgaCcpJ2BrZGdpYFVpZGZgbWppYWB3dic%2FcXdwYHgl"
80+
}
81+
},
82+
"livemode": true,
83+
"pending_webhooks": 1,
84+
"request": null,
85+
"type": "checkout.session.completed"
86+
}

fundraising/tests/test_views.py

+23-28
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,10 @@ def _stripe_signature_header(data):
251251

252252
class TestWebhooks(TestCase):
253253
def setUp(self):
254-
self.hero = DjangoHero.objects.create(email="[email protected]")
254+
self.hero = DjangoHero.objects.create(
255+
256+
stripe_customer_id="cus_3MXPY5pvYMWTBf",
257+
)
255258
self.donation = Donation.objects.create(
256259
donor=self.hero,
257260
interval="monthly",
@@ -308,33 +311,25 @@ def test_zero_invoice_amount(self):
308311
self.assertEqual(response.status_code, 201)
309312
self.assertEqual(self.donation.payment_set.count(), 0)
310313

311-
def test_missing_signature_header(self):
312-
response = self.client.post(
313-
reverse("fundraising:receive-webhook"),
314-
data=json.dumps({}),
315-
content_type="application/json",
316-
)
317-
self.assertEqual(response.status_code, 422)
318-
319-
def test_invalid_json(self):
320-
response = self.client.post(
321-
reverse("fundraising:receive-webhook"),
322-
data="<invalid>",
323-
content_type="application/json",
314+
@patch("stripe.PaymentIntent.retrieve")
315+
def test_checkout_session_completed(self, payment_retrieve):
316+
payment_retrieve.return_value = self.stripe_data("payment_intent")
317+
response = self.post_event(self.stripe_data("session_completed"))
318+
self.assertEqual(response.status_code, 204)
319+
self.assertEqual(len(mail.outbox), 1)
320+
expected_url = django_hosts_reverse(
321+
"fundraising:manage-donations", kwargs={"hero": self.hero.id}
324322
)
325-
self.assertEqual(response.status_code, 422)
323+
self.assertTrue(expected_url in mail.outbox[0].body)
324+
self.assertEqual(DjangoHero.objects.count(), 1)
326325

327-
def test_invalid_signature(self):
328-
response = self.client.post(
329-
reverse("fundraising:receive-webhook"),
330-
data=json.dumps({}),
331-
content_type="application/json",
332-
headers={"stripe-signature": "<invalid>"},
333-
)
334-
self.assertEqual(response.status_code, 422)
326+
@patch("stripe.PaymentIntent.retrieve")
327+
def test_checkout_session_completed_new_hero(self, payment_retrieve):
328+
payment_retrieve.return_value = self.stripe_data("payment_intent")
329+
self.hero.stripe_customer_id = ""
330+
self.hero.save()
331+
response = self.post_event(self.stripe_data("session_completed"))
335332

336-
def test_unknown_event_type(self):
337-
data = self.stripe_data("zero_invoice_amount")
338-
data["type"] = "unknown"
339-
response = self.post_event(data)
340-
self.assertEqual(response.status_code, 422)
333+
self.assertEqual(len(mail.outbox), 1)
334+
self.assertEqual(response.status_code, 204)
335+
self.assertEqual(DjangoHero.objects.count(), 2)

0 commit comments

Comments
 (0)