billing: Create customer_has_credit_card_as_default_source function.

This commit is contained in:
Vishnu KS
2020-10-14 15:47:03 +05:30
committed by Tim Abbott
parent 6e3d4e7e75
commit cdf683e36f
7 changed files with 355 additions and 0 deletions

View File

@@ -338,6 +338,19 @@ def do_replace_payment_source(
return updated_stripe_customer return updated_stripe_customer
def stripe_customer_has_credit_card_as_default_source(stripe_customer: stripe.Customer) -> bool:
if not stripe_customer.default_source:
return False
return stripe_customer.default_source.object == "card"
def customer_has_credit_card_as_default_source(customer: Customer) -> bool:
if not customer.stripe_customer_id:
return False
stripe_customer = stripe_get_customer(customer.stripe_customer_id)
return stripe_customer_has_credit_card_as_default_source(stripe_customer)
# event_time should roughly be timezone_now(). Not designed to handle # event_time should roughly be timezone_now(). Not designed to handle
# event_times in the past or future # event_times in the past or future
@transaction.atomic @transaction.atomic

View File

@@ -0,0 +1,54 @@
{
"account_balance": 0,
"address": null,
"balance": 0,
"created": 1000000000,
"currency": null,
"default_source": null,
"delinquent": false,
"description": "zulip (Zulip Dev)",
"discount": null,
"email": "iago@zulip.com",
"id": "cus_NORMALIZED0001",
"invoice_prefix": "NORMA01",
"invoice_settings": {
"custom_fields": null,
"default_payment_method": null,
"footer": null
},
"livemode": false,
"metadata": {
"realm_id": "1",
"realm_str": "zulip"
},
"name": null,
"next_invoice_sequence": 1,
"object": "customer",
"phone": null,
"preferred_locales": [],
"shipping": null,
"sources": {
"data": [],
"has_more": false,
"object": "list",
"total_count": 0,
"url": "/v1/customers/cus_NORMALIZED0001/sources"
},
"subscriptions": {
"data": [],
"has_more": false,
"object": "list",
"total_count": 0,
"url": "/v1/customers/cus_NORMALIZED0001/subscriptions"
},
"tax_exempt": "none",
"tax_ids": {
"data": [],
"has_more": false,
"object": "list",
"total_count": 0,
"url": "/v1/customers/cus_NORMALIZED0001/tax_ids"
},
"tax_info": null,
"tax_info_verification": null
}

View File

@@ -0,0 +1,80 @@
{
"account_balance": 0,
"address": null,
"balance": 0,
"created": 1000000000,
"currency": null,
"default_source": "card_NORMALIZED00000000000001",
"delinquent": false,
"description": "zulip (Zulip Dev)",
"discount": null,
"email": "iago@zulip.com",
"id": "cus_NORMALIZED0002",
"invoice_prefix": "NORMA02",
"invoice_settings": {
"custom_fields": null,
"default_payment_method": null,
"footer": null
},
"livemode": false,
"metadata": {
"realm_id": "1",
"realm_str": "zulip"
},
"name": null,
"next_invoice_sequence": 1,
"object": "customer",
"phone": null,
"preferred_locales": [],
"shipping": null,
"sources": {
"data": [
{
"address_city": "Pacific",
"address_country": "United States",
"address_line1": "Under the sea,",
"address_line1_check": "pass",
"address_line2": null,
"address_state": null,
"address_zip": "33333",
"address_zip_check": "pass",
"brand": "Visa",
"country": "US",
"customer": "cus_NORMALIZED0002",
"cvc_check": "pass",
"dynamic_last4": null,
"exp_month": 3,
"exp_year": 2033,
"fingerprint": "NORMALIZED000001",
"funding": "credit",
"id": "card_NORMALIZED00000000000001",
"last4": "4242",
"metadata": {},
"name": "Ada Starr",
"object": "card",
"tokenization_method": null
}
],
"has_more": false,
"object": "list",
"total_count": 1,
"url": "/v1/customers/cus_NORMALIZED0002/sources"
},
"subscriptions": {
"data": [],
"has_more": false,
"object": "list",
"total_count": 0,
"url": "/v1/customers/cus_NORMALIZED0002/subscriptions"
},
"tax_exempt": "none",
"tax_ids": {
"data": [],
"has_more": false,
"object": "list",
"total_count": 0,
"url": "/v1/customers/cus_NORMALIZED0002/tax_ids"
},
"tax_info": null,
"tax_info_verification": null
}

View File

@@ -0,0 +1,54 @@
{
"account_balance": 0,
"address": null,
"balance": 0,
"created": 1000000000,
"currency": null,
"default_source": null,
"delinquent": false,
"description": "zulip (Zulip Dev)",
"discount": null,
"email": "iago@zulip.com",
"id": "cus_NORMALIZED0001",
"invoice_prefix": "NORMA01",
"invoice_settings": {
"custom_fields": null,
"default_payment_method": null,
"footer": null
},
"livemode": false,
"metadata": {
"realm_id": "1",
"realm_str": "zulip"
},
"name": null,
"next_invoice_sequence": 1,
"object": "customer",
"phone": null,
"preferred_locales": [],
"shipping": null,
"sources": {
"data": [],
"has_more": false,
"object": "list",
"total_count": 0,
"url": "/v1/customers/cus_NORMALIZED0001/sources"
},
"subscriptions": {
"data": [],
"has_more": false,
"object": "list",
"total_count": 0,
"url": "/v1/customers/cus_NORMALIZED0001/subscriptions"
},
"tax_exempt": "none",
"tax_ids": {
"data": [],
"has_more": false,
"object": "list",
"total_count": 0,
"url": "/v1/customers/cus_NORMALIZED0001/tax_ids"
},
"tax_info": null,
"tax_info_verification": null
}

View File

@@ -0,0 +1,104 @@
{
"account_balance": 0,
"address": null,
"balance": 0,
"created": 1000000000,
"currency": null,
"default_source": {
"address_city": "Pacific",
"address_country": "United States",
"address_line1": "Under the sea,",
"address_line1_check": "pass",
"address_line2": null,
"address_state": null,
"address_zip": "33333",
"address_zip_check": "pass",
"brand": "Visa",
"country": "US",
"customer": "cus_NORMALIZED0002",
"cvc_check": "pass",
"dynamic_last4": null,
"exp_month": 3,
"exp_year": 2033,
"fingerprint": "NORMALIZED000001",
"funding": "credit",
"id": "card_NORMALIZED00000000000001",
"last4": "4242",
"metadata": {},
"name": "Ada Starr",
"object": "card",
"tokenization_method": null
},
"delinquent": false,
"description": "zulip (Zulip Dev)",
"discount": null,
"email": "iago@zulip.com",
"id": "cus_NORMALIZED0002",
"invoice_prefix": "NORMA02",
"invoice_settings": {
"custom_fields": null,
"default_payment_method": null,
"footer": null
},
"livemode": false,
"metadata": {
"realm_id": "1",
"realm_str": "zulip"
},
"name": null,
"next_invoice_sequence": 1,
"object": "customer",
"phone": null,
"preferred_locales": [],
"shipping": null,
"sources": {
"data": [
{
"address_city": "Pacific",
"address_country": "United States",
"address_line1": "Under the sea,",
"address_line1_check": "pass",
"address_line2": null,
"address_state": null,
"address_zip": "33333",
"address_zip_check": "pass",
"brand": "Visa",
"country": "US",
"customer": "cus_NORMALIZED0002",
"cvc_check": "pass",
"dynamic_last4": null,
"exp_month": 3,
"exp_year": 2033,
"fingerprint": "NORMALIZED000001",
"funding": "credit",
"id": "card_NORMALIZED00000000000001",
"last4": "4242",
"metadata": {},
"name": "Ada Starr",
"object": "card",
"tokenization_method": null
}
],
"has_more": false,
"object": "list",
"total_count": 1,
"url": "/v1/customers/cus_NORMALIZED0002/sources"
},
"subscriptions": {
"data": [],
"has_more": false,
"object": "list",
"total_count": 0,
"url": "/v1/customers/cus_NORMALIZED0002/subscriptions"
},
"tax_exempt": "none",
"tax_ids": {
"data": [],
"has_more": false,
"object": "list",
"total_count": 0,
"url": "/v1/customers/cus_NORMALIZED0002/tax_ids"
},
"tax_info": null,
"tax_info_verification": null
}

View File

@@ -0,0 +1,33 @@
{
"card": {
"address_city": "Pacific",
"address_country": "United States",
"address_line1": "Under the sea,",
"address_line1_check": "unchecked",
"address_line2": null,
"address_state": null,
"address_zip": "33333",
"address_zip_check": "unchecked",
"brand": "Visa",
"country": "US",
"cvc_check": "unchecked",
"dynamic_last4": null,
"exp_month": 3,
"exp_year": 2033,
"fingerprint": "NORMALIZED000001",
"funding": "credit",
"id": "card_NORMALIZED00000000000001",
"last4": "4242",
"metadata": {},
"name": "Ada Starr",
"object": "card",
"tokenization_method": null
},
"client_ip": "0.0.0.0",
"created": 1000000000,
"id": "tok_NORMALIZED00000000000001",
"livemode": false,
"object": "token",
"type": "card",
"used": false
}

View File

@@ -29,6 +29,8 @@ from corporate.lib.stripe import (
attach_discount_to_realm, attach_discount_to_realm,
catch_stripe_errors, catch_stripe_errors,
compute_plan_parameters, compute_plan_parameters,
customer_has_credit_card_as_default_source,
do_create_stripe_customer,
get_discount_for_realm, get_discount_for_realm,
get_latest_seat_count, get_latest_seat_count,
get_price_per_license, get_price_per_license,
@@ -40,6 +42,7 @@ from corporate.lib.stripe import (
next_month, next_month,
process_initial_upgrade, process_initial_upgrade,
sign_string, sign_string,
stripe_customer_has_credit_card_as_default_source,
stripe_get_customer, stripe_get_customer,
unsign_string, unsign_string,
update_billing_method_of_current_plan, update_billing_method_of_current_plan,
@@ -502,6 +505,7 @@ class StripeTest(StripeTestCase):
Customer.objects.get(realm=user.realm).stripe_customer_id Customer.objects.get(realm=user.realm).stripe_customer_id
) )
self.assertEqual(stripe_customer.default_source.id[:5], "card_") self.assertEqual(stripe_customer.default_source.id[:5], "card_")
self.assertTrue(stripe_customer_has_credit_card_as_default_source(stripe_customer))
self.assertEqual(stripe_customer.description, "zulip (Zulip Dev)") self.assertEqual(stripe_customer.description, "zulip (Zulip Dev)")
self.assertEqual(stripe_customer.discount, None) self.assertEqual(stripe_customer.discount, None)
self.assertEqual(stripe_customer.email, user.email) self.assertEqual(stripe_customer.email, user.email)
@@ -662,6 +666,7 @@ class StripeTest(StripeTestCase):
stripe_customer = stripe_get_customer( stripe_customer = stripe_get_customer(
Customer.objects.get(realm=user.realm).stripe_customer_id Customer.objects.get(realm=user.realm).stripe_customer_id
) )
self.assertFalse(stripe_customer_has_credit_card_as_default_source(stripe_customer))
# It can take a second for Stripe to attach the source to the customer, and in # It can take a second for Stripe to attach the source to the customer, and in
# particular it may not be attached at the time stripe_get_customer is called above, # particular it may not be attached at the time stripe_get_customer is called above,
# causing test flakes. # causing test flakes.
@@ -2684,6 +2689,18 @@ class StripeTest(StripeTestCase):
self.assertEqual(realm_audit_log.acting_user, iago) self.assertEqual(realm_audit_log.acting_user, iago)
self.assertEqual(realm_audit_log.extra_data, str(expected_extra_data)) self.assertEqual(realm_audit_log.extra_data, str(expected_extra_data))
@mock_stripe()
def test_customer_has_credit_card_as_default_source(self, *mocks: Mock) -> None:
iago = self.example_user("iago")
customer = Customer.objects.create(realm=iago.realm)
self.assertFalse(customer_has_credit_card_as_default_source(customer))
customer = do_create_stripe_customer(iago)
self.assertFalse(customer_has_credit_card_as_default_source(customer))
customer = do_create_stripe_customer(iago, stripe_token=stripe_create_token().id)
self.assertTrue(customer_has_credit_card_as_default_source(customer))
class RequiresBillingAccessTest(ZulipTestCase): class RequiresBillingAccessTest(ZulipTestCase):
def setUp(self) -> None: def setUp(self) -> None: