From e0f5fadb7980d0d268b48a0ce50414ab129e5eab Mon Sep 17 00:00:00 2001 From: Vishnu KS Date: Fri, 11 Jun 2021 16:23:45 +0530 Subject: [PATCH] billing: Downgrade small realms that are behind on payments. An organization with at most 5 users that is behind on payments isn't worth spending time on investigating the situation. For larger organizations, we likely want somewhat different logic that at least does not void invoices. --- corporate/lib/stripe.py | 42 +++ ...payments_as_needed--Customer.create.1.json | 54 ++++ ...payments_as_needed--Customer.create.2.json | 54 ++++ ...payments_as_needed--Customer.create.3.json | 54 ++++ ...payments_as_needed--Customer.create.4.json | 54 ++++ ...payments_as_needed--Customer.create.5.json | 54 ++++ ...payments_as_needed--Customer.create.6.json | 54 ++++ ..._payments_as_needed--Invoice.create.1.json | 133 +++++++++ ..._payments_as_needed--Invoice.create.2.json | 133 +++++++++ ..._payments_as_needed--Invoice.create.3.json | 133 +++++++++ ..._payments_as_needed--Invoice.create.4.json | 133 +++++++++ ..._payments_as_needed--Invoice.create.5.json | 133 +++++++++ ..._payments_as_needed--Invoice.create.6.json | 133 +++++++++ ..._payments_as_needed--Invoice.create.7.json | 133 +++++++++ ...as_needed--Invoice.finalize_invoice.1.json | 133 +++++++++ ...as_needed--Invoice.finalize_invoice.2.json | 133 +++++++++ ...as_needed--Invoice.finalize_invoice.3.json | 133 +++++++++ ...as_needed--Invoice.finalize_invoice.4.json | 133 +++++++++ ...as_needed--Invoice.finalize_invoice.5.json | 133 +++++++++ ...as_needed--Invoice.finalize_invoice.6.json | 133 +++++++++ ...as_needed--Invoice.finalize_invoice.7.json | 133 +++++++++ ...on_payments_as_needed--Invoice.list.1.json | 6 + ...on_payments_as_needed--Invoice.list.2.json | 140 +++++++++ ...on_payments_as_needed--Invoice.list.3.json | 273 ++++++++++++++++++ ...on_payments_as_needed--Invoice.list.4.json | 273 ++++++++++++++++++ ..._on_payments_as_needed--Invoice.pay.1.json | 133 +++++++++ ..._on_payments_as_needed--Invoice.pay.2.json | 133 +++++++++ ...ments_as_needed--InvoiceItem.create.1.json | 44 +++ ...ments_as_needed--InvoiceItem.create.2.json | 44 +++ ...ments_as_needed--InvoiceItem.create.3.json | 44 +++ ...ments_as_needed--InvoiceItem.create.4.json | 44 +++ ...ments_as_needed--InvoiceItem.create.5.json | 44 +++ ...ments_as_needed--InvoiceItem.create.6.json | 44 +++ ...ments_as_needed--InvoiceItem.create.7.json | 44 +++ corporate/tests/test_stripe.py | 135 ++++++++- .../downgrade-small-realms-behind-on-payments | 5 + .../manifests/prod_app_frontend_once.pp | 8 + stubs/stripe/__init__.pyi | 6 +- .../emails/realm_auto_downgraded.source.html | 25 ++ .../emails/realm_auto_downgraded.subject.txt | 1 + .../zerver/emails/realm_auto_downgraded.txt | 8 + version.py | 2 +- zerver/lib/send_email.py | 18 ++ zerver/tests/test_management_commands.py | 13 + ...wngrade_small_realms_behind_on_payments.py | 11 + 45 files changed, 3723 insertions(+), 3 deletions(-) create mode 100644 corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Customer.create.1.json create mode 100644 corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Customer.create.2.json create mode 100644 corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Customer.create.3.json create mode 100644 corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Customer.create.4.json create mode 100644 corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Customer.create.5.json create mode 100644 corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Customer.create.6.json create mode 100644 corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Invoice.create.1.json create mode 100644 corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Invoice.create.2.json create mode 100644 corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Invoice.create.3.json create mode 100644 corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Invoice.create.4.json create mode 100644 corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Invoice.create.5.json create mode 100644 corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Invoice.create.6.json create mode 100644 corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Invoice.create.7.json create mode 100644 corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Invoice.finalize_invoice.1.json create mode 100644 corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Invoice.finalize_invoice.2.json create mode 100644 corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Invoice.finalize_invoice.3.json create mode 100644 corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Invoice.finalize_invoice.4.json create mode 100644 corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Invoice.finalize_invoice.5.json create mode 100644 corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Invoice.finalize_invoice.6.json create mode 100644 corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Invoice.finalize_invoice.7.json create mode 100644 corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Invoice.list.1.json create mode 100644 corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Invoice.list.2.json create mode 100644 corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Invoice.list.3.json create mode 100644 corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Invoice.list.4.json create mode 100644 corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Invoice.pay.1.json create mode 100644 corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Invoice.pay.2.json create mode 100644 corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--InvoiceItem.create.1.json create mode 100644 corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--InvoiceItem.create.2.json create mode 100644 corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--InvoiceItem.create.3.json create mode 100644 corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--InvoiceItem.create.4.json create mode 100644 corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--InvoiceItem.create.5.json create mode 100644 corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--InvoiceItem.create.6.json create mode 100644 corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--InvoiceItem.create.7.json create mode 100644 puppet/zulip/files/cron.d/downgrade-small-realms-behind-on-payments create mode 100644 templates/zerver/emails/realm_auto_downgraded.source.html create mode 100644 templates/zerver/emails/realm_auto_downgraded.subject.txt create mode 100644 templates/zerver/emails/realm_auto_downgraded.txt create mode 100644 zilencer/management/commands/downgrade_small_realms_behind_on_payments.py diff --git a/corporate/lib/stripe.py b/corporate/lib/stripe.py index 8d8a085cc9..ebafe6da48 100644 --- a/corporate/lib/stripe.py +++ b/corporate/lib/stripe.py @@ -12,6 +12,7 @@ import stripe from django.conf import settings from django.core.signing import Signer from django.db import transaction +from django.urls import reverse from django.utils.timezone import now as timezone_now from django.utils.translation import gettext as _ from django.utils.translation import gettext_lazy @@ -26,6 +27,7 @@ from corporate.models import ( get_customer_by_realm, ) from zerver.lib.logging_util import log_to_file +from zerver.lib.send_email import FromAddress, send_email_to_billing_admins_and_realm_owners from zerver.lib.timestamp import datetime_to_timestamp, timestamp_to_datetime from zerver.models import Realm, RealmAuditLog, UserProfile, get_system_bot from zproject.config import get_secret @@ -988,6 +990,46 @@ def void_all_open_invoices(realm: Realm) -> int: return voided_invoices_count +def downgrade_small_realms_behind_on_payments_as_needed() -> None: + customers = Customer.objects.all() + for customer in customers: + realm = customer.realm + + # For larger realms, we generally want to talk to the customer + # before downgrading; so this logic only applies with 5. + if get_latest_seat_count(realm) >= 5: + continue + + if get_current_plan_by_customer(customer) is None: + continue + + due_invoice_count = 0 + for invoice in stripe.Invoice.list(customer=customer.stripe_customer_id, limit=2): + if invoice.status == "open": + due_invoice_count += 1 + + # Customers with only 1 overdue invoice are ignored. + if due_invoice_count < 2: + continue + + # We've now decided to downgrade this customer and void all invoices, and the below will execute this. + + downgrade_now_without_creating_additional_invoices(realm) + void_all_open_invoices(realm) + context: Dict[str, str] = { + "upgrade_url": f"{realm.uri}{reverse('initial_upgrade')}", + "realm": realm, + } + send_email_to_billing_admins_and_realm_owners( + "zerver/emails/realm_auto_downgraded", + realm, + from_name=FromAddress.security_email_from_name(language=realm.default_language), + from_address=FromAddress.tokenized_no_reply_address(), + language=realm.default_language, + context=context, + ) + + def update_billing_method_of_current_plan( realm: Realm, charge_automatically: bool, *, acting_user: Optional[UserProfile] ) -> None: diff --git a/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Customer.create.1.json b/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Customer.create.1.json new file mode 100644 index 0000000000..3776562c5a --- /dev/null +++ b/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Customer.create.1.json @@ -0,0 +1,54 @@ +{ + "account_balance": 0, + "address": null, + "balance": 0, + "created": 1000000000, + "currency": null, + "default_source": null, + "delinquent": false, + "description": "realm_425511 (None)", + "discount": null, + "email": "user-0-realm_425511@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": "realm_425511" + }, + "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 +} diff --git a/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Customer.create.2.json b/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Customer.create.2.json new file mode 100644 index 0000000000..770b0eb0a3 --- /dev/null +++ b/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Customer.create.2.json @@ -0,0 +1,54 @@ +{ + "account_balance": 0, + "address": null, + "balance": 0, + "created": 1000000000, + "currency": null, + "default_source": null, + "delinquent": false, + "description": "realm_701713 (None)", + "discount": null, + "email": "user-0-realm_701713@zulip.com", + "id": "cus_NORMALIZED0002", + "invoice_prefix": "NORMA02", + "invoice_settings": { + "custom_fields": null, + "default_payment_method": null, + "footer": null + }, + "livemode": false, + "metadata": { + "realm_id": "2", + "realm_str": "realm_701713" + }, + "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_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 +} diff --git a/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Customer.create.3.json b/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Customer.create.3.json new file mode 100644 index 0000000000..9b906b42f9 --- /dev/null +++ b/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Customer.create.3.json @@ -0,0 +1,54 @@ +{ + "account_balance": 0, + "address": null, + "balance": 0, + "created": 1000000000, + "currency": null, + "default_source": null, + "delinquent": false, + "description": "realm_612438 (None)", + "discount": null, + "email": "user-0-realm_612438@zulip.com", + "id": "cus_NORMALIZED0003", + "invoice_prefix": "NORMA03", + "invoice_settings": { + "custom_fields": null, + "default_payment_method": null, + "footer": null + }, + "livemode": false, + "metadata": { + "realm_id": "3", + "realm_str": "realm_612438" + }, + "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_NORMALIZED0003/sources" + }, + "subscriptions": { + "data": [], + "has_more": false, + "object": "list", + "total_count": 0, + "url": "/v1/customers/cus_NORMALIZED0003/subscriptions" + }, + "tax_exempt": "none", + "tax_ids": { + "data": [], + "has_more": false, + "object": "list", + "total_count": 0, + "url": "/v1/customers/cus_NORMALIZED0003/tax_ids" + }, + "tax_info": null, + "tax_info_verification": null +} diff --git a/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Customer.create.4.json b/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Customer.create.4.json new file mode 100644 index 0000000000..2947e819fe --- /dev/null +++ b/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Customer.create.4.json @@ -0,0 +1,54 @@ +{ + "account_balance": 0, + "address": null, + "balance": 0, + "created": 1000000000, + "currency": null, + "default_source": null, + "delinquent": false, + "description": "realm_158636 (None)", + "discount": null, + "email": "user-0-realm_158636@zulip.com", + "id": "cus_NORMALIZED0004", + "invoice_prefix": "NORMA04", + "invoice_settings": { + "custom_fields": null, + "default_payment_method": null, + "footer": null + }, + "livemode": false, + "metadata": { + "realm_id": "4", + "realm_str": "realm_158636" + }, + "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_NORMALIZED0004/sources" + }, + "subscriptions": { + "data": [], + "has_more": false, + "object": "list", + "total_count": 0, + "url": "/v1/customers/cus_NORMALIZED0004/subscriptions" + }, + "tax_exempt": "none", + "tax_ids": { + "data": [], + "has_more": false, + "object": "list", + "total_count": 0, + "url": "/v1/customers/cus_NORMALIZED0004/tax_ids" + }, + "tax_info": null, + "tax_info_verification": null +} diff --git a/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Customer.create.5.json b/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Customer.create.5.json new file mode 100644 index 0000000000..3987d8d629 --- /dev/null +++ b/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Customer.create.5.json @@ -0,0 +1,54 @@ +{ + "account_balance": 0, + "address": null, + "balance": 0, + "created": 1000000000, + "currency": null, + "default_source": null, + "delinquent": false, + "description": "realm_345953 (None)", + "discount": null, + "email": "user-0-realm_345953@zulip.com", + "id": "cus_NORMALIZED0005", + "invoice_prefix": "NORMA05", + "invoice_settings": { + "custom_fields": null, + "default_payment_method": null, + "footer": null + }, + "livemode": false, + "metadata": { + "realm_id": "5", + "realm_str": "realm_345953" + }, + "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_NORMALIZED0005/sources" + }, + "subscriptions": { + "data": [], + "has_more": false, + "object": "list", + "total_count": 0, + "url": "/v1/customers/cus_NORMALIZED0005/subscriptions" + }, + "tax_exempt": "none", + "tax_ids": { + "data": [], + "has_more": false, + "object": "list", + "total_count": 0, + "url": "/v1/customers/cus_NORMALIZED0005/tax_ids" + }, + "tax_info": null, + "tax_info_verification": null +} diff --git a/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Customer.create.6.json b/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Customer.create.6.json new file mode 100644 index 0000000000..ccdd4d1df0 --- /dev/null +++ b/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Customer.create.6.json @@ -0,0 +1,54 @@ +{ + "account_balance": 0, + "address": null, + "balance": 0, + "created": 1000000000, + "currency": null, + "default_source": null, + "delinquent": false, + "description": "realm_186548 (None)", + "discount": null, + "email": "user-0-realm_186548@zulip.com", + "id": "cus_NORMALIZED0006", + "invoice_prefix": "NORMA06", + "invoice_settings": { + "custom_fields": null, + "default_payment_method": null, + "footer": null + }, + "livemode": false, + "metadata": { + "realm_id": "6", + "realm_str": "realm_186548" + }, + "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_NORMALIZED0006/sources" + }, + "subscriptions": { + "data": [], + "has_more": false, + "object": "list", + "total_count": 0, + "url": "/v1/customers/cus_NORMALIZED0006/subscriptions" + }, + "tax_exempt": "none", + "tax_ids": { + "data": [], + "has_more": false, + "object": "list", + "total_count": 0, + "url": "/v1/customers/cus_NORMALIZED0006/tax_ids" + }, + "tax_info": null, + "tax_info_verification": null +} diff --git a/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Invoice.create.1.json b/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Invoice.create.1.json new file mode 100644 index 0000000000..90dd65f168 --- /dev/null +++ b/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Invoice.create.1.json @@ -0,0 +1,133 @@ +{ + "account_country": "US", + "account_name": "NORMALIZED-1", + "account_tax_ids": null, + "amount_due": 10000, + "amount_paid": 0, + "amount_remaining": 10000, + "application_fee": null, + "attempt_count": 0, + "attempted": false, + "auto_advance": true, + "automatic_tax": { + "enabled": false, + "status": null + }, + "billing": "send_invoice", + "billing_reason": "manual", + "charge": null, + "collection_method": "send_invoice", + "created": 1000000000, + "currency": "usd", + "custom_fields": null, + "customer": "cus_NORMALIZED0003", + "customer_address": null, + "customer_email": "user-0-realm_612438@zulip.com", + "customer_name": null, + "customer_phone": null, + "customer_shipping": null, + "customer_tax_exempt": "none", + "customer_tax_ids": [], + "date": 1000000000, + "default_payment_method": null, + "default_source": null, + "default_tax_rates": [], + "description": null, + "discount": null, + "discounts": [], + "due_date": 1000000000, + "ending_balance": null, + "finalized_at": null, + "footer": null, + "hosted_invoice_url": null, + "id": "in_NORMALIZED00000000000001", + "invoice_pdf": null, + "last_finalization_error": null, + "lines": { + "data": [ + { + "amount": 10000, + "currency": "usd", + "description": "Zulip standard", + "discount_amounts": [], + "discountable": false, + "discounts": [], + "id": "ii_NORMALIZED00000000000001", + "invoice_item": "ii_NORMALIZED00000000000001", + "livemode": false, + "metadata": {}, + "object": "line_item", + "period": { + "end": 1000000000, + "start": 1000000000 + }, + "plan": null, + "price": { + "active": false, + "billing_scheme": "per_unit", + "created": 1000000000, + "currency": "usd", + "id": "price_1J5YW6D2X8vgpBNGNRpvTb7X", + "livemode": false, + "lookup_key": null, + "metadata": {}, + "nickname": null, + "object": "price", + "product": "prod_Jj0XEKpg4Z7fzV", + "recurring": null, + "tiers_mode": null, + "transform_quantity": null, + "type": "one_time", + "unit_amount": 10000, + "unit_amount_decimal": "10000" + }, + "proration": false, + "quantity": 1, + "subscription": null, + "tax_amounts": [], + "tax_rates": [], + "type": "invoiceitem", + "unique_id": "il_1J5YW6D2X8vgpBNGJfmJCwyJ" + } + ], + "has_more": false, + "object": "list", + "total_count": 1, + "url": "/v1/invoices/in_NORMALIZED00000000000001/lines" + }, + "livemode": false, + "metadata": {}, + "next_payment_attempt": null, + "number": "NORMALI-0001", + "object": "invoice", + "on_behalf_of": null, + "paid": false, + "payment_intent": null, + "payment_settings": { + "payment_method_options": null, + "payment_method_types": null + }, + "period_end": 1000000000, + "period_start": 1000000000, + "post_payment_credit_notes_amount": 0, + "pre_payment_credit_notes_amount": 0, + "receipt_number": null, + "starting_balance": 0, + "statement_descriptor": "Zulip Standard", + "status": "draft", + "status_transitions": { + "finalized_at": null, + "marked_uncollectible_at": null, + "paid_at": null, + "voided_at": null + }, + "subscription": null, + "subtotal": 10000, + "tax": null, + "tax_percent": null, + "total": 10000, + "total_discount_amounts": [], + "total_tax_amounts": [], + "transfer_data": null, + "webhooks_delivered_at": null +} diff --git a/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Invoice.create.2.json b/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Invoice.create.2.json new file mode 100644 index 0000000000..6005a53c14 --- /dev/null +++ b/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Invoice.create.2.json @@ -0,0 +1,133 @@ +{ + "account_country": "US", + "account_name": "NORMALIZED-1", + "account_tax_ids": null, + "amount_due": 10000, + "amount_paid": 0, + "amount_remaining": 10000, + "application_fee": null, + "attempt_count": 0, + "attempted": false, + "auto_advance": true, + "automatic_tax": { + "enabled": false, + "status": null + }, + "billing": "send_invoice", + "billing_reason": "manual", + "charge": null, + "collection_method": "send_invoice", + "created": 1000000000, + "currency": "usd", + "custom_fields": null, + "customer": "cus_NORMALIZED0004", + "customer_address": null, + "customer_email": "user-0-realm_158636@zulip.com", + "customer_name": null, + "customer_phone": null, + "customer_shipping": null, + "customer_tax_exempt": "none", + "customer_tax_ids": [], + "date": 1000000000, + "default_payment_method": null, + "default_source": null, + "default_tax_rates": [], + "description": null, + "discount": null, + "discounts": [], + "due_date": 1000000000, + "ending_balance": null, + "finalized_at": null, + "footer": null, + "hosted_invoice_url": null, + "id": "in_NORMALIZED00000000000002", + "invoice_pdf": null, + "last_finalization_error": null, + "lines": { + "data": [ + { + "amount": 10000, + "currency": "usd", + "description": "Zulip standard", + "discount_amounts": [], + "discountable": false, + "discounts": [], + "id": "ii_NORMALIZED00000000000002", + "invoice_item": "ii_NORMALIZED00000000000002", + "livemode": false, + "metadata": {}, + "object": "line_item", + "period": { + "end": 1000000000, + "start": 1000000000 + }, + "plan": null, + "price": { + "active": false, + "billing_scheme": "per_unit", + "created": 1000000000, + "currency": "usd", + "id": "price_1J5YW8D2X8vgpBNGjQO5ySQC", + "livemode": false, + "lookup_key": null, + "metadata": {}, + "nickname": null, + "object": "price", + "product": "prod_Jj0XcBc6MoFHgE", + "recurring": null, + "tiers_mode": null, + "transform_quantity": null, + "type": "one_time", + "unit_amount": 10000, + "unit_amount_decimal": "10000" + }, + "proration": false, + "quantity": 1, + "subscription": null, + "tax_amounts": [], + "tax_rates": [], + "type": "invoiceitem", + "unique_id": "il_1J5YW8D2X8vgpBNGf92BtWOV" + } + ], + "has_more": false, + "object": "list", + "total_count": 1, + "url": "/v1/invoices/in_NORMALIZED00000000000002/lines" + }, + "livemode": false, + "metadata": {}, + "next_payment_attempt": null, + "number": "NORMALI-0002", + "object": "invoice", + "on_behalf_of": null, + "paid": false, + "payment_intent": null, + "payment_settings": { + "payment_method_options": null, + "payment_method_types": null + }, + "period_end": 1000000000, + "period_start": 1000000000, + "post_payment_credit_notes_amount": 0, + "pre_payment_credit_notes_amount": 0, + "receipt_number": null, + "starting_balance": 0, + "statement_descriptor": "Zulip Standard", + "status": "draft", + "status_transitions": { + "finalized_at": null, + "marked_uncollectible_at": null, + "paid_at": null, + "voided_at": null + }, + "subscription": null, + "subtotal": 10000, + "tax": null, + "tax_percent": null, + "total": 10000, + "total_discount_amounts": [], + "total_tax_amounts": [], + "transfer_data": null, + "webhooks_delivered_at": null +} diff --git a/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Invoice.create.3.json b/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Invoice.create.3.json new file mode 100644 index 0000000000..6ce4cb688d --- /dev/null +++ b/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Invoice.create.3.json @@ -0,0 +1,133 @@ +{ + "account_country": "US", + "account_name": "NORMALIZED-1", + "account_tax_ids": null, + "amount_due": 10000, + "amount_paid": 0, + "amount_remaining": 10000, + "application_fee": null, + "attempt_count": 0, + "attempted": false, + "auto_advance": true, + "automatic_tax": { + "enabled": false, + "status": null + }, + "billing": "send_invoice", + "billing_reason": "manual", + "charge": null, + "collection_method": "send_invoice", + "created": 1000000000, + "currency": "usd", + "custom_fields": null, + "customer": "cus_NORMALIZED0004", + "customer_address": null, + "customer_email": "user-0-realm_158636@zulip.com", + "customer_name": null, + "customer_phone": null, + "customer_shipping": null, + "customer_tax_exempt": "none", + "customer_tax_ids": [], + "date": 1000000000, + "default_payment_method": null, + "default_source": null, + "default_tax_rates": [], + "description": null, + "discount": null, + "discounts": [], + "due_date": 1000000000, + "ending_balance": null, + "finalized_at": null, + "footer": null, + "hosted_invoice_url": null, + "id": "in_NORMALIZED00000000000003", + "invoice_pdf": null, + "last_finalization_error": null, + "lines": { + "data": [ + { + "amount": 10000, + "currency": "usd", + "description": "Zulip standard", + "discount_amounts": [], + "discountable": false, + "discounts": [], + "id": "ii_NORMALIZED00000000000003", + "invoice_item": "ii_NORMALIZED00000000000003", + "livemode": false, + "metadata": {}, + "object": "line_item", + "period": { + "end": 1000000000, + "start": 1000000000 + }, + "plan": null, + "price": { + "active": false, + "billing_scheme": "per_unit", + "created": 1000000000, + "currency": "usd", + "id": "price_1J5YWBD2X8vgpBNGk65s4Bby", + "livemode": false, + "lookup_key": null, + "metadata": {}, + "nickname": null, + "object": "price", + "product": "prod_Jj0XXEj43ttifM", + "recurring": null, + "tiers_mode": null, + "transform_quantity": null, + "type": "one_time", + "unit_amount": 10000, + "unit_amount_decimal": "10000" + }, + "proration": false, + "quantity": 1, + "subscription": null, + "tax_amounts": [], + "tax_rates": [], + "type": "invoiceitem", + "unique_id": "il_1J5YWBD2X8vgpBNGdBrYTZWS" + } + ], + "has_more": false, + "object": "list", + "total_count": 1, + "url": "/v1/invoices/in_NORMALIZED00000000000003/lines" + }, + "livemode": false, + "metadata": {}, + "next_payment_attempt": null, + "number": "NORMALI-0003", + "object": "invoice", + "on_behalf_of": null, + "paid": false, + "payment_intent": null, + "payment_settings": { + "payment_method_options": null, + "payment_method_types": null + }, + "period_end": 1000000000, + "period_start": 1000000000, + "post_payment_credit_notes_amount": 0, + "pre_payment_credit_notes_amount": 0, + "receipt_number": null, + "starting_balance": 0, + "statement_descriptor": "Zulip Standard", + "status": "draft", + "status_transitions": { + "finalized_at": null, + "marked_uncollectible_at": null, + "paid_at": null, + "voided_at": null + }, + "subscription": null, + "subtotal": 10000, + "tax": null, + "tax_percent": null, + "total": 10000, + "total_discount_amounts": [], + "total_tax_amounts": [], + "transfer_data": null, + "webhooks_delivered_at": null +} diff --git a/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Invoice.create.4.json b/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Invoice.create.4.json new file mode 100644 index 0000000000..8d29349432 --- /dev/null +++ b/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Invoice.create.4.json @@ -0,0 +1,133 @@ +{ + "account_country": "US", + "account_name": "NORMALIZED-1", + "account_tax_ids": null, + "amount_due": 10000, + "amount_paid": 0, + "amount_remaining": 10000, + "application_fee": null, + "attempt_count": 0, + "attempted": false, + "auto_advance": true, + "automatic_tax": { + "enabled": false, + "status": null + }, + "billing": "send_invoice", + "billing_reason": "manual", + "charge": null, + "collection_method": "send_invoice", + "created": 1000000000, + "currency": "usd", + "custom_fields": null, + "customer": "cus_NORMALIZED0005", + "customer_address": null, + "customer_email": "user-0-realm_345953@zulip.com", + "customer_name": null, + "customer_phone": null, + "customer_shipping": null, + "customer_tax_exempt": "none", + "customer_tax_ids": [], + "date": 1000000000, + "default_payment_method": null, + "default_source": null, + "default_tax_rates": [], + "description": null, + "discount": null, + "discounts": [], + "due_date": 1000000000, + "ending_balance": null, + "finalized_at": null, + "footer": null, + "hosted_invoice_url": null, + "id": "in_NORMALIZED00000000000004", + "invoice_pdf": null, + "last_finalization_error": null, + "lines": { + "data": [ + { + "amount": 10000, + "currency": "usd", + "description": "Zulip standard", + "discount_amounts": [], + "discountable": false, + "discounts": [], + "id": "ii_NORMALIZED00000000000004", + "invoice_item": "ii_NORMALIZED00000000000004", + "livemode": false, + "metadata": {}, + "object": "line_item", + "period": { + "end": 1000000000, + "start": 1000000000 + }, + "plan": null, + "price": { + "active": false, + "billing_scheme": "per_unit", + "created": 1000000000, + "currency": "usd", + "id": "price_1J5YWED2X8vgpBNGJPqvl8ls", + "livemode": false, + "lookup_key": null, + "metadata": {}, + "nickname": null, + "object": "price", + "product": "prod_Jj0XOrbeS8UGnX", + "recurring": null, + "tiers_mode": null, + "transform_quantity": null, + "type": "one_time", + "unit_amount": 10000, + "unit_amount_decimal": "10000" + }, + "proration": false, + "quantity": 1, + "subscription": null, + "tax_amounts": [], + "tax_rates": [], + "type": "invoiceitem", + "unique_id": "il_1J5YWED2X8vgpBNGcPMTOMfD" + } + ], + "has_more": false, + "object": "list", + "total_count": 1, + "url": "/v1/invoices/in_NORMALIZED00000000000004/lines" + }, + "livemode": false, + "metadata": {}, + "next_payment_attempt": null, + "number": "NORMALI-0004", + "object": "invoice", + "on_behalf_of": null, + "paid": false, + "payment_intent": null, + "payment_settings": { + "payment_method_options": null, + "payment_method_types": null + }, + "period_end": 1000000000, + "period_start": 1000000000, + "post_payment_credit_notes_amount": 0, + "pre_payment_credit_notes_amount": 0, + "receipt_number": null, + "starting_balance": 0, + "statement_descriptor": "Zulip Standard", + "status": "draft", + "status_transitions": { + "finalized_at": null, + "marked_uncollectible_at": null, + "paid_at": null, + "voided_at": null + }, + "subscription": null, + "subtotal": 10000, + "tax": null, + "tax_percent": null, + "total": 10000, + "total_discount_amounts": [], + "total_tax_amounts": [], + "transfer_data": null, + "webhooks_delivered_at": null +} diff --git a/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Invoice.create.5.json b/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Invoice.create.5.json new file mode 100644 index 0000000000..59e25e602f --- /dev/null +++ b/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Invoice.create.5.json @@ -0,0 +1,133 @@ +{ + "account_country": "US", + "account_name": "NORMALIZED-1", + "account_tax_ids": null, + "amount_due": 10000, + "amount_paid": 0, + "amount_remaining": 10000, + "application_fee": null, + "attempt_count": 0, + "attempted": false, + "auto_advance": true, + "automatic_tax": { + "enabled": false, + "status": null + }, + "billing": "send_invoice", + "billing_reason": "manual", + "charge": null, + "collection_method": "send_invoice", + "created": 1000000000, + "currency": "usd", + "custom_fields": null, + "customer": "cus_NORMALIZED0005", + "customer_address": null, + "customer_email": "user-0-realm_345953@zulip.com", + "customer_name": null, + "customer_phone": null, + "customer_shipping": null, + "customer_tax_exempt": "none", + "customer_tax_ids": [], + "date": 1000000000, + "default_payment_method": null, + "default_source": null, + "default_tax_rates": [], + "description": null, + "discount": null, + "discounts": [], + "due_date": 1000000000, + "ending_balance": null, + "finalized_at": null, + "footer": null, + "hosted_invoice_url": null, + "id": "in_NORMALIZED00000000000005", + "invoice_pdf": null, + "last_finalization_error": null, + "lines": { + "data": [ + { + "amount": 10000, + "currency": "usd", + "description": "Zulip standard", + "discount_amounts": [], + "discountable": false, + "discounts": [], + "id": "ii_NORMALIZED00000000000005", + "invoice_item": "ii_NORMALIZED00000000000005", + "livemode": false, + "metadata": {}, + "object": "line_item", + "period": { + "end": 1000000000, + "start": 1000000000 + }, + "plan": null, + "price": { + "active": false, + "billing_scheme": "per_unit", + "created": 1000000000, + "currency": "usd", + "id": "price_1J5YWHD2X8vgpBNGwEA9hn4b", + "livemode": false, + "lookup_key": null, + "metadata": {}, + "nickname": null, + "object": "price", + "product": "prod_Jj0XvpvSalZcLc", + "recurring": null, + "tiers_mode": null, + "transform_quantity": null, + "type": "one_time", + "unit_amount": 10000, + "unit_amount_decimal": "10000" + }, + "proration": false, + "quantity": 1, + "subscription": null, + "tax_amounts": [], + "tax_rates": [], + "type": "invoiceitem", + "unique_id": "il_1J5YWHD2X8vgpBNGmcQgjQGx" + } + ], + "has_more": false, + "object": "list", + "total_count": 1, + "url": "/v1/invoices/in_NORMALIZED00000000000005/lines" + }, + "livemode": false, + "metadata": {}, + "next_payment_attempt": null, + "number": "NORMALI-0005", + "object": "invoice", + "on_behalf_of": null, + "paid": false, + "payment_intent": null, + "payment_settings": { + "payment_method_options": null, + "payment_method_types": null + }, + "period_end": 1000000000, + "period_start": 1000000000, + "post_payment_credit_notes_amount": 0, + "pre_payment_credit_notes_amount": 0, + "receipt_number": null, + "starting_balance": 0, + "statement_descriptor": "Zulip Standard", + "status": "draft", + "status_transitions": { + "finalized_at": null, + "marked_uncollectible_at": null, + "paid_at": null, + "voided_at": null + }, + "subscription": null, + "subtotal": 10000, + "tax": null, + "tax_percent": null, + "total": 10000, + "total_discount_amounts": [], + "total_tax_amounts": [], + "transfer_data": null, + "webhooks_delivered_at": null +} diff --git a/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Invoice.create.6.json b/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Invoice.create.6.json new file mode 100644 index 0000000000..ca1621a0c4 --- /dev/null +++ b/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Invoice.create.6.json @@ -0,0 +1,133 @@ +{ + "account_country": "US", + "account_name": "NORMALIZED-1", + "account_tax_ids": null, + "amount_due": 10000, + "amount_paid": 0, + "amount_remaining": 10000, + "application_fee": null, + "attempt_count": 0, + "attempted": false, + "auto_advance": true, + "automatic_tax": { + "enabled": false, + "status": null + }, + "billing": "send_invoice", + "billing_reason": "manual", + "charge": null, + "collection_method": "send_invoice", + "created": 1000000000, + "currency": "usd", + "custom_fields": null, + "customer": "cus_NORMALIZED0006", + "customer_address": null, + "customer_email": "user-0-realm_186548@zulip.com", + "customer_name": null, + "customer_phone": null, + "customer_shipping": null, + "customer_tax_exempt": "none", + "customer_tax_ids": [], + "date": 1000000000, + "default_payment_method": null, + "default_source": null, + "default_tax_rates": [], + "description": null, + "discount": null, + "discounts": [], + "due_date": 1000000000, + "ending_balance": null, + "finalized_at": null, + "footer": null, + "hosted_invoice_url": null, + "id": "in_NORMALIZED00000000000006", + "invoice_pdf": null, + "last_finalization_error": null, + "lines": { + "data": [ + { + "amount": 10000, + "currency": "usd", + "description": "Zulip standard", + "discount_amounts": [], + "discountable": false, + "discounts": [], + "id": "ii_NORMALIZED00000000000006", + "invoice_item": "ii_NORMALIZED00000000000006", + "livemode": false, + "metadata": {}, + "object": "line_item", + "period": { + "end": 1000000000, + "start": 1000000000 + }, + "plan": null, + "price": { + "active": false, + "billing_scheme": "per_unit", + "created": 1000000000, + "currency": "usd", + "id": "price_1J5YWMD2X8vgpBNGGQAGsuaj", + "livemode": false, + "lookup_key": null, + "metadata": {}, + "nickname": null, + "object": "price", + "product": "prod_Jj0XRALl1QAQk5", + "recurring": null, + "tiers_mode": null, + "transform_quantity": null, + "type": "one_time", + "unit_amount": 10000, + "unit_amount_decimal": "10000" + }, + "proration": false, + "quantity": 1, + "subscription": null, + "tax_amounts": [], + "tax_rates": [], + "type": "invoiceitem", + "unique_id": "il_1J5YWMD2X8vgpBNGTMs8cYiR" + } + ], + "has_more": false, + "object": "list", + "total_count": 1, + "url": "/v1/invoices/in_NORMALIZED00000000000006/lines" + }, + "livemode": false, + "metadata": {}, + "next_payment_attempt": null, + "number": "NORMALI-0006", + "object": "invoice", + "on_behalf_of": null, + "paid": false, + "payment_intent": null, + "payment_settings": { + "payment_method_options": null, + "payment_method_types": null + }, + "period_end": 1000000000, + "period_start": 1000000000, + "post_payment_credit_notes_amount": 0, + "pre_payment_credit_notes_amount": 0, + "receipt_number": null, + "starting_balance": 0, + "statement_descriptor": "Zulip Standard", + "status": "draft", + "status_transitions": { + "finalized_at": null, + "marked_uncollectible_at": null, + "paid_at": null, + "voided_at": null + }, + "subscription": null, + "subtotal": 10000, + "tax": null, + "tax_percent": null, + "total": 10000, + "total_discount_amounts": [], + "total_tax_amounts": [], + "transfer_data": null, + "webhooks_delivered_at": null +} diff --git a/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Invoice.create.7.json b/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Invoice.create.7.json new file mode 100644 index 0000000000..a3242d50df --- /dev/null +++ b/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Invoice.create.7.json @@ -0,0 +1,133 @@ +{ + "account_country": "US", + "account_name": "NORMALIZED-1", + "account_tax_ids": null, + "amount_due": 10000, + "amount_paid": 0, + "amount_remaining": 10000, + "application_fee": null, + "attempt_count": 0, + "attempted": false, + "auto_advance": true, + "automatic_tax": { + "enabled": false, + "status": null + }, + "billing": "send_invoice", + "billing_reason": "manual", + "charge": null, + "collection_method": "send_invoice", + "created": 1000000000, + "currency": "usd", + "custom_fields": null, + "customer": "cus_NORMALIZED0006", + "customer_address": null, + "customer_email": "user-0-realm_186548@zulip.com", + "customer_name": null, + "customer_phone": null, + "customer_shipping": null, + "customer_tax_exempt": "none", + "customer_tax_ids": [], + "date": 1000000000, + "default_payment_method": null, + "default_source": null, + "default_tax_rates": [], + "description": null, + "discount": null, + "discounts": [], + "due_date": 1000000000, + "ending_balance": null, + "finalized_at": null, + "footer": null, + "hosted_invoice_url": null, + "id": "in_NORMALIZED00000000000007", + "invoice_pdf": null, + "last_finalization_error": null, + "lines": { + "data": [ + { + "amount": 10000, + "currency": "usd", + "description": "Zulip standard", + "discount_amounts": [], + "discountable": false, + "discounts": [], + "id": "ii_NORMALIZED00000000000007", + "invoice_item": "ii_NORMALIZED00000000000007", + "livemode": false, + "metadata": {}, + "object": "line_item", + "period": { + "end": 1000000000, + "start": 1000000000 + }, + "plan": null, + "price": { + "active": false, + "billing_scheme": "per_unit", + "created": 1000000000, + "currency": "usd", + "id": "price_1J5YWPD2X8vgpBNGdU5odKe9", + "livemode": false, + "lookup_key": null, + "metadata": {}, + "nickname": null, + "object": "price", + "product": "prod_Jj0X7KLLErkxlq", + "recurring": null, + "tiers_mode": null, + "transform_quantity": null, + "type": "one_time", + "unit_amount": 10000, + "unit_amount_decimal": "10000" + }, + "proration": false, + "quantity": 1, + "subscription": null, + "tax_amounts": [], + "tax_rates": [], + "type": "invoiceitem", + "unique_id": "il_1J5YWPD2X8vgpBNGayzkkXN2" + } + ], + "has_more": false, + "object": "list", + "total_count": 1, + "url": "/v1/invoices/in_NORMALIZED00000000000007/lines" + }, + "livemode": false, + "metadata": {}, + "next_payment_attempt": null, + "number": "NORMALI-0007", + "object": "invoice", + "on_behalf_of": null, + "paid": false, + "payment_intent": null, + "payment_settings": { + "payment_method_options": null, + "payment_method_types": null + }, + "period_end": 1000000000, + "period_start": 1000000000, + "post_payment_credit_notes_amount": 0, + "pre_payment_credit_notes_amount": 0, + "receipt_number": null, + "starting_balance": 0, + "statement_descriptor": "Zulip Standard", + "status": "draft", + "status_transitions": { + "finalized_at": null, + "marked_uncollectible_at": null, + "paid_at": null, + "voided_at": null + }, + "subscription": null, + "subtotal": 10000, + "tax": null, + "tax_percent": null, + "total": 10000, + "total_discount_amounts": [], + "total_tax_amounts": [], + "transfer_data": null, + "webhooks_delivered_at": null +} diff --git a/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Invoice.finalize_invoice.1.json b/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Invoice.finalize_invoice.1.json new file mode 100644 index 0000000000..c202e76b61 --- /dev/null +++ b/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Invoice.finalize_invoice.1.json @@ -0,0 +1,133 @@ +{ + "account_country": "US", + "account_name": "NORMALIZED-1", + "account_tax_ids": null, + "amount_due": 10000, + "amount_paid": 0, + "amount_remaining": 10000, + "application_fee": null, + "attempt_count": 0, + "attempted": false, + "auto_advance": true, + "automatic_tax": { + "enabled": false, + "status": null + }, + "billing": "send_invoice", + "billing_reason": "manual", + "charge": null, + "collection_method": "send_invoice", + "created": 1000000000, + "currency": "usd", + "custom_fields": null, + "customer": "cus_NORMALIZED0003", + "customer_address": null, + "customer_email": "user-0-realm_612438@zulip.com", + "customer_name": null, + "customer_phone": null, + "customer_shipping": null, + "customer_tax_exempt": "none", + "customer_tax_ids": [], + "date": 1000000000, + "default_payment_method": null, + "default_source": null, + "default_tax_rates": [], + "description": null, + "discount": null, + "discounts": [], + "due_date": 1000000000, + "ending_balance": 0, + "finalized_at": 1000000000, + "footer": null, + "hosted_invoice_url": "https://invoice.stripe.com/i/acct_NORMALIZED000001/invst_NORMALIZED0000000000000001w2De8", + "id": "in_NORMALIZED00000000000001", + "invoice_pdf": "https://pay.stripe.com/invoice/acct_NORMALIZED000001/invst_NORMALIZED0000000000000001w2De8/pdf", + "last_finalization_error": null, + "lines": { + "data": [ + { + "amount": 10000, + "currency": "usd", + "description": "Zulip standard", + "discount_amounts": [], + "discountable": false, + "discounts": [], + "id": "ii_NORMALIZED00000000000001", + "invoice_item": "ii_NORMALIZED00000000000001", + "livemode": false, + "metadata": {}, + "object": "line_item", + "period": { + "end": 1000000000, + "start": 1000000000 + }, + "plan": null, + "price": { + "active": false, + "billing_scheme": "per_unit", + "created": 1000000000, + "currency": "usd", + "id": "price_1J5YW6D2X8vgpBNGNRpvTb7X", + "livemode": false, + "lookup_key": null, + "metadata": {}, + "nickname": null, + "object": "price", + "product": "prod_Jj0XEKpg4Z7fzV", + "recurring": null, + "tiers_mode": null, + "transform_quantity": null, + "type": "one_time", + "unit_amount": 10000, + "unit_amount_decimal": "10000" + }, + "proration": false, + "quantity": 1, + "subscription": null, + "tax_amounts": [], + "tax_rates": [], + "type": "invoiceitem", + "unique_id": "il_1J5YW6D2X8vgpBNGJfmJCwyJ" + } + ], + "has_more": false, + "object": "list", + "total_count": 1, + "url": "/v1/invoices/in_NORMALIZED00000000000001/lines" + }, + "livemode": false, + "metadata": {}, + "next_payment_attempt": null, + "number": "NORMALI-0001", + "object": "invoice", + "on_behalf_of": null, + "paid": false, + "payment_intent": "pi_1J5YW7D2X8vgpBNG5Yb5dYds", + "payment_settings": { + "payment_method_options": null, + "payment_method_types": null + }, + "period_end": 1000000000, + "period_start": 1000000000, + "post_payment_credit_notes_amount": 0, + "pre_payment_credit_notes_amount": 0, + "receipt_number": null, + "starting_balance": 0, + "statement_descriptor": "Zulip Standard", + "status": "open", + "status_transitions": { + "finalized_at": 1000000000, + "marked_uncollectible_at": null, + "paid_at": null, + "voided_at": null + }, + "subscription": null, + "subtotal": 10000, + "tax": null, + "tax_percent": null, + "total": 10000, + "total_discount_amounts": [], + "total_tax_amounts": [], + "transfer_data": null, + "webhooks_delivered_at": null +} diff --git a/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Invoice.finalize_invoice.2.json b/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Invoice.finalize_invoice.2.json new file mode 100644 index 0000000000..4aad5a8ff1 --- /dev/null +++ b/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Invoice.finalize_invoice.2.json @@ -0,0 +1,133 @@ +{ + "account_country": "US", + "account_name": "NORMALIZED-1", + "account_tax_ids": null, + "amount_due": 10000, + "amount_paid": 0, + "amount_remaining": 10000, + "application_fee": null, + "attempt_count": 0, + "attempted": false, + "auto_advance": true, + "automatic_tax": { + "enabled": false, + "status": null + }, + "billing": "send_invoice", + "billing_reason": "manual", + "charge": null, + "collection_method": "send_invoice", + "created": 1000000000, + "currency": "usd", + "custom_fields": null, + "customer": "cus_NORMALIZED0004", + "customer_address": null, + "customer_email": "user-0-realm_158636@zulip.com", + "customer_name": null, + "customer_phone": null, + "customer_shipping": null, + "customer_tax_exempt": "none", + "customer_tax_ids": [], + "date": 1000000000, + "default_payment_method": null, + "default_source": null, + "default_tax_rates": [], + "description": null, + "discount": null, + "discounts": [], + "due_date": 1000000000, + "ending_balance": 0, + "finalized_at": 1000000000, + "footer": null, + "hosted_invoice_url": "https://invoice.stripe.com/i/acct_NORMALIZED000001/invst_NORMALIZED0000000000000002idVh7", + "id": "in_NORMALIZED00000000000002", + "invoice_pdf": "https://pay.stripe.com/invoice/acct_NORMALIZED000001/invst_NORMALIZED0000000000000002idVh7/pdf", + "last_finalization_error": null, + "lines": { + "data": [ + { + "amount": 10000, + "currency": "usd", + "description": "Zulip standard", + "discount_amounts": [], + "discountable": false, + "discounts": [], + "id": "ii_NORMALIZED00000000000002", + "invoice_item": "ii_NORMALIZED00000000000002", + "livemode": false, + "metadata": {}, + "object": "line_item", + "period": { + "end": 1000000000, + "start": 1000000000 + }, + "plan": null, + "price": { + "active": false, + "billing_scheme": "per_unit", + "created": 1000000000, + "currency": "usd", + "id": "price_1J5YW8D2X8vgpBNGjQO5ySQC", + "livemode": false, + "lookup_key": null, + "metadata": {}, + "nickname": null, + "object": "price", + "product": "prod_Jj0XcBc6MoFHgE", + "recurring": null, + "tiers_mode": null, + "transform_quantity": null, + "type": "one_time", + "unit_amount": 10000, + "unit_amount_decimal": "10000" + }, + "proration": false, + "quantity": 1, + "subscription": null, + "tax_amounts": [], + "tax_rates": [], + "type": "invoiceitem", + "unique_id": "il_1J5YW8D2X8vgpBNGf92BtWOV" + } + ], + "has_more": false, + "object": "list", + "total_count": 1, + "url": "/v1/invoices/in_NORMALIZED00000000000002/lines" + }, + "livemode": false, + "metadata": {}, + "next_payment_attempt": null, + "number": "NORMALI-0002", + "object": "invoice", + "on_behalf_of": null, + "paid": false, + "payment_intent": "pi_1J5YWAD2X8vgpBNGXw0Js5PH", + "payment_settings": { + "payment_method_options": null, + "payment_method_types": null + }, + "period_end": 1000000000, + "period_start": 1000000000, + "post_payment_credit_notes_amount": 0, + "pre_payment_credit_notes_amount": 0, + "receipt_number": null, + "starting_balance": 0, + "statement_descriptor": "Zulip Standard", + "status": "open", + "status_transitions": { + "finalized_at": 1000000000, + "marked_uncollectible_at": null, + "paid_at": null, + "voided_at": null + }, + "subscription": null, + "subtotal": 10000, + "tax": null, + "tax_percent": null, + "total": 10000, + "total_discount_amounts": [], + "total_tax_amounts": [], + "transfer_data": null, + "webhooks_delivered_at": null +} diff --git a/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Invoice.finalize_invoice.3.json b/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Invoice.finalize_invoice.3.json new file mode 100644 index 0000000000..a18f9821c9 --- /dev/null +++ b/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Invoice.finalize_invoice.3.json @@ -0,0 +1,133 @@ +{ + "account_country": "US", + "account_name": "NORMALIZED-1", + "account_tax_ids": null, + "amount_due": 10000, + "amount_paid": 0, + "amount_remaining": 10000, + "application_fee": null, + "attempt_count": 0, + "attempted": false, + "auto_advance": true, + "automatic_tax": { + "enabled": false, + "status": null + }, + "billing": "send_invoice", + "billing_reason": "manual", + "charge": null, + "collection_method": "send_invoice", + "created": 1000000000, + "currency": "usd", + "custom_fields": null, + "customer": "cus_NORMALIZED0004", + "customer_address": null, + "customer_email": "user-0-realm_158636@zulip.com", + "customer_name": null, + "customer_phone": null, + "customer_shipping": null, + "customer_tax_exempt": "none", + "customer_tax_ids": [], + "date": 1000000000, + "default_payment_method": null, + "default_source": null, + "default_tax_rates": [], + "description": null, + "discount": null, + "discounts": [], + "due_date": 1000000000, + "ending_balance": 0, + "finalized_at": 1000000000, + "footer": null, + "hosted_invoice_url": "https://invoice.stripe.com/i/acct_NORMALIZED000001/invst_NORMALIZED0000000000000003nsJBj", + "id": "in_NORMALIZED00000000000003", + "invoice_pdf": "https://pay.stripe.com/invoice/acct_NORMALIZED000001/invst_NORMALIZED0000000000000003nsJBj/pdf", + "last_finalization_error": null, + "lines": { + "data": [ + { + "amount": 10000, + "currency": "usd", + "description": "Zulip standard", + "discount_amounts": [], + "discountable": false, + "discounts": [], + "id": "ii_NORMALIZED00000000000003", + "invoice_item": "ii_NORMALIZED00000000000003", + "livemode": false, + "metadata": {}, + "object": "line_item", + "period": { + "end": 1000000000, + "start": 1000000000 + }, + "plan": null, + "price": { + "active": false, + "billing_scheme": "per_unit", + "created": 1000000000, + "currency": "usd", + "id": "price_1J5YWBD2X8vgpBNGk65s4Bby", + "livemode": false, + "lookup_key": null, + "metadata": {}, + "nickname": null, + "object": "price", + "product": "prod_Jj0XXEj43ttifM", + "recurring": null, + "tiers_mode": null, + "transform_quantity": null, + "type": "one_time", + "unit_amount": 10000, + "unit_amount_decimal": "10000" + }, + "proration": false, + "quantity": 1, + "subscription": null, + "tax_amounts": [], + "tax_rates": [], + "type": "invoiceitem", + "unique_id": "il_1J5YWBD2X8vgpBNGdBrYTZWS" + } + ], + "has_more": false, + "object": "list", + "total_count": 1, + "url": "/v1/invoices/in_NORMALIZED00000000000003/lines" + }, + "livemode": false, + "metadata": {}, + "next_payment_attempt": null, + "number": "NORMALI-0003", + "object": "invoice", + "on_behalf_of": null, + "paid": false, + "payment_intent": "pi_1J5YWDD2X8vgpBNGVKjx2VAT", + "payment_settings": { + "payment_method_options": null, + "payment_method_types": null + }, + "period_end": 1000000000, + "period_start": 1000000000, + "post_payment_credit_notes_amount": 0, + "pre_payment_credit_notes_amount": 0, + "receipt_number": null, + "starting_balance": 0, + "statement_descriptor": "Zulip Standard", + "status": "open", + "status_transitions": { + "finalized_at": 1000000000, + "marked_uncollectible_at": null, + "paid_at": null, + "voided_at": null + }, + "subscription": null, + "subtotal": 10000, + "tax": null, + "tax_percent": null, + "total": 10000, + "total_discount_amounts": [], + "total_tax_amounts": [], + "transfer_data": null, + "webhooks_delivered_at": null +} diff --git a/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Invoice.finalize_invoice.4.json b/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Invoice.finalize_invoice.4.json new file mode 100644 index 0000000000..881f2cd722 --- /dev/null +++ b/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Invoice.finalize_invoice.4.json @@ -0,0 +1,133 @@ +{ + "account_country": "US", + "account_name": "NORMALIZED-1", + "account_tax_ids": null, + "amount_due": 10000, + "amount_paid": 0, + "amount_remaining": 10000, + "application_fee": null, + "attempt_count": 0, + "attempted": false, + "auto_advance": true, + "automatic_tax": { + "enabled": false, + "status": null + }, + "billing": "send_invoice", + "billing_reason": "manual", + "charge": null, + "collection_method": "send_invoice", + "created": 1000000000, + "currency": "usd", + "custom_fields": null, + "customer": "cus_NORMALIZED0005", + "customer_address": null, + "customer_email": "user-0-realm_345953@zulip.com", + "customer_name": null, + "customer_phone": null, + "customer_shipping": null, + "customer_tax_exempt": "none", + "customer_tax_ids": [], + "date": 1000000000, + "default_payment_method": null, + "default_source": null, + "default_tax_rates": [], + "description": null, + "discount": null, + "discounts": [], + "due_date": 1000000000, + "ending_balance": 0, + "finalized_at": 1000000000, + "footer": null, + "hosted_invoice_url": "https://invoice.stripe.com/i/acct_NORMALIZED000001/invst_NORMALIZED0000000000000004w1nCX", + "id": "in_NORMALIZED00000000000004", + "invoice_pdf": "https://pay.stripe.com/invoice/acct_NORMALIZED000001/invst_NORMALIZED0000000000000004w1nCX/pdf", + "last_finalization_error": null, + "lines": { + "data": [ + { + "amount": 10000, + "currency": "usd", + "description": "Zulip standard", + "discount_amounts": [], + "discountable": false, + "discounts": [], + "id": "ii_NORMALIZED00000000000004", + "invoice_item": "ii_NORMALIZED00000000000004", + "livemode": false, + "metadata": {}, + "object": "line_item", + "period": { + "end": 1000000000, + "start": 1000000000 + }, + "plan": null, + "price": { + "active": false, + "billing_scheme": "per_unit", + "created": 1000000000, + "currency": "usd", + "id": "price_1J5YWED2X8vgpBNGJPqvl8ls", + "livemode": false, + "lookup_key": null, + "metadata": {}, + "nickname": null, + "object": "price", + "product": "prod_Jj0XOrbeS8UGnX", + "recurring": null, + "tiers_mode": null, + "transform_quantity": null, + "type": "one_time", + "unit_amount": 10000, + "unit_amount_decimal": "10000" + }, + "proration": false, + "quantity": 1, + "subscription": null, + "tax_amounts": [], + "tax_rates": [], + "type": "invoiceitem", + "unique_id": "il_1J5YWED2X8vgpBNGcPMTOMfD" + } + ], + "has_more": false, + "object": "list", + "total_count": 1, + "url": "/v1/invoices/in_NORMALIZED00000000000004/lines" + }, + "livemode": false, + "metadata": {}, + "next_payment_attempt": null, + "number": "NORMALI-0004", + "object": "invoice", + "on_behalf_of": null, + "paid": false, + "payment_intent": "pi_1J5YWGD2X8vgpBNGOTyIX1H3", + "payment_settings": { + "payment_method_options": null, + "payment_method_types": null + }, + "period_end": 1000000000, + "period_start": 1000000000, + "post_payment_credit_notes_amount": 0, + "pre_payment_credit_notes_amount": 0, + "receipt_number": null, + "starting_balance": 0, + "statement_descriptor": "Zulip Standard", + "status": "open", + "status_transitions": { + "finalized_at": 1000000000, + "marked_uncollectible_at": null, + "paid_at": null, + "voided_at": null + }, + "subscription": null, + "subtotal": 10000, + "tax": null, + "tax_percent": null, + "total": 10000, + "total_discount_amounts": [], + "total_tax_amounts": [], + "transfer_data": null, + "webhooks_delivered_at": null +} diff --git a/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Invoice.finalize_invoice.5.json b/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Invoice.finalize_invoice.5.json new file mode 100644 index 0000000000..b7683eba7b --- /dev/null +++ b/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Invoice.finalize_invoice.5.json @@ -0,0 +1,133 @@ +{ + "account_country": "US", + "account_name": "NORMALIZED-1", + "account_tax_ids": null, + "amount_due": 10000, + "amount_paid": 0, + "amount_remaining": 10000, + "application_fee": null, + "attempt_count": 0, + "attempted": false, + "auto_advance": true, + "automatic_tax": { + "enabled": false, + "status": null + }, + "billing": "send_invoice", + "billing_reason": "manual", + "charge": null, + "collection_method": "send_invoice", + "created": 1000000000, + "currency": "usd", + "custom_fields": null, + "customer": "cus_NORMALIZED0005", + "customer_address": null, + "customer_email": "user-0-realm_345953@zulip.com", + "customer_name": null, + "customer_phone": null, + "customer_shipping": null, + "customer_tax_exempt": "none", + "customer_tax_ids": [], + "date": 1000000000, + "default_payment_method": null, + "default_source": null, + "default_tax_rates": [], + "description": null, + "discount": null, + "discounts": [], + "due_date": 1000000000, + "ending_balance": 0, + "finalized_at": 1000000000, + "footer": null, + "hosted_invoice_url": "https://invoice.stripe.com/i/acct_NORMALIZED000001/invst_NORMALIZED0000000000000005e3XWY", + "id": "in_NORMALIZED00000000000005", + "invoice_pdf": "https://pay.stripe.com/invoice/acct_NORMALIZED000001/invst_NORMALIZED0000000000000005e3XWY/pdf", + "last_finalization_error": null, + "lines": { + "data": [ + { + "amount": 10000, + "currency": "usd", + "description": "Zulip standard", + "discount_amounts": [], + "discountable": false, + "discounts": [], + "id": "ii_NORMALIZED00000000000005", + "invoice_item": "ii_NORMALIZED00000000000005", + "livemode": false, + "metadata": {}, + "object": "line_item", + "period": { + "end": 1000000000, + "start": 1000000000 + }, + "plan": null, + "price": { + "active": false, + "billing_scheme": "per_unit", + "created": 1000000000, + "currency": "usd", + "id": "price_1J5YWHD2X8vgpBNGwEA9hn4b", + "livemode": false, + "lookup_key": null, + "metadata": {}, + "nickname": null, + "object": "price", + "product": "prod_Jj0XvpvSalZcLc", + "recurring": null, + "tiers_mode": null, + "transform_quantity": null, + "type": "one_time", + "unit_amount": 10000, + "unit_amount_decimal": "10000" + }, + "proration": false, + "quantity": 1, + "subscription": null, + "tax_amounts": [], + "tax_rates": [], + "type": "invoiceitem", + "unique_id": "il_1J5YWHD2X8vgpBNGmcQgjQGx" + } + ], + "has_more": false, + "object": "list", + "total_count": 1, + "url": "/v1/invoices/in_NORMALIZED00000000000005/lines" + }, + "livemode": false, + "metadata": {}, + "next_payment_attempt": null, + "number": "NORMALI-0005", + "object": "invoice", + "on_behalf_of": null, + "paid": false, + "payment_intent": "pi_1J5YWJD2X8vgpBNGkNf5igPN", + "payment_settings": { + "payment_method_options": null, + "payment_method_types": null + }, + "period_end": 1000000000, + "period_start": 1000000000, + "post_payment_credit_notes_amount": 0, + "pre_payment_credit_notes_amount": 0, + "receipt_number": null, + "starting_balance": 0, + "statement_descriptor": "Zulip Standard", + "status": "open", + "status_transitions": { + "finalized_at": 1000000000, + "marked_uncollectible_at": null, + "paid_at": null, + "voided_at": null + }, + "subscription": null, + "subtotal": 10000, + "tax": null, + "tax_percent": null, + "total": 10000, + "total_discount_amounts": [], + "total_tax_amounts": [], + "transfer_data": null, + "webhooks_delivered_at": 1000000000 +} diff --git a/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Invoice.finalize_invoice.6.json b/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Invoice.finalize_invoice.6.json new file mode 100644 index 0000000000..8eb21abd54 --- /dev/null +++ b/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Invoice.finalize_invoice.6.json @@ -0,0 +1,133 @@ +{ + "account_country": "US", + "account_name": "NORMALIZED-1", + "account_tax_ids": null, + "amount_due": 10000, + "amount_paid": 0, + "amount_remaining": 10000, + "application_fee": null, + "attempt_count": 0, + "attempted": false, + "auto_advance": true, + "automatic_tax": { + "enabled": false, + "status": null + }, + "billing": "send_invoice", + "billing_reason": "manual", + "charge": null, + "collection_method": "send_invoice", + "created": 1000000000, + "currency": "usd", + "custom_fields": null, + "customer": "cus_NORMALIZED0006", + "customer_address": null, + "customer_email": "user-0-realm_186548@zulip.com", + "customer_name": null, + "customer_phone": null, + "customer_shipping": null, + "customer_tax_exempt": "none", + "customer_tax_ids": [], + "date": 1000000000, + "default_payment_method": null, + "default_source": null, + "default_tax_rates": [], + "description": null, + "discount": null, + "discounts": [], + "due_date": 1000000000, + "ending_balance": 0, + "finalized_at": 1000000000, + "footer": null, + "hosted_invoice_url": "https://invoice.stripe.com/i/acct_NORMALIZED000001/invst_NORMALIZED0000000000000006s5YVx", + "id": "in_NORMALIZED00000000000006", + "invoice_pdf": "https://pay.stripe.com/invoice/acct_NORMALIZED000001/invst_NORMALIZED0000000000000006s5YVx/pdf", + "last_finalization_error": null, + "lines": { + "data": [ + { + "amount": 10000, + "currency": "usd", + "description": "Zulip standard", + "discount_amounts": [], + "discountable": false, + "discounts": [], + "id": "ii_NORMALIZED00000000000006", + "invoice_item": "ii_NORMALIZED00000000000006", + "livemode": false, + "metadata": {}, + "object": "line_item", + "period": { + "end": 1000000000, + "start": 1000000000 + }, + "plan": null, + "price": { + "active": false, + "billing_scheme": "per_unit", + "created": 1000000000, + "currency": "usd", + "id": "price_1J5YWMD2X8vgpBNGGQAGsuaj", + "livemode": false, + "lookup_key": null, + "metadata": {}, + "nickname": null, + "object": "price", + "product": "prod_Jj0XRALl1QAQk5", + "recurring": null, + "tiers_mode": null, + "transform_quantity": null, + "type": "one_time", + "unit_amount": 10000, + "unit_amount_decimal": "10000" + }, + "proration": false, + "quantity": 1, + "subscription": null, + "tax_amounts": [], + "tax_rates": [], + "type": "invoiceitem", + "unique_id": "il_1J5YWMD2X8vgpBNGTMs8cYiR" + } + ], + "has_more": false, + "object": "list", + "total_count": 1, + "url": "/v1/invoices/in_NORMALIZED00000000000006/lines" + }, + "livemode": false, + "metadata": {}, + "next_payment_attempt": null, + "number": "NORMALI-0006", + "object": "invoice", + "on_behalf_of": null, + "paid": false, + "payment_intent": "pi_1J5YWOD2X8vgpBNGoAHwi7t6", + "payment_settings": { + "payment_method_options": null, + "payment_method_types": null + }, + "period_end": 1000000000, + "period_start": 1000000000, + "post_payment_credit_notes_amount": 0, + "pre_payment_credit_notes_amount": 0, + "receipt_number": null, + "starting_balance": 0, + "statement_descriptor": "Zulip Standard", + "status": "open", + "status_transitions": { + "finalized_at": 1000000000, + "marked_uncollectible_at": null, + "paid_at": null, + "voided_at": null + }, + "subscription": null, + "subtotal": 10000, + "tax": null, + "tax_percent": null, + "total": 10000, + "total_discount_amounts": [], + "total_tax_amounts": [], + "transfer_data": null, + "webhooks_delivered_at": null +} diff --git a/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Invoice.finalize_invoice.7.json b/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Invoice.finalize_invoice.7.json new file mode 100644 index 0000000000..585210a4d9 --- /dev/null +++ b/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Invoice.finalize_invoice.7.json @@ -0,0 +1,133 @@ +{ + "account_country": "US", + "account_name": "NORMALIZED-1", + "account_tax_ids": null, + "amount_due": 10000, + "amount_paid": 0, + "amount_remaining": 10000, + "application_fee": null, + "attempt_count": 0, + "attempted": false, + "auto_advance": true, + "automatic_tax": { + "enabled": false, + "status": null + }, + "billing": "send_invoice", + "billing_reason": "manual", + "charge": null, + "collection_method": "send_invoice", + "created": 1000000000, + "currency": "usd", + "custom_fields": null, + "customer": "cus_NORMALIZED0006", + "customer_address": null, + "customer_email": "user-0-realm_186548@zulip.com", + "customer_name": null, + "customer_phone": null, + "customer_shipping": null, + "customer_tax_exempt": "none", + "customer_tax_ids": [], + "date": 1000000000, + "default_payment_method": null, + "default_source": null, + "default_tax_rates": [], + "description": null, + "discount": null, + "discounts": [], + "due_date": 1000000000, + "ending_balance": 0, + "finalized_at": 1000000000, + "footer": null, + "hosted_invoice_url": "https://invoice.stripe.com/i/acct_NORMALIZED000001/invst_NORMALIZED0000000000000007m8tcn", + "id": "in_NORMALIZED00000000000007", + "invoice_pdf": "https://pay.stripe.com/invoice/acct_NORMALIZED000001/invst_NORMALIZED0000000000000007m8tcn/pdf", + "last_finalization_error": null, + "lines": { + "data": [ + { + "amount": 10000, + "currency": "usd", + "description": "Zulip standard", + "discount_amounts": [], + "discountable": false, + "discounts": [], + "id": "ii_NORMALIZED00000000000007", + "invoice_item": "ii_NORMALIZED00000000000007", + "livemode": false, + "metadata": {}, + "object": "line_item", + "period": { + "end": 1000000000, + "start": 1000000000 + }, + "plan": null, + "price": { + "active": false, + "billing_scheme": "per_unit", + "created": 1000000000, + "currency": "usd", + "id": "price_1J5YWPD2X8vgpBNGdU5odKe9", + "livemode": false, + "lookup_key": null, + "metadata": {}, + "nickname": null, + "object": "price", + "product": "prod_Jj0X7KLLErkxlq", + "recurring": null, + "tiers_mode": null, + "transform_quantity": null, + "type": "one_time", + "unit_amount": 10000, + "unit_amount_decimal": "10000" + }, + "proration": false, + "quantity": 1, + "subscription": null, + "tax_amounts": [], + "tax_rates": [], + "type": "invoiceitem", + "unique_id": "il_1J5YWPD2X8vgpBNGayzkkXN2" + } + ], + "has_more": false, + "object": "list", + "total_count": 1, + "url": "/v1/invoices/in_NORMALIZED00000000000007/lines" + }, + "livemode": false, + "metadata": {}, + "next_payment_attempt": null, + "number": "NORMALI-0007", + "object": "invoice", + "on_behalf_of": null, + "paid": false, + "payment_intent": "pi_1J5YWQD2X8vgpBNGCa6w5Qan", + "payment_settings": { + "payment_method_options": null, + "payment_method_types": null + }, + "period_end": 1000000000, + "period_start": 1000000000, + "post_payment_credit_notes_amount": 0, + "pre_payment_credit_notes_amount": 0, + "receipt_number": null, + "starting_balance": 0, + "statement_descriptor": "Zulip Standard", + "status": "open", + "status_transitions": { + "finalized_at": 1000000000, + "marked_uncollectible_at": null, + "paid_at": null, + "voided_at": null + }, + "subscription": null, + "subtotal": 10000, + "tax": null, + "tax_percent": null, + "total": 10000, + "total_discount_amounts": [], + "total_tax_amounts": [], + "transfer_data": null, + "webhooks_delivered_at": null +} diff --git a/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Invoice.list.1.json b/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Invoice.list.1.json new file mode 100644 index 0000000000..e39960ab72 --- /dev/null +++ b/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Invoice.list.1.json @@ -0,0 +1,6 @@ +{ + "data": [], + "has_more": false, + "object": "list", + "url": "/v1/invoices" +} diff --git a/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Invoice.list.2.json b/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Invoice.list.2.json new file mode 100644 index 0000000000..72bbc0f46e --- /dev/null +++ b/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Invoice.list.2.json @@ -0,0 +1,140 @@ +{ + "data": [ + { + "account_country": "US", + "account_name": "NORMALIZED-1", + "account_tax_ids": null, + "amount_due": 10000, + "amount_paid": 0, + "amount_remaining": 10000, + "application_fee": null, + "attempt_count": 0, + "attempted": false, + "auto_advance": true, + "automatic_tax": { + "enabled": false, + "status": null + }, + "billing": "send_invoice", + "billing_reason": "manual", + "charge": null, + "collection_method": "send_invoice", + "created": 1000000000, + "currency": "usd", + "custom_fields": null, + "customer": "cus_NORMALIZED0003", + "customer_address": null, + "customer_email": "user-0-realm_612438@zulip.com", + "customer_name": null, + "customer_phone": null, + "customer_shipping": null, + "customer_tax_exempt": "none", + "customer_tax_ids": [], + "date": 1000000000, + "default_payment_method": null, + "default_source": null, + "default_tax_rates": [], + "description": null, + "discount": null, + "discounts": [], + "due_date": 1000000000, + "ending_balance": 0, + "finalized_at": 1000000000, + "footer": null, + "hosted_invoice_url": "https://invoice.stripe.com/i/acct_NORMALIZED000001/invst_NORMALIZED0000000000000001w2De8", + "id": "in_NORMALIZED00000000000001", + "invoice_pdf": "https://pay.stripe.com/invoice/acct_NORMALIZED000001/invst_NORMALIZED0000000000000001w2De8/pdf", + "last_finalization_error": null, + "lines": { + "data": [ + { + "amount": 10000, + "currency": "usd", + "description": "Zulip standard", + "discount_amounts": [], + "discountable": false, + "discounts": [], + "id": "ii_NORMALIZED00000000000001", + "invoice_item": "ii_NORMALIZED00000000000001", + "livemode": false, + "metadata": {}, + "object": "line_item", + "period": { + "end": 1000000000, + "start": 1000000000 + }, + "plan": null, + "price": { + "active": false, + "billing_scheme": "per_unit", + "created": 1000000000, + "currency": "usd", + "id": "price_1J5YW6D2X8vgpBNGNRpvTb7X", + "livemode": false, + "lookup_key": null, + "metadata": {}, + "nickname": null, + "object": "price", + "product": "prod_Jj0XEKpg4Z7fzV", + "recurring": null, + "tiers_mode": null, + "transform_quantity": null, + "type": "one_time", + "unit_amount": 10000, + "unit_amount_decimal": "10000" + }, + "proration": false, + "quantity": 1, + "subscription": null, + "tax_amounts": [], + "tax_rates": [], + "type": "invoiceitem", + "unique_id": "il_1J5YW6D2X8vgpBNGJfmJCwyJ" + } + ], + "has_more": false, + "object": "list", + "total_count": 1, + "url": "/v1/invoices/in_NORMALIZED00000000000001/lines" + }, + "livemode": false, + "metadata": {}, + "next_payment_attempt": null, + "number": "NORMALI-0001", + "object": "invoice", + "on_behalf_of": null, + "paid": false, + "payment_intent": "pi_1J5YW7D2X8vgpBNG5Yb5dYds", + "payment_settings": { + "payment_method_options": null, + "payment_method_types": null + }, + "period_end": 1000000000, + "period_start": 1000000000, + "post_payment_credit_notes_amount": 0, + "pre_payment_credit_notes_amount": 0, + "receipt_number": null, + "starting_balance": 0, + "statement_descriptor": "Zulip Standard", + "status": "open", + "status_transitions": { + "finalized_at": 1000000000, + "marked_uncollectible_at": null, + "paid_at": null, + "voided_at": null + }, + "subscription": null, + "subtotal": 10000, + "tax": null, + "tax_percent": null, + "total": 10000, + "total_discount_amounts": [], + "total_tax_amounts": [], + "transfer_data": null, + "webhooks_delivered_at": 1000000000 + } + ], + "has_more": false, + "object": "list", + "url": "/v1/invoices" +} diff --git a/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Invoice.list.3.json b/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Invoice.list.3.json new file mode 100644 index 0000000000..ffbf76d4b5 --- /dev/null +++ b/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Invoice.list.3.json @@ -0,0 +1,273 @@ +{ + "data": [ + { + "account_country": "US", + "account_name": "NORMALIZED-1", + "account_tax_ids": null, + "amount_due": 10000, + "amount_paid": 0, + "amount_remaining": 10000, + "application_fee": null, + "attempt_count": 0, + "attempted": false, + "auto_advance": true, + "automatic_tax": { + "enabled": false, + "status": null + }, + "billing": "send_invoice", + "billing_reason": "manual", + "charge": null, + "collection_method": "send_invoice", + "created": 1000000000, + "currency": "usd", + "custom_fields": null, + "customer": "cus_NORMALIZED0004", + "customer_address": null, + "customer_email": "user-0-realm_158636@zulip.com", + "customer_name": null, + "customer_phone": null, + "customer_shipping": null, + "customer_tax_exempt": "none", + "customer_tax_ids": [], + "date": 1000000000, + "default_payment_method": null, + "default_source": null, + "default_tax_rates": [], + "description": null, + "discount": null, + "discounts": [], + "due_date": 1000000000, + "ending_balance": 0, + "finalized_at": 1000000000, + "footer": null, + "hosted_invoice_url": "https://invoice.stripe.com/i/acct_NORMALIZED000001/invst_NORMALIZED0000000000000003nsJBj", + "id": "in_NORMALIZED00000000000003", + "invoice_pdf": "https://pay.stripe.com/invoice/acct_NORMALIZED000001/invst_NORMALIZED0000000000000003nsJBj/pdf", + "last_finalization_error": null, + "lines": { + "data": [ + { + "amount": 10000, + "currency": "usd", + "description": "Zulip standard", + "discount_amounts": [], + "discountable": false, + "discounts": [], + "id": "ii_NORMALIZED00000000000003", + "invoice_item": "ii_NORMALIZED00000000000003", + "livemode": false, + "metadata": {}, + "object": "line_item", + "period": { + "end": 1000000000, + "start": 1000000000 + }, + "plan": null, + "price": { + "active": false, + "billing_scheme": "per_unit", + "created": 1000000000, + "currency": "usd", + "id": "price_1J5YWBD2X8vgpBNGk65s4Bby", + "livemode": false, + "lookup_key": null, + "metadata": {}, + "nickname": null, + "object": "price", + "product": "prod_Jj0XXEj43ttifM", + "recurring": null, + "tiers_mode": null, + "transform_quantity": null, + "type": "one_time", + "unit_amount": 10000, + "unit_amount_decimal": "10000" + }, + "proration": false, + "quantity": 1, + "subscription": null, + "tax_amounts": [], + "tax_rates": [], + "type": "invoiceitem", + "unique_id": "il_1J5YWBD2X8vgpBNGdBrYTZWS" + } + ], + "has_more": false, + "object": "list", + "total_count": 1, + "url": "/v1/invoices/in_NORMALIZED00000000000003/lines" + }, + "livemode": false, + "metadata": {}, + "next_payment_attempt": null, + "number": "NORMALI-0003", + "object": "invoice", + "on_behalf_of": null, + "paid": false, + "payment_intent": "pi_1J5YWDD2X8vgpBNGVKjx2VAT", + "payment_settings": { + "payment_method_options": null, + "payment_method_types": null + }, + "period_end": 1000000000, + "period_start": 1000000000, + "post_payment_credit_notes_amount": 0, + "pre_payment_credit_notes_amount": 0, + "receipt_number": null, + "starting_balance": 0, + "statement_descriptor": "Zulip Standard", + "status": "open", + "status_transitions": { + "finalized_at": 1000000000, + "marked_uncollectible_at": null, + "paid_at": null, + "voided_at": null + }, + "subscription": null, + "subtotal": 10000, + "tax": null, + "tax_percent": null, + "total": 10000, + "total_discount_amounts": [], + "total_tax_amounts": [], + "transfer_data": null, + "webhooks_delivered_at": 1000000000 + }, + { + "account_country": "US", + "account_name": "NORMALIZED-1", + "account_tax_ids": null, + "amount_due": 10000, + "amount_paid": 0, + "amount_remaining": 10000, + "application_fee": null, + "attempt_count": 0, + "attempted": false, + "auto_advance": true, + "automatic_tax": { + "enabled": false, + "status": null + }, + "billing": "send_invoice", + "billing_reason": "manual", + "charge": null, + "collection_method": "send_invoice", + "created": 1000000000, + "currency": "usd", + "custom_fields": null, + "customer": "cus_NORMALIZED0004", + "customer_address": null, + "customer_email": "user-0-realm_158636@zulip.com", + "customer_name": null, + "customer_phone": null, + "customer_shipping": null, + "customer_tax_exempt": "none", + "customer_tax_ids": [], + "date": 1000000000, + "default_payment_method": null, + "default_source": null, + "default_tax_rates": [], + "description": null, + "discount": null, + "discounts": [], + "due_date": 1000000000, + "ending_balance": 0, + "finalized_at": 1000000000, + "footer": null, + "hosted_invoice_url": "https://invoice.stripe.com/i/acct_NORMALIZED000001/invst_NORMALIZED0000000000000002idVh7", + "id": "in_NORMALIZED00000000000002", + "invoice_pdf": "https://pay.stripe.com/invoice/acct_NORMALIZED000001/invst_NORMALIZED0000000000000002idVh7/pdf", + "last_finalization_error": null, + "lines": { + "data": [ + { + "amount": 10000, + "currency": "usd", + "description": "Zulip standard", + "discount_amounts": [], + "discountable": false, + "discounts": [], + "id": "ii_NORMALIZED00000000000002", + "invoice_item": "ii_NORMALIZED00000000000002", + "livemode": false, + "metadata": {}, + "object": "line_item", + "period": { + "end": 1000000000, + "start": 1000000000 + }, + "plan": null, + "price": { + "active": false, + "billing_scheme": "per_unit", + "created": 1000000000, + "currency": "usd", + "id": "price_1J5YW8D2X8vgpBNGjQO5ySQC", + "livemode": false, + "lookup_key": null, + "metadata": {}, + "nickname": null, + "object": "price", + "product": "prod_Jj0XcBc6MoFHgE", + "recurring": null, + "tiers_mode": null, + "transform_quantity": null, + "type": "one_time", + "unit_amount": 10000, + "unit_amount_decimal": "10000" + }, + "proration": false, + "quantity": 1, + "subscription": null, + "tax_amounts": [], + "tax_rates": [], + "type": "invoiceitem", + "unique_id": "il_1J5YW8D2X8vgpBNGf92BtWOV" + } + ], + "has_more": false, + "object": "list", + "total_count": 1, + "url": "/v1/invoices/in_NORMALIZED00000000000002/lines" + }, + "livemode": false, + "metadata": {}, + "next_payment_attempt": null, + "number": "NORMALI-0002", + "object": "invoice", + "on_behalf_of": null, + "paid": false, + "payment_intent": "pi_1J5YWAD2X8vgpBNGXw0Js5PH", + "payment_settings": { + "payment_method_options": null, + "payment_method_types": null + }, + "period_end": 1000000000, + "period_start": 1000000000, + "post_payment_credit_notes_amount": 0, + "pre_payment_credit_notes_amount": 0, + "receipt_number": null, + "starting_balance": 0, + "statement_descriptor": "Zulip Standard", + "status": "open", + "status_transitions": { + "finalized_at": 1000000000, + "marked_uncollectible_at": null, + "paid_at": null, + "voided_at": null + }, + "subscription": null, + "subtotal": 10000, + "tax": null, + "tax_percent": null, + "total": 10000, + "total_discount_amounts": [], + "total_tax_amounts": [], + "transfer_data": null, + "webhooks_delivered_at": 1000000000 + } + ], + "has_more": false, + "object": "list", + "url": "/v1/invoices" +} diff --git a/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Invoice.list.4.json b/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Invoice.list.4.json new file mode 100644 index 0000000000..fca90201bf --- /dev/null +++ b/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Invoice.list.4.json @@ -0,0 +1,273 @@ +{ + "data": [ + { + "account_country": "US", + "account_name": "NORMALIZED-1", + "account_tax_ids": null, + "amount_due": 10000, + "amount_paid": 0, + "amount_remaining": 10000, + "application_fee": null, + "attempt_count": 0, + "attempted": false, + "auto_advance": false, + "automatic_tax": { + "enabled": false, + "status": null + }, + "billing": "send_invoice", + "billing_reason": "manual", + "charge": null, + "collection_method": "send_invoice", + "created": 1000000000, + "currency": "usd", + "custom_fields": null, + "customer": "cus_NORMALIZED0005", + "customer_address": null, + "customer_email": "user-0-realm_345953@zulip.com", + "customer_name": null, + "customer_phone": null, + "customer_shipping": null, + "customer_tax_exempt": "none", + "customer_tax_ids": [], + "date": 1000000000, + "default_payment_method": null, + "default_source": null, + "default_tax_rates": [], + "description": null, + "discount": null, + "discounts": [], + "due_date": 1000000000, + "ending_balance": 0, + "finalized_at": 1000000000, + "footer": null, + "hosted_invoice_url": "https://invoice.stripe.com/i/acct_NORMALIZED000001/invst_NORMALIZED0000000000000005e3XWY", + "id": "in_NORMALIZED00000000000005", + "invoice_pdf": "https://pay.stripe.com/invoice/acct_NORMALIZED000001/invst_NORMALIZED0000000000000005e3XWY/pdf", + "last_finalization_error": null, + "lines": { + "data": [ + { + "amount": 10000, + "currency": "usd", + "description": "Zulip standard", + "discount_amounts": [], + "discountable": false, + "discounts": [], + "id": "ii_NORMALIZED00000000000005", + "invoice_item": "ii_NORMALIZED00000000000005", + "livemode": false, + "metadata": {}, + "object": "line_item", + "period": { + "end": 1000000000, + "start": 1000000000 + }, + "plan": null, + "price": { + "active": false, + "billing_scheme": "per_unit", + "created": 1000000000, + "currency": "usd", + "id": "price_1J5YWHD2X8vgpBNGwEA9hn4b", + "livemode": false, + "lookup_key": null, + "metadata": {}, + "nickname": null, + "object": "price", + "product": "prod_Jj0XvpvSalZcLc", + "recurring": null, + "tiers_mode": null, + "transform_quantity": null, + "type": "one_time", + "unit_amount": 10000, + "unit_amount_decimal": "10000" + }, + "proration": false, + "quantity": 1, + "subscription": null, + "tax_amounts": [], + "tax_rates": [], + "type": "invoiceitem", + "unique_id": "il_1J5YWHD2X8vgpBNGmcQgjQGx" + } + ], + "has_more": false, + "object": "list", + "total_count": 1, + "url": "/v1/invoices/in_NORMALIZED00000000000005/lines" + }, + "livemode": false, + "metadata": {}, + "next_payment_attempt": null, + "number": "NORMALI-0005", + "object": "invoice", + "on_behalf_of": null, + "paid": true, + "payment_intent": null, + "payment_settings": { + "payment_method_options": null, + "payment_method_types": null + }, + "period_end": 1000000000, + "period_start": 1000000000, + "post_payment_credit_notes_amount": 0, + "pre_payment_credit_notes_amount": 0, + "receipt_number": null, + "starting_balance": 0, + "statement_descriptor": "Zulip Standard", + "status": "paid", + "status_transitions": { + "finalized_at": 1000000000, + "marked_uncollectible_at": null, + "paid_at": 1000000000, + "voided_at": null + }, + "subscription": null, + "subtotal": 10000, + "tax": null, + "tax_percent": null, + "total": 10000, + "total_discount_amounts": [], + "total_tax_amounts": [], + "transfer_data": null, + "webhooks_delivered_at": 1000000000 + }, + { + "account_country": "US", + "account_name": "NORMALIZED-1", + "account_tax_ids": null, + "amount_due": 10000, + "amount_paid": 0, + "amount_remaining": 10000, + "application_fee": null, + "attempt_count": 0, + "attempted": false, + "auto_advance": false, + "automatic_tax": { + "enabled": false, + "status": null + }, + "billing": "send_invoice", + "billing_reason": "manual", + "charge": null, + "collection_method": "send_invoice", + "created": 1000000000, + "currency": "usd", + "custom_fields": null, + "customer": "cus_NORMALIZED0005", + "customer_address": null, + "customer_email": "user-0-realm_345953@zulip.com", + "customer_name": null, + "customer_phone": null, + "customer_shipping": null, + "customer_tax_exempt": "none", + "customer_tax_ids": [], + "date": 1000000000, + "default_payment_method": null, + "default_source": null, + "default_tax_rates": [], + "description": null, + "discount": null, + "discounts": [], + "due_date": 1000000000, + "ending_balance": 0, + "finalized_at": 1000000000, + "footer": null, + "hosted_invoice_url": "https://invoice.stripe.com/i/acct_NORMALIZED000001/invst_NORMALIZED0000000000000004w1nCX", + "id": "in_NORMALIZED00000000000004", + "invoice_pdf": "https://pay.stripe.com/invoice/acct_NORMALIZED000001/invst_NORMALIZED0000000000000004w1nCX/pdf", + "last_finalization_error": null, + "lines": { + "data": [ + { + "amount": 10000, + "currency": "usd", + "description": "Zulip standard", + "discount_amounts": [], + "discountable": false, + "discounts": [], + "id": "ii_NORMALIZED00000000000004", + "invoice_item": "ii_NORMALIZED00000000000004", + "livemode": false, + "metadata": {}, + "object": "line_item", + "period": { + "end": 1000000000, + "start": 1000000000 + }, + "plan": null, + "price": { + "active": false, + "billing_scheme": "per_unit", + "created": 1000000000, + "currency": "usd", + "id": "price_1J5YWED2X8vgpBNGJPqvl8ls", + "livemode": false, + "lookup_key": null, + "metadata": {}, + "nickname": null, + "object": "price", + "product": "prod_Jj0XOrbeS8UGnX", + "recurring": null, + "tiers_mode": null, + "transform_quantity": null, + "type": "one_time", + "unit_amount": 10000, + "unit_amount_decimal": "10000" + }, + "proration": false, + "quantity": 1, + "subscription": null, + "tax_amounts": [], + "tax_rates": [], + "type": "invoiceitem", + "unique_id": "il_1J5YWED2X8vgpBNGcPMTOMfD" + } + ], + "has_more": false, + "object": "list", + "total_count": 1, + "url": "/v1/invoices/in_NORMALIZED00000000000004/lines" + }, + "livemode": false, + "metadata": {}, + "next_payment_attempt": null, + "number": "NORMALI-0004", + "object": "invoice", + "on_behalf_of": null, + "paid": true, + "payment_intent": null, + "payment_settings": { + "payment_method_options": null, + "payment_method_types": null + }, + "period_end": 1000000000, + "period_start": 1000000000, + "post_payment_credit_notes_amount": 0, + "pre_payment_credit_notes_amount": 0, + "receipt_number": null, + "starting_balance": 0, + "statement_descriptor": "Zulip Standard", + "status": "paid", + "status_transitions": { + "finalized_at": 1000000000, + "marked_uncollectible_at": null, + "paid_at": 1000000000, + "voided_at": null + }, + "subscription": null, + "subtotal": 10000, + "tax": null, + "tax_percent": null, + "total": 10000, + "total_discount_amounts": [], + "total_tax_amounts": [], + "transfer_data": null, + "webhooks_delivered_at": 1000000000 + } + ], + "has_more": false, + "object": "list", + "url": "/v1/invoices" +} diff --git a/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Invoice.pay.1.json b/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Invoice.pay.1.json new file mode 100644 index 0000000000..267220fde6 --- /dev/null +++ b/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Invoice.pay.1.json @@ -0,0 +1,133 @@ +{ + "account_country": "US", + "account_name": "NORMALIZED-1", + "account_tax_ids": null, + "amount_due": 10000, + "amount_paid": 0, + "amount_remaining": 10000, + "application_fee": null, + "attempt_count": 0, + "attempted": false, + "auto_advance": false, + "automatic_tax": { + "enabled": false, + "status": null + }, + "billing": "send_invoice", + "billing_reason": "manual", + "charge": null, + "collection_method": "send_invoice", + "created": 1000000000, + "currency": "usd", + "custom_fields": null, + "customer": "cus_NORMALIZED0005", + "customer_address": null, + "customer_email": "user-0-realm_345953@zulip.com", + "customer_name": null, + "customer_phone": null, + "customer_shipping": null, + "customer_tax_exempt": "none", + "customer_tax_ids": [], + "date": 1000000000, + "default_payment_method": null, + "default_source": null, + "default_tax_rates": [], + "description": null, + "discount": null, + "discounts": [], + "due_date": 1000000000, + "ending_balance": 0, + "finalized_at": 1000000000, + "footer": null, + "hosted_invoice_url": "https://invoice.stripe.com/i/acct_NORMALIZED000001/invst_NORMALIZED0000000000000004w1nCX", + "id": "in_NORMALIZED00000000000004", + "invoice_pdf": "https://pay.stripe.com/invoice/acct_NORMALIZED000001/invst_NORMALIZED0000000000000004w1nCX/pdf", + "last_finalization_error": null, + "lines": { + "data": [ + { + "amount": 10000, + "currency": "usd", + "description": "Zulip standard", + "discount_amounts": [], + "discountable": false, + "discounts": [], + "id": "ii_NORMALIZED00000000000004", + "invoice_item": "ii_NORMALIZED00000000000004", + "livemode": false, + "metadata": {}, + "object": "line_item", + "period": { + "end": 1000000000, + "start": 1000000000 + }, + "plan": null, + "price": { + "active": false, + "billing_scheme": "per_unit", + "created": 1000000000, + "currency": "usd", + "id": "price_1J5YWED2X8vgpBNGJPqvl8ls", + "livemode": false, + "lookup_key": null, + "metadata": {}, + "nickname": null, + "object": "price", + "product": "prod_Jj0XOrbeS8UGnX", + "recurring": null, + "tiers_mode": null, + "transform_quantity": null, + "type": "one_time", + "unit_amount": 10000, + "unit_amount_decimal": "10000" + }, + "proration": false, + "quantity": 1, + "subscription": null, + "tax_amounts": [], + "tax_rates": [], + "type": "invoiceitem", + "unique_id": "il_1J5YWED2X8vgpBNGcPMTOMfD" + } + ], + "has_more": false, + "object": "list", + "total_count": 1, + "url": "/v1/invoices/in_NORMALIZED00000000000004/lines" + }, + "livemode": false, + "metadata": {}, + "next_payment_attempt": null, + "number": "NORMALI-0004", + "object": "invoice", + "on_behalf_of": null, + "paid": true, + "payment_intent": null, + "payment_settings": { + "payment_method_options": null, + "payment_method_types": null + }, + "period_end": 1000000000, + "period_start": 1000000000, + "post_payment_credit_notes_amount": 0, + "pre_payment_credit_notes_amount": 0, + "receipt_number": null, + "starting_balance": 0, + "statement_descriptor": "Zulip Standard", + "status": "paid", + "status_transitions": { + "finalized_at": 1000000000, + "marked_uncollectible_at": null, + "paid_at": 1000000000, + "voided_at": null + }, + "subscription": null, + "subtotal": 10000, + "tax": null, + "tax_percent": null, + "total": 10000, + "total_discount_amounts": [], + "total_tax_amounts": [], + "transfer_data": null, + "webhooks_delivered_at": 1000000000 +} diff --git a/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Invoice.pay.2.json b/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Invoice.pay.2.json new file mode 100644 index 0000000000..b44f8aec57 --- /dev/null +++ b/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--Invoice.pay.2.json @@ -0,0 +1,133 @@ +{ + "account_country": "US", + "account_name": "NORMALIZED-1", + "account_tax_ids": null, + "amount_due": 10000, + "amount_paid": 0, + "amount_remaining": 10000, + "application_fee": null, + "attempt_count": 0, + "attempted": false, + "auto_advance": false, + "automatic_tax": { + "enabled": false, + "status": null + }, + "billing": "send_invoice", + "billing_reason": "manual", + "charge": null, + "collection_method": "send_invoice", + "created": 1000000000, + "currency": "usd", + "custom_fields": null, + "customer": "cus_NORMALIZED0005", + "customer_address": null, + "customer_email": "user-0-realm_345953@zulip.com", + "customer_name": null, + "customer_phone": null, + "customer_shipping": null, + "customer_tax_exempt": "none", + "customer_tax_ids": [], + "date": 1000000000, + "default_payment_method": null, + "default_source": null, + "default_tax_rates": [], + "description": null, + "discount": null, + "discounts": [], + "due_date": 1000000000, + "ending_balance": 0, + "finalized_at": 1000000000, + "footer": null, + "hosted_invoice_url": "https://invoice.stripe.com/i/acct_NORMALIZED000001/invst_NORMALIZED0000000000000005e3XWY", + "id": "in_NORMALIZED00000000000005", + "invoice_pdf": "https://pay.stripe.com/invoice/acct_NORMALIZED000001/invst_NORMALIZED0000000000000005e3XWY/pdf", + "last_finalization_error": null, + "lines": { + "data": [ + { + "amount": 10000, + "currency": "usd", + "description": "Zulip standard", + "discount_amounts": [], + "discountable": false, + "discounts": [], + "id": "ii_NORMALIZED00000000000005", + "invoice_item": "ii_NORMALIZED00000000000005", + "livemode": false, + "metadata": {}, + "object": "line_item", + "period": { + "end": 1000000000, + "start": 1000000000 + }, + "plan": null, + "price": { + "active": false, + "billing_scheme": "per_unit", + "created": 1000000000, + "currency": "usd", + "id": "price_1J5YWHD2X8vgpBNGwEA9hn4b", + "livemode": false, + "lookup_key": null, + "metadata": {}, + "nickname": null, + "object": "price", + "product": "prod_Jj0XvpvSalZcLc", + "recurring": null, + "tiers_mode": null, + "transform_quantity": null, + "type": "one_time", + "unit_amount": 10000, + "unit_amount_decimal": "10000" + }, + "proration": false, + "quantity": 1, + "subscription": null, + "tax_amounts": [], + "tax_rates": [], + "type": "invoiceitem", + "unique_id": "il_1J5YWHD2X8vgpBNGmcQgjQGx" + } + ], + "has_more": false, + "object": "list", + "total_count": 1, + "url": "/v1/invoices/in_NORMALIZED00000000000005/lines" + }, + "livemode": false, + "metadata": {}, + "next_payment_attempt": null, + "number": "NORMALI-0005", + "object": "invoice", + "on_behalf_of": null, + "paid": true, + "payment_intent": null, + "payment_settings": { + "payment_method_options": null, + "payment_method_types": null + }, + "period_end": 1000000000, + "period_start": 1000000000, + "post_payment_credit_notes_amount": 0, + "pre_payment_credit_notes_amount": 0, + "receipt_number": null, + "starting_balance": 0, + "statement_descriptor": "Zulip Standard", + "status": "paid", + "status_transitions": { + "finalized_at": 1000000000, + "marked_uncollectible_at": null, + "paid_at": 1000000000, + "voided_at": null + }, + "subscription": null, + "subtotal": 10000, + "tax": null, + "tax_percent": null, + "total": 10000, + "total_discount_amounts": [], + "total_tax_amounts": [], + "transfer_data": null, + "webhooks_delivered_at": 1000000000 +} diff --git a/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--InvoiceItem.create.1.json b/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--InvoiceItem.create.1.json new file mode 100644 index 0000000000..1457f1a436 --- /dev/null +++ b/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--InvoiceItem.create.1.json @@ -0,0 +1,44 @@ +{ + "amount": 10000, + "currency": "usd", + "customer": "cus_NORMALIZED0003", + "date": 1000000000, + "description": "Zulip standard", + "discountable": false, + "discounts": [], + "id": "ii_NORMALIZED00000000000001", + "invoice": null, + "livemode": false, + "metadata": {}, + "object": "invoiceitem", + "period": { + "end": 1000000000, + "start": 1000000000 + }, + "plan": null, + "price": { + "active": false, + "billing_scheme": "per_unit", + "created": 1000000000, + "currency": "usd", + "id": "price_1J5YW6D2X8vgpBNGNRpvTb7X", + "livemode": false, + "lookup_key": null, + "metadata": {}, + "nickname": null, + "object": "price", + "product": "prod_Jj0XEKpg4Z7fzV", + "recurring": null, + "tiers_mode": null, + "transform_quantity": null, + "type": "one_time", + "unit_amount": 10000, + "unit_amount_decimal": "10000" + }, + "proration": false, + "quantity": 1, + "subscription": null, + "tax_rates": [], + "unit_amount": 10000, + "unit_amount_decimal": "10000" +} diff --git a/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--InvoiceItem.create.2.json b/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--InvoiceItem.create.2.json new file mode 100644 index 0000000000..e33c0caff0 --- /dev/null +++ b/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--InvoiceItem.create.2.json @@ -0,0 +1,44 @@ +{ + "amount": 10000, + "currency": "usd", + "customer": "cus_NORMALIZED0004", + "date": 1000000000, + "description": "Zulip standard", + "discountable": false, + "discounts": [], + "id": "ii_NORMALIZED00000000000002", + "invoice": null, + "livemode": false, + "metadata": {}, + "object": "invoiceitem", + "period": { + "end": 1000000000, + "start": 1000000000 + }, + "plan": null, + "price": { + "active": false, + "billing_scheme": "per_unit", + "created": 1000000000, + "currency": "usd", + "id": "price_1J5YW8D2X8vgpBNGjQO5ySQC", + "livemode": false, + "lookup_key": null, + "metadata": {}, + "nickname": null, + "object": "price", + "product": "prod_Jj0XcBc6MoFHgE", + "recurring": null, + "tiers_mode": null, + "transform_quantity": null, + "type": "one_time", + "unit_amount": 10000, + "unit_amount_decimal": "10000" + }, + "proration": false, + "quantity": 1, + "subscription": null, + "tax_rates": [], + "unit_amount": 10000, + "unit_amount_decimal": "10000" +} diff --git a/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--InvoiceItem.create.3.json b/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--InvoiceItem.create.3.json new file mode 100644 index 0000000000..a12ccf83b8 --- /dev/null +++ b/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--InvoiceItem.create.3.json @@ -0,0 +1,44 @@ +{ + "amount": 10000, + "currency": "usd", + "customer": "cus_NORMALIZED0004", + "date": 1000000000, + "description": "Zulip standard", + "discountable": false, + "discounts": [], + "id": "ii_NORMALIZED00000000000003", + "invoice": null, + "livemode": false, + "metadata": {}, + "object": "invoiceitem", + "period": { + "end": 1000000000, + "start": 1000000000 + }, + "plan": null, + "price": { + "active": false, + "billing_scheme": "per_unit", + "created": 1000000000, + "currency": "usd", + "id": "price_1J5YWBD2X8vgpBNGk65s4Bby", + "livemode": false, + "lookup_key": null, + "metadata": {}, + "nickname": null, + "object": "price", + "product": "prod_Jj0XXEj43ttifM", + "recurring": null, + "tiers_mode": null, + "transform_quantity": null, + "type": "one_time", + "unit_amount": 10000, + "unit_amount_decimal": "10000" + }, + "proration": false, + "quantity": 1, + "subscription": null, + "tax_rates": [], + "unit_amount": 10000, + "unit_amount_decimal": "10000" +} diff --git a/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--InvoiceItem.create.4.json b/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--InvoiceItem.create.4.json new file mode 100644 index 0000000000..9f7dba9443 --- /dev/null +++ b/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--InvoiceItem.create.4.json @@ -0,0 +1,44 @@ +{ + "amount": 10000, + "currency": "usd", + "customer": "cus_NORMALIZED0005", + "date": 1000000000, + "description": "Zulip standard", + "discountable": false, + "discounts": [], + "id": "ii_NORMALIZED00000000000004", + "invoice": null, + "livemode": false, + "metadata": {}, + "object": "invoiceitem", + "period": { + "end": 1000000000, + "start": 1000000000 + }, + "plan": null, + "price": { + "active": false, + "billing_scheme": "per_unit", + "created": 1000000000, + "currency": "usd", + "id": "price_1J5YWED2X8vgpBNGJPqvl8ls", + "livemode": false, + "lookup_key": null, + "metadata": {}, + "nickname": null, + "object": "price", + "product": "prod_Jj0XOrbeS8UGnX", + "recurring": null, + "tiers_mode": null, + "transform_quantity": null, + "type": "one_time", + "unit_amount": 10000, + "unit_amount_decimal": "10000" + }, + "proration": false, + "quantity": 1, + "subscription": null, + "tax_rates": [], + "unit_amount": 10000, + "unit_amount_decimal": "10000" +} diff --git a/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--InvoiceItem.create.5.json b/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--InvoiceItem.create.5.json new file mode 100644 index 0000000000..4c6adef06c --- /dev/null +++ b/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--InvoiceItem.create.5.json @@ -0,0 +1,44 @@ +{ + "amount": 10000, + "currency": "usd", + "customer": "cus_NORMALIZED0005", + "date": 1000000000, + "description": "Zulip standard", + "discountable": false, + "discounts": [], + "id": "ii_NORMALIZED00000000000005", + "invoice": null, + "livemode": false, + "metadata": {}, + "object": "invoiceitem", + "period": { + "end": 1000000000, + "start": 1000000000 + }, + "plan": null, + "price": { + "active": false, + "billing_scheme": "per_unit", + "created": 1000000000, + "currency": "usd", + "id": "price_1J5YWHD2X8vgpBNGwEA9hn4b", + "livemode": false, + "lookup_key": null, + "metadata": {}, + "nickname": null, + "object": "price", + "product": "prod_Jj0XvpvSalZcLc", + "recurring": null, + "tiers_mode": null, + "transform_quantity": null, + "type": "one_time", + "unit_amount": 10000, + "unit_amount_decimal": "10000" + }, + "proration": false, + "quantity": 1, + "subscription": null, + "tax_rates": [], + "unit_amount": 10000, + "unit_amount_decimal": "10000" +} diff --git a/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--InvoiceItem.create.6.json b/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--InvoiceItem.create.6.json new file mode 100644 index 0000000000..becb8f0323 --- /dev/null +++ b/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--InvoiceItem.create.6.json @@ -0,0 +1,44 @@ +{ + "amount": 10000, + "currency": "usd", + "customer": "cus_NORMALIZED0006", + "date": 1000000000, + "description": "Zulip standard", + "discountable": false, + "discounts": [], + "id": "ii_NORMALIZED00000000000006", + "invoice": null, + "livemode": false, + "metadata": {}, + "object": "invoiceitem", + "period": { + "end": 1000000000, + "start": 1000000000 + }, + "plan": null, + "price": { + "active": false, + "billing_scheme": "per_unit", + "created": 1000000000, + "currency": "usd", + "id": "price_1J5YWMD2X8vgpBNGGQAGsuaj", + "livemode": false, + "lookup_key": null, + "metadata": {}, + "nickname": null, + "object": "price", + "product": "prod_Jj0XRALl1QAQk5", + "recurring": null, + "tiers_mode": null, + "transform_quantity": null, + "type": "one_time", + "unit_amount": 10000, + "unit_amount_decimal": "10000" + }, + "proration": false, + "quantity": 1, + "subscription": null, + "tax_rates": [], + "unit_amount": 10000, + "unit_amount_decimal": "10000" +} diff --git a/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--InvoiceItem.create.7.json b/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--InvoiceItem.create.7.json new file mode 100644 index 0000000000..a666db3a9a --- /dev/null +++ b/corporate/tests/stripe_fixtures/downgrade_small_realms_behind_on_payments_as_needed--InvoiceItem.create.7.json @@ -0,0 +1,44 @@ +{ + "amount": 10000, + "currency": "usd", + "customer": "cus_NORMALIZED0006", + "date": 1000000000, + "description": "Zulip standard", + "discountable": false, + "discounts": [], + "id": "ii_NORMALIZED00000000000007", + "invoice": null, + "livemode": false, + "metadata": {}, + "object": "invoiceitem", + "period": { + "end": 1000000000, + "start": 1000000000 + }, + "plan": null, + "price": { + "active": false, + "billing_scheme": "per_unit", + "created": 1000000000, + "currency": "usd", + "id": "price_1J5YWPD2X8vgpBNGdU5odKe9", + "livemode": false, + "lookup_key": null, + "metadata": {}, + "nickname": null, + "object": "price", + "product": "prod_Jj0X7KLLErkxlq", + "recurring": null, + "tiers_mode": null, + "transform_quantity": null, + "type": "one_time", + "unit_amount": 10000, + "unit_amount_decimal": "10000" + }, + "proration": false, + "quantity": 1, + "subscription": null, + "tax_rates": [], + "unit_amount": 10000, + "unit_amount_decimal": "10000" +} diff --git a/corporate/tests/test_stripe.py b/corporate/tests/test_stripe.py index ec0a5251d8..456869ba35 100644 --- a/corporate/tests/test_stripe.py +++ b/corporate/tests/test_stripe.py @@ -1,12 +1,13 @@ import json import operator import os +import random import re import sys from datetime import datetime, timedelta, timezone from decimal import Decimal from functools import wraps -from typing import Any, Callable, Dict, List, Mapping, Optional, Sequence, TypeVar, cast +from typing import Any, Callable, Dict, List, Mapping, Optional, Sequence, Tuple, TypeVar, cast from unittest.mock import Mock, patch import orjson @@ -19,6 +20,7 @@ from django.urls.resolvers import get_resolver from django.utils.timezone import now as timezone_now from corporate.lib.stripe import ( + DEFAULT_INVOICE_DAYS_UNTIL_DUE, MAX_INVOICED_LICENSES, MIN_INVOICED_LICENSES, BillingError, @@ -31,6 +33,7 @@ from corporate.lib.stripe import ( compute_plan_parameters, customer_has_credit_card_as_default_source, do_create_stripe_customer, + downgrade_small_realms_behind_on_payments_as_needed, get_discount_for_realm, get_latest_seat_count, get_price_per_license, @@ -273,6 +276,7 @@ MOCKED_STRIPE_FUNCTION_NAMES = [ "Invoice.finalize_invoice", "Invoice.list", "Invoice.pay", + "Invoice.refresh", "Invoice.upcoming", "Invoice.void_invoice", "InvoiceItem.create", @@ -2696,6 +2700,135 @@ class StripeTest(StripeTestCase): for invoice in invoices: self.assertEqual(invoice.status, "void") + @mock_stripe() + def test_downgrade_small_realms_behind_on_payments_as_needed(self, *mock: Mock) -> None: + def create_realm( + users_to_create: int, + create_stripe_customer: bool, + create_plan: bool, + ) -> Tuple[Realm, Optional[Customer], Optional[CustomerPlan]]: + realm_string_id = "realm_" + str(random.randrange(1, 1000000)) + realm = Realm.objects.create(string_id=realm_string_id) + users = [] + for i in range(users_to_create): + user = UserProfile.objects.create( + delivery_email=f"user-{i}-{realm_string_id}@zulip.com", + email=f"user-{i}-{realm_string_id}@zulip.com", + realm=realm, + ) + users.append(user) + + customer = None + if create_stripe_customer: + customer = do_create_stripe_customer(users[0]) + plan = None + if create_plan: + plan, _ = self.subscribe_realm_to_monthly_plan_on_manual_license_management( + realm, users_to_create, users_to_create + ) + return realm, customer, plan + + def create_invoices(customer: Customer, num_invoices: int) -> List[stripe.Invoice]: + invoices = [] + assert customer.stripe_customer_id is not None + for _ in range(num_invoices): + stripe.InvoiceItem.create( + amount=10000, + currency="usd", + customer=customer.stripe_customer_id, + description="Zulip standard", + discountable=False, + ) + invoice = stripe.Invoice.create( + auto_advance=True, + billing="send_invoice", + customer=customer.stripe_customer_id, + days_until_due=DEFAULT_INVOICE_DAYS_UNTIL_DUE, + statement_descriptor="Zulip Standard", + ) + stripe.Invoice.finalize_invoice(invoice) + invoices.append(invoice) + return invoices + + realm_1, _, _ = create_realm( + users_to_create=1, create_stripe_customer=True, create_plan=False + ) + + realm_2, _, plan_2 = create_realm( + users_to_create=1, create_stripe_customer=True, create_plan=True + ) + assert plan_2 + + realm_3, customer_3, plan_3 = create_realm( + users_to_create=1, create_stripe_customer=True, create_plan=True + ) + assert customer_3 and plan_3 + create_invoices(customer_3, num_invoices=1) + + realm_4, customer_4, plan_4 = create_realm( + users_to_create=3, create_stripe_customer=True, create_plan=True + ) + assert customer_4 and plan_4 + create_invoices(customer_4, num_invoices=2) + + realm_5, customer_5, plan_5 = create_realm( + users_to_create=1, create_stripe_customer=True, create_plan=True + ) + assert customer_5 and plan_5 + realm_5_invoices = create_invoices(customer_5, num_invoices=2) + for invoice in realm_5_invoices: + stripe.Invoice.pay(invoice, paid_out_of_band=True) + + realm_6, customer_6, plan_6 = create_realm( + users_to_create=20, create_stripe_customer=True, create_plan=True + ) + assert customer_6 and plan_6 + create_invoices(customer_6, num_invoices=2) + + with patch("corporate.lib.stripe.void_all_open_invoices") as void_all_open_invoices_mock: + downgrade_small_realms_behind_on_payments_as_needed() + + realm_1.refresh_from_db() + self.assertEqual(realm_1.plan_type, Realm.SELF_HOSTED) + + realm_2.refresh_from_db() + self.assertEqual(realm_2.plan_type, Realm.STANDARD) + plan_2.refresh_from_db() + self.assertEqual(plan_2.status, CustomerPlan.ACTIVE) + + realm_3.refresh_from_db() + self.assertEqual(realm_3.plan_type, Realm.STANDARD) + plan_3.refresh_from_db() + self.assertEqual(plan_3.status, CustomerPlan.ACTIVE) + + realm_4.refresh_from_db() + self.assertEqual(realm_4.plan_type, Realm.LIMITED) + plan_4.refresh_from_db() + self.assertEqual(plan_4.status, CustomerPlan.ENDED) + void_all_open_invoices_mock.assert_called_once_with(realm_4) + + realm_5.refresh_from_db() + self.assertEqual(realm_5.plan_type, Realm.STANDARD) + plan_5.refresh_from_db() + self.assertEqual(plan_5.status, CustomerPlan.ACTIVE) + + realm_6.refresh_from_db() + self.assertEqual(realm_6.plan_type, Realm.STANDARD) + plan_6.refresh_from_db() + self.assertEqual(plan_6.status, CustomerPlan.ACTIVE) + + from django.core.mail import outbox + + self.assert_length(outbox, 1) + self.assertIn( + f"Your organization, http://{realm_4.string_id}.testserver, has been downgraded", + outbox[0].body, + ) + self.assert_length(outbox[0].to, 1) + recipient = UserProfile.objects.get(email=outbox[0].to[0]) + self.assertEqual(recipient.realm, realm_4) + self.assertTrue(recipient.is_billing_admin) + def test_update_billing_method_of_current_plan(self) -> None: realm = get_realm("zulip") customer = Customer.objects.create(realm=realm, stripe_customer_id="cus_12345") diff --git a/puppet/zulip/files/cron.d/downgrade-small-realms-behind-on-payments b/puppet/zulip/files/cron.d/downgrade-small-realms-behind-on-payments new file mode 100644 index 0000000000..e0b3d126f3 --- /dev/null +++ b/puppet/zulip/files/cron.d/downgrade-small-realms-behind-on-payments @@ -0,0 +1,5 @@ +SHELL=/bin/bash +PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin +USER=zulip + +0 17 * * * zulip /home/zulip/deployments/current/manage.py downgrade_small_realms_behind_on_payments diff --git a/puppet/zulip_ops/manifests/prod_app_frontend_once.pp b/puppet/zulip_ops/manifests/prod_app_frontend_once.pp index 54a2a123fb..79e0c63027 100644 --- a/puppet/zulip_ops/manifests/prod_app_frontend_once.pp +++ b/puppet/zulip_ops/manifests/prod_app_frontend_once.pp @@ -17,6 +17,14 @@ class zulip_ops::prod_app_frontend_once { source => 'puppet:///modules/zulip/cron.d/invoice-plans', } + file { '/etc/cron.d/downgrade-small-realms-behind-on-payments': + ensure => file, + owner => 'root', + group => 'root', + mode => '0644', + source => 'puppet:///modules/zulip/cron.d/downgrade-small-realms-behind-on-payments', + } + file { '/etc/cron.d/check_send_receive_time': ensure => file, owner => 'root', diff --git a/stubs/stripe/__init__.pyi b/stubs/stripe/__init__.pyi index 5ef2d1b4a0..9561f78562 100644 --- a/stubs/stripe/__init__.pyi +++ b/stubs/stripe/__init__.pyi @@ -81,7 +81,7 @@ class Invoice: ... @staticmethod - def pay(invoice: Invoice) -> Invoice: + def pay(invoice: Invoice, paid_out_of_band: bool=False) -> Invoice: ... @staticmethod @@ -91,6 +91,10 @@ class Invoice: def get(self, key: str) -> Any: ... + @staticmethod + def refresh(invoice: Invoice) -> Invoice: + ... + class Subscription: created: int status: str diff --git a/templates/zerver/emails/realm_auto_downgraded.source.html b/templates/zerver/emails/realm_auto_downgraded.source.html new file mode 100644 index 0000000000..34ca26b7e9 --- /dev/null +++ b/templates/zerver/emails/realm_auto_downgraded.source.html @@ -0,0 +1,25 @@ +{% extends "zerver/emails/compiled/email_base_default.html" %} + +{% block illustration %} + +{% endblock %} + +{% block content %} + {% trans organization_name_with_link=macros.link_tag(realm.uri, realm.string_id) %} + Your organization, {{ organization_name_with_link }}, has been downgraded to the Zulip Cloud + Free plan because of unpaid invoices. The unpaid invoices have been voided. + {% endtrans %} +
+
+ + {% trans upgrade_url=macros.link_tag(upgrade_url) %} + To continue on the Zulip Cloud Standard plan, please upgrade again by going to {{ upgrade_url }}. + {% endtrans %} + +
+
+ + {% trans support_email=macros.email_tag(support_email) %} + If you think this was a mistake or need more details, please reach out to us at {{ support_email }}. + {% endtrans %} +{% endblock %} diff --git a/templates/zerver/emails/realm_auto_downgraded.subject.txt b/templates/zerver/emails/realm_auto_downgraded.subject.txt new file mode 100644 index 0000000000..1aaae918c2 --- /dev/null +++ b/templates/zerver/emails/realm_auto_downgraded.subject.txt @@ -0,0 +1 @@ +{{ realm.string_id }}: Your organization has been downgraded to Zulip Cloud Free diff --git a/templates/zerver/emails/realm_auto_downgraded.txt b/templates/zerver/emails/realm_auto_downgraded.txt new file mode 100644 index 0000000000..2464239565 --- /dev/null +++ b/templates/zerver/emails/realm_auto_downgraded.txt @@ -0,0 +1,8 @@ +Your organization, {{ realm.uri }}, has been downgraded to the Zulip Cloud +Free plan because of unpaid invoices. The unpaid invoices have been voided. + +To continue on the Zulip Cloud Standard plan, please upgrade again by going +to {{ upgrade_url }}. + +If you think this was a mistake or need more details, please reach out +to us at support@zulip.com. diff --git a/version.py b/version.py index a4614dea0e..8bde12df69 100644 --- a/version.py +++ b/version.py @@ -48,4 +48,4 @@ API_FEATURE_LEVEL = 75 # historical commits sharing the same major version, in which case a # minor version bump suffices. -PROVISION_VERSION = "150.5" +PROVISION_VERSION = "150.6" diff --git a/zerver/lib/send_email.py b/zerver/lib/send_email.py index b4638f4c2f..5e7d020eb5 100644 --- a/zerver/lib/send_email.py +++ b/zerver/lib/send_email.py @@ -380,6 +380,24 @@ def send_email_to_admins( ) +def send_email_to_billing_admins_and_realm_owners( + template_prefix: str, + realm: Realm, + from_name: Optional[str] = None, + from_address: Optional[str] = None, + language: Optional[str] = None, + context: Dict[str, Any] = {}, +) -> None: + send_email( + template_prefix, + to_user_ids=[user.id for user in realm.get_human_billing_admin_and_realm_owner_users()], + from_name=from_name, + from_address=from_address, + language=language, + context=context, + ) + + def clear_scheduled_invitation_emails(email: str) -> None: """Unlike most scheduled emails, invitation emails don't have an existing user object to key off of, so we filter by address here.""" diff --git a/zerver/tests/test_management_commands.py b/zerver/tests/test_management_commands.py index 026a73c6c2..a046cdf2e1 100644 --- a/zerver/tests/test_management_commands.py +++ b/zerver/tests/test_management_commands.py @@ -483,6 +483,19 @@ class TestInvoicePlans(ZulipTestCase): m.assert_called_once() +@skipUnless(settings.ZILENCER_ENABLED, "requires zilencer") +class TestDowngradeSmallRealmsBehindOnPayments(ZulipTestCase): + COMMAND_NAME = "downgrade_small_realms_behind_on_payments" + + def test_if_command_calls_downgrade_small_realms_behind_on_payments_as_needed(self) -> None: + with patch( + "zilencer.management.commands.downgrade_small_realms_behind_on_payments.downgrade_small_realms_behind_on_payments_as_needed" + ) as m: + call_command(self.COMMAND_NAME) + + m.assert_called_once() + + class TestExport(ZulipTestCase): COMMAND_NAME = "export" diff --git a/zilencer/management/commands/downgrade_small_realms_behind_on_payments.py b/zilencer/management/commands/downgrade_small_realms_behind_on_payments.py new file mode 100644 index 0000000000..5a9abf85d4 --- /dev/null +++ b/zilencer/management/commands/downgrade_small_realms_behind_on_payments.py @@ -0,0 +1,11 @@ +from typing import Any + +from corporate.lib.stripe import downgrade_small_realms_behind_on_payments_as_needed +from zerver.lib.management import ZulipBaseCommand + + +class Command(ZulipBaseCommand): + help = "Downgrade small realms that are running behind on payments" + + def handle(self, *args: Any, **options: Any) -> None: + downgrade_small_realms_behind_on_payments_as_needed()