docs/subsystems/billing.md
This section deals with developing and testing the billing system.
STRIPE_API_VERSION defined in
corporate/lib/stripe.py. You can upgrade to a higher version from
the Stripe dashboard.stripe_secret_key to zproject/dev-secrets.conf.Manual testing involves testing the various flows like upgrade, card change, etc. through the browser. This is the bare minimum testing that you need to do when you review a billing PR or when you are working on adding a new feature to billing.
Apart from the common setup mentioned above, you also need to set up your development environment to receive webhook events from Stripe.
stripe login.stripe listen --forward-to http://localhost:9991/stripe/webhook/stripe listen command in the previous step to output the
webhook signing secret.
stripe_webhook_endpoint_secret
in zproject/dev-secrets.conf.tools/run-dev stopped, you can run ./manage.py populate_billing_realms to populate different billing states, both
Cloud and self-hosted, with various initial plans and billing schedules.populate_billing_realms to add more states if they
seem useful in your testing. After running the command, you will see a list of
populated organizations.Realms can be accessed as follows:
localhost:9991/devlogin.Realms dropdown you wist to test./billing.RemoteZulipServer customers can be accessed by going to
http://selfhosting.zulipdev.com:9991/serverlogin/ and providing the
credentials in the login form for the server state you wish to
test. The credentials are printed in the terminal by ./manage.py populate_billing_realms.RemoteRealm customers can be accessed simply by following
their links printed in the terminal in the ./manage.py populate_billing_realms output.Stripe provides various card numbers to test for specific responses from Stripe. The most commonly used ones are mentioned in below wherever appropriate. The full list is available here.
There are various flows that you can test manually from the browser. Here are a few things to keep in mind while conducting these tests manually:
./manage.py invoice_plans --date 2024-04-30T08:12:53 -- this will run invoicing, including
end-of-cycle updates, as though the current date is as specified.Here are some flows to test when upgrading a Zulip Cloud organization:
When free trials are not enabled, i.e. CLOUD_FREE_TRIAL_DAYS is not set
to any value in dev_settings.py (aka the default). You can
double-check that the setting is disabled by verifying
./scripts/get-django-setting CLOUD_FREE_TRIAL_DAYS returns 0.
4242 4242 4242 4242, the
official Visa example credit card number.4000000000000341, which will add the card
to the customer account but the charge will fail.
Upgrade an organization when free trials are enabled. The free
trials setting has been (possibly permanently) disabled in
production for some time now, so testing this code path is not a
priority. You can set CLOUD_FREE_TRIAL_DAYS to any number greater than
0 in dev_settings.py to enable free trials. There are two
different flows to test here:
/billing page and upgrading the organization.Here are some flows to test when upgrading a remote Zulip organization:
Free trial for remote organizations is enabled by default by setting
SELF_HOSTING_FREE_TRIAL_DAYS to 30 days. You can change this
value and other settings for your development environment only in
zproject/custom_dev_settings.py, or secrets in
zproject/dev-secrets.conf. Note that this only provides free trail
for the basic plan.
4242 4242 4242 4242, the
official Visa example credit card number.4000000000000341, which will add the card
to the customer account but the charge will fail.
Try upgrading to Zulip Business using Pay by card as described above or
Pay by Invoice.
The following flow should be tested when updating cards in our billing system:
/billing page of an organization that has already been upgraded
using a card. Try changing the card to another valid card such as
5555555555554444.
Stripe makes pretty regular updates to their API. The process for upgrading our code is:
tools/test-backend --generate-stripe-fixtures --parallel=1 corporate/.git diff to understand
the changes. Ensure that there are no material changes.STRIPE_API_VERSION in corporate/lib/stripe.py.We currently aren't set up to do version upgrades where there are breaking changes, though breaking changes should be unlikely given the parts of the product we use. The main remaining work for handling breaking version upgrades is ensuring that we set the stripe version in our API calls. Stripe's documentation for Upgrading your API version has some additional information.
Writing new tests is fairly easy. Most of the tests are placed in
test_stripe. If you need do API calls to stripe, wrap the test
function in @mock_stripe and run tools/test-backend TEST_NAME --generate-stripe-fixtures. It will run all your calls and generate
fixtures for any API calls to stripe, so that they can be used to
consistently run that test offline. You can then commit the new test
fixtures along with your code changes.
Regenerating the fixtures for all of our existing billing tests is expensive, in that it creates extremely large diffs from editing dates/IDs that grow the zulip/zulip Git repository and make PRs harder to read, both visually and by making the GitHub UI very slow.
So you should generally aim to only (re)generate fixtures where it's necessary, such as when we change how we're calling some Stripe APIs or adding new tests.
So you'll usually want to pass --generate-stripe-fixtures only when
running the tests for a specific set of tests whose behavior you know
that you changed. Once you've committed those changes, you can verify
that everything would pass if new fixtures were generated as follows:
tools/test-backend corporate/ --generate-stripe-fixtures.git reset --hard to drop the
unnecessary fixture updates.--generate-stripe-fixtures as you debug them.