mirror of
https://github.com/zulip/zulip.git
synced 2025-10-23 04:52:12 +00:00
billing: Update /billing to work with new subscription model.
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
{
|
||||
"account_balance": 0,
|
||||
"created": 1000000000,
|
||||
"currency": "usd",
|
||||
"default_source": {
|
||||
"address_city": "Pacific",
|
||||
"address_country": "United States",
|
||||
"address_line1": "Under the sea,",
|
||||
"address_line1_check": "pass",
|
||||
"address_line2": null,
|
||||
"address_state": null,
|
||||
"address_zip": "33333",
|
||||
"address_zip_check": "pass",
|
||||
"brand": "Visa",
|
||||
"country": "US",
|
||||
"customer": "cus_NORMALIZED0001",
|
||||
"cvc_check": "pass",
|
||||
"dynamic_last4": null,
|
||||
"exp_month": 3,
|
||||
"exp_year": 2033,
|
||||
"fingerprint": "NORMALIZED000001",
|
||||
"funding": "credit",
|
||||
"id": "card_NORMALIZED00000000000001",
|
||||
"last4": "4242",
|
||||
"metadata": {},
|
||||
"name": "Ada Starr",
|
||||
"object": "card",
|
||||
"tokenization_method": null
|
||||
},
|
||||
"delinquent": false,
|
||||
"description": "zulip (Zulip Dev)",
|
||||
"discount": null,
|
||||
"email": "hamlet@zulip.com",
|
||||
"id": "cus_NORMALIZED0001",
|
||||
"invoice_prefix": "NORMA01",
|
||||
"livemode": false,
|
||||
"metadata": {
|
||||
"realm_id": "1",
|
||||
"realm_str": "zulip"
|
||||
},
|
||||
"object": "customer",
|
||||
"shipping": null,
|
||||
"sources": {
|
||||
"data": [
|
||||
{
|
||||
"address_city": "Pacific",
|
||||
"address_country": "United States",
|
||||
"address_line1": "Under the sea,",
|
||||
"address_line1_check": "pass",
|
||||
"address_line2": null,
|
||||
"address_state": null,
|
||||
"address_zip": "33333",
|
||||
"address_zip_check": "pass",
|
||||
"brand": "Visa",
|
||||
"country": "US",
|
||||
"customer": "cus_NORMALIZED0001",
|
||||
"cvc_check": "pass",
|
||||
"dynamic_last4": null,
|
||||
"exp_month": 3,
|
||||
"exp_year": 2033,
|
||||
"fingerprint": "NORMALIZED000001",
|
||||
"funding": "credit",
|
||||
"id": "card_NORMALIZED00000000000001",
|
||||
"last4": "4242",
|
||||
"metadata": {},
|
||||
"name": "Ada Starr",
|
||||
"object": "card",
|
||||
"tokenization_method": null
|
||||
}
|
||||
],
|
||||
"has_more": false,
|
||||
"object": "list",
|
||||
"total_count": 1,
|
||||
"url": "/v1/customers/cus_NORMALIZED0001/sources"
|
||||
},
|
||||
"subscriptions": {
|
||||
"data": [],
|
||||
"has_more": false,
|
||||
"object": "list",
|
||||
"total_count": 0,
|
||||
"url": "/v1/customers/cus_NORMALIZED0001/subscriptions"
|
||||
},
|
||||
"tax_info": null,
|
||||
"tax_info_verification": null
|
||||
}
|
@@ -0,0 +1,85 @@
|
||||
{
|
||||
"account_balance": 0,
|
||||
"created": 1000000000,
|
||||
"currency": "usd",
|
||||
"default_source": {
|
||||
"address_city": "Pacific",
|
||||
"address_country": "United States",
|
||||
"address_line1": "Under the sea,",
|
||||
"address_line1_check": "pass",
|
||||
"address_line2": null,
|
||||
"address_state": null,
|
||||
"address_zip": "33333",
|
||||
"address_zip_check": "pass",
|
||||
"brand": "Visa",
|
||||
"country": "US",
|
||||
"customer": "cus_NORMALIZED0001",
|
||||
"cvc_check": "pass",
|
||||
"dynamic_last4": null,
|
||||
"exp_month": 3,
|
||||
"exp_year": 2033,
|
||||
"fingerprint": "NORMALIZED000001",
|
||||
"funding": "credit",
|
||||
"id": "card_NORMALIZED00000000000001",
|
||||
"last4": "4242",
|
||||
"metadata": {},
|
||||
"name": "Ada Starr",
|
||||
"object": "card",
|
||||
"tokenization_method": null
|
||||
},
|
||||
"delinquent": false,
|
||||
"description": "zulip (Zulip Dev)",
|
||||
"discount": null,
|
||||
"email": "hamlet@zulip.com",
|
||||
"id": "cus_NORMALIZED0001",
|
||||
"invoice_prefix": "NORMA01",
|
||||
"livemode": false,
|
||||
"metadata": {
|
||||
"realm_id": "1",
|
||||
"realm_str": "zulip"
|
||||
},
|
||||
"object": "customer",
|
||||
"shipping": null,
|
||||
"sources": {
|
||||
"data": [
|
||||
{
|
||||
"address_city": "Pacific",
|
||||
"address_country": "United States",
|
||||
"address_line1": "Under the sea,",
|
||||
"address_line1_check": "pass",
|
||||
"address_line2": null,
|
||||
"address_state": null,
|
||||
"address_zip": "33333",
|
||||
"address_zip_check": "pass",
|
||||
"brand": "Visa",
|
||||
"country": "US",
|
||||
"customer": "cus_NORMALIZED0001",
|
||||
"cvc_check": "pass",
|
||||
"dynamic_last4": null,
|
||||
"exp_month": 3,
|
||||
"exp_year": 2033,
|
||||
"fingerprint": "NORMALIZED000001",
|
||||
"funding": "credit",
|
||||
"id": "card_NORMALIZED00000000000001",
|
||||
"last4": "4242",
|
||||
"metadata": {},
|
||||
"name": "Ada Starr",
|
||||
"object": "card",
|
||||
"tokenization_method": null
|
||||
}
|
||||
],
|
||||
"has_more": false,
|
||||
"object": "list",
|
||||
"total_count": 1,
|
||||
"url": "/v1/customers/cus_NORMALIZED0001/sources"
|
||||
},
|
||||
"subscriptions": {
|
||||
"data": [],
|
||||
"has_more": false,
|
||||
"object": "list",
|
||||
"total_count": 0,
|
||||
"url": "/v1/customers/cus_NORMALIZED0001/subscriptions"
|
||||
},
|
||||
"tax_info": null,
|
||||
"tax_info_verification": null
|
||||
}
|
@@ -18,9 +18,9 @@
|
||||
"due_date": 1000000000,
|
||||
"ending_balance": 0,
|
||||
"finalized_at": 1000000000,
|
||||
"hosted_invoice_url": "https://pay.stripe.com/invoice/invst_daf4CWkp5EbV5fMjyaF0P8Yu3h",
|
||||
"hosted_invoice_url": "https://pay.stripe.com/invoice/invst_NORMALIZED0000000000000001",
|
||||
"id": "in_NORMALIZED00000000000001",
|
||||
"invoice_pdf": "https://pay.stripe.com/invoice/invst_daf4CWkp5EbV5fMjyaF0P8Yu3h/pdf",
|
||||
"invoice_pdf": "https://pay.stripe.com/invoice/invst_NORMALIZED0000000000000001/pdf",
|
||||
"lines": {
|
||||
"data": [
|
||||
{
|
||||
|
@@ -0,0 +1,85 @@
|
||||
{
|
||||
"account_balance": 0,
|
||||
"created": 1010000002,
|
||||
"currency": "usd",
|
||||
"default_source": {
|
||||
"address_city": "Pacific",
|
||||
"address_country": "United States",
|
||||
"address_line1": "Under the sea,",
|
||||
"address_line1_check": "pass",
|
||||
"address_line2": null,
|
||||
"address_state": null,
|
||||
"address_zip": "33333",
|
||||
"address_zip_check": "pass",
|
||||
"brand": "Visa",
|
||||
"country": "US",
|
||||
"customer": "cus_NORMALIZED0001",
|
||||
"cvc_check": "pass",
|
||||
"dynamic_last4": null,
|
||||
"exp_month": 3,
|
||||
"exp_year": 2033,
|
||||
"fingerprint": "NORMALIZED000001",
|
||||
"funding": "credit",
|
||||
"id": "card_NORMALIZED00000000000001",
|
||||
"last4": "4242",
|
||||
"metadata": {},
|
||||
"name": "Ada Starr",
|
||||
"object": "card",
|
||||
"tokenization_method": null
|
||||
},
|
||||
"delinquent": false,
|
||||
"description": "zulip (Zulip Dev)",
|
||||
"discount": null,
|
||||
"email": "hamlet@zulip.com",
|
||||
"id": "cus_NORMALIZED0001",
|
||||
"invoice_prefix": "NORMA01",
|
||||
"livemode": false,
|
||||
"metadata": {
|
||||
"realm_id": "1",
|
||||
"realm_str": "zulip"
|
||||
},
|
||||
"object": "customer",
|
||||
"shipping": null,
|
||||
"sources": {
|
||||
"data": [
|
||||
{
|
||||
"address_city": "Pacific",
|
||||
"address_country": "United States",
|
||||
"address_line1": "Under the sea,",
|
||||
"address_line1_check": "pass",
|
||||
"address_line2": null,
|
||||
"address_state": null,
|
||||
"address_zip": "33333",
|
||||
"address_zip_check": "pass",
|
||||
"brand": "Visa",
|
||||
"country": "US",
|
||||
"customer": "cus_NORMALIZED0001",
|
||||
"cvc_check": "pass",
|
||||
"dynamic_last4": null,
|
||||
"exp_month": 3,
|
||||
"exp_year": 2033,
|
||||
"fingerprint": "NORMALIZED000001",
|
||||
"funding": "credit",
|
||||
"id": "card_NORMALIZED00000000000001",
|
||||
"last4": "4242",
|
||||
"metadata": {},
|
||||
"name": "Ada Starr",
|
||||
"object": "card",
|
||||
"tokenization_method": null
|
||||
}
|
||||
],
|
||||
"has_more": false,
|
||||
"object": "list",
|
||||
"total_count": 1,
|
||||
"url": "/v1/customers/cus_NORMALIZED0001/sources"
|
||||
},
|
||||
"subscriptions": {
|
||||
"data": [],
|
||||
"has_more": false,
|
||||
"object": "list",
|
||||
"total_count": 0,
|
||||
"url": "/v1/customers/cus_NORMALIZED0001/subscriptions"
|
||||
},
|
||||
"tax_info": null,
|
||||
"tax_info_verification": null
|
||||
}
|
@@ -18,9 +18,9 @@
|
||||
"due_date": 1000000000,
|
||||
"ending_balance": 0,
|
||||
"finalized_at": 1000000000,
|
||||
"hosted_invoice_url": "https://pay.stripe.com/invoice/invst_sh6wGq3YCdgkwFxDfHonbxi1JB",
|
||||
"hosted_invoice_url": "https://pay.stripe.com/invoice/invst_NORMALIZED0000000000000001",
|
||||
"id": "in_NORMALIZED00000000000001",
|
||||
"invoice_pdf": "https://pay.stripe.com/invoice/invst_sh6wGq3YCdgkwFxDfHonbxi1JB/pdf",
|
||||
"invoice_pdf": "https://pay.stripe.com/invoice/invst_NORMALIZED0000000000000001/pdf",
|
||||
"lines": {
|
||||
"data": [
|
||||
{
|
||||
|
@@ -20,9 +20,9 @@
|
||||
"due_date": 1000000000,
|
||||
"ending_balance": 0,
|
||||
"finalized_at": 1000000000,
|
||||
"hosted_invoice_url": "https://pay.stripe.com/invoice/invst_sh6wGq3YCdgkwFxDfHonbxi1JB",
|
||||
"hosted_invoice_url": "https://pay.stripe.com/invoice/invst_NORMALIZED0000000000000001",
|
||||
"id": "in_NORMALIZED00000000000001",
|
||||
"invoice_pdf": "https://pay.stripe.com/invoice/invst_sh6wGq3YCdgkwFxDfHonbxi1JB/pdf",
|
||||
"invoice_pdf": "https://pay.stripe.com/invoice/invst_NORMALIZED0000000000000001/pdf",
|
||||
"lines": {
|
||||
"data": [
|
||||
{
|
||||
|
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"account_balance": 0,
|
||||
"created": 1010000001,
|
||||
"currency": "usd",
|
||||
"default_source": null,
|
||||
"delinquent": false,
|
||||
"description": "zulip (Zulip Dev)",
|
||||
"discount": null,
|
||||
"email": "hamlet@zulip.com",
|
||||
"id": "cus_NORMALIZED0001",
|
||||
"invoice_prefix": "NORMA01",
|
||||
"livemode": false,
|
||||
"metadata": {
|
||||
"realm_id": "1",
|
||||
"realm_str": "zulip"
|
||||
},
|
||||
"object": "customer",
|
||||
"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_info": null,
|
||||
"tax_info_verification": null
|
||||
}
|
@@ -18,9 +18,9 @@
|
||||
"due_date": 1000000000,
|
||||
"ending_balance": 0,
|
||||
"finalized_at": 1000000000,
|
||||
"hosted_invoice_url": "https://pay.stripe.com/invoice/invst_O7KxfsK8GxMVb8EGiyIlGF9OUe",
|
||||
"hosted_invoice_url": "https://pay.stripe.com/invoice/invst_NORMALIZED0000000000000001",
|
||||
"id": "in_NORMALIZED00000000000001",
|
||||
"invoice_pdf": "https://pay.stripe.com/invoice/invst_O7KxfsK8GxMVb8EGiyIlGF9OUe/pdf",
|
||||
"invoice_pdf": "https://pay.stripe.com/invoice/invst_NORMALIZED0000000000000001/pdf",
|
||||
"lines": {
|
||||
"data": [
|
||||
{
|
||||
|
@@ -20,9 +20,9 @@
|
||||
"due_date": 1000000000,
|
||||
"ending_balance": 0,
|
||||
"finalized_at": 1000000000,
|
||||
"hosted_invoice_url": "https://pay.stripe.com/invoice/invst_O7KxfsK8GxMVb8EGiyIlGF9OUe",
|
||||
"hosted_invoice_url": "https://pay.stripe.com/invoice/invst_NORMALIZED0000000000000001",
|
||||
"id": "in_NORMALIZED00000000000001",
|
||||
"invoice_pdf": "https://pay.stripe.com/invoice/invst_O7KxfsK8GxMVb8EGiyIlGF9OUe/pdf",
|
||||
"invoice_pdf": "https://pay.stripe.com/invoice/invst_NORMALIZED0000000000000001/pdf",
|
||||
"lines": {
|
||||
"data": [
|
||||
{
|
||||
|
@@ -118,7 +118,7 @@ def normalize_fixture_data(decorated_function: CallableT,
|
||||
id_lengths = [
|
||||
('cus', 14), ('sub', 14), ('si', 14), ('sli', 14), ('req', 14), ('tok', 24), ('card', 24),
|
||||
('txn', 24), ('ch', 24), ('in', 24), ('ii', 24), ('test', 12), ('src_client_secret', 24),
|
||||
('src', 24)]
|
||||
('src', 24), ('invst', 26)]
|
||||
# We'll replace cus_D7OT2jf5YAtZQ2 with something like cus_NORMALIZED0001
|
||||
pattern_translations = {
|
||||
"%s_[A-Za-z0-9]{%d}" % (prefix, length): "%s_NORMALIZED%%0%dd" % (prefix, length - 10)
|
||||
@@ -399,12 +399,15 @@ class StripeTest(ZulipTestCase):
|
||||
self.assertEqual(response.status_code, 302)
|
||||
self.assertEqual('/billing/', response.url)
|
||||
|
||||
# TODO: Check /billing has the correct information
|
||||
# response = self.client_get("/billing/")
|
||||
# self.assert_not_in_success_response(['Pay annually'], response)
|
||||
# for substring in ['Your plan will renew on', '$%s.00' % (80 * self.seat_count,),
|
||||
# 'Card ending in 4242', 'Update card']:
|
||||
# self.assert_in_response(substring, response)
|
||||
# Check /billing has the correct information
|
||||
response = self.client_get("/billing/")
|
||||
self.assert_not_in_success_response(['Pay annually'], response)
|
||||
for substring in [
|
||||
'Zulip Standard', str(self.seat_count),
|
||||
'Your plan will renew on', 'January 2, 2013', '$%s.00' % (80 * self.seat_count,),
|
||||
'Visa ending in 4242',
|
||||
'Update card']:
|
||||
self.assert_in_response(substring, response)
|
||||
|
||||
@mock_stripe(tested_timestamp_fields=["created"])
|
||||
def test_upgrade_by_invoice(self, *mocks: Mock) -> None:
|
||||
@@ -477,12 +480,14 @@ class StripeTest(ZulipTestCase):
|
||||
self.assertEqual(response.status_code, 302)
|
||||
self.assertEqual('/billing/', response.url)
|
||||
|
||||
# TODO: Check /billing has the correct information
|
||||
# response = self.client_get("/billing/")
|
||||
# self.assert_not_in_success_response(['Pay annually'], response)
|
||||
# for substring in ['Your plan will renew on', '$%s.00' % (80 * self.seat_count,),
|
||||
# 'Card ending in 4242', 'Update card']:
|
||||
# self.assert_in_response(substring, response)
|
||||
# Check /billing has the correct information
|
||||
response = self.client_get("/billing/")
|
||||
self.assert_not_in_success_response(['Pay annually', 'Update card'], response)
|
||||
for substring in [
|
||||
'Zulip Standard', str(123),
|
||||
'Your plan will renew on', 'January 2, 2013', '$9,840.00', # 9840 = 80 * 123
|
||||
'Billed by invoice']:
|
||||
self.assert_in_response(substring, response)
|
||||
|
||||
@mock_stripe()
|
||||
def test_billing_page_permissions(self, *mocks: Mock) -> None:
|
||||
@@ -727,10 +732,6 @@ class StripeTest(ZulipTestCase):
|
||||
# card on file, and should show it
|
||||
# TODO
|
||||
|
||||
# If you signup via invoice, and then downgrade immediately, the
|
||||
# default_source is in a weird intermediate state.
|
||||
# TODO
|
||||
|
||||
@mock_stripe()
|
||||
def test_attach_discount_to_realm(self, *mocks: Mock) -> None:
|
||||
# Attach discount before Stripe customer exists
|
||||
|
@@ -54,24 +54,20 @@ def check_upgrade_parameters(
|
||||
raise BillingError('not enough licenses',
|
||||
_("You must invoice for at least {} users.".format(min_licenses)))
|
||||
|
||||
# TODO
|
||||
def payment_method_string(stripe_customer: stripe.Customer) -> str: # nocoverage: TODO
|
||||
subscription = None # extract_current_subscription(stripe_customer)
|
||||
if subscription is not None and subscription.billing == "send_invoice":
|
||||
return _("Billed by invoice")
|
||||
# Should only be called if the customer is being charged automatically
|
||||
def payment_method_string(stripe_customer: stripe.Customer) -> str:
|
||||
stripe_source = stripe_customer.default_source
|
||||
# In case of e.g. an expired card
|
||||
if stripe_source is None: # nocoverage
|
||||
return _("No payment method on file")
|
||||
if stripe_source.object == "card":
|
||||
return _("Card ending in %(last4)s" % {'last4': cast(stripe.Card, stripe_source).last4})
|
||||
# You can get here if e.g. you sign up to pay by invoice, and then
|
||||
# immediately downgrade. In that case, stripe_source.object == 'source',
|
||||
# and stripe_source.type = 'ach_credit_transfer'.
|
||||
# Using a catch-all error message here since there might be one-off stuff we
|
||||
# do for a particular customer that would land them here. E.g. by default we
|
||||
# don't support ACH for automatic payments, but in theory we could add it for
|
||||
# a customer via the Stripe dashboard.
|
||||
return _("%(brand)s ending in %(last4)s" % {
|
||||
'brand': cast(stripe.Card, stripe_source).brand,
|
||||
'last4': cast(stripe.Card, stripe_source).last4})
|
||||
# There might be one-off stuff we do for a particular customer that
|
||||
# would land them here. E.g. by default we don't support ACH for
|
||||
# automatic payments, but in theory we could add it for a customer via
|
||||
# the Stripe dashboard.
|
||||
return _("Unknown payment method. Please contact %s." % (settings.ZULIP_ADMINISTRATOR,)) # nocoverage
|
||||
|
||||
@has_request_variables
|
||||
@@ -163,7 +159,7 @@ def billing_home(request: HttpRequest) -> HttpResponse:
|
||||
return render(request, 'corporate/billing.html', context=context)
|
||||
context = {'admin_access': True}
|
||||
|
||||
charge_automatically = False
|
||||
stripe_customer = stripe_get_customer(customer.stripe_customer_id)
|
||||
plan = get_active_plan(customer)
|
||||
if plan is not None:
|
||||
plan_name = {
|
||||
@@ -171,16 +167,14 @@ def billing_home(request: HttpRequest) -> HttpResponse:
|
||||
CustomerPlan.PLUS: 'Zulip Plus',
|
||||
}[plan.tier]
|
||||
licenses = plan.licenses
|
||||
# Need user's timezone to do this properly
|
||||
# Should do this in javascript, using the user's timezone
|
||||
renewal_date = '{dt:%B} {dt.day}, {dt.year}'.format(dt=next_renewal_date(plan))
|
||||
renewal_cents = renewal_amount(plan)
|
||||
charge_automatically = plan.charge_automatically
|
||||
if charge_automatically: # nocoverage: TODO
|
||||
# TODO get last4
|
||||
payment_method = 'Card on file'
|
||||
else: # nocoverage: TODO
|
||||
if charge_automatically:
|
||||
payment_method = payment_method_string(stripe_customer)
|
||||
else:
|
||||
payment_method = 'Billed by invoice'
|
||||
billed_by_invoice = not plan.charge_automatically
|
||||
# Can only get here by subscribing and then downgrading. We don't support downgrading
|
||||
# yet, but keeping this code here since we will soon.
|
||||
else: # nocoverage
|
||||
@@ -189,6 +183,7 @@ def billing_home(request: HttpRequest) -> HttpResponse:
|
||||
renewal_date = ''
|
||||
renewal_cents = 0
|
||||
payment_method = ''
|
||||
charge_automatically = False
|
||||
|
||||
context.update({
|
||||
'plan_name': plan_name,
|
||||
@@ -196,13 +191,10 @@ def billing_home(request: HttpRequest) -> HttpResponse:
|
||||
'renewal_date': renewal_date,
|
||||
'renewal_amount': '{:,.2f}'.format(renewal_cents / 100.),
|
||||
'payment_method': payment_method,
|
||||
# TODO: Rename to charge_automatically
|
||||
'billed_by_invoice': billed_by_invoice,
|
||||
'charge_automatically': charge_automatically,
|
||||
'publishable_key': STRIPE_PUBLISHABLE_KEY,
|
||||
# TODO: get actual stripe email?
|
||||
'stripe_email': user.email,
|
||||
'stripe_email': stripe_customer.email,
|
||||
})
|
||||
|
||||
return render(request, 'corporate/billing.html', context=context)
|
||||
|
||||
@require_billing_access
|
||||
|
@@ -116,6 +116,7 @@ class Source:
|
||||
|
||||
class Card:
|
||||
id: str
|
||||
brand: str
|
||||
last4: str
|
||||
object: str
|
||||
|
||||
|
@@ -29,16 +29,15 @@
|
||||
|
||||
<div class="tab-content">
|
||||
<div class="tab-pane active" id="overview">
|
||||
<p>Your current plan is <strong>{{ plan_name }}</strong></p>
|
||||
<p>Your current plan is <strong>{{ plan_name }}</strong>.</p>
|
||||
<p>You are paying for <strong>{{ licenses }} users</strong>.</p>
|
||||
<p>Your plan will renew on <strong>{{ renewal_date }}</strong> for <strong>${{ renewal_amount }}</strong>.</p>
|
||||
{% if account_charges %}
|
||||
<p>You have <strong>${{ account_charges }}</strong> in charges that will be added to your next bill.</p>
|
||||
{% elif account_credits %}
|
||||
<p>You have <strong>${{ account_credits }}</strong> in credits that will be automatically applied to your next bill.</p>
|
||||
{% endif %}
|
||||
<p>
|
||||
Your plan will renew on <strong>{{ renewal_date }}</strong> for
|
||||
<strong>${{ renewal_amount }}</strong>.
|
||||
</p>
|
||||
</div>
|
||||
<div class="tab-pane" id="payment-method" data-email="{{stripe_email}}" data-csrf="{{csrf_token}}" data-key="{{publishable_key}}">
|
||||
<div class="tab-pane" id="payment-method" data-email="{{stripe_email}}"
|
||||
data-csrf="{{csrf_token}}" data-key="{{publishable_key}}">
|
||||
<div id="payment-section">
|
||||
<p>Current payment method: <strong>{{ payment_method }}</strong></p>
|
||||
{% if charge_automatically %}
|
||||
@@ -67,13 +66,15 @@
|
||||
</div>
|
||||
|
||||
<div class="support-link">
|
||||
<p>Contact <a href="mailto:support@zulipchat.com">support@zulipchat.com</a> for billing history or to make changes to your subscription.</p>
|
||||
<p>
|
||||
Contact <a href="mailto:support@zulipchat.com">support@zulipchat.com</a>
|
||||
for billing history or to make changes to your subscription.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{% else %}
|
||||
<p>
|
||||
You must be an organization administrator or a
|
||||
billing administrator to view this page.
|
||||
You must be an organization administrator or a billing administrator to view this page.
|
||||
</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
Reference in New Issue
Block a user