Skip to content

Commit

Permalink
Added Chargebee adapter. #4
Browse files Browse the repository at this point in the history
  • Loading branch information
jezzsantos committed Nov 25, 2024
1 parent 570b894 commit d688f62
Show file tree
Hide file tree
Showing 65 changed files with 9,937 additions and 21 deletions.
22 changes: 11 additions & 11 deletions docs/design-principles/0180-billing-integration.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ There is much that will change over time with both active subscriptions and the
Instead of building our own system, SaaStack has been designed to be fully two-way integrated with established 3rd party billing providers (e.g., [Chargebee](https://www.chargebee.com), [Maxio](https://www.chargify.com/), or [Stripe Billing](https://www.stripe.com/billing)).

All these providers offer APIs for integration as well as management portals and tools for handling subscriptions, plans, pricing, trials, discounts, and coupons. The API interface (and webhooks) provided by the BMS becomes the user interface of your product that your customers can self-serve with. The management portal the BMS provides becomes an administrative tool your business (product, support & success, etc) can use to manage customers, billing and pricing changes long term.
All these providers offer APIs for integration as well as management portals and tools for handling subscriptions, plans, pricing, trials, discounts, and coupons. The API interface (and webhooks) provided by the BMS becomes the user interface of your product that your customers can self-serve with. The management portal the BMS provides becomes an administrative tool your business (product, support & success, etc) can use to manage customers, billing, and pricing changes long term.

This bidirectional approach introduces a need for seamless synchronization between your SaaS product and third-party services, as changes can occur in both systems independently (due to different actors). Therefore, the product's backend API will be needed (via webhooks) to modify and synchronize subscriptions from the BMS and ensure consistency between the two systems. Eventual consistency is completely tolerable in this scenario.

Expand Down Expand Up @@ -137,11 +137,11 @@ The default set of tiers (`SubscriptionTier`), modeled in SaaStack, has been des
The progression through these tiers represents a variant of a very common "Freemium" model, where:

1. The end-new user starts on the `Standard` tier, which is initially a "free" tier (with or without a Trial period)
2. If Trials are supported by the BMS, the end-user gets to try out `Standard` tier features for a period of time before the trial ends, at which point the subscription will require payment of some kind (a valid `PaymentMethod`). If payment is received (in time), the end-user keeps `Standard` tier access from that point in time (and the Trial ends). If no payment is received (in time), the end-user is automatically downgraded to the `Unsubscribed` tier, which has permanent "free" access to a limited set of basic features.
2. If Trials are supported by the BMS, the end-user gets to try out `Standard` tier features for a period of time before the trial ends, at which point the subscription will require payment of some kind (a valid payment method). If payment is received (in time), the end-user keeps `Standard` tier access from that point in time (and the Trial ends). If no payment is received (in time), the end-user is automatically downgraded to the `Unsubscribed` tier, which has permanent "free" access to a limited set of basic features.
3. At any time during the trial (or outside a trial period), at any tier, the end-user can upgrade to any other tier. They can also cancel their subscription and will be automatically reverted to the `Unsubscribed` tier.
4. Lastly, in some rare cases, if a subscription in the BMS system itself is "deleted/destroyed" (by a business "administrator" of some kind), the subscription will be reverted to the `Unsubscribed` tier again, as a fallback.
4. Lastly, in some rare cases, if a subscription in the BMS system itself is "deleted/destroyed" (by a business "administrator" of some kind), the subscription will be reverted to the `Unsubscribed` tier again as a fallback.

Bottom line, is that this is flexible strategy to get started for most SaaS businesses, that will, no doubt adapt this default workflow moving forward.
The bottom line is that this is a flexible strategy to get started for most SaaS businesses that will, no doubt, adapt this default workflow moving forward.

> You are free to change these default tiers and add or remove your own. The details that drive the restrictions will come from the plan configuration in the BMS and need to be synchronized in the code, too.
Expand Down Expand Up @@ -216,7 +216,7 @@ Through this API, end-users (members of an organization, by default) can perform

The API itself, will interact with the `IBillingProvider` to achieve those things. In that way, it delegates some of those [transactional] commands directly with the BMS. But at the same time, it maintains a cache of relevant metadata (about the subscription and plan from the BMS) in the API, so that the API does not have to contact the BMS for all non-transactional activities.

Lastly, in order to maintain eventual consistency between data changing in the BMS, which can change quite independently in the BMS (from other actors), the `Subscription` subdomain needs to handle webhook events originating from the BMS, or use polling techniques to obtain those changes.
Lastly, in order to maintain eventual consistency between data changing in the BMS, which can change quite independently in the BMS (from other actors), the `Subscription` subdomain needs to handle webhook events originating from the BMS or use polling techniques to obtain those changes.

> The webhooks will be different for different BMSs.
Expand Down Expand Up @@ -267,7 +267,7 @@ In any SaaS product, it is common to restrict access to certain features and fun

Some plans define access to whole feature sets, while others put limits and quotas on the usage of those features.

> Some features of a SaaS product may not be "tenanted" and will require access to be granted to individual users, rather than to specific members of organizations.
> Some features of a SaaS product may not be "tenanted" and will require access to be granted to individual users rather than to specific members of organizations.
Since a plan can be changed at any time during the use of the SaaS product, and since the features of the product cannot be deployed to each user on-demand instantly, access to features is required to be *dynamically* controlled by the software itself, as it is being used by specific end-users.

Expand Down Expand Up @@ -360,11 +360,11 @@ As no credit card would be provided, when a `Subscription` is first created, the

When a new `Subscription` is created (for an `Organization`), it automatically assigns the "creator" of the organization to the "buyer" of the subscription.

As a "buyer" of the subscription, they have full payment authority, and they are responsible for paying any charges for the subscription (e.g. setup fees and/or monthly/annual subscription fees). Charging will happen on a frequency defined by the subscription plan and any other terms of service. But there will not be any `PaymentMethod` at this point in time to charge.
As a "buyer" of the subscription, they have full payment authority, and they are responsible for paying any charges for the subscription (e.g. setup fees and/or monthly/annual subscription fees). Charging will happen on a frequency defined by the subscription plan and any other terms of service. But there will not be any payment method at this point in time to charge.

In order to be charged, the "buyer" will have needed to register a valid `PaymentMethod` for the subscription, that can be used to charge when that time comes.

> By default, the `SimpleBillingProvider` will never require any charges, and therefore never requires a valid `PaymentMethod`
> By default, the `SimpleBillingProvider` will never require any charges and therefore, never requires a valid `PaymentMethod`
Once there are charges, there are generally restrictions on feature access associated with the "tier" of the subscription (e.g., "Basic" versus "Premium").

Expand Down Expand Up @@ -454,7 +454,7 @@ By default, there are a number of constraints and rules placed on `Subscriptions

#### Role Access

These are the roles and rules with respect to billing, and organizations.
These are the roles and rules with respect to billing and organizations.

| End User | Is Creator | Is Current Buyer | Roles |
|-----------------------------------------|------------|------------------------------|----------------------------|
Expand All @@ -463,7 +463,7 @@ These are the roles and rules with respect to billing, and organizations.
| An (Organization) Owner | never | never | `TenantRoles.Owner` |
| An (Organization) Member | never | never | `TenantRoles.Member` |

> Note: the roles `TenantRoles.BillingAdmin` and `TenantRoles.Owner` are hierarchical and supersets of other roles (like `TenantRoles.Member`).
> Note: the roles `TenantRoles.BillingAdmin` and `TenantRoles.Owner` are hierarchical and are supersets of other roles (like `TenantRoles.Member`).
Rules:

Expand Down Expand Up @@ -575,6 +575,6 @@ Either defining limits or quotas for specific kinds of plans.

### Grandfathering

Subscription pricing for SaaS businesses can change frequently, and existing subscriptions are bound by legal agreements. "Grandfathering" allows past purchasers to retain their original terms, or be moved to equivalent new plans in the new pricing model.
Subscription pricing for SaaS businesses can change frequently, and existing subscriptions are bound by legal agreements. "Grandfathering" allows past purchasers to retain their original terms or be moved to equivalent new plans in the new pricing model.

While third-party BMSs (e.g., [Chargebee](https://www.chargebee.com), [Maxio](https://www.chargify.com/), or [Stripe Billing](https://www.stripe.com/billing), etc.) provide support for grandfathering, supporting it fully may require some additional work in each `IBillingProvider`, depending on the extent of it.
16 changes: 8 additions & 8 deletions docs/how-to-guides/900-migrate-billing-provider.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ There are two pieces of this mechanism:
1. An implementation of an `IBillingProvider` specific to the BMS.
2. Webhooks, or custom syncing mechanisms to ensure that changes in the BMS reach this product.

> We highly recommend using Webhooks notifications where possible; otherwise, you really must poll the BMS on a frequent basis, and risk being rate-limited.
> We highly recommend using Webhooks notifications where possible; otherwise, you must poll the BMS frequently and you risk being rate-limited.
## Where to start?

Expand Down Expand Up @@ -41,7 +41,7 @@ Other BMS will have slightly different conceptual models, and you will need to u

You will also need to configure the basic rules and other policies in the BMS first, before you start configuring your customer data.

For example, configure API Keys, Webhooks etc.
For example, configure API Keys, Webhooks, etc.

The last thing will be to explore whether the BMS supports a "sandbox" environment for you to play around with and test your migration. You don't want to be adding test data to your production customer data.

Expand All @@ -55,7 +55,7 @@ Use the API endpoint `GET /subscriptions/export` to view the data available for
This data represents all the subscriptions created in the product so far.

This is the data you will need to import into your chosen BMS, during the migration.
This is the data you will need to import into your chosen BMS during the migration.

> Note: some of the values are simply encoded JSON values
Expand Down Expand Up @@ -91,15 +91,15 @@ This is the data you will need to import into your chosen BMS, during the migrat
}
```

### Build Your Scripts
### Build Your Migration Scripts

You will likely need to build some scripts that translate the raw data above and automate the creation of various related data structures in the new BMS.

For example, in Chargebee:

1. You would create a Chargebee Customer record using the data in the `buyer` property. You would save the `buyer.id` in the metadata of the Chargebee Customer record.
2. You would create a Chargebee Subscription for the Chargebee Customer. You would also save the `id` and `owningEntityId` as metadata in the Chargebee Subscription.
3. You would define some Chargebee Plans, and assign one of those plans to the Chargebee subscription.
3. You would define some Chargebee Plans and assign one of those plans to the Chargebee Subscription.

Next, during the migration, once you have automated the creation of the BMS records, you will also need a collection of metadata of those BMS records back into the data of the `IBillingProvider` for when it is being used.

Expand Down Expand Up @@ -131,7 +131,7 @@ In the product, by default, we have defined the following tiers (see: `Subscript
* Professional
* Enterprise

You are free to rename, add, or remove these tiers (in the code) to whatever you would like to support in your future pricing plans in your new BMS. Essentially, we have 3 paid tiers, where `Standard` may have a trial, and is generally the default plan for new users.
You are free to rename, add, or remove these tiers (in the code) to whatever you would like to support in your future pricing plans in your new BMS. Essentially, we have 3 paid tiers, where `Standard` may have a trial and is generally the default plan for new users.

> Remember, if you modify these tiers, you will also need to modify the mapping between these tiers and the feature levels you will be supporting in your pricing plans. see the `EndUserRoot` for details.
Expand All @@ -146,7 +146,7 @@ In your BMS, we recommend defining at least the following plans:
You will need to define all the parameters for each of these new plans, including pricing, limits, frequency of billing, etc.

### Configure the BillingProvider
### Configure the Billing Provider

Your newly chosen BMS will require a built and tested implementation of the `IBillingProvider` to work with it.

Expand All @@ -160,7 +160,7 @@ To swap out the existing `IBillingProvider` (e.g. `SimpleBillingProvider`) with
You will also need to make sure that you provide all the necessary configuration settings for your new `IBillingProvider` in the relevant `appsettings.json` files for the host project where the `Subscriptions` subdomain is deployed.

You might also consider updating your web/mobile apps to support the self-serve of capturing credit cards (payment methods), and support self-serve for changing plans. However, the built-in pricing page in the `WebsiteHost` should be already updated with your new plans.
You might also consider updating your web/mobile apps to support the self-serve of capturing credit cards (payment methods), and support self-serve for changing plans. However, the built-in pricing page in the `WebsiteHost` should already be updated with your new plans.

> None of the BMS-specific UX is built in when using the `SimpleBillingProvider` as this provider neither allows you to select from a list of plans nor captures payment methods.
Expand Down
16 changes: 16 additions & 0 deletions src/ApiHost1/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,22 @@
"AesSecret": "V7z5SZnhHRa7z68adsvazQjeIbSiWWcR+4KuAUikhe0=::u4ErEVotb170bM8qKWyT8A=="
}
},
"Chargebee": {
"BaseUrl": "https://localhost:5656/chargebee/",
"ApiKey": "anapikey",
"SiteName": "asitename",
"ProductFamilyId": "afamilyid",
"Plans": {
"StartingPlanId": "apaidtrial",
"Tier1PlanIds": "apaidtrial",
"Tier2PlanIds": "apaid2",
"Tier3PlanIds": "apaid3"
},
"Webhook": {
"Username": "ausername",
"Password": "apassword"
}
},
"Flagsmith": {
"BaseUrl": "https://localhost:5656/flagsmith/",
"EnvironmentKey": ""
Expand Down
9 changes: 9 additions & 0 deletions src/Application.Interfaces/Audits.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions src/Application.Interfaces/Audits.resx
Original file line number Diff line number Diff line change
Expand Up @@ -114,4 +114,7 @@
<data name="AuthTokensApplication_Refresh_AccountSuspended" xml:space="preserve">
<value>Authentication.Any.Refreshed.Failed.AccountSuspended</value>
</data>
<data name="ChargebeeApi_WebhookAuthentication_Failed" xml:space="preserve">
<value>Chargebee.Authentication.Failed</value>
</data>
</root>
Loading

0 comments on commit d688f62

Please sign in to comment.