diff --git a/corporate/lib/stripe.py b/corporate/lib/stripe.py index a93cc14740..e9fc4f3bd1 100644 --- a/corporate/lib/stripe.py +++ b/corporate/lib/stripe.py @@ -636,3 +636,17 @@ def downgrade_now_without_creating_additional_invoices(realm: Realm) -> None: plan.invoiced_through = LicenseLedger.objects.filter(plan=plan).order_by('id').last() plan.next_invoice_date = next_invoice_date(plan) plan.save(update_fields=["invoiced_through", "next_invoice_date"]) + +def void_all_open_invoices(realm: Realm) -> int: + customer = get_customer_by_realm(realm) + if customer is None: + return 0 + invoices = stripe.Invoice.list(customer=customer.stripe_customer_id) + voided_invoices_count = 0 + for invoice in invoices: + if invoice.status == "open": + stripe.Invoice.void_invoice( + invoice.id + ) + voided_invoices_count += 1 + return voided_invoices_count diff --git a/corporate/tests/stripe_fixtures/void_all_open_invoices--Customer.create.1.json b/corporate/tests/stripe_fixtures/void_all_open_invoices--Customer.create.1.json new file mode 100644 index 0000000000..611715f7d6 --- /dev/null +++ b/corporate/tests/stripe_fixtures/void_all_open_invoices--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": "zulip (Zulip Dev)", + "discount": null, + "email": "iago@zulip.com", + "id": "cus_NORMALIZED0001", + "invoice_prefix": "NORMA01", + "invoice_settings": { + "custom_fields": null, + "default_payment_method": null, + "footer": null + }, + "livemode": false, + "metadata": { + "realm_id": "1", + "realm_str": "zulip" + }, + "name": null, + "next_invoice_sequence": 1, + "object": "customer", + "phone": null, + "preferred_locales": [], + "shipping": null, + "sources": { + "data": [], + "has_more": false, + "object": "list", + "total_count": 0, + "url": "/v1/customers/cus_NORMALIZED0001/sources" + }, + "subscriptions": { + "data": [], + "has_more": false, + "object": "list", + "total_count": 0, + "url": "/v1/customers/cus_NORMALIZED0001/subscriptions" + }, + "tax_exempt": "none", + "tax_ids": { + "data": [], + "has_more": false, + "object": "list", + "total_count": 0, + "url": "/v1/customers/cus_NORMALIZED0001/tax_ids" + }, + "tax_info": null, + "tax_info_verification": null +} diff --git a/corporate/tests/stripe_fixtures/void_all_open_invoices--Invoice.create.1.json b/corporate/tests/stripe_fixtures/void_all_open_invoices--Invoice.create.1.json new file mode 100644 index 0000000000..9f107b7be5 --- /dev/null +++ b/corporate/tests/stripe_fixtures/void_all_open_invoices--Invoice.create.1.json @@ -0,0 +1,122 @@ +{ + "account_country": "US", + "account_name": null, + "amount_due": 6400, + "amount_paid": 0, + "amount_remaining": 6400, + "application_fee": null, + "attempt_count": 0, + "attempted": false, + "auto_advance": true, + "billing": "send_invoice", + "billing_reason": "manual", + "charge": null, + "collection_method": "send_invoice", + "created": 1000000000, + "currency": "usd", + "custom_fields": null, + "customer": "cus_NORMALIZED0001", + "customer_address": null, + "customer_email": "iago@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, + "lines": { + "data": [ + { + "amount": 6400, + "currency": "usd", + "description": "Zulip standard upgrade", + "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_1HFiX7D2X8vgpBNGZKqyye0n", + "livemode": false, + "lookup_key": null, + "metadata": {}, + "nickname": null, + "object": "price", + "product": "prod_HpNH3ZK9Bpc89P", + "recurring": null, + "tiers_mode": null, + "transform_quantity": null, + "type": "one_time", + "unit_amount": 800, + "unit_amount_decimal": "800" + }, + "proration": false, + "quantity": 8, + "subscription": null, + "tax_amounts": [], + "tax_rates": [], + "type": "invoiceitem", + "unique_id": "il_1HFiX7D2X8vgpBNGYzYwN35m" + } + ], + "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", + "paid": false, + "payment_intent": 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": 6400, + "tax": null, + "tax_percent": null, + "total": 6400, + "total_discount_amounts": [], + "total_tax_amounts": [], + "transfer_data": null, + "webhooks_delivered_at": 1000000000 +} diff --git a/corporate/tests/stripe_fixtures/void_all_open_invoices--Invoice.finalize_invoice.1.json b/corporate/tests/stripe_fixtures/void_all_open_invoices--Invoice.finalize_invoice.1.json new file mode 100644 index 0000000000..475bef780b --- /dev/null +++ b/corporate/tests/stripe_fixtures/void_all_open_invoices--Invoice.finalize_invoice.1.json @@ -0,0 +1,122 @@ +{ + "account_country": "US", + "account_name": null, + "amount_due": 6400, + "amount_paid": 0, + "amount_remaining": 6400, + "application_fee": null, + "attempt_count": 0, + "attempted": false, + "auto_advance": true, + "billing": "send_invoice", + "billing_reason": "manual", + "charge": null, + "collection_method": "send_invoice", + "created": 1000000000, + "currency": "usd", + "custom_fields": null, + "customer": "cus_NORMALIZED0001", + "customer_address": null, + "customer_email": "iago@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://pay.stripe.com/invoice/acct_NORMALIZED000001/invst_NORMALIZED0000000000000001CU7gS", + "id": "in_NORMALIZED00000000000001", + "invoice_pdf": "https://pay.stripe.com/invoice/acct_NORMALIZED000001/invst_NORMALIZED0000000000000001CU7gS/pdf", + "lines": { + "data": [ + { + "amount": 6400, + "currency": "usd", + "description": "Zulip standard upgrade", + "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_1HFiX7D2X8vgpBNGZKqyye0n", + "livemode": false, + "lookup_key": null, + "metadata": {}, + "nickname": null, + "object": "price", + "product": "prod_HpNH3ZK9Bpc89P", + "recurring": null, + "tiers_mode": null, + "transform_quantity": null, + "type": "one_time", + "unit_amount": 800, + "unit_amount_decimal": "800" + }, + "proration": false, + "quantity": 8, + "subscription": null, + "tax_amounts": [], + "tax_rates": [], + "type": "invoiceitem", + "unique_id": "il_1HFiX7D2X8vgpBNGYzYwN35m" + } + ], + "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", + "paid": false, + "payment_intent": "pi_1HFiX8D2X8vgpBNGGjAHPbBr", + "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": 6400, + "tax": null, + "tax_percent": null, + "total": 6400, + "total_discount_amounts": [], + "total_tax_amounts": [], + "transfer_data": null, + "webhooks_delivered_at": 1000000000 +} diff --git a/corporate/tests/stripe_fixtures/void_all_open_invoices--Invoice.list.1.json b/corporate/tests/stripe_fixtures/void_all_open_invoices--Invoice.list.1.json new file mode 100644 index 0000000000..3ea16e20d1 --- /dev/null +++ b/corporate/tests/stripe_fixtures/void_all_open_invoices--Invoice.list.1.json @@ -0,0 +1,129 @@ +{ + "data": [ + { + "account_country": "US", + "account_name": null, + "amount_due": 6400, + "amount_paid": 0, + "amount_remaining": 6400, + "application_fee": null, + "attempt_count": 0, + "attempted": false, + "auto_advance": true, + "billing": "send_invoice", + "billing_reason": "manual", + "charge": null, + "collection_method": "send_invoice", + "created": 1000000000, + "currency": "usd", + "custom_fields": null, + "customer": "cus_NORMALIZED0001", + "customer_address": null, + "customer_email": "iago@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://pay.stripe.com/invoice/acct_NORMALIZED000001/invst_NORMALIZED0000000000000001CU7gS", + "id": "in_NORMALIZED00000000000001", + "invoice_pdf": "https://pay.stripe.com/invoice/acct_NORMALIZED000001/invst_NORMALIZED0000000000000001CU7gS/pdf", + "lines": { + "data": [ + { + "amount": 6400, + "currency": "usd", + "description": "Zulip standard upgrade", + "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_1HFiX7D2X8vgpBNGZKqyye0n", + "livemode": false, + "lookup_key": null, + "metadata": {}, + "nickname": null, + "object": "price", + "product": "prod_HpNH3ZK9Bpc89P", + "recurring": null, + "tiers_mode": null, + "transform_quantity": null, + "type": "one_time", + "unit_amount": 800, + "unit_amount_decimal": "800" + }, + "proration": false, + "quantity": 8, + "subscription": null, + "tax_amounts": [], + "tax_rates": [], + "type": "invoiceitem", + "unique_id": "il_1HFiX7D2X8vgpBNGYzYwN35m" + } + ], + "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", + "paid": false, + "payment_intent": "pi_1HFiX8D2X8vgpBNGGjAHPbBr", + "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": 6400, + "tax": null, + "tax_percent": null, + "total": 6400, + "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/void_all_open_invoices--Invoice.list.2.json b/corporate/tests/stripe_fixtures/void_all_open_invoices--Invoice.list.2.json new file mode 100644 index 0000000000..01d93a468b --- /dev/null +++ b/corporate/tests/stripe_fixtures/void_all_open_invoices--Invoice.list.2.json @@ -0,0 +1,129 @@ +{ + "data": [ + { + "account_country": "US", + "account_name": null, + "amount_due": 6400, + "amount_paid": 0, + "amount_remaining": 6400, + "application_fee": null, + "attempt_count": 0, + "attempted": false, + "auto_advance": false, + "billing": "send_invoice", + "billing_reason": "manual", + "charge": null, + "collection_method": "send_invoice", + "created": 1000000000, + "currency": "usd", + "custom_fields": null, + "customer": "cus_NORMALIZED0001", + "customer_address": null, + "customer_email": "iago@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://pay.stripe.com/invoice/acct_NORMALIZED000001/invst_NORMALIZED0000000000000001CU7gS", + "id": "in_NORMALIZED00000000000001", + "invoice_pdf": "https://pay.stripe.com/invoice/acct_NORMALIZED000001/invst_NORMALIZED0000000000000001CU7gS/pdf", + "lines": { + "data": [ + { + "amount": 6400, + "currency": "usd", + "description": "Zulip standard upgrade", + "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_1HFiX7D2X8vgpBNGZKqyye0n", + "livemode": false, + "lookup_key": null, + "metadata": {}, + "nickname": null, + "object": "price", + "product": "prod_HpNH3ZK9Bpc89P", + "recurring": null, + "tiers_mode": null, + "transform_quantity": null, + "type": "one_time", + "unit_amount": 800, + "unit_amount_decimal": "800" + }, + "proration": false, + "quantity": 8, + "subscription": null, + "tax_amounts": [], + "tax_rates": [], + "type": "invoiceitem", + "unique_id": "il_1HFiX7D2X8vgpBNGYzYwN35m" + } + ], + "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", + "paid": false, + "payment_intent": "pi_1HFiX8D2X8vgpBNGGjAHPbBr", + "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": "void", + "status_transitions": { + "finalized_at": 1000000000, + "marked_uncollectible_at": null, + "paid_at": null, + "voided_at": 1000000000 + }, + "subscription": null, + "subtotal": 6400, + "tax": null, + "tax_percent": null, + "total": 6400, + "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/void_all_open_invoices--Invoice.void_invoice.1.json b/corporate/tests/stripe_fixtures/void_all_open_invoices--Invoice.void_invoice.1.json new file mode 100644 index 0000000000..39f3ca9acf --- /dev/null +++ b/corporate/tests/stripe_fixtures/void_all_open_invoices--Invoice.void_invoice.1.json @@ -0,0 +1,122 @@ +{ + "account_country": "US", + "account_name": null, + "amount_due": 6400, + "amount_paid": 0, + "amount_remaining": 6400, + "application_fee": null, + "attempt_count": 0, + "attempted": false, + "auto_advance": false, + "billing": "send_invoice", + "billing_reason": "manual", + "charge": null, + "collection_method": "send_invoice", + "created": 1000000000, + "currency": "usd", + "custom_fields": null, + "customer": "cus_NORMALIZED0001", + "customer_address": null, + "customer_email": "iago@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://pay.stripe.com/invoice/acct_NORMALIZED000001/invst_NORMALIZED0000000000000001CU7gS", + "id": "in_NORMALIZED00000000000001", + "invoice_pdf": "https://pay.stripe.com/invoice/acct_NORMALIZED000001/invst_NORMALIZED0000000000000001CU7gS/pdf", + "lines": { + "data": [ + { + "amount": 6400, + "currency": "usd", + "description": "Zulip standard upgrade", + "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_1HFiX7D2X8vgpBNGZKqyye0n", + "livemode": false, + "lookup_key": null, + "metadata": {}, + "nickname": null, + "object": "price", + "product": "prod_HpNH3ZK9Bpc89P", + "recurring": null, + "tiers_mode": null, + "transform_quantity": null, + "type": "one_time", + "unit_amount": 800, + "unit_amount_decimal": "800" + }, + "proration": false, + "quantity": 8, + "subscription": null, + "tax_amounts": [], + "tax_rates": [], + "type": "invoiceitem", + "unique_id": "il_1HFiX7D2X8vgpBNGYzYwN35m" + } + ], + "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", + "paid": false, + "payment_intent": "pi_1HFiX8D2X8vgpBNGGjAHPbBr", + "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": "void", + "status_transitions": { + "finalized_at": 1000000000, + "marked_uncollectible_at": null, + "paid_at": null, + "voided_at": 1000000000 + }, + "subscription": null, + "subtotal": 6400, + "tax": null, + "tax_percent": null, + "total": 6400, + "total_discount_amounts": [], + "total_tax_amounts": [], + "transfer_data": null, + "webhooks_delivered_at": 1000000000 +} diff --git a/corporate/tests/stripe_fixtures/void_all_open_invoices--InvoiceItem.create.1.json b/corporate/tests/stripe_fixtures/void_all_open_invoices--InvoiceItem.create.1.json new file mode 100644 index 0000000000..3a4c338d1c --- /dev/null +++ b/corporate/tests/stripe_fixtures/void_all_open_invoices--InvoiceItem.create.1.json @@ -0,0 +1,44 @@ +{ + "amount": 6400, + "currency": "usd", + "customer": "cus_NORMALIZED0001", + "date": 1000000000, + "description": "Zulip standard upgrade", + "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_1HFiX7D2X8vgpBNGZKqyye0n", + "livemode": false, + "lookup_key": null, + "metadata": {}, + "nickname": null, + "object": "price", + "product": "prod_HpNH3ZK9Bpc89P", + "recurring": null, + "tiers_mode": null, + "transform_quantity": null, + "type": "one_time", + "unit_amount": 800, + "unit_amount_decimal": "800" + }, + "proration": false, + "quantity": 8, + "subscription": null, + "tax_rates": [], + "unit_amount": 800, + "unit_amount_decimal": "800" +} diff --git a/corporate/tests/test_stripe.py b/corporate/tests/test_stripe.py index 37ff6ba1ee..8ac77be47c 100644 --- a/corporate/tests/test_stripe.py +++ b/corporate/tests/test_stripe.py @@ -40,6 +40,7 @@ from corporate.lib.stripe import ( update_license_ledger_for_automanaged_plan, update_license_ledger_if_needed, update_or_create_stripe_customer, + void_all_open_invoices, ) from corporate.models import ( Customer, @@ -201,7 +202,7 @@ MOCKED_STRIPE_FUNCTION_NAMES = [f"stripe.{name}" for name in [ "Charge.create", "Charge.list", "Coupon.create", "Customer.create", "Customer.retrieve", "Customer.save", - "Invoice.create", "Invoice.finalize_invoice", "Invoice.list", "Invoice.pay", "Invoice.upcoming", + "Invoice.create", "Invoice.finalize_invoice", "Invoice.list", "Invoice.pay", "Invoice.upcoming", "Invoice.void_invoice", "InvoiceItem.create", "InvoiceItem.list", "Plan.create", "Product.create", @@ -1816,6 +1817,35 @@ class StripeTest(StripeTestCase): self.assertEqual(old_plan.next_invoice_date, None) self.assertEqual(old_plan.status, CustomerPlan.ENDED) + @mock_stripe() + def test_void_all_open_invoices(self, *mock: Mock) -> None: + iago = self.example_user("iago") + self.assertEqual(void_all_open_invoices(iago.realm), 0) + customer = update_or_create_stripe_customer(iago) + + stripe.InvoiceItem.create( + currency='usd', + customer=customer.stripe_customer_id, + description="Zulip standard upgrade", + discountable=False, + unit_amount=800, + quantity=8 + ) + stripe_invoice = stripe.Invoice.create( + auto_advance=True, + billing="send_invoice", + customer=customer.stripe_customer_id, + days_until_due=30, + statement_descriptor='Zulip Standard' + ) + stripe.Invoice.finalize_invoice(stripe_invoice) + + self.assertEqual(void_all_open_invoices(iago.realm), 1) + invoices = stripe.Invoice.list(customer=customer.stripe_customer_id) + self.assertEqual(len(invoices), 1) + for invoice in invoices: + self.assertEqual(invoice.status, "void") + class RequiresBillingAccessTest(ZulipTestCase): def setUp(self) -> None: super().setUp() diff --git a/stubs/stripe/__init__.pyi b/stubs/stripe/__init__.pyi index e978874250..61ca1aa4df 100644 --- a/stubs/stripe/__init__.pyi +++ b/stubs/stripe/__init__.pyi @@ -47,6 +47,7 @@ class Customer: class Invoice: + id: str auto_advance: bool amount_due: int billing: str @@ -81,6 +82,10 @@ class Invoice: def pay(invoice: Invoice) -> Invoice: ... + @staticmethod + def void_invoice(id: str) -> None: + ... + def get(self, key: str) -> Any: ...