mirror of
https://github.com/zulip/zulip.git
synced 2025-11-09 08:26:11 +00:00
billing: Allow free trial orgs to switch billing frequency.
Fixes #27855
This commit is contained in:
@@ -491,6 +491,7 @@ class UpdatePlanRequest:
|
|||||||
status: Optional[int]
|
status: Optional[int]
|
||||||
licenses: Optional[int]
|
licenses: Optional[int]
|
||||||
licenses_at_next_renewal: Optional[int]
|
licenses_at_next_renewal: Optional[int]
|
||||||
|
schedule: Optional[int]
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
@@ -1035,6 +1036,72 @@ class BillingSession(ABC):
|
|||||||
data["stripe_payment_intent_id"] = stripe_payment_intent_id
|
data["stripe_payment_intent_id"] = stripe_payment_intent_id
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
def do_change_schedule_after_free_trial(self, plan: CustomerPlan, schedule: int) -> None:
|
||||||
|
# Change the billing frequency of the plan after the free trial ends.
|
||||||
|
assert schedule in (CustomerPlan.MONTHLY, CustomerPlan.ANNUAL)
|
||||||
|
last_ledger_entry = LicenseLedger.objects.filter(plan=plan).order_by("-id").first()
|
||||||
|
assert last_ledger_entry is not None
|
||||||
|
licenses_at_next_renewal = last_ledger_entry.licenses_at_next_renewal
|
||||||
|
assert licenses_at_next_renewal is not None
|
||||||
|
assert plan.next_invoice_date is not None
|
||||||
|
next_billing_cycle = plan.next_invoice_date
|
||||||
|
|
||||||
|
if plan.fixed_price is not None: # nocoverage
|
||||||
|
raise BillingError("Customer is already on monthly fixed plan.")
|
||||||
|
|
||||||
|
plan.status = CustomerPlan.ENDED
|
||||||
|
plan.save(update_fields=["status"])
|
||||||
|
|
||||||
|
discount = plan.customer.default_discount or plan.discount
|
||||||
|
_, _, _, price_per_license = compute_plan_parameters(
|
||||||
|
tier=plan.tier,
|
||||||
|
automanage_licenses=plan.automanage_licenses,
|
||||||
|
billing_schedule=schedule,
|
||||||
|
discount=plan.discount,
|
||||||
|
)
|
||||||
|
|
||||||
|
new_plan = CustomerPlan.objects.create(
|
||||||
|
customer=plan.customer,
|
||||||
|
billing_schedule=schedule,
|
||||||
|
automanage_licenses=plan.automanage_licenses,
|
||||||
|
charge_automatically=plan.charge_automatically,
|
||||||
|
price_per_license=price_per_license,
|
||||||
|
discount=discount,
|
||||||
|
billing_cycle_anchor=plan.billing_cycle_anchor,
|
||||||
|
tier=plan.tier,
|
||||||
|
status=CustomerPlan.FREE_TRIAL,
|
||||||
|
next_invoice_date=next_billing_cycle,
|
||||||
|
invoiced_through=None,
|
||||||
|
invoicing_status=CustomerPlan.INITIAL_INVOICE_TO_BE_SENT,
|
||||||
|
)
|
||||||
|
|
||||||
|
LicenseLedger.objects.create(
|
||||||
|
plan=new_plan,
|
||||||
|
is_renewal=True,
|
||||||
|
event_time=plan.billing_cycle_anchor,
|
||||||
|
licenses=licenses_at_next_renewal,
|
||||||
|
licenses_at_next_renewal=licenses_at_next_renewal,
|
||||||
|
)
|
||||||
|
|
||||||
|
if schedule == CustomerPlan.ANNUAL:
|
||||||
|
self.write_to_audit_log(
|
||||||
|
event_type=AuditLogEventType.CUSTOMER_SWITCHED_FROM_MONTHLY_TO_ANNUAL_PLAN,
|
||||||
|
event_time=timezone_now(),
|
||||||
|
extra_data={
|
||||||
|
"monthly_plan_id": plan.id,
|
||||||
|
"annual_plan_id": new_plan.id,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self.write_to_audit_log(
|
||||||
|
event_type=AuditLogEventType.CUSTOMER_SWITCHED_FROM_ANNUAL_TO_MONTHLY_PLAN,
|
||||||
|
event_time=timezone_now(),
|
||||||
|
extra_data={
|
||||||
|
"annual_plan_id": plan.id,
|
||||||
|
"monthly_plan_id": new_plan.id,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
def get_next_billing_cycle(self, plan: CustomerPlan) -> datetime:
|
def get_next_billing_cycle(self, plan: CustomerPlan) -> datetime:
|
||||||
last_ledger_renewal = (
|
last_ledger_renewal = (
|
||||||
LicenseLedger.objects.filter(plan=plan, is_renewal=True).order_by("-id").first()
|
LicenseLedger.objects.filter(plan=plan, is_renewal=True).order_by("-id").first()
|
||||||
@@ -1061,8 +1128,9 @@ class BillingSession(ABC):
|
|||||||
) -> Tuple[Optional[CustomerPlan], Optional[LicenseLedger]]:
|
) -> Tuple[Optional[CustomerPlan], Optional[LicenseLedger]]:
|
||||||
last_ledger_entry = LicenseLedger.objects.filter(plan=plan).order_by("-id").first()
|
last_ledger_entry = LicenseLedger.objects.filter(plan=plan).order_by("-id").first()
|
||||||
next_billing_cycle = self.get_next_billing_cycle(plan)
|
next_billing_cycle = self.get_next_billing_cycle(plan)
|
||||||
|
event_in_next_billing_cycle = next_billing_cycle <= event_time
|
||||||
|
|
||||||
if next_billing_cycle <= event_time and last_ledger_entry is not None:
|
if event_in_next_billing_cycle and last_ledger_entry is not None:
|
||||||
licenses_at_next_renewal = last_ledger_entry.licenses_at_next_renewal
|
licenses_at_next_renewal = last_ledger_entry.licenses_at_next_renewal
|
||||||
assert licenses_at_next_renewal is not None
|
assert licenses_at_next_renewal is not None
|
||||||
if plan.status == CustomerPlan.ACTIVE:
|
if plan.status == CustomerPlan.ACTIVE:
|
||||||
@@ -1187,6 +1255,7 @@ class BillingSession(ABC):
|
|||||||
|
|
||||||
if plan.status == CustomerPlan.DOWNGRADE_AT_END_OF_CYCLE:
|
if plan.status == CustomerPlan.DOWNGRADE_AT_END_OF_CYCLE:
|
||||||
self.process_downgrade(plan)
|
self.process_downgrade(plan)
|
||||||
|
|
||||||
return None, None
|
return None, None
|
||||||
return None, last_ledger_entry
|
return None, last_ledger_entry
|
||||||
|
|
||||||
@@ -1441,6 +1510,9 @@ class BillingSession(ABC):
|
|||||||
assert plan.is_free_trial()
|
assert plan.is_free_trial()
|
||||||
do_change_plan_status(plan, status)
|
do_change_plan_status(plan, status)
|
||||||
elif status == CustomerPlan.FREE_TRIAL:
|
elif status == CustomerPlan.FREE_TRIAL:
|
||||||
|
if update_plan_request.schedule is not None:
|
||||||
|
self.do_change_schedule_after_free_trial(plan, update_plan_request.schedule)
|
||||||
|
else:
|
||||||
assert plan.status == CustomerPlan.DOWNGRADE_AT_END_OF_FREE_TRIAL
|
assert plan.status == CustomerPlan.DOWNGRADE_AT_END_OF_FREE_TRIAL
|
||||||
do_change_plan_status(plan, status)
|
do_change_plan_status(plan, status)
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
"address": null,
|
||||||
|
"balance": 0,
|
||||||
|
"created": 1701007131,
|
||||||
|
"currency": null,
|
||||||
|
"default_currency": null,
|
||||||
|
"default_source": null,
|
||||||
|
"delinquent": false,
|
||||||
|
"description": "zulip (Zulip Dev)",
|
||||||
|
"discount": null,
|
||||||
|
"email": "hamlet@zulip.com",
|
||||||
|
"id": "cus_P4scN21egyF63j",
|
||||||
|
"invoice_prefix": "BFEDDCEB",
|
||||||
|
"invoice_settings": {
|
||||||
|
"custom_fields": null,
|
||||||
|
"default_payment_method": null,
|
||||||
|
"footer": null,
|
||||||
|
"rendering_options": null
|
||||||
|
},
|
||||||
|
"livemode": false,
|
||||||
|
"metadata": {
|
||||||
|
"realm_id": "2",
|
||||||
|
"realm_str": "zulip"
|
||||||
|
},
|
||||||
|
"name": null,
|
||||||
|
"next_invoice_sequence": 1,
|
||||||
|
"object": "customer",
|
||||||
|
"phone": null,
|
||||||
|
"preferred_locales": [],
|
||||||
|
"shipping": null,
|
||||||
|
"tax_exempt": "none",
|
||||||
|
"test_clock": null
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
"address": null,
|
||||||
|
"balance": 0,
|
||||||
|
"created": 1701007131,
|
||||||
|
"currency": null,
|
||||||
|
"default_currency": null,
|
||||||
|
"default_source": null,
|
||||||
|
"delinquent": false,
|
||||||
|
"description": "zulip (Zulip Dev)",
|
||||||
|
"discount": null,
|
||||||
|
"email": "hamlet@zulip.com",
|
||||||
|
"id": "cus_P4scN21egyF63j",
|
||||||
|
"invoice_prefix": "BFEDDCEB",
|
||||||
|
"invoice_settings": {
|
||||||
|
"custom_fields": null,
|
||||||
|
"default_payment_method": "pm_1OGirJDEQaroqDjs0fg5PtPl",
|
||||||
|
"footer": null,
|
||||||
|
"rendering_options": null
|
||||||
|
},
|
||||||
|
"livemode": false,
|
||||||
|
"metadata": {
|
||||||
|
"realm_id": "2",
|
||||||
|
"realm_str": "zulip"
|
||||||
|
},
|
||||||
|
"name": null,
|
||||||
|
"next_invoice_sequence": 1,
|
||||||
|
"object": "customer",
|
||||||
|
"phone": null,
|
||||||
|
"preferred_locales": [],
|
||||||
|
"shipping": null,
|
||||||
|
"tax_exempt": "none",
|
||||||
|
"test_clock": null
|
||||||
|
}
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
{
|
||||||
|
"address": null,
|
||||||
|
"balance": 0,
|
||||||
|
"created": 1701007131,
|
||||||
|
"currency": null,
|
||||||
|
"default_currency": null,
|
||||||
|
"default_source": null,
|
||||||
|
"delinquent": false,
|
||||||
|
"description": "zulip (Zulip Dev)",
|
||||||
|
"discount": null,
|
||||||
|
"email": "hamlet@zulip.com",
|
||||||
|
"id": "cus_P4scN21egyF63j",
|
||||||
|
"invoice_prefix": "BFEDDCEB",
|
||||||
|
"invoice_settings": {
|
||||||
|
"custom_fields": null,
|
||||||
|
"default_payment_method": {
|
||||||
|
"billing_details": {
|
||||||
|
"address": {
|
||||||
|
"city": null,
|
||||||
|
"country": null,
|
||||||
|
"line1": null,
|
||||||
|
"line2": null,
|
||||||
|
"postal_code": null,
|
||||||
|
"state": null
|
||||||
|
},
|
||||||
|
"email": null,
|
||||||
|
"name": null,
|
||||||
|
"phone": null
|
||||||
|
},
|
||||||
|
"card": {
|
||||||
|
"brand": "visa",
|
||||||
|
"checks": {
|
||||||
|
"address_line1_check": null,
|
||||||
|
"address_postal_code_check": null,
|
||||||
|
"cvc_check": "pass"
|
||||||
|
},
|
||||||
|
"country": "US",
|
||||||
|
"exp_month": 11,
|
||||||
|
"exp_year": 2024,
|
||||||
|
"fingerprint": "9XKsMixKBi6kIIzd",
|
||||||
|
"funding": "credit",
|
||||||
|
"generated_from": null,
|
||||||
|
"last4": "4242",
|
||||||
|
"networks": {
|
||||||
|
"available": [
|
||||||
|
"visa"
|
||||||
|
],
|
||||||
|
"preferred": null
|
||||||
|
},
|
||||||
|
"three_d_secure_usage": {
|
||||||
|
"supported": true
|
||||||
|
},
|
||||||
|
"wallet": null
|
||||||
|
},
|
||||||
|
"created": 1701007133,
|
||||||
|
"customer": "cus_P4scN21egyF63j",
|
||||||
|
"id": "pm_1OGirJDEQaroqDjs0fg5PtPl",
|
||||||
|
"livemode": false,
|
||||||
|
"metadata": {},
|
||||||
|
"object": "payment_method",
|
||||||
|
"type": "card"
|
||||||
|
},
|
||||||
|
"footer": null,
|
||||||
|
"rendering_options": null
|
||||||
|
},
|
||||||
|
"livemode": false,
|
||||||
|
"metadata": {
|
||||||
|
"realm_id": "2",
|
||||||
|
"realm_str": "zulip"
|
||||||
|
},
|
||||||
|
"name": null,
|
||||||
|
"next_invoice_sequence": 1,
|
||||||
|
"object": "customer",
|
||||||
|
"phone": null,
|
||||||
|
"preferred_locales": [],
|
||||||
|
"shipping": null,
|
||||||
|
"tax_exempt": "none",
|
||||||
|
"test_clock": null
|
||||||
|
}
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
{
|
||||||
|
"address": null,
|
||||||
|
"balance": 0,
|
||||||
|
"created": 1701007131,
|
||||||
|
"currency": null,
|
||||||
|
"default_currency": null,
|
||||||
|
"default_source": null,
|
||||||
|
"delinquent": false,
|
||||||
|
"description": "zulip (Zulip Dev)",
|
||||||
|
"discount": null,
|
||||||
|
"email": "hamlet@zulip.com",
|
||||||
|
"id": "cus_P4scN21egyF63j",
|
||||||
|
"invoice_prefix": "BFEDDCEB",
|
||||||
|
"invoice_settings": {
|
||||||
|
"custom_fields": null,
|
||||||
|
"default_payment_method": {
|
||||||
|
"billing_details": {
|
||||||
|
"address": {
|
||||||
|
"city": null,
|
||||||
|
"country": null,
|
||||||
|
"line1": null,
|
||||||
|
"line2": null,
|
||||||
|
"postal_code": null,
|
||||||
|
"state": null
|
||||||
|
},
|
||||||
|
"email": null,
|
||||||
|
"name": null,
|
||||||
|
"phone": null
|
||||||
|
},
|
||||||
|
"card": {
|
||||||
|
"brand": "visa",
|
||||||
|
"checks": {
|
||||||
|
"address_line1_check": null,
|
||||||
|
"address_postal_code_check": null,
|
||||||
|
"cvc_check": "pass"
|
||||||
|
},
|
||||||
|
"country": "US",
|
||||||
|
"exp_month": 11,
|
||||||
|
"exp_year": 2024,
|
||||||
|
"fingerprint": "9XKsMixKBi6kIIzd",
|
||||||
|
"funding": "credit",
|
||||||
|
"generated_from": null,
|
||||||
|
"last4": "4242",
|
||||||
|
"networks": {
|
||||||
|
"available": [
|
||||||
|
"visa"
|
||||||
|
],
|
||||||
|
"preferred": null
|
||||||
|
},
|
||||||
|
"three_d_secure_usage": {
|
||||||
|
"supported": true
|
||||||
|
},
|
||||||
|
"wallet": null
|
||||||
|
},
|
||||||
|
"created": 1701007133,
|
||||||
|
"customer": "cus_P4scN21egyF63j",
|
||||||
|
"id": "pm_1OGirJDEQaroqDjs0fg5PtPl",
|
||||||
|
"livemode": false,
|
||||||
|
"metadata": {},
|
||||||
|
"object": "payment_method",
|
||||||
|
"type": "card"
|
||||||
|
},
|
||||||
|
"footer": null,
|
||||||
|
"rendering_options": null
|
||||||
|
},
|
||||||
|
"livemode": false,
|
||||||
|
"metadata": {
|
||||||
|
"realm_id": "2",
|
||||||
|
"realm_str": "zulip"
|
||||||
|
},
|
||||||
|
"name": null,
|
||||||
|
"next_invoice_sequence": 1,
|
||||||
|
"object": "customer",
|
||||||
|
"phone": null,
|
||||||
|
"preferred_locales": [],
|
||||||
|
"shipping": null,
|
||||||
|
"tax_exempt": "none",
|
||||||
|
"test_clock": null
|
||||||
|
}
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
{
|
||||||
|
"address": null,
|
||||||
|
"balance": 0,
|
||||||
|
"created": 1701007131,
|
||||||
|
"currency": null,
|
||||||
|
"default_currency": null,
|
||||||
|
"default_source": null,
|
||||||
|
"delinquent": false,
|
||||||
|
"description": "zulip (Zulip Dev)",
|
||||||
|
"discount": null,
|
||||||
|
"email": "hamlet@zulip.com",
|
||||||
|
"id": "cus_P4scN21egyF63j",
|
||||||
|
"invoice_prefix": "BFEDDCEB",
|
||||||
|
"invoice_settings": {
|
||||||
|
"custom_fields": null,
|
||||||
|
"default_payment_method": {
|
||||||
|
"billing_details": {
|
||||||
|
"address": {
|
||||||
|
"city": null,
|
||||||
|
"country": null,
|
||||||
|
"line1": null,
|
||||||
|
"line2": null,
|
||||||
|
"postal_code": null,
|
||||||
|
"state": null
|
||||||
|
},
|
||||||
|
"email": null,
|
||||||
|
"name": null,
|
||||||
|
"phone": null
|
||||||
|
},
|
||||||
|
"card": {
|
||||||
|
"brand": "visa",
|
||||||
|
"checks": {
|
||||||
|
"address_line1_check": null,
|
||||||
|
"address_postal_code_check": null,
|
||||||
|
"cvc_check": "pass"
|
||||||
|
},
|
||||||
|
"country": "US",
|
||||||
|
"exp_month": 11,
|
||||||
|
"exp_year": 2024,
|
||||||
|
"fingerprint": "9XKsMixKBi6kIIzd",
|
||||||
|
"funding": "credit",
|
||||||
|
"generated_from": null,
|
||||||
|
"last4": "4242",
|
||||||
|
"networks": {
|
||||||
|
"available": [
|
||||||
|
"visa"
|
||||||
|
],
|
||||||
|
"preferred": null
|
||||||
|
},
|
||||||
|
"three_d_secure_usage": {
|
||||||
|
"supported": true
|
||||||
|
},
|
||||||
|
"wallet": null
|
||||||
|
},
|
||||||
|
"created": 1701007133,
|
||||||
|
"customer": "cus_P4scN21egyF63j",
|
||||||
|
"id": "pm_1OGirJDEQaroqDjs0fg5PtPl",
|
||||||
|
"livemode": false,
|
||||||
|
"metadata": {},
|
||||||
|
"object": "payment_method",
|
||||||
|
"type": "card"
|
||||||
|
},
|
||||||
|
"footer": null,
|
||||||
|
"rendering_options": null
|
||||||
|
},
|
||||||
|
"livemode": false,
|
||||||
|
"metadata": {
|
||||||
|
"realm_id": "2",
|
||||||
|
"realm_str": "zulip"
|
||||||
|
},
|
||||||
|
"name": null,
|
||||||
|
"next_invoice_sequence": 1,
|
||||||
|
"object": "customer",
|
||||||
|
"phone": null,
|
||||||
|
"preferred_locales": [],
|
||||||
|
"shipping": null,
|
||||||
|
"tax_exempt": "none",
|
||||||
|
"test_clock": null
|
||||||
|
}
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
{
|
||||||
|
"address": null,
|
||||||
|
"balance": 0,
|
||||||
|
"created": 1701007131,
|
||||||
|
"currency": null,
|
||||||
|
"default_currency": null,
|
||||||
|
"default_source": null,
|
||||||
|
"delinquent": false,
|
||||||
|
"description": "zulip (Zulip Dev)",
|
||||||
|
"discount": null,
|
||||||
|
"email": "hamlet@zulip.com",
|
||||||
|
"id": "cus_P4scN21egyF63j",
|
||||||
|
"invoice_prefix": "BFEDDCEB",
|
||||||
|
"invoice_settings": {
|
||||||
|
"custom_fields": null,
|
||||||
|
"default_payment_method": {
|
||||||
|
"billing_details": {
|
||||||
|
"address": {
|
||||||
|
"city": null,
|
||||||
|
"country": null,
|
||||||
|
"line1": null,
|
||||||
|
"line2": null,
|
||||||
|
"postal_code": null,
|
||||||
|
"state": null
|
||||||
|
},
|
||||||
|
"email": null,
|
||||||
|
"name": null,
|
||||||
|
"phone": null
|
||||||
|
},
|
||||||
|
"card": {
|
||||||
|
"brand": "visa",
|
||||||
|
"checks": {
|
||||||
|
"address_line1_check": null,
|
||||||
|
"address_postal_code_check": null,
|
||||||
|
"cvc_check": "pass"
|
||||||
|
},
|
||||||
|
"country": "US",
|
||||||
|
"exp_month": 11,
|
||||||
|
"exp_year": 2024,
|
||||||
|
"fingerprint": "9XKsMixKBi6kIIzd",
|
||||||
|
"funding": "credit",
|
||||||
|
"generated_from": null,
|
||||||
|
"last4": "4242",
|
||||||
|
"networks": {
|
||||||
|
"available": [
|
||||||
|
"visa"
|
||||||
|
],
|
||||||
|
"preferred": null
|
||||||
|
},
|
||||||
|
"three_d_secure_usage": {
|
||||||
|
"supported": true
|
||||||
|
},
|
||||||
|
"wallet": null
|
||||||
|
},
|
||||||
|
"created": 1701007133,
|
||||||
|
"customer": "cus_P4scN21egyF63j",
|
||||||
|
"id": "pm_1OGirJDEQaroqDjs0fg5PtPl",
|
||||||
|
"livemode": false,
|
||||||
|
"metadata": {},
|
||||||
|
"object": "payment_method",
|
||||||
|
"type": "card"
|
||||||
|
},
|
||||||
|
"footer": null,
|
||||||
|
"rendering_options": null
|
||||||
|
},
|
||||||
|
"livemode": false,
|
||||||
|
"metadata": {
|
||||||
|
"realm_id": "2",
|
||||||
|
"realm_str": "zulip"
|
||||||
|
},
|
||||||
|
"name": null,
|
||||||
|
"next_invoice_sequence": 1,
|
||||||
|
"object": "customer",
|
||||||
|
"phone": null,
|
||||||
|
"preferred_locales": [],
|
||||||
|
"shipping": null,
|
||||||
|
"tax_exempt": "none",
|
||||||
|
"test_clock": null
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
{
|
||||||
|
"data": [
|
||||||
|
{
|
||||||
|
"api_version": "2020-08-27",
|
||||||
|
"created": 1701007135,
|
||||||
|
"data": {
|
||||||
|
"object": {
|
||||||
|
"address": null,
|
||||||
|
"balance": 0,
|
||||||
|
"created": 1701007131,
|
||||||
|
"currency": null,
|
||||||
|
"default_currency": null,
|
||||||
|
"default_source": null,
|
||||||
|
"delinquent": false,
|
||||||
|
"description": "zulip (Zulip Dev)",
|
||||||
|
"discount": null,
|
||||||
|
"email": "hamlet@zulip.com",
|
||||||
|
"id": "cus_P4scN21egyF63j",
|
||||||
|
"invoice_prefix": "BFEDDCEB",
|
||||||
|
"invoice_settings": {
|
||||||
|
"custom_fields": null,
|
||||||
|
"default_payment_method": "pm_1OGirJDEQaroqDjs0fg5PtPl",
|
||||||
|
"footer": null,
|
||||||
|
"rendering_options": null
|
||||||
|
},
|
||||||
|
"livemode": false,
|
||||||
|
"metadata": {
|
||||||
|
"realm_id": "2",
|
||||||
|
"realm_str": "zulip"
|
||||||
|
},
|
||||||
|
"name": null,
|
||||||
|
"next_invoice_sequence": 1,
|
||||||
|
"object": "customer",
|
||||||
|
"phone": null,
|
||||||
|
"preferred_locales": [],
|
||||||
|
"shipping": null,
|
||||||
|
"tax_exempt": "none",
|
||||||
|
"test_clock": null
|
||||||
|
},
|
||||||
|
"previous_attributes": {
|
||||||
|
"invoice_settings": {
|
||||||
|
"default_payment_method": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"id": "evt_1OGirLDEQaroqDjsz8SKiaqo",
|
||||||
|
"livemode": false,
|
||||||
|
"object": "event",
|
||||||
|
"pending_webhooks": 0,
|
||||||
|
"request": {
|
||||||
|
"id": "req_AyeuVEyieC6onh",
|
||||||
|
"idempotency_key": "89b1c0ff-4445-484e-a46d-edb738e19727"
|
||||||
|
},
|
||||||
|
"type": "customer.updated"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"has_more": true,
|
||||||
|
"object": "list",
|
||||||
|
"url": "/v1/events"
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"data": [],
|
||||||
|
"has_more": false,
|
||||||
|
"object": "list",
|
||||||
|
"url": "/v1/invoices"
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
{
|
||||||
|
"application": null,
|
||||||
|
"automatic_payment_methods": null,
|
||||||
|
"cancellation_reason": null,
|
||||||
|
"client_secret": "seti_1OGirJDEQaroqDjsYpipSEB0_secret_P4scaxG6yYSKO4CtptkXClW7kitH4Z5",
|
||||||
|
"created": 1701007133,
|
||||||
|
"customer": "cus_P4scN21egyF63j",
|
||||||
|
"description": null,
|
||||||
|
"flow_directions": null,
|
||||||
|
"id": "seti_1OGirJDEQaroqDjsYpipSEB0",
|
||||||
|
"last_setup_error": null,
|
||||||
|
"latest_attempt": "setatt_1OGirJDEQaroqDjskmjNC3F7",
|
||||||
|
"livemode": false,
|
||||||
|
"mandate": null,
|
||||||
|
"metadata": {},
|
||||||
|
"next_action": null,
|
||||||
|
"object": "setup_intent",
|
||||||
|
"on_behalf_of": null,
|
||||||
|
"payment_method": "pm_1OGirJDEQaroqDjs0fg5PtPl",
|
||||||
|
"payment_method_configuration_details": null,
|
||||||
|
"payment_method_options": {
|
||||||
|
"card": {
|
||||||
|
"mandate_options": null,
|
||||||
|
"network": null,
|
||||||
|
"request_three_d_secure": "automatic"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"payment_method_types": [
|
||||||
|
"card"
|
||||||
|
],
|
||||||
|
"single_use_mandate": null,
|
||||||
|
"status": "succeeded",
|
||||||
|
"usage": "off_session"
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
{
|
||||||
|
"data": [
|
||||||
|
{
|
||||||
|
"application": null,
|
||||||
|
"automatic_payment_methods": null,
|
||||||
|
"cancellation_reason": null,
|
||||||
|
"client_secret": "seti_1OGirHDEQaroqDjsWRgeKxys_secret_P4scp8ZoQF4uJncgCdd4XzNWWTw16yl",
|
||||||
|
"created": 1701007131,
|
||||||
|
"customer": "cus_P4scN21egyF63j",
|
||||||
|
"description": null,
|
||||||
|
"flow_directions": null,
|
||||||
|
"id": "seti_1OGirHDEQaroqDjsWRgeKxys",
|
||||||
|
"last_setup_error": null,
|
||||||
|
"latest_attempt": null,
|
||||||
|
"livemode": false,
|
||||||
|
"mandate": null,
|
||||||
|
"metadata": {},
|
||||||
|
"next_action": null,
|
||||||
|
"object": "setup_intent",
|
||||||
|
"on_behalf_of": null,
|
||||||
|
"payment_method": null,
|
||||||
|
"payment_method_configuration_details": null,
|
||||||
|
"payment_method_options": {
|
||||||
|
"card": {
|
||||||
|
"mandate_options": null,
|
||||||
|
"network": null,
|
||||||
|
"request_three_d_secure": "automatic"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"payment_method_types": [
|
||||||
|
"card"
|
||||||
|
],
|
||||||
|
"single_use_mandate": null,
|
||||||
|
"status": "requires_payment_method",
|
||||||
|
"usage": "off_session"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"has_more": true,
|
||||||
|
"object": "list",
|
||||||
|
"url": "/v1/setup_intents"
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
{
|
||||||
|
"application": null,
|
||||||
|
"automatic_payment_methods": null,
|
||||||
|
"cancellation_reason": null,
|
||||||
|
"client_secret": "seti_1OGirJDEQaroqDjsYpipSEB0_secret_P4scaxG6yYSKO4CtptkXClW7kitH4Z5",
|
||||||
|
"created": 1701007133,
|
||||||
|
"customer": "cus_P4scN21egyF63j",
|
||||||
|
"description": null,
|
||||||
|
"flow_directions": null,
|
||||||
|
"id": "seti_1OGirJDEQaroqDjsYpipSEB0",
|
||||||
|
"last_setup_error": null,
|
||||||
|
"latest_attempt": "setatt_1OGirJDEQaroqDjskmjNC3F7",
|
||||||
|
"livemode": false,
|
||||||
|
"mandate": null,
|
||||||
|
"metadata": {},
|
||||||
|
"next_action": null,
|
||||||
|
"object": "setup_intent",
|
||||||
|
"on_behalf_of": null,
|
||||||
|
"payment_method": "pm_1OGirJDEQaroqDjs0fg5PtPl",
|
||||||
|
"payment_method_configuration_details": null,
|
||||||
|
"payment_method_options": {
|
||||||
|
"card": {
|
||||||
|
"mandate_options": null,
|
||||||
|
"network": null,
|
||||||
|
"request_three_d_secure": "automatic"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"payment_method_types": [
|
||||||
|
"card"
|
||||||
|
],
|
||||||
|
"single_use_mandate": null,
|
||||||
|
"status": "succeeded",
|
||||||
|
"usage": "off_session"
|
||||||
|
}
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
{
|
||||||
|
"after_expiration": null,
|
||||||
|
"allow_promotion_codes": null,
|
||||||
|
"amount_subtotal": null,
|
||||||
|
"amount_total": null,
|
||||||
|
"automatic_tax": {
|
||||||
|
"enabled": false,
|
||||||
|
"status": null
|
||||||
|
},
|
||||||
|
"billing_address_collection": null,
|
||||||
|
"cancel_url": "http://zulip.testserver/upgrade/",
|
||||||
|
"client_reference_id": null,
|
||||||
|
"client_secret": null,
|
||||||
|
"consent": null,
|
||||||
|
"consent_collection": null,
|
||||||
|
"created": 1701007131,
|
||||||
|
"currency": null,
|
||||||
|
"currency_conversion": null,
|
||||||
|
"custom_fields": [],
|
||||||
|
"custom_text": {
|
||||||
|
"shipping_address": null,
|
||||||
|
"submit": null,
|
||||||
|
"terms_of_service_acceptance": null
|
||||||
|
},
|
||||||
|
"customer": "cus_P4scN21egyF63j",
|
||||||
|
"customer_creation": null,
|
||||||
|
"customer_details": {
|
||||||
|
"address": null,
|
||||||
|
"email": "hamlet@zulip.com",
|
||||||
|
"name": null,
|
||||||
|
"phone": null,
|
||||||
|
"tax_exempt": null,
|
||||||
|
"tax_ids": null
|
||||||
|
},
|
||||||
|
"customer_email": null,
|
||||||
|
"expires_at": 1701093531,
|
||||||
|
"id": "cs_test_c1fBDZcNaDnv5cqg1WNBXNCDmlUwwLBP59mIYRwRB1OUAEYocADHCi5EOH",
|
||||||
|
"invoice": null,
|
||||||
|
"invoice_creation": null,
|
||||||
|
"livemode": false,
|
||||||
|
"locale": null,
|
||||||
|
"metadata": {
|
||||||
|
"type": "card_update",
|
||||||
|
"user_id": "10"
|
||||||
|
},
|
||||||
|
"mode": "setup",
|
||||||
|
"object": "checkout.session",
|
||||||
|
"payment_intent": null,
|
||||||
|
"payment_link": null,
|
||||||
|
"payment_method_collection": "always",
|
||||||
|
"payment_method_configuration_details": null,
|
||||||
|
"payment_method_options": {},
|
||||||
|
"payment_method_types": [
|
||||||
|
"card"
|
||||||
|
],
|
||||||
|
"payment_status": "no_payment_required",
|
||||||
|
"phone_number_collection": {
|
||||||
|
"enabled": false
|
||||||
|
},
|
||||||
|
"recovered_from": null,
|
||||||
|
"setup_intent": "seti_1OGirHDEQaroqDjsWRgeKxys",
|
||||||
|
"shipping": null,
|
||||||
|
"shipping_address_collection": null,
|
||||||
|
"shipping_options": [],
|
||||||
|
"shipping_rate": null,
|
||||||
|
"status": "open",
|
||||||
|
"submit_type": null,
|
||||||
|
"subscription": null,
|
||||||
|
"success_url": "http://zulip.testserver/billing/event_status?stripe_session_id={CHECKOUT_SESSION_ID}",
|
||||||
|
"total_details": null,
|
||||||
|
"ui_mode": "hosted",
|
||||||
|
"url": "https://checkout.stripe.com/c/pay/cs_test_c1fBDZcNaDnv5cqg1WNBXNCDmlUwwLBP59mIYRwRB1OUAEYocADHCi5EOH#fidkdWxOYHwnPyd1blpxYHZxWl1UZjFOczZJXUE2PUpzUWNPV2ZpdlFzUCcpJ2N3amhWYHdzYHcnP3F3cGApJ2lkfGpwcVF8dWAnPyd2bGtiaWBaZmppcGhrJyknYGtkZ2lgVWlkZmBtamlhYHd2Jz9xd3BgeCUl"
|
||||||
|
}
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
{
|
||||||
|
"data": [
|
||||||
|
{
|
||||||
|
"after_expiration": null,
|
||||||
|
"allow_promotion_codes": null,
|
||||||
|
"amount_subtotal": null,
|
||||||
|
"amount_total": null,
|
||||||
|
"automatic_tax": {
|
||||||
|
"enabled": false,
|
||||||
|
"status": null
|
||||||
|
},
|
||||||
|
"billing_address_collection": null,
|
||||||
|
"cancel_url": "http://zulip.testserver/upgrade/",
|
||||||
|
"client_reference_id": null,
|
||||||
|
"client_secret": null,
|
||||||
|
"consent": null,
|
||||||
|
"consent_collection": null,
|
||||||
|
"created": 1701007131,
|
||||||
|
"currency": null,
|
||||||
|
"currency_conversion": null,
|
||||||
|
"custom_fields": [],
|
||||||
|
"custom_text": {
|
||||||
|
"shipping_address": null,
|
||||||
|
"submit": null,
|
||||||
|
"terms_of_service_acceptance": null
|
||||||
|
},
|
||||||
|
"customer": "cus_P4scN21egyF63j",
|
||||||
|
"customer_creation": null,
|
||||||
|
"customer_details": {
|
||||||
|
"address": null,
|
||||||
|
"email": "hamlet@zulip.com",
|
||||||
|
"name": null,
|
||||||
|
"phone": null,
|
||||||
|
"tax_exempt": null,
|
||||||
|
"tax_ids": null
|
||||||
|
},
|
||||||
|
"customer_email": null,
|
||||||
|
"expires_at": 1701093531,
|
||||||
|
"id": "cs_test_c1fBDZcNaDnv5cqg1WNBXNCDmlUwwLBP59mIYRwRB1OUAEYocADHCi5EOH",
|
||||||
|
"invoice": null,
|
||||||
|
"invoice_creation": null,
|
||||||
|
"livemode": false,
|
||||||
|
"locale": null,
|
||||||
|
"metadata": {
|
||||||
|
"type": "card_update",
|
||||||
|
"user_id": "10"
|
||||||
|
},
|
||||||
|
"mode": "setup",
|
||||||
|
"object": "checkout.session",
|
||||||
|
"payment_intent": null,
|
||||||
|
"payment_link": null,
|
||||||
|
"payment_method_collection": "always",
|
||||||
|
"payment_method_configuration_details": null,
|
||||||
|
"payment_method_options": {},
|
||||||
|
"payment_method_types": [
|
||||||
|
"card"
|
||||||
|
],
|
||||||
|
"payment_status": "no_payment_required",
|
||||||
|
"phone_number_collection": {
|
||||||
|
"enabled": false
|
||||||
|
},
|
||||||
|
"recovered_from": null,
|
||||||
|
"setup_intent": "seti_1OGirHDEQaroqDjsWRgeKxys",
|
||||||
|
"shipping": null,
|
||||||
|
"shipping_address_collection": null,
|
||||||
|
"shipping_options": [],
|
||||||
|
"shipping_rate": null,
|
||||||
|
"status": "open",
|
||||||
|
"submit_type": null,
|
||||||
|
"subscription": null,
|
||||||
|
"success_url": "http://zulip.testserver/billing/event_status?stripe_session_id={CHECKOUT_SESSION_ID}",
|
||||||
|
"total_details": null,
|
||||||
|
"ui_mode": "hosted",
|
||||||
|
"url": "https://checkout.stripe.com/c/pay/cs_test_c1fBDZcNaDnv5cqg1WNBXNCDmlUwwLBP59mIYRwRB1OUAEYocADHCi5EOH#fidkdWxOYHwnPyd1blpxYHZxWl1UZjFOczZJXUE2PUpzUWNPV2ZpdlFzUCcpJ2N3amhWYHdzYHcnP3F3cGApJ2lkfGpwcVF8dWAnPyd2bGtiaWBaZmppcGhrJyknYGtkZ2lgVWlkZmBtamlhYHd2Jz9xd3BgeCUl"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"has_more": true,
|
||||||
|
"object": "list",
|
||||||
|
"url": "/v1/checkout/sessions"
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
"address": null,
|
||||||
|
"balance": 0,
|
||||||
|
"created": 1000000000,
|
||||||
|
"currency": null,
|
||||||
|
"default_currency": null,
|
||||||
|
"default_source": null,
|
||||||
|
"delinquent": false,
|
||||||
|
"description": "zulip (Zulip Dev)",
|
||||||
|
"discount": null,
|
||||||
|
"email": "hamlet@zulip.com",
|
||||||
|
"id": "cus_NORMALIZED0001",
|
||||||
|
"invoice_prefix": "NORMA01",
|
||||||
|
"invoice_settings": {
|
||||||
|
"custom_fields": null,
|
||||||
|
"default_payment_method": null,
|
||||||
|
"footer": null,
|
||||||
|
"rendering_options": null
|
||||||
|
},
|
||||||
|
"livemode": false,
|
||||||
|
"metadata": {
|
||||||
|
"realm_id": "1",
|
||||||
|
"realm_str": "zulip"
|
||||||
|
},
|
||||||
|
"name": null,
|
||||||
|
"next_invoice_sequence": 1,
|
||||||
|
"object": "customer",
|
||||||
|
"phone": null,
|
||||||
|
"preferred_locales": [],
|
||||||
|
"shipping": null,
|
||||||
|
"tax_exempt": "none",
|
||||||
|
"test_clock": null
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
"address": null,
|
||||||
|
"balance": 0,
|
||||||
|
"created": 1000000000,
|
||||||
|
"currency": null,
|
||||||
|
"default_currency": null,
|
||||||
|
"default_source": null,
|
||||||
|
"delinquent": false,
|
||||||
|
"description": "zulip (Zulip Dev)",
|
||||||
|
"discount": null,
|
||||||
|
"email": "hamlet@zulip.com",
|
||||||
|
"id": "cus_NORMALIZED0001",
|
||||||
|
"invoice_prefix": "NORMA01",
|
||||||
|
"invoice_settings": {
|
||||||
|
"custom_fields": null,
|
||||||
|
"default_payment_method": "pm_1OGjOiDEQaroqDjs1saPoOzz",
|
||||||
|
"footer": null,
|
||||||
|
"rendering_options": null
|
||||||
|
},
|
||||||
|
"livemode": false,
|
||||||
|
"metadata": {
|
||||||
|
"realm_id": "1",
|
||||||
|
"realm_str": "zulip"
|
||||||
|
},
|
||||||
|
"name": null,
|
||||||
|
"next_invoice_sequence": 1,
|
||||||
|
"object": "customer",
|
||||||
|
"phone": null,
|
||||||
|
"preferred_locales": [],
|
||||||
|
"shipping": null,
|
||||||
|
"tax_exempt": "none",
|
||||||
|
"test_clock": null
|
||||||
|
}
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
{
|
||||||
|
"address": null,
|
||||||
|
"balance": 0,
|
||||||
|
"created": 1000000000,
|
||||||
|
"currency": null,
|
||||||
|
"default_currency": null,
|
||||||
|
"default_source": null,
|
||||||
|
"delinquent": false,
|
||||||
|
"description": "zulip (Zulip Dev)",
|
||||||
|
"discount": null,
|
||||||
|
"email": "hamlet@zulip.com",
|
||||||
|
"id": "cus_NORMALIZED0001",
|
||||||
|
"invoice_prefix": "NORMA01",
|
||||||
|
"invoice_settings": {
|
||||||
|
"custom_fields": null,
|
||||||
|
"default_payment_method": {
|
||||||
|
"billing_details": {
|
||||||
|
"address": {
|
||||||
|
"city": null,
|
||||||
|
"country": null,
|
||||||
|
"line1": null,
|
||||||
|
"line2": null,
|
||||||
|
"postal_code": null,
|
||||||
|
"state": null
|
||||||
|
},
|
||||||
|
"email": null,
|
||||||
|
"name": null,
|
||||||
|
"phone": null
|
||||||
|
},
|
||||||
|
"card": {
|
||||||
|
"brand": "visa",
|
||||||
|
"checks": {
|
||||||
|
"address_line1_check": null,
|
||||||
|
"address_postal_code_check": null,
|
||||||
|
"cvc_check": "pass"
|
||||||
|
},
|
||||||
|
"country": "US",
|
||||||
|
"exp_month": 11,
|
||||||
|
"exp_year": 2024,
|
||||||
|
"fingerprint": "NORMALIZED000001",
|
||||||
|
"funding": "credit",
|
||||||
|
"generated_from": null,
|
||||||
|
"last4": "4242",
|
||||||
|
"networks": {
|
||||||
|
"available": [
|
||||||
|
"visa"
|
||||||
|
],
|
||||||
|
"preferred": null
|
||||||
|
},
|
||||||
|
"three_d_secure_usage": {
|
||||||
|
"supported": true
|
||||||
|
},
|
||||||
|
"wallet": null
|
||||||
|
},
|
||||||
|
"created": 1000000000,
|
||||||
|
"customer": "cus_NORMALIZED0001",
|
||||||
|
"id": "pm_1OGjOiDEQaroqDjs1saPoOzz",
|
||||||
|
"livemode": false,
|
||||||
|
"metadata": {},
|
||||||
|
"object": "payment_method",
|
||||||
|
"type": "card"
|
||||||
|
},
|
||||||
|
"footer": null,
|
||||||
|
"rendering_options": null
|
||||||
|
},
|
||||||
|
"livemode": false,
|
||||||
|
"metadata": {
|
||||||
|
"realm_id": "1",
|
||||||
|
"realm_str": "zulip"
|
||||||
|
},
|
||||||
|
"name": null,
|
||||||
|
"next_invoice_sequence": 1,
|
||||||
|
"object": "customer",
|
||||||
|
"phone": null,
|
||||||
|
"preferred_locales": [],
|
||||||
|
"shipping": null,
|
||||||
|
"tax_exempt": "none",
|
||||||
|
"test_clock": null
|
||||||
|
}
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
{
|
||||||
|
"address": null,
|
||||||
|
"balance": 0,
|
||||||
|
"created": 1000000000,
|
||||||
|
"currency": null,
|
||||||
|
"default_currency": null,
|
||||||
|
"default_source": null,
|
||||||
|
"delinquent": false,
|
||||||
|
"description": "zulip (Zulip Dev)",
|
||||||
|
"discount": null,
|
||||||
|
"email": "hamlet@zulip.com",
|
||||||
|
"id": "cus_NORMALIZED0001",
|
||||||
|
"invoice_prefix": "NORMA01",
|
||||||
|
"invoice_settings": {
|
||||||
|
"custom_fields": null,
|
||||||
|
"default_payment_method": {
|
||||||
|
"billing_details": {
|
||||||
|
"address": {
|
||||||
|
"city": null,
|
||||||
|
"country": null,
|
||||||
|
"line1": null,
|
||||||
|
"line2": null,
|
||||||
|
"postal_code": null,
|
||||||
|
"state": null
|
||||||
|
},
|
||||||
|
"email": null,
|
||||||
|
"name": null,
|
||||||
|
"phone": null
|
||||||
|
},
|
||||||
|
"card": {
|
||||||
|
"brand": "visa",
|
||||||
|
"checks": {
|
||||||
|
"address_line1_check": null,
|
||||||
|
"address_postal_code_check": null,
|
||||||
|
"cvc_check": "pass"
|
||||||
|
},
|
||||||
|
"country": "US",
|
||||||
|
"exp_month": 11,
|
||||||
|
"exp_year": 2024,
|
||||||
|
"fingerprint": "NORMALIZED000001",
|
||||||
|
"funding": "credit",
|
||||||
|
"generated_from": null,
|
||||||
|
"last4": "4242",
|
||||||
|
"networks": {
|
||||||
|
"available": [
|
||||||
|
"visa"
|
||||||
|
],
|
||||||
|
"preferred": null
|
||||||
|
},
|
||||||
|
"three_d_secure_usage": {
|
||||||
|
"supported": true
|
||||||
|
},
|
||||||
|
"wallet": null
|
||||||
|
},
|
||||||
|
"created": 1000000000,
|
||||||
|
"customer": "cus_NORMALIZED0001",
|
||||||
|
"id": "pm_1OGjOiDEQaroqDjs1saPoOzz",
|
||||||
|
"livemode": false,
|
||||||
|
"metadata": {},
|
||||||
|
"object": "payment_method",
|
||||||
|
"type": "card"
|
||||||
|
},
|
||||||
|
"footer": null,
|
||||||
|
"rendering_options": null
|
||||||
|
},
|
||||||
|
"livemode": false,
|
||||||
|
"metadata": {
|
||||||
|
"realm_id": "1",
|
||||||
|
"realm_str": "zulip"
|
||||||
|
},
|
||||||
|
"name": null,
|
||||||
|
"next_invoice_sequence": 1,
|
||||||
|
"object": "customer",
|
||||||
|
"phone": null,
|
||||||
|
"preferred_locales": [],
|
||||||
|
"shipping": null,
|
||||||
|
"tax_exempt": "none",
|
||||||
|
"test_clock": null
|
||||||
|
}
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
{
|
||||||
|
"address": null,
|
||||||
|
"balance": 0,
|
||||||
|
"created": 1000000000,
|
||||||
|
"currency": null,
|
||||||
|
"default_currency": null,
|
||||||
|
"default_source": null,
|
||||||
|
"delinquent": false,
|
||||||
|
"description": "zulip (Zulip Dev)",
|
||||||
|
"discount": null,
|
||||||
|
"email": "hamlet@zulip.com",
|
||||||
|
"id": "cus_NORMALIZED0001",
|
||||||
|
"invoice_prefix": "NORMA01",
|
||||||
|
"invoice_settings": {
|
||||||
|
"custom_fields": null,
|
||||||
|
"default_payment_method": {
|
||||||
|
"billing_details": {
|
||||||
|
"address": {
|
||||||
|
"city": null,
|
||||||
|
"country": null,
|
||||||
|
"line1": null,
|
||||||
|
"line2": null,
|
||||||
|
"postal_code": null,
|
||||||
|
"state": null
|
||||||
|
},
|
||||||
|
"email": null,
|
||||||
|
"name": null,
|
||||||
|
"phone": null
|
||||||
|
},
|
||||||
|
"card": {
|
||||||
|
"brand": "visa",
|
||||||
|
"checks": {
|
||||||
|
"address_line1_check": null,
|
||||||
|
"address_postal_code_check": null,
|
||||||
|
"cvc_check": "pass"
|
||||||
|
},
|
||||||
|
"country": "US",
|
||||||
|
"exp_month": 11,
|
||||||
|
"exp_year": 2024,
|
||||||
|
"fingerprint": "NORMALIZED000001",
|
||||||
|
"funding": "credit",
|
||||||
|
"generated_from": null,
|
||||||
|
"last4": "4242",
|
||||||
|
"networks": {
|
||||||
|
"available": [
|
||||||
|
"visa"
|
||||||
|
],
|
||||||
|
"preferred": null
|
||||||
|
},
|
||||||
|
"three_d_secure_usage": {
|
||||||
|
"supported": true
|
||||||
|
},
|
||||||
|
"wallet": null
|
||||||
|
},
|
||||||
|
"created": 1000000000,
|
||||||
|
"customer": "cus_NORMALIZED0001",
|
||||||
|
"id": "pm_1OGjOiDEQaroqDjs1saPoOzz",
|
||||||
|
"livemode": false,
|
||||||
|
"metadata": {},
|
||||||
|
"object": "payment_method",
|
||||||
|
"type": "card"
|
||||||
|
},
|
||||||
|
"footer": null,
|
||||||
|
"rendering_options": null
|
||||||
|
},
|
||||||
|
"livemode": false,
|
||||||
|
"metadata": {
|
||||||
|
"realm_id": "1",
|
||||||
|
"realm_str": "zulip"
|
||||||
|
},
|
||||||
|
"name": null,
|
||||||
|
"next_invoice_sequence": 1,
|
||||||
|
"object": "customer",
|
||||||
|
"phone": null,
|
||||||
|
"preferred_locales": [],
|
||||||
|
"shipping": null,
|
||||||
|
"tax_exempt": "none",
|
||||||
|
"test_clock": null
|
||||||
|
}
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
{
|
||||||
|
"address": null,
|
||||||
|
"balance": 0,
|
||||||
|
"created": 1000000000,
|
||||||
|
"currency": null,
|
||||||
|
"default_currency": null,
|
||||||
|
"default_source": null,
|
||||||
|
"delinquent": false,
|
||||||
|
"description": "zulip (Zulip Dev)",
|
||||||
|
"discount": null,
|
||||||
|
"email": "hamlet@zulip.com",
|
||||||
|
"id": "cus_NORMALIZED0001",
|
||||||
|
"invoice_prefix": "NORMA01",
|
||||||
|
"invoice_settings": {
|
||||||
|
"custom_fields": null,
|
||||||
|
"default_payment_method": {
|
||||||
|
"billing_details": {
|
||||||
|
"address": {
|
||||||
|
"city": null,
|
||||||
|
"country": null,
|
||||||
|
"line1": null,
|
||||||
|
"line2": null,
|
||||||
|
"postal_code": null,
|
||||||
|
"state": null
|
||||||
|
},
|
||||||
|
"email": null,
|
||||||
|
"name": null,
|
||||||
|
"phone": null
|
||||||
|
},
|
||||||
|
"card": {
|
||||||
|
"brand": "visa",
|
||||||
|
"checks": {
|
||||||
|
"address_line1_check": null,
|
||||||
|
"address_postal_code_check": null,
|
||||||
|
"cvc_check": "pass"
|
||||||
|
},
|
||||||
|
"country": "US",
|
||||||
|
"exp_month": 11,
|
||||||
|
"exp_year": 2024,
|
||||||
|
"fingerprint": "NORMALIZED000001",
|
||||||
|
"funding": "credit",
|
||||||
|
"generated_from": null,
|
||||||
|
"last4": "4242",
|
||||||
|
"networks": {
|
||||||
|
"available": [
|
||||||
|
"visa"
|
||||||
|
],
|
||||||
|
"preferred": null
|
||||||
|
},
|
||||||
|
"three_d_secure_usage": {
|
||||||
|
"supported": true
|
||||||
|
},
|
||||||
|
"wallet": null
|
||||||
|
},
|
||||||
|
"created": 1000000000,
|
||||||
|
"customer": "cus_NORMALIZED0001",
|
||||||
|
"id": "pm_1OGjOiDEQaroqDjs1saPoOzz",
|
||||||
|
"livemode": false,
|
||||||
|
"metadata": {},
|
||||||
|
"object": "payment_method",
|
||||||
|
"type": "card"
|
||||||
|
},
|
||||||
|
"footer": null,
|
||||||
|
"rendering_options": null
|
||||||
|
},
|
||||||
|
"livemode": false,
|
||||||
|
"metadata": {
|
||||||
|
"realm_id": "1",
|
||||||
|
"realm_str": "zulip"
|
||||||
|
},
|
||||||
|
"name": null,
|
||||||
|
"next_invoice_sequence": 1,
|
||||||
|
"object": "customer",
|
||||||
|
"phone": null,
|
||||||
|
"preferred_locales": [],
|
||||||
|
"shipping": null,
|
||||||
|
"tax_exempt": "none",
|
||||||
|
"test_clock": null
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
{
|
||||||
|
"data": [
|
||||||
|
{
|
||||||
|
"api_version": "2020-08-27",
|
||||||
|
"created": 1000000000,
|
||||||
|
"data": {
|
||||||
|
"object": {
|
||||||
|
"address": null,
|
||||||
|
"balance": 0,
|
||||||
|
"created": 1000000000,
|
||||||
|
"currency": null,
|
||||||
|
"default_currency": null,
|
||||||
|
"default_source": null,
|
||||||
|
"delinquent": false,
|
||||||
|
"description": "zulip (Zulip Dev)",
|
||||||
|
"discount": null,
|
||||||
|
"email": "hamlet@zulip.com",
|
||||||
|
"id": "cus_NORMALIZED0001",
|
||||||
|
"invoice_prefix": "NORMA01",
|
||||||
|
"invoice_settings": {
|
||||||
|
"custom_fields": null,
|
||||||
|
"default_payment_method": "pm_1OGjOiDEQaroqDjs1saPoOzz",
|
||||||
|
"footer": null,
|
||||||
|
"rendering_options": null
|
||||||
|
},
|
||||||
|
"livemode": false,
|
||||||
|
"metadata": {
|
||||||
|
"realm_id": "1",
|
||||||
|
"realm_str": "zulip"
|
||||||
|
},
|
||||||
|
"name": null,
|
||||||
|
"next_invoice_sequence": 1,
|
||||||
|
"object": "customer",
|
||||||
|
"phone": null,
|
||||||
|
"preferred_locales": [],
|
||||||
|
"shipping": null,
|
||||||
|
"tax_exempt": "none",
|
||||||
|
"test_clock": null
|
||||||
|
},
|
||||||
|
"previous_attributes": {
|
||||||
|
"invoice_settings": {
|
||||||
|
"default_payment_method": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"id": "evt_1OGjOjDEQaroqDjsgh56akwA",
|
||||||
|
"livemode": false,
|
||||||
|
"object": "event",
|
||||||
|
"pending_webhooks": 0,
|
||||||
|
"request": {
|
||||||
|
"id": "req_NORMALIZED0001",
|
||||||
|
"idempotency_key": "82ec0759-4649-4aa5-ab13-f80c5b6300cc"
|
||||||
|
},
|
||||||
|
"type": "customer.updated"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"has_more": true,
|
||||||
|
"object": "list",
|
||||||
|
"url": "/v1/events"
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"data": [],
|
||||||
|
"has_more": false,
|
||||||
|
"object": "list",
|
||||||
|
"url": "/v1/invoices"
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
{
|
||||||
|
"application": null,
|
||||||
|
"automatic_payment_methods": null,
|
||||||
|
"cancellation_reason": null,
|
||||||
|
"client_secret": "seti_1OGjOiDEQaroqDjs0mczPK5Q_secret_P4tBceetMSlLfbfrbp4RXKdBjnNp4In",
|
||||||
|
"created": 1000000000,
|
||||||
|
"customer": "cus_NORMALIZED0001",
|
||||||
|
"description": null,
|
||||||
|
"flow_directions": null,
|
||||||
|
"id": "seti_1OGjOiDEQaroqDjs0mczPK5Q",
|
||||||
|
"last_setup_error": null,
|
||||||
|
"latest_attempt": "setatt_1OGjOiDEQaroqDjsZve6lwXi",
|
||||||
|
"livemode": false,
|
||||||
|
"mandate": null,
|
||||||
|
"metadata": {},
|
||||||
|
"next_action": null,
|
||||||
|
"object": "setup_intent",
|
||||||
|
"on_behalf_of": null,
|
||||||
|
"payment_method": "pm_1OGjOiDEQaroqDjs1saPoOzz",
|
||||||
|
"payment_method_configuration_details": null,
|
||||||
|
"payment_method_options": {
|
||||||
|
"card": {
|
||||||
|
"mandate_options": null,
|
||||||
|
"network": null,
|
||||||
|
"request_three_d_secure": "automatic"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"payment_method_types": [
|
||||||
|
"card"
|
||||||
|
],
|
||||||
|
"single_use_mandate": null,
|
||||||
|
"status": "succeeded",
|
||||||
|
"usage": "off_session"
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
{
|
||||||
|
"data": [
|
||||||
|
{
|
||||||
|
"application": null,
|
||||||
|
"automatic_payment_methods": null,
|
||||||
|
"cancellation_reason": null,
|
||||||
|
"client_secret": "seti_1OGjOgDEQaroqDjsnqXN4MEe_secret_P4tBu0OljOeHnxDc1xFcHJLxe3YbMFs",
|
||||||
|
"created": 1000000000,
|
||||||
|
"customer": "cus_NORMALIZED0001",
|
||||||
|
"description": null,
|
||||||
|
"flow_directions": null,
|
||||||
|
"id": "seti_1OGjOgDEQaroqDjsnqXN4MEe",
|
||||||
|
"last_setup_error": null,
|
||||||
|
"latest_attempt": null,
|
||||||
|
"livemode": false,
|
||||||
|
"mandate": null,
|
||||||
|
"metadata": {},
|
||||||
|
"next_action": null,
|
||||||
|
"object": "setup_intent",
|
||||||
|
"on_behalf_of": null,
|
||||||
|
"payment_method": null,
|
||||||
|
"payment_method_configuration_details": null,
|
||||||
|
"payment_method_options": {
|
||||||
|
"card": {
|
||||||
|
"mandate_options": null,
|
||||||
|
"network": null,
|
||||||
|
"request_three_d_secure": "automatic"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"payment_method_types": [
|
||||||
|
"card"
|
||||||
|
],
|
||||||
|
"single_use_mandate": null,
|
||||||
|
"status": "requires_payment_method",
|
||||||
|
"usage": "off_session"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"has_more": true,
|
||||||
|
"object": "list",
|
||||||
|
"url": "/v1/setup_intents"
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
{
|
||||||
|
"application": null,
|
||||||
|
"automatic_payment_methods": null,
|
||||||
|
"cancellation_reason": null,
|
||||||
|
"client_secret": "seti_1OGjOiDEQaroqDjs0mczPK5Q_secret_P4tBceetMSlLfbfrbp4RXKdBjnNp4In",
|
||||||
|
"created": 1000000000,
|
||||||
|
"customer": "cus_NORMALIZED0001",
|
||||||
|
"description": null,
|
||||||
|
"flow_directions": null,
|
||||||
|
"id": "seti_1OGjOiDEQaroqDjs0mczPK5Q",
|
||||||
|
"last_setup_error": null,
|
||||||
|
"latest_attempt": "setatt_1OGjOiDEQaroqDjsZve6lwXi",
|
||||||
|
"livemode": false,
|
||||||
|
"mandate": null,
|
||||||
|
"metadata": {},
|
||||||
|
"next_action": null,
|
||||||
|
"object": "setup_intent",
|
||||||
|
"on_behalf_of": null,
|
||||||
|
"payment_method": "pm_1OGjOiDEQaroqDjs1saPoOzz",
|
||||||
|
"payment_method_configuration_details": null,
|
||||||
|
"payment_method_options": {
|
||||||
|
"card": {
|
||||||
|
"mandate_options": null,
|
||||||
|
"network": null,
|
||||||
|
"request_three_d_secure": "automatic"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"payment_method_types": [
|
||||||
|
"card"
|
||||||
|
],
|
||||||
|
"single_use_mandate": null,
|
||||||
|
"status": "succeeded",
|
||||||
|
"usage": "off_session"
|
||||||
|
}
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
{
|
||||||
|
"after_expiration": null,
|
||||||
|
"allow_promotion_codes": null,
|
||||||
|
"amount_subtotal": null,
|
||||||
|
"amount_total": null,
|
||||||
|
"automatic_tax": {
|
||||||
|
"enabled": false,
|
||||||
|
"status": null
|
||||||
|
},
|
||||||
|
"billing_address_collection": null,
|
||||||
|
"cancel_url": "http://zulip.testserver/upgrade/",
|
||||||
|
"client_reference_id": null,
|
||||||
|
"client_secret": null,
|
||||||
|
"consent": null,
|
||||||
|
"consent_collection": null,
|
||||||
|
"created": 1000000000,
|
||||||
|
"currency": null,
|
||||||
|
"currency_conversion": null,
|
||||||
|
"custom_fields": [],
|
||||||
|
"custom_text": {
|
||||||
|
"shipping_address": null,
|
||||||
|
"submit": null,
|
||||||
|
"terms_of_service_acceptance": null
|
||||||
|
},
|
||||||
|
"customer": "cus_NORMALIZED0001",
|
||||||
|
"customer_creation": null,
|
||||||
|
"customer_details": {
|
||||||
|
"address": null,
|
||||||
|
"email": "hamlet@zulip.com",
|
||||||
|
"name": null,
|
||||||
|
"phone": null,
|
||||||
|
"tax_exempt": null,
|
||||||
|
"tax_ids": null
|
||||||
|
},
|
||||||
|
"customer_email": null,
|
||||||
|
"expires_at": 1000000000,
|
||||||
|
"id": "cs_test_NORMALIZED01X2pCpFwoBMHMedIXu5mI7Xubvoh8wc4oenhkdkooMUfP5a",
|
||||||
|
"invoice": null,
|
||||||
|
"invoice_creation": null,
|
||||||
|
"livemode": false,
|
||||||
|
"locale": null,
|
||||||
|
"metadata": {
|
||||||
|
"type": "card_update",
|
||||||
|
"user_id": "10"
|
||||||
|
},
|
||||||
|
"mode": "setup",
|
||||||
|
"object": "checkout.session",
|
||||||
|
"payment_intent": null,
|
||||||
|
"payment_link": null,
|
||||||
|
"payment_method_collection": "always",
|
||||||
|
"payment_method_configuration_details": null,
|
||||||
|
"payment_method_options": {},
|
||||||
|
"payment_method_types": [
|
||||||
|
"card"
|
||||||
|
],
|
||||||
|
"payment_status": "no_payment_required",
|
||||||
|
"phone_number_collection": {
|
||||||
|
"enabled": false
|
||||||
|
},
|
||||||
|
"recovered_from": null,
|
||||||
|
"setup_intent": "seti_1OGjOgDEQaroqDjsnqXN4MEe",
|
||||||
|
"shipping": null,
|
||||||
|
"shipping_address_collection": null,
|
||||||
|
"shipping_options": [],
|
||||||
|
"shipping_rate": null,
|
||||||
|
"status": "open",
|
||||||
|
"submit_type": null,
|
||||||
|
"subscription": null,
|
||||||
|
"success_url": "http://zulip.testserver/billing/event_status?stripe_session_id={CHECKOUT_SESSION_ID}",
|
||||||
|
"total_details": null,
|
||||||
|
"ui_mode": "hosted",
|
||||||
|
"url": "https://checkout.stripe.com/c/pay/cs_test_NORMALIZED01X2pCpFwoBMHMedIXu5mI7Xubvoh8wc4oenhkdkooMUfP5a#fidkdWxOYHwnPyd1blpxYHZxWl1UZjFOczZJXUE2PUpzUWNPV2ZpdlFzUCcpJ2N3amhWYHdzYHcnP3F3cGApJ2lkfGpwcVF8dWAnPyd2bGtiaWBaZmppcGhrJyknYGtkZ2lgVWlkZmBtamlhYHd2Jz9xd3BgeCUl"
|
||||||
|
}
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
{
|
||||||
|
"data": [
|
||||||
|
{
|
||||||
|
"after_expiration": null,
|
||||||
|
"allow_promotion_codes": null,
|
||||||
|
"amount_subtotal": null,
|
||||||
|
"amount_total": null,
|
||||||
|
"automatic_tax": {
|
||||||
|
"enabled": false,
|
||||||
|
"status": null
|
||||||
|
},
|
||||||
|
"billing_address_collection": null,
|
||||||
|
"cancel_url": "http://zulip.testserver/upgrade/",
|
||||||
|
"client_reference_id": null,
|
||||||
|
"client_secret": null,
|
||||||
|
"consent": null,
|
||||||
|
"consent_collection": null,
|
||||||
|
"created": 1000000000,
|
||||||
|
"currency": null,
|
||||||
|
"currency_conversion": null,
|
||||||
|
"custom_fields": [],
|
||||||
|
"custom_text": {
|
||||||
|
"shipping_address": null,
|
||||||
|
"submit": null,
|
||||||
|
"terms_of_service_acceptance": null
|
||||||
|
},
|
||||||
|
"customer": "cus_NORMALIZED0001",
|
||||||
|
"customer_creation": null,
|
||||||
|
"customer_details": {
|
||||||
|
"address": null,
|
||||||
|
"email": "hamlet@zulip.com",
|
||||||
|
"name": null,
|
||||||
|
"phone": null,
|
||||||
|
"tax_exempt": null,
|
||||||
|
"tax_ids": null
|
||||||
|
},
|
||||||
|
"customer_email": null,
|
||||||
|
"expires_at": 1000000000,
|
||||||
|
"id": "cs_test_NORMALIZED01X2pCpFwoBMHMedIXu5mI7Xubvoh8wc4oenhkdkooMUfP5a",
|
||||||
|
"invoice": null,
|
||||||
|
"invoice_creation": null,
|
||||||
|
"livemode": false,
|
||||||
|
"locale": null,
|
||||||
|
"metadata": {
|
||||||
|
"type": "card_update",
|
||||||
|
"user_id": "10"
|
||||||
|
},
|
||||||
|
"mode": "setup",
|
||||||
|
"object": "checkout.session",
|
||||||
|
"payment_intent": null,
|
||||||
|
"payment_link": null,
|
||||||
|
"payment_method_collection": "always",
|
||||||
|
"payment_method_configuration_details": null,
|
||||||
|
"payment_method_options": {},
|
||||||
|
"payment_method_types": [
|
||||||
|
"card"
|
||||||
|
],
|
||||||
|
"payment_status": "no_payment_required",
|
||||||
|
"phone_number_collection": {
|
||||||
|
"enabled": false
|
||||||
|
},
|
||||||
|
"recovered_from": null,
|
||||||
|
"setup_intent": "seti_1OGjOgDEQaroqDjsnqXN4MEe",
|
||||||
|
"shipping": null,
|
||||||
|
"shipping_address_collection": null,
|
||||||
|
"shipping_options": [],
|
||||||
|
"shipping_rate": null,
|
||||||
|
"status": "open",
|
||||||
|
"submit_type": null,
|
||||||
|
"subscription": null,
|
||||||
|
"success_url": "http://zulip.testserver/billing/event_status?stripe_session_id={CHECKOUT_SESSION_ID}",
|
||||||
|
"total_details": null,
|
||||||
|
"ui_mode": "hosted",
|
||||||
|
"url": "https://checkout.stripe.com/c/pay/cs_test_NORMALIZED01X2pCpFwoBMHMedIXu5mI7Xubvoh8wc4oenhkdkooMUfP5a#fidkdWxOYHwnPyd1blpxYHZxWl1UZjFOczZJXUE2PUpzUWNPV2ZpdlFzUCcpJ2N3amhWYHdzYHcnP3F3cGApJ2lkfGpwcVF8dWAnPyd2bGtiaWBaZmppcGhrJyknYGtkZ2lgVWlkZmBtamlhYHd2Jz9xd3BgeCUl"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"has_more": true,
|
||||||
|
"object": "list",
|
||||||
|
"url": "/v1/checkout/sessions"
|
||||||
|
}
|
||||||
@@ -2839,6 +2839,115 @@ class StripeTest(StripeTestCase):
|
|||||||
self.assertIsNone(plan.next_invoice_date)
|
self.assertIsNone(plan.next_invoice_date)
|
||||||
self.assertEqual(plan.status, CustomerPlan.ENDED)
|
self.assertEqual(plan.status, CustomerPlan.ENDED)
|
||||||
|
|
||||||
|
@mock_stripe()
|
||||||
|
def test_switch_now_free_trial_from_monthly_to_annual(self, *mocks: Mock) -> None:
|
||||||
|
user = self.example_user("hamlet")
|
||||||
|
self.login_user(user)
|
||||||
|
|
||||||
|
free_trial_end_date = self.now + timedelta(days=60)
|
||||||
|
with self.settings(FREE_TRIAL_DAYS=60):
|
||||||
|
with patch("corporate.lib.stripe.timezone_now", return_value=self.now):
|
||||||
|
self.add_card_and_upgrade(user, schedule="monthly")
|
||||||
|
plan = CustomerPlan.objects.get()
|
||||||
|
self.assertEqual(plan.next_invoice_date, free_trial_end_date)
|
||||||
|
self.assertEqual(get_realm("zulip").plan_type, Realm.PLAN_TYPE_STANDARD)
|
||||||
|
self.assertEqual(plan.status, CustomerPlan.FREE_TRIAL)
|
||||||
|
|
||||||
|
customer = get_customer_by_realm(user.realm)
|
||||||
|
assert customer is not None
|
||||||
|
result = self.client_patch(
|
||||||
|
"/json/billing/plan",
|
||||||
|
{
|
||||||
|
"status": CustomerPlan.FREE_TRIAL,
|
||||||
|
"schedule": CustomerPlan.ANNUAL,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
self.assert_json_success(result)
|
||||||
|
|
||||||
|
plan.refresh_from_db()
|
||||||
|
self.assertEqual(plan.status, CustomerPlan.ENDED)
|
||||||
|
|
||||||
|
plan = CustomerPlan.objects.get(
|
||||||
|
customer=customer,
|
||||||
|
automanage_licenses=True,
|
||||||
|
price_per_license=8000,
|
||||||
|
fixed_price=None,
|
||||||
|
discount=None,
|
||||||
|
billing_cycle_anchor=self.now,
|
||||||
|
billing_schedule=CustomerPlan.ANNUAL,
|
||||||
|
invoiced_through=None,
|
||||||
|
next_invoice_date=free_trial_end_date,
|
||||||
|
tier=CustomerPlan.STANDARD,
|
||||||
|
status=CustomerPlan.FREE_TRIAL,
|
||||||
|
charge_automatically=True,
|
||||||
|
)
|
||||||
|
LicenseLedger.objects.get(
|
||||||
|
plan=plan,
|
||||||
|
is_renewal=True,
|
||||||
|
event_time=self.now,
|
||||||
|
licenses=self.seat_count,
|
||||||
|
licenses_at_next_renewal=self.seat_count,
|
||||||
|
)
|
||||||
|
|
||||||
|
realm_audit_log = RealmAuditLog.objects.filter(
|
||||||
|
event_type=RealmAuditLog.CUSTOMER_SWITCHED_FROM_MONTHLY_TO_ANNUAL_PLAN
|
||||||
|
).last()
|
||||||
|
assert realm_audit_log is not None
|
||||||
|
|
||||||
|
@mock_stripe()
|
||||||
|
def test_switch_now_free_trial_from_annual_to_monthly(self, *mocks: Mock) -> None:
|
||||||
|
user = self.example_user("hamlet")
|
||||||
|
self.login_user(user)
|
||||||
|
|
||||||
|
free_trial_end_date = self.now + timedelta(days=60)
|
||||||
|
with self.settings(FREE_TRIAL_DAYS=60):
|
||||||
|
with patch("corporate.lib.stripe.timezone_now", return_value=self.now):
|
||||||
|
self.add_card_and_upgrade(user, schedule="annual")
|
||||||
|
plan = CustomerPlan.objects.get()
|
||||||
|
self.assertEqual(plan.next_invoice_date, free_trial_end_date)
|
||||||
|
self.assertEqual(get_realm("zulip").plan_type, Realm.PLAN_TYPE_STANDARD)
|
||||||
|
self.assertEqual(plan.status, CustomerPlan.FREE_TRIAL)
|
||||||
|
|
||||||
|
customer = get_customer_by_realm(user.realm)
|
||||||
|
assert customer is not None
|
||||||
|
result = self.client_patch(
|
||||||
|
"/json/billing/plan",
|
||||||
|
{
|
||||||
|
"status": CustomerPlan.FREE_TRIAL,
|
||||||
|
"schedule": CustomerPlan.MONTHLY,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
self.assert_json_success(result)
|
||||||
|
plan.refresh_from_db()
|
||||||
|
self.assertEqual(plan.status, CustomerPlan.ENDED)
|
||||||
|
|
||||||
|
plan = CustomerPlan.objects.get(
|
||||||
|
customer=customer,
|
||||||
|
automanage_licenses=True,
|
||||||
|
price_per_license=800,
|
||||||
|
fixed_price=None,
|
||||||
|
discount=None,
|
||||||
|
billing_cycle_anchor=self.now,
|
||||||
|
billing_schedule=CustomerPlan.MONTHLY,
|
||||||
|
invoiced_through=None,
|
||||||
|
next_invoice_date=free_trial_end_date,
|
||||||
|
tier=CustomerPlan.STANDARD,
|
||||||
|
status=CustomerPlan.FREE_TRIAL,
|
||||||
|
charge_automatically=True,
|
||||||
|
)
|
||||||
|
LicenseLedger.objects.get(
|
||||||
|
plan=plan,
|
||||||
|
is_renewal=True,
|
||||||
|
event_time=self.now,
|
||||||
|
licenses=self.seat_count,
|
||||||
|
licenses_at_next_renewal=self.seat_count,
|
||||||
|
)
|
||||||
|
|
||||||
|
realm_audit_log = RealmAuditLog.objects.filter(
|
||||||
|
event_type=RealmAuditLog.CUSTOMER_SWITCHED_FROM_ANNUAL_TO_MONTHLY_PLAN
|
||||||
|
).last()
|
||||||
|
assert realm_audit_log is not None
|
||||||
|
|
||||||
def test_end_free_trial(self) -> None:
|
def test_end_free_trial(self) -> None:
|
||||||
user = self.example_user("hamlet")
|
user = self.example_user("hamlet")
|
||||||
|
|
||||||
|
|||||||
@@ -110,11 +110,13 @@ def update_plan(
|
|||||||
licenses_at_next_renewal: Optional[int] = REQ(
|
licenses_at_next_renewal: Optional[int] = REQ(
|
||||||
"licenses_at_next_renewal", json_validator=check_int, default=None
|
"licenses_at_next_renewal", json_validator=check_int, default=None
|
||||||
),
|
),
|
||||||
|
schedule: Optional[int] = REQ("schedule", json_validator=check_int, default=None),
|
||||||
) -> HttpResponse:
|
) -> HttpResponse:
|
||||||
update_plan_request = UpdatePlanRequest(
|
update_plan_request = UpdatePlanRequest(
|
||||||
status=status,
|
status=status,
|
||||||
licenses=licenses,
|
licenses=licenses,
|
||||||
licenses_at_next_renewal=licenses_at_next_renewal,
|
licenses_at_next_renewal=licenses_at_next_renewal,
|
||||||
|
schedule=schedule,
|
||||||
)
|
)
|
||||||
billing_session = RealmBillingSession(user=user)
|
billing_session = RealmBillingSession(user=user)
|
||||||
billing_session.do_update_plan(update_plan_request)
|
billing_session.do_update_plan(update_plan_request)
|
||||||
|
|||||||
@@ -41,7 +41,7 @@
|
|||||||
{%if switch_to_monthly_at_end_of_cycle %}data-switch-to-monthly-eoc="true"{% endif %}
|
{%if switch_to_monthly_at_end_of_cycle %}data-switch-to-monthly-eoc="true"{% endif %}
|
||||||
{%if switch_to_annual_at_end_of_cycle %}data-switch-to-annual-eoc="true"{% endif %}>
|
{%if switch_to_annual_at_end_of_cycle %}data-switch-to-annual-eoc="true"{% endif %}>
|
||||||
<label for="org-billing-frequency">Billing frequency</label>
|
<label for="org-billing-frequency">Billing frequency</label>
|
||||||
{% if free_trial or downgrade_at_end_of_free_trial or downgrade_at_end_of_cycle %}
|
{% if downgrade_at_end_of_free_trial or downgrade_at_end_of_cycle %}
|
||||||
<div class="not-editable-realm-field">
|
<div class="not-editable-realm-field">
|
||||||
{{ billing_frequency }}
|
{{ billing_frequency }}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -7,6 +7,12 @@ import * as helpers from "./helpers";
|
|||||||
|
|
||||||
const billing_frequency_schema = z.enum(["Monthly", "Annual"]);
|
const billing_frequency_schema = z.enum(["Monthly", "Annual"]);
|
||||||
|
|
||||||
|
// Matches the CustomerPlan model in the backend.
|
||||||
|
enum BillingFrequency {
|
||||||
|
ANNUAL = 1,
|
||||||
|
MONTHLY = 2,
|
||||||
|
}
|
||||||
|
|
||||||
enum CustomerPlanStatus {
|
enum CustomerPlanStatus {
|
||||||
ACTIVE = 1,
|
ACTIVE = 1,
|
||||||
DOWNGRADE_AT_END_OF_CYCLE = 2,
|
DOWNGRADE_AT_END_OF_CYCLE = 2,
|
||||||
@@ -290,24 +296,36 @@ export function initialize(): void {
|
|||||||
$("#org-billing-frequency-confirm-button").attr("data-status", new_status);
|
$("#org-billing-frequency-confirm-button").attr("data-status", new_status);
|
||||||
} else if (current_billing_frequency !== billing_frequency_selected) {
|
} else if (current_billing_frequency !== billing_frequency_selected) {
|
||||||
$("#org-billing-frequency-confirm-button").toggleClass("hide", false);
|
$("#org-billing-frequency-confirm-button").toggleClass("hide", false);
|
||||||
let new_status = CustomerPlanStatus.SWITCH_TO_ANNUAL_AT_END_OF_CYCLE;
|
let new_status = free_trial
|
||||||
|
? CustomerPlanStatus.FREE_TRIAL
|
||||||
|
: CustomerPlanStatus.SWITCH_TO_ANNUAL_AT_END_OF_CYCLE;
|
||||||
|
let new_schedule = BillingFrequency.ANNUAL;
|
||||||
if (billing_frequency_selected === "Monthly") {
|
if (billing_frequency_selected === "Monthly") {
|
||||||
new_status = CustomerPlanStatus.SWITCH_TO_MONTHLY_AT_END_OF_CYCLE;
|
new_status = free_trial
|
||||||
|
? CustomerPlanStatus.FREE_TRIAL
|
||||||
|
: CustomerPlanStatus.SWITCH_TO_MONTHLY_AT_END_OF_CYCLE;
|
||||||
|
new_schedule = BillingFrequency.MONTHLY;
|
||||||
}
|
}
|
||||||
$("#org-billing-frequency-confirm-button").attr("data-status", new_status);
|
$("#org-billing-frequency-confirm-button").attr("data-status", new_status);
|
||||||
|
if (free_trial) {
|
||||||
|
// Only set schedule for free trial since it is a different process to update the frequency immediately.
|
||||||
|
$("#org-billing-frequency-confirm-button").attr("data-schedule", new_schedule);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
$("#org-billing-frequency-confirm-button").toggleClass("hide", true);
|
$("#org-billing-frequency-confirm-button").toggleClass("hide", true);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
$("#org-billing-frequency-confirm-button").on("click", (e) => {
|
$("#org-billing-frequency-confirm-button").on("click", (e) => {
|
||||||
|
const data = {
|
||||||
|
status: $("#org-billing-frequency-confirm-button").attr("data-status"),
|
||||||
|
schedule: $("#org-billing-frequency-confirm-button").attr("data-schedule"),
|
||||||
|
};
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
void $.ajax({
|
void $.ajax({
|
||||||
type: "patch",
|
type: "patch",
|
||||||
url: "/json/billing/plan",
|
url: "/json/billing/plan",
|
||||||
data: {
|
data,
|
||||||
status: $("#org-billing-frequency-confirm-button").attr("data-status"),
|
|
||||||
},
|
|
||||||
success() {
|
success() {
|
||||||
window.location.replace(
|
window.location.replace(
|
||||||
"/billing/?success_message=" +
|
"/billing/?success_message=" +
|
||||||
|
|||||||
Reference in New Issue
Block a user