Skip to content
This repository was archived by the owner on Jun 13, 2025. It is now read-only.

Commit 5a6d0c4

Browse files
fix: Handle multiple invoices with charges edge case
1 parent 04e3745 commit 5a6d0c4

File tree

3 files changed

+9
-10
lines changed

3 files changed

+9
-10
lines changed

services/billing.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -173,9 +173,12 @@ def delete_subscription(self, owner: Owner):
173173
stripe.Subscription.cancel(owner.stripe_subscription_id)
174174

175175
invoices_list = stripe.Invoice.list(subscription=owner.stripe_subscription_id, status="paid")
176-
charge = invoices_list["data"][0]["charge"] if len(invoices_list["data"]) >= 1 and invoices_list["data"][0]["charge"] else None
177-
if charge:
178-
stripe.Refund.create(charge=charge)
176+
created_refund = False
177+
for invoice in invoices_list["data"]:
178+
if invoice["charge"] is not None and invoice["amount_remaining"] == 0 and invoice["created"] < current_subscription_timestamp:
179+
stripe.Refund.create(invoice["charge"])
180+
created_refund = True
181+
if created_refund == True:
179182
stripe.Customer.modify(
180183
owner.stripe_customer_id,
181184
balance=0,

services/tests/samples/stripe_invoice.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@
120120
"total_tax_amounts": [],
121121
"webhooks_delivered_at": 1489789437
122122
},
123-
{
123+
{
124124
"id": "in_19yTU92eZvKYlo2C7uDjvu69",
125125
"object": "invoice",
126126
"account_country": "US",

services/tests/test_billing.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -365,12 +365,10 @@ def test_delete_subscription_with_schedule_releases_schedule_and_cancels_subscri
365365
):
366366
with open("./services/tests/samples/stripe_invoice.json") as f:
367367
stripe_invoice_response = json.load(f)
368-
stripe_invoice_response["data"] = stripe_invoice_response["data"] * 2
369368
list_invoice_mock.return_value = stripe_invoice_response
370369
plan = PlanName.CODECOV_PRO_YEARLY.value
371370
stripe_subscription_id = "sub_1K77Y5GlVGuVgOrkJrLjRnne"
372371
stripe_schedule_id = "sub_sched_sch1K77Y5GlVGuVgOrkJrLjRnne"
373-
charge = "ch_19yUQN2eZvKYlo2CQf7aWpSX"
374372
owner = OwnerFactory(
375373
stripe_subscription_id=stripe_subscription_id,
376374
plan=plan,
@@ -397,7 +395,7 @@ def test_delete_subscription_with_schedule_releases_schedule_and_cancels_subscri
397395
schedule_release_mock.assert_called_once_with(stripe_schedule_id)
398396
cancel_sub_mock.assert_called_once_with(stripe_subscription_id)
399397
list_invoice_mock.assert_called_once_with(subscription=stripe_subscription_id, status="paid")
400-
create_refund_mock.assert_called_once_with(charge=charge)
398+
self.assertEqual(create_refund_mock.call_count, 2)
401399
modify_customer_mock.assert_called_once_with(owner.stripe_customer_id, balance=0)
402400
modify_sub_mock.assert_not_called()
403401

@@ -420,12 +418,10 @@ def test_delete_subscription_with_schedule_releases_schedule_and_cancels_subscri
420418
):
421419
with open("./services/tests/samples/stripe_invoice.json") as f:
422420
stripe_invoice_response = json.load(f)
423-
stripe_invoice_response["data"] = stripe_invoice_response["data"] * 2
424421
list_invoice_mock.return_value = stripe_invoice_response
425422
plan = PlanName.CODECOV_PRO_YEARLY.value
426423
stripe_subscription_id = "sub_1K77Y5GlVGuVgOrkJrLjRnne"
427424
stripe_schedule_id = "sub_sched_sch1K77Y5GlVGuVgOrkJrLjRnne"
428-
charge = "ch_19yUQN2eZvKYlo2CQf7aWpSX"
429425
owner = OwnerFactory(
430426
stripe_subscription_id=stripe_subscription_id,
431427
plan=plan,
@@ -452,7 +448,7 @@ def test_delete_subscription_with_schedule_releases_schedule_and_cancels_subscri
452448
schedule_release_mock.assert_called_once_with(stripe_schedule_id)
453449
cancel_sub_mock.assert_called_once_with(stripe_subscription_id)
454450
list_invoice_mock.assert_called_once_with(subscription=stripe_subscription_id, status="paid")
455-
create_refund_mock.assert_called_once_with(charge=charge)
451+
self.assertEqual(create_refund_mock.call_count, 2)
456452
modify_customer_mock.assert_called_once_with(owner.stripe_customer_id, balance=0)
457453
modify_sub_mock.assert_not_called()
458454

0 commit comments

Comments
 (0)