Commit Graph

256 Commits

Author SHA1 Message Date
Lauryn Menard b275e9c4d6 stripe: Add billing portal for customer name and address.
Adds a link on the upgrade and billing pages that opens a stripe
billing portal for the customer to update their name and address
that will appear on invoices and receipts.

On the billing page, updating the credit card information will
no longer update the customer billing address, since they can
now do this directly through the billing portal. To be consistent
with the credit card form on the upgrade page, we still require
inputting a billing address for the card.

Note that, once an invoice is paid/complete, then changes to the
customer's name and address will not be applied to those invoices.
2024-02-13 14:18:38 -08:00
Aman Agrawal 8cba101e05 support: Add button to delete configured fixed next plan.
This will help us modify the configured plan if we need to.
2024-02-13 09:40:53 -08:00
Aman Agrawal 7b33d660eb billing: Rename self-managed to free. 2024-02-12 21:25:19 -08:00
Aman Agrawal 7ca85bed71 stripe: Fix $0 invoices being generated on upgrade.
Instead of charging the customer using the attached payment
method and then creating the invoice, we create an invoice and
force an immediate payment for the invoice via the attached
payment method.
2024-02-12 15:15:56 -08:00
Prakhar Pratyush 2055dfa83e support: Add support to configure fixed-price plan with pay-by-invoice.
* Manually create & send invoice
* Configure a fixed-price plan with sent invoice-id.
* When customer pays, upgrade them to concerned plan.
2024-02-06 18:43:23 -08:00
Lauryn Menard 5479053a9f remote-support: Add server data for last audit log update time.
This is useful in the support view in case the audit log data is
stale and user counts are not updated for billing.

Also, renames formatting function for optional datetimes that is
used in the support and activity views/charts. And instead of
showing these datetime strings in the eastern US timezone, we
now show and label them as UTC.
2024-02-06 18:09:45 -08:00
Aman Agrawal de33aa4b7b stripe: Add page to show list of past customer invoices. 2024-02-04 17:38:24 -08:00
Aman Agrawal 130aecbf9e stripe: Extract method to get org_name. 2024-02-01 09:29:08 -08:00
Lauryn Menard ff8552269d activity: Move links columns to be first in chart.
Moves links in installation and remote server activity charts
to be the first column.
2024-02-01 09:17:26 -08:00
Lauryn Menard afe0161bc2 remote-activity: Add column for rate paid on displayed plan.
Adds a column with the percentage rate that the remote server
or realm is paying on the displayed plan.

We display 0% for community plans that are 100% sponsored.
For legacy plans or plans with a scheduled downgrade, we
display a placeholder, "---". Otherwise, the value is
calculated from the CustomerPlan discount field.
2024-01-31 16:08:15 -08:00
Prakhar Pratyush 82da6abb24 corporate: Restrict free-trial when fixed-price plan is configured.
When fixed-price plan is configured:
* Don't show 'free-trial for 30 days' button on plans page.
* Remove free-trial related texts from /upgrade page.
2024-01-31 12:25:31 -08:00
Lauryn Menard 12ec133224 templates: Move billing templates to /templates/corporate/billing. 2024-01-30 10:06:48 -08:00
Lauryn Menard 916892e437 templates: Move support templates to /templates/corporate/support.
Also, makes small updates to `next_plan_forms_support.html`.
Removes unneeded "btn" and "btn-default" classes, and updates
the placeholder text for the input as not marked for translation.
2024-01-30 10:06:48 -08:00
Lauryn Menard efd0d689fc templates: Move activity templates to /templates/corporate/activity.
Also, renames `ad_hoc_query.html` to `activity_table.html`,
`realm_summary_table.html` to `installation_activity_table.html`,
and `activity_details_template.html` to `activity.html`.

Removes the style attribute in the installation activity template
and uses a CSS class, "installation-activity-header", to center the
h3 and p tags instead. This removes an exception from the custom
lint check.
2024-01-30 10:06:48 -08:00
Lauryn Menard df2f4b6469 corporate: Move support and activity views to /corporate.
View functions in `analytics/views/support.py` are moved to
`corporate/views/support.py`.

Shared activity functions in `analytics/views/activity_common.py`
are moved to `corporate/lib/activity.py`, which was also renamed
from `corporate/lib/analytics.py`.
2024-01-30 10:06:48 -08:00
Anders Kaseorg 93198a19ed requirements: Upgrade Python requirements.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
2024-01-29 10:41:54 -08:00
Mateusz Mandera 4ccddda074 remote_billing: Pass language argument to send_email calls.
This was an oversight in the original implementation.
2024-01-22 18:31:06 -08:00
Mateusz Mandera 649b4885e8 remote_billing: Add rate-limiting for confirmation email sending.
These should be rate-limited by both IP using our regular
sends_email_by_ip bucket as well as by server, using a new bucket
dedicated to this.
2024-01-18 12:06:16 -08:00
Mateusz Mandera 3a12e41c35 remote_billing: Fix handle_customer_migration_from_server_to_realms.
This was a bug from 4715a058b0 where this
was just incorrectly called. get_realms_info_for_push_bouncer() is a
function meant to be called on a self-hosted server - and this
handle_... call happens on the bouncer. Therefore this returns all
zulipchat realms in product.

With the way, handle_... is being called right now, there's no reason
for it to have an argument for passing a list of realms. It should just
fetch the relevant RemoteRealm entries  by itself, given the server arg.
2024-01-12 15:28:41 -08:00
Mateusz Mandera a5538636f0 remote_billing: Remove stale comment about ToS in the server flow.
That comment is stale, now we just do a proper tos_consent check, based
on the last. version the user consented to.
2024-01-12 08:39:48 -08:00
Mateusz Mandera 63254f18ec remote_billing: Handle two confirmation links for same user correctly.
The bug was that a user could do the first part of the flow twice,
receiving two confirmation links, before finishing signup. Then they
could use the first link, followed by the second, which would case an
IntegrityError due to trying to create the RemoteRealmBillingUser
for the second time.

When the second link gets clicked, we should just transparently redirect
the user further into the flow so that they can proceed.
2024-01-12 08:39:48 -08:00
Prakhar Pratyush 4715a058b0 migrate_customers: Migrate customer from server to realms during login.
Earlier, the 'handle_customer_migration_from_server_to_realms'
function was called during the send analytics step.

It resulted in an error for customers having multiple Zulip servers,
one for testing and the others for not-testing, sharing a
push bouncer registration.

The migration step when run in a test instance caused customers to
have their legacy plan migrated to a test realm, resulting in them
losing their legacy plan.

This commit moves the migration step to run during plan management
login step. This reduces the chances of losing legacy
plan as we expect them to only verify that 8.0 upgrade works and
not bother trying to login to plan management from their test instance.
2024-01-05 12:02:54 -08:00
Prakhar Pratyush a03f9078e5 test_stripe: Add migrate customer from server to realms E2E test. 2024-01-02 11:20:56 -08:00
Mateusz Mandera 367d552052 billing: Improve make_end_of_cycle_... interactions with audit logs.
- Make `self.write_to_audit_log` support a `background_update:
  bool=False` parameter that can be passed when code that might have an
  acting user happens to trigger a background update.
- Make `make_end_of_cycle_updates_if_needed` pass that parameter for its
  direct audit log writes.
- Audit code that `make_end_of_cycle_updates_if_needed` calls and make
  sure those write audit logs this way too.
- Pass the user in the `billing_page` code that had to avoid it as a
  workaround:
```
    # BUG: This should pass the acting_user; this is just working
    # around that make_end_of_cycle_updates_if_needed doesn't do audit
    # logging not using the session user properly.
    billing_session = RealmBillingSession(user=None, realm=user.realm)
```
2024-01-02 10:59:05 -08:00
Karl Stolley a37354f92a corporate: Add a Basic plan. 2023-12-20 23:09:21 -08:00
Prakhar Pratyush 63bb63ad80 corporate: Remove '#nocoverage' comments.
This commit removes '#nocoverage' comment as we have added
few E2E tests in 'test_stripe.py'.
2023-12-19 10:59:38 -08:00
Aman Agrawal d962814a30 upgrade: Make it possible to upgrade to a provided tier.
Main work is maintaining context during various redirects and
and passing the context to the final upgrade process.
2023-12-18 12:23:46 -08:00
Aman Agrawal 6b1f71872b plans: Fix same page hashtag link.
Fixes /plans fragment links redirecting users to different page.
2023-12-16 22:51:21 -08:00
Alex Vandiver 940c2a1ded remote_billing_page: Allow deactivation logins with realm plans. 2023-12-15 14:04:35 -08:00
Karl Stolley e44ba3b1f1 portico: Add new self-hosted plans.
Co-authored-by: Alya Abbott <alya@zulip.com>
2023-12-15 11:03:42 -08:00
Tim Abbott 97799b279b billing: Enable billing system in production. 2023-12-15 11:03:42 -08:00
Prakhar Pratyush 1588f49b4f test_stripe: Add end-to-end test for RemoteRealm billing flow. 2023-12-15 09:08:48 -08:00
Alya Abbott ba80084ea7
remote_billing_page: Deny login for server / remote realm.
If server has plan, deny login for realm.
If realm has plan, deny login for server.

Co-authored-by: Aman Agrawal <amanagr@zulip.com>
Co-authored-by: Alya Abbott <alya@zulip.com>
2023-12-14 14:25:12 -08:00
Tim Abbott 1757b88760 billing: Offer release announcement subscriptions.
Also avoid prompting for full name time more than once.
Adds TOS version field to Remote server user.

Co-authored-by: Karl Stolley <karl@zulip.com>
Co-authored-by: Aman Agrawal <amanagr@zulip.com>
2023-12-14 10:51:16 -08:00
Aman Agrawal d795400b21 billing: Allow upgrade scheduled legacy orgs to view billing page.
Don't redirect them to sponsorship page.
2023-12-14 08:22:25 -08:00
Lauryn Menard 8bce83709f corporate: Add billing support email constant.
Adds BILLING_SUPPORT_EMAIL to `stripe.py` with a value of
"sales@zulip.com" so that it can be consistently used in
billing code.
2023-12-14 08:12:01 -08:00
Tim Abbott 6308e07e53 billing: Standardize remote server plan type IDs.
This will likely save us at least one headache.
2023-12-13 16:40:44 -08:00
Aman Agrawal 9efb236c35 remote_billing_page: Fix redirects for self hosted login. 2023-12-13 08:13:33 -08:00
Aman Agrawal 45a2f7eac1 portico: Pass requested sponsorship context to plan page. 2023-12-12 23:27:39 -08:00
Aman Agrawal 63f4fc51de server_deactivate: Show error message for server on active plan. 2023-12-12 21:00:42 -08:00
Aman Agrawal c2636354a5 remote_billing_login: Show different title for deactivate login. 2023-12-12 21:00:42 -08:00
Karl Stolley 180dd00d5f portico: Update strings for RemoteRealm login flow. 2023-12-12 20:32:42 -08:00
Lauryn Menard aebafcc43f corporate: Show error page if mobile push data has not been uploaded.
Adds a template that displays an error message when the billing
or upgrade context for a remote server or remote realm raises a
MissingDataError.
2023-12-12 19:36:06 -08:00
Mateusz Mandera e515574b3e remote_billing: Add endpoint and a helper to make deactivation links.
This is a general link for logging into the billing system on behalf of
a server, but it's tied to the .contact_email and takes the user
straight to the /deactivate/ page via the next_page mechanism.
2023-12-12 13:31:59 -08:00
Aman Agrawal cf68f8ae24 legacy_server_logic: Rename id and key variable names.
Rename server_org_id to zulip_org_id.
Rename server_org_secret/server_org_key to zulip_org_key.
2023-12-12 08:05:45 -08:00
Aman Agrawal ab1a8a0151 legacy_server_login: Do better error handling.
Show better error messages and validate zulip_org_id in JS before
sending it to the server and give an appropriate error message.
2023-12-12 08:05:45 -08:00
Aman Agrawal eb92b31e1d stripe: Disable free trial for self hosted customers. 2023-12-11 13:23:49 -08:00
Aman Agrawal 23d712391e post_analytics: Migrate plan from server to realm after upgrade. 2023-12-11 13:23:49 -08:00
Tim Abbott f1ffb1f56e emails: Extend expiration for login confirmation links. 2023-12-11 10:16:40 -08:00
Alya Abbott eb57b4c4f7 emails: Adjust wording in remote realm login link email. 2023-12-11 10:16:40 -08:00
Alya Abbott 9e90afc504 emails: Adjust wording in legacy server login link email. 2023-12-11 10:16:40 -08:00
Mateusz Mandera c800951966 remote_billing: Add some useful fields to Remote...User models.
These are useful for auditing and follow what we have for UserProfile.
And is_active will be used in the future when we add user deactivation.
2023-12-11 09:39:24 -08:00
Aman Agrawal ac8d5a5f0b remote_billing_page: Show error page for registration mismatch.
When a self-hosted Zulip server does a data export and then import
process into a different hosting environment (i.e. not sharing the
RemoteZulipServer with the original, we'll have various things that
fail where we look up the RemoteRealm by UUID and find it but the
RemoteZulipServer it is associated with is the wrong one.

Right now, we ask user to contact support via an error page but
might develop UI to help user do the migration directly.
2023-12-10 19:33:48 -08:00
Tim Abbott f78db57c6b corporate: Remove temporary billing scaffolding. 2023-12-10 16:46:39 -08:00
Mateusz Mandera 1e6d9d28f8 remote_billing: Fix up templates and HTML names in them.
These pages are generally used by both remoterealm and legacy server
flows, so should have general names.
2023-12-10 16:15:28 -08:00
Mateusz Mandera 423aebf98e remote_billing: Implement confirmation flow for RemoteRealm auth.
The way the flow goes now is this:
1. The user initiaties login via "Billing" in the gear menu.
2. That takes them to `/self-hosted-billing/` (possibly with a
   `next_page` param if we use that for some gear menu options).
3. The server queries the bouncer to give the user a link with a signed
   access token.
4. The user is redirected to that link (on `selfhosting.zulipchat.com`).
Now we have two cases, either the user is logging in for the first time
and already did in the past.
If this is the first time, we have:
5. The user is asked to fill in their email in a form that's shown,
   pre-filled with the value provided inside the signed access token.
   They POST this to the next endpoint.
6. The next endpoint sends a confirmation email to that address and asks
   the user to go check their email.
7. The user clicks the link in their email is taken to the
   from_confirmation endpoint.
8. Their initial RemoteBillingUser is created, a new signed link like in
   (3) is generated and they're transparently taken back to (4),
   where now that they have a RemoteBillingUser, they're handled
   just like a user who already logged in before:
If the user already logged in before, they go straight here:
9. "Confirm login" page - they're shown their information (email and
   full_name), can update
   their full name in the form if they want. They also accept ToS here
   if necessary. They POST this form back to
   the endpoint and finally have a logged in session.
10. They're redirected to billing (or `next_page`) now that they have
    access.
2023-12-10 16:15:28 -08:00
Aman Agrawal ce56e19d1c stripe: Separate activation of free trial for remote realm/server.
Add a separate setting to only enable free trial for remote
realm / server.
2023-12-10 15:18:01 -08:00
Aman Agrawal 34704daee7 stripe: Use a function to get free trial days. 2023-12-10 15:18:01 -08:00
Aman Agrawal e44f3d448d stripe: Use `next_plan` instead of `new_plan` for legacy upgrades. 2023-12-09 12:50:05 -08:00
Mateusz Mandera abdfdeffe4 remote_billing: Implement confirmation flow for legacy servers.
For the last form (with Full Name and ToS consent field), this pretty
shamelessly re-uses and directly renders the
corporate/remote_realm_billing_finalize_login_confirmation.html
template. That's probably good in terms of re-use, but calls for a
clean-up commit that will generalize the name of this template and the
classes/ids in the HTML.
2023-12-08 23:49:10 -08:00
Aman Agrawal 2165486c88 upgrade: Allow showing downgrade success message for legacy server. 2023-12-07 13:55:39 -08:00
Aman Agrawal 825986ac3a billing: Allow legacy servers to cancel upgrade. 2023-12-07 13:55:39 -08:00
Aman Agrawal bbacee7fe9 decorator: Pass patch parameters in request.POST.
We need to manually process the parameters from request.body
since PATCH parameters are present in body and pass it in
`request.POST` to allow PATCH requests via `update_plan_for..` to
work.
2023-12-07 13:55:39 -08:00
Aman Agrawal f381fb1afd billing_page: Check for live plan before redirecting to upgrade page.
Instead of checking for any `CustomerPlan`, we need to check if
the customer has any live plan.
2023-12-06 12:04:10 -08:00
Aman Agrawal b8c0265d3a billing_page: Directly used billing_session method get base url. 2023-12-06 12:04:10 -08:00
Lauryn Menard 906e667a70 corporate: Implement support_url for all BillingSession child classes.
Adds a helper since there are only a few different parameters for
all BillingSession child clases, `build_support_url`.

Also, renames `get_support_url` to more explicitly note that it
is for realms: `get_realm_support_url`.
2023-12-06 11:04:54 -08:00
Aman Agrawal ca3569165a billing_page: Fix redirect URLs for remote_realm billing page. 2023-12-05 23:44:29 -08:00
Aman Agrawal 34730203b3 plans: Show special text for legacy orgs scheduled for upgrade. 2023-12-05 23:44:29 -08:00
Aman Agrawal 8d9a7679bc plans: Show buttons as per current context.
Also show correct tab based on remote / cloud user.
2023-12-05 23:44:29 -08:00
Aman Agrawal 49908ba166 sponsorship: Populate sponsorship page with correct context.
Fixes sponsorship page to work for remote realm and server.
2023-12-05 23:44:29 -08:00
Mateusz Mandera 250b52e3dc remote_billing: Add a "confirm login" page in RemoteRealm auth flow. 2023-12-05 11:34:57 -08:00
Mateusz Mandera 04bb60a05e remote_billing: Increase signed link validity to 2 hours.
This cannot be so short if we're adding an intermittent "check your
details, agree to ToS and confirm login" page. We're also considering
having users potentially share these links.
2023-12-05 11:34:57 -08:00
Mateusz Mandera c23339f295 remote_billing: Rename the _billing_entry and _finalize_login functions.
These names were picked when I still thought these endpoints would serve
both the RemoteRealm and RemoteZulipServer based flows. Now that it's
known these are RemoteRealm-only endpoints, the _server in the names no
longer makes sense.
2023-12-05 11:34:57 -08:00
Aman Agrawal 00f4f8cb04 billing: Use PATCH requests for update plan for remote instances.
This fixes the update requests for billing frequency and other
requests not working on billing page for cloud customers.
2023-12-04 12:36:24 -08:00
Aman Agrawal cd45b6f6f8 upgrade: Allow legacy servers to upgrade to business plan. 2023-12-04 12:36:24 -08:00
Aman Agrawal 8a1630ee42 remote_billing_page: Redirect servers to correct URL after login. 2023-12-03 15:15:04 -08:00
Aman Agrawal a59245e932 billing: Make various buttons on billing page work.
We pass billing_base_url to the template and use it to construct
session specific URLs. Also, add corresponding function on server
to support them.
2023-12-03 15:15:04 -08:00
Aman Agrawal f942bbd70f stripe: Show tier information correctly on billing and upgrade page. 2023-12-03 15:15:04 -08:00
Mateusz Mandera 134e3bfa68 remote_billing: Add redirects to login for unauthed user in legacy flow.
Analogical to the more complex mechanism implemented for the RemoteRealm
flow in a previous commit in
authenticated_remote_realm_management_endpoint.

As explained in the code comment, this is much easier because:

In this flow, we can only redirect to our local "legacy server flow
login" page. That means that we can do it universally whether the user
has an expired
identity_dict, or just lacks any form of authentication info at all -
there are no security concerns since this is just a local redirect.
2023-12-03 10:39:56 -08:00
Mateusz Mandera 44ac99b8fc remote_billing: Redirect via next_page param in legacy server flow.
Analogical to 1df8e00d7c which implemented
this for the RemoteRealm auth flow.
Except here we don't need to add next_page to the IdentityDict
(LegacyServerIdentityDict in this flow), because the redirect happens
immediately at remote_billing_legacy_server_login upon login - so no
need to have a structure to carry the info through intermediate steps.
2023-12-03 10:39:56 -08:00
Mateusz Mandera 3d6863b5b9 remote_billing_legacy_server_login: Only accept credentials via POST.
This is an obvious standard practice.
2023-12-03 10:39:56 -08:00
Mateusz Mandera ec7245d4e1 remote_billing: Add redirect flow for users with expired session.
Implements a nice redirect flow to give a good UX for users attempting
to access a remote billing page with an expired RemoteRealm session e.g.
/realm/some-uuid/sponsorship - perhaps through their browser
history or just their session expired while they were doing things in
this billing system.

The logic has a few pieces:
1. get_remote_realm_from_session, if the user doesn't have a
   identity_dict will raise RemoteBillingAuthenticationError.
2. If the user has an identity_dict, but it's expired, then
   get_identity_dict_from_session inside of get_remote_realm_from_session
   will raise RemoteBillingIdentityExpiredError.
3. The decorator authenticated_remote_realm_management_endpoint
   catches that exception and uses some general logic, described in more
   detail in the comments in the code, to figure out the right URL to
   redirect them to. Something like:
   https://theirserver.example.com/self-hosted-billing/?next_page=...
   where the next_page param is determined based on parsing request.path
   to see what kind of endpoint they're trying to access.
4. The remote_server_billing_entry endpoint is tweaked to also send
   its uri scheme to the bouncer, so that the bouncer can know whether
   to do the redirect on http or https.
2023-12-03 10:39:56 -08:00
Aman Agrawal 5d49e54d33 upgrade: Show tier corresponding to session on upgrade page. 2023-12-01 08:55:58 -08:00
Aman Agrawal 8500eae87e billing_page: Successfully render for remote realms and server. 2023-12-01 08:55:58 -08:00
Aman Agrawal 42c0e2ca3e billing_page: Use URL name redirect instead of importing it. 2023-12-01 08:55:58 -08:00
Aman Agrawal 0888608fcc billing_page: Remove completed TODO.
See 003b29ba14
2023-12-01 08:55:58 -08:00
Aman Agrawal e949fb47ff billing: Rename billing_home to billing_page. 2023-12-01 08:55:58 -08:00
Aman Agrawal c822e953be urls: Rename function names to match naming syntax of remote pages. 2023-12-01 08:55:58 -08:00
Aman Agrawal d0c0b11fbf upgrade: Make purchase upgrade work for remove servers and realms.
We are upgrading them to cloud standard right now, we can easily
change tiers in future while adding pricing and configuration for
them.
2023-12-01 08:55:58 -08:00
Aman Agrawal e9bbb67035 upgrade: Make card add / update work for remote servers. 2023-12-01 08:55:58 -08:00
Aman Agrawal 9889dc38fe event_status: Pass billing_base_url to calculate realm specific URLs. 2023-12-01 08:55:58 -08:00
Aman Agrawal 222077804b upgrade: Simplify getting session data for card update. 2023-12-01 08:55:58 -08:00
Aman Agrawal 25cf0f71a3 event_status: Remove unused variables in context. 2023-12-01 08:55:58 -08:00
Mateusz Mandera 7fad8f1f54 remote_billing: Implement session expiry mechanism.
We still need to add better UX than these JSON errors. We'll want to
utilize the next parameter and redirect the user back to login.
2023-11-30 15:51:10 -08:00
Mateusz Mandera ea9e2ece49 remote_billing: Extract RemoteBillingUserDict sub-dict. 2023-11-30 15:51:10 -08:00
Mateusz Mandera 5a198c639e remote_billing: Sort out remote_billing_identities typing.
This does two important things:
1. Fix return type of get_identity_dict_from_session to correctly be
   Optional[Union[RemoteBillingIdentityDict, LegacyServerIdentityDict]].
   RemoteBillingIdentityDict is the type in the 8.0+ auth flow,
   LegacyServerIdentityDict is the type in old servers flow, where only
   the server uuid info is available.
2. The uuid key used in request.session["remote_billing_identities"]
   should be explicitly namespaced depending on which flow and type
   we're
   dealing with - to avoid confusion in case of collisions between a
   realm and server that have the same UUID. Such a situation should not
   occur naturally and I haven't come up with any actual exploitation
   ideas that could utilize this by manipulating your server/realm
   uuids, but it's much easier to just not think about such collision
   security implications by making them impossible.
2023-11-30 15:51:10 -08:00
Aman Agrawal 8d485726e4 upgrade: Make add card workflow functional.
Add / update card for remote realms on /upgrade page works now.
2023-11-30 11:22:19 -08:00
Aman Agrawal a39cb2bda3 session: Migrate to typed endpoint. 2023-11-30 11:22:19 -08:00
Aman Agrawal d05315b051 event_status: Migrate to typed_endpoint. 2023-11-30 11:22:19 -08:00
Aman Agrawal 05f2ad5299 event_status: Migrate to typed_endpoint. 2023-11-30 11:22:19 -08:00