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

Commit 4967464

Browse files
committed
update logic to support multiple subscriptions
1 parent 4ed721d commit 4967464

File tree

2 files changed

+138
-11
lines changed

2 files changed

+138
-11
lines changed

billing/tests/test_views.py

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1643,6 +1643,119 @@ class MockPaymentMethod:
16431643
"sub_123", default_payment_method=payment_method_retrieve_mock.return_value
16441644
)
16451645

1646+
@patch("services.billing.stripe.PaymentMethod.attach")
1647+
@patch("services.billing.stripe.Customer.modify")
1648+
@patch("services.billing.stripe.Subscription.modify")
1649+
@patch("services.billing.stripe.PaymentMethod.retrieve")
1650+
def test_check_and_handle_delayed_notification_payment_methods_no_subscription(
1651+
self,
1652+
payment_method_retrieve_mock,
1653+
subscription_modify_mock,
1654+
customer_modify_mock,
1655+
payment_method_attach_mock,
1656+
):
1657+
class MockPaymentMethod:
1658+
type = "us_bank_account"
1659+
us_bank_account = {}
1660+
id = "pm_123"
1661+
1662+
payment_method_retrieve_mock.return_value = MockPaymentMethod()
1663+
1664+
self.owner.stripe_subscription_id = None
1665+
self.owner.stripe_customer_id = "cus_123"
1666+
self.owner.save()
1667+
1668+
handler = StripeWebhookHandler()
1669+
handler._check_and_handle_delayed_notification_payment_methods(
1670+
"cus_123", "pm_123"
1671+
)
1672+
1673+
payment_method_retrieve_mock.assert_called_once_with("pm_123")
1674+
payment_method_attach_mock.assert_not_called()
1675+
customer_modify_mock.assert_not_called()
1676+
subscription_modify_mock.assert_not_called()
1677+
1678+
@patch("services.billing.stripe.PaymentMethod.attach")
1679+
@patch("services.billing.stripe.Customer.modify")
1680+
@patch("services.billing.stripe.Subscription.modify")
1681+
@patch("services.billing.stripe.PaymentMethod.retrieve")
1682+
def test_check_and_handle_delayed_notification_payment_methods_no_customer(
1683+
self,
1684+
payment_method_retrieve_mock,
1685+
subscription_modify_mock,
1686+
customer_modify_mock,
1687+
payment_method_attach_mock,
1688+
):
1689+
class MockPaymentMethod:
1690+
type = "us_bank_account"
1691+
us_bank_account = {}
1692+
id = "pm_123"
1693+
1694+
payment_method_retrieve_mock.return_value = MockPaymentMethod()
1695+
1696+
handler = StripeWebhookHandler()
1697+
handler._check_and_handle_delayed_notification_payment_methods(
1698+
"cus_123", "pm_123"
1699+
)
1700+
1701+
payment_method_retrieve_mock.assert_called_once_with("pm_123")
1702+
payment_method_attach_mock.assert_not_called()
1703+
customer_modify_mock.assert_not_called()
1704+
subscription_modify_mock.assert_not_called()
1705+
1706+
@patch("logging.Logger.error")
1707+
@patch("services.billing.stripe.PaymentMethod.attach")
1708+
@patch("services.billing.stripe.Customer.modify")
1709+
@patch("services.billing.stripe.Subscription.modify")
1710+
@patch("services.billing.stripe.PaymentMethod.retrieve")
1711+
def test_check_and_handle_delayed_notification_payment_methods_multiple_subscriptions(
1712+
self,
1713+
payment_method_retrieve_mock,
1714+
subscription_modify_mock,
1715+
customer_modify_mock,
1716+
payment_method_attach_mock,
1717+
):
1718+
class MockPaymentMethod:
1719+
type = "us_bank_account"
1720+
us_bank_account = {}
1721+
id = "pm_123"
1722+
1723+
payment_method_retrieve_mock.return_value = MockPaymentMethod()
1724+
1725+
self.owner.stripe_subscription_id = "sub_123"
1726+
self.owner.stripe_customer_id = "cus_123"
1727+
self.owner.save()
1728+
1729+
OwnerFactory(stripe_subscription_id="sub_124", stripe_customer_id="cus_123")
1730+
1731+
handler = StripeWebhookHandler()
1732+
handler._check_and_handle_delayed_notification_payment_methods(
1733+
"cus_123", "pm_123"
1734+
)
1735+
1736+
payment_method_retrieve_mock.assert_called_once_with("pm_123")
1737+
payment_method_attach_mock.assert_called_once_with(
1738+
payment_method_retrieve_mock.return_value, customer="cus_123"
1739+
)
1740+
customer_modify_mock.assert_called_once_with(
1741+
"cus_123",
1742+
invoice_settings={
1743+
"default_payment_method": payment_method_retrieve_mock.return_value
1744+
},
1745+
)
1746+
subscription_modify_mock.assert_has_calls(
1747+
[
1748+
call(
1749+
"sub_123",
1750+
default_payment_method=payment_method_retrieve_mock.return_value,
1751+
),
1752+
call(
1753+
"sub_124",
1754+
default_payment_method=payment_method_retrieve_mock.return_value,
1755+
),
1756+
]
1757+
)
1758+
16461759
@patch(
16471760
"billing.views.StripeWebhookHandler._check_and_handle_delayed_notification_payment_methods"
16481761
)

billing/views.py

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -539,7 +539,6 @@ def _check_and_handle_delayed_notification_payment_methods(
539539
When verification succeeds, this attaches the payment method to the customer and sets
540540
it as the default payment method for both the customer and subscription.
541541
"""
542-
owner = Owner.objects.get(stripe_customer_id=customer_id)
543542
payment_method = stripe.PaymentMethod.retrieve(payment_method_id)
544543

545544
is_us_bank_account = payment_method.type == "us_bank_account" and hasattr(
@@ -548,19 +547,34 @@ def _check_and_handle_delayed_notification_payment_methods(
548547

549548
should_set_as_default = is_us_bank_account
550549

550+
# attach the payment method + set as default on the invoice and subscription
551551
if should_set_as_default:
552-
# attach the payment method + set as default on the invoice and subscription
553-
stripe.PaymentMethod.attach(
554-
payment_method, customer=owner.stripe_customer_id
555-
)
556-
stripe.Customer.modify(
557-
owner.stripe_customer_id,
558-
invoice_settings={"default_payment_method": payment_method},
559-
)
560-
stripe.Subscription.modify(
561-
owner.stripe_subscription_id, default_payment_method=payment_method
552+
# retrieve the number of owners to update
553+
owners = Owner.objects.filter(
554+
stripe_customer_id=customer_id, stripe_subscription_id__isnull=False
562555
)
563556

557+
if owners.exists():
558+
# Even if multiple results are returned, these two stripe calls are
559+
# just for a single customer
560+
stripe.PaymentMethod.attach(payment_method, customer=customer_id)
561+
stripe.Customer.modify(
562+
customer_id,
563+
invoice_settings={"default_payment_method": payment_method},
564+
)
565+
566+
# But this one is for each subscription an owner may have
567+
for owner in owners:
568+
stripe.Subscription.modify(
569+
owner.stripe_subscription_id,
570+
default_payment_method=payment_method,
571+
)
572+
else:
573+
log.error(
574+
"No owners found with that customer_id, something went wrong",
575+
extra=dict(customer_id=customer_id),
576+
)
577+
564578
def payment_intent_succeeded(self, payment_intent: stripe.PaymentIntent) -> None:
565579
"""
566580
Stripe payment_intent.succeeded webhook event is emitted when a

0 commit comments

Comments
 (0)