Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add draft monetization docs #335

Merged
merged 43 commits into from
Feb 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
c9a7994
add draft monetization docs
Feb 20, 2024
350682c
update image
Feb 20, 2024
934bbc1
last touch
Feb 20, 2024
06a5794
Merge branch 'main' into monetization-docs
ntotten Feb 20, 2024
cf5dd32
new step 1
ntotten Feb 21, 2024
9140c7c
fixed sitebar
ntotten Feb 21, 2024
0ba7651
fixed image uploader
ntotten Feb 21, 2024
0c3ae96
updated image urls
ntotten Feb 21, 2024
3452057
fix typo and icon
lcampos Feb 21, 2024
d73837c
step 2
ntotten Feb 21, 2024
dad6e8a
fixed sidebar
ntotten Feb 21, 2024
9749c64
Updates to intro and step2
lcampos Feb 21, 2024
6445b9e
section 3
ntotten Feb 21, 2024
5bb79d3
Fix small typos
vazexqi Feb 21, 2024
37869ab
minor fixes
joshtwist Feb 21, 2024
6a0a83b
Merge branch 'monetization-docs' of https://github.com/zuplo/docs int…
joshtwist Feb 21, 2024
82f96aa
edit stripe note
lcampos Feb 21, 2024
ac7ab10
updated intro image
joshtwist Feb 21, 2024
8b0cd6b
updated url
ntotten Feb 21, 2024
9105659
Merge branch 'monetization-docs' of https://github.com/zuplo/docs int…
joshtwist Feb 21, 2024
c1b748f
added missing image
joshtwist Feb 21, 2024
a8d6980
added new image
joshtwist Feb 21, 2024
5760a84
images
joshtwist Feb 21, 2024
34bcf3b
small changes
Feb 21, 2024
8ff95df
More intro edits
lcampos Feb 21, 2024
e6e4201
Minor tweaks to dev-portal-setup
vazexqi Feb 21, 2024
14d35f3
more intro tweaks
lcampos Feb 21, 2024
fdf0ff6
tweak intro v3
lcampos Feb 21, 2024
faeed1f
Update step 1 title
lcampos Feb 21, 2024
aae6205
edit step 1
lcampos Feb 21, 2024
b5d7b85
edit section 1 step 1
lcampos Feb 21, 2024
1bca9a1
More tweaks to step 1
lcampos Feb 21, 2024
8cb56c2
edit intro & zuplo setup in step 1
lcampos Feb 21, 2024
97d9f6e
step 1 fixes
lcampos Feb 21, 2024
65c8668
Change steps to begin with verbs
vazexqi Feb 21, 2024
41aba89
tweaks to step 1 title and intro
lcampos Feb 21, 2024
cd35e96
updated sidebar
ntotten Feb 21, 2024
54eb708
first edits step 2
lcampos Feb 21, 2024
b855383
more edits step 2
lcampos Feb 21, 2024
44ca439
fix build
lcampos Feb 21, 2024
aacc02f
Added programatic docs
ntotten Feb 21, 2024
942c267
More tweaks to step 2
lcampos Feb 21, 2024
2d83e5c
step 3 initial changes
lcampos Feb 21, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions .github/ISSUE_TEMPLATE/policies-doc.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
---
name: Policies Doc
about: File a bug or suggestion for a policy
title: ''
labels: ''
assignees: ''

title: ""
labels: ""
assignees: ""
---

<!-- Describe the issue or suggestion for the policy -->
3 changes: 0 additions & 3 deletions .github/workflows/static.yaml
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
name: Static Assets
on:
push:
branches:
- main
- nextjs_v2

jobs:
policies:
Expand Down
2 changes: 1 addition & 1 deletion cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
// language - current active spelling language
"language": "en",
// words - list of words to be always considered correct
"words": ["Kubernetes", "Linkerd", "Quickstart", "quickstarts"],
"words": ["Kubernetes", "Linkerd", "Quickstart", "quickstarts", "Zuplo"],
// flagWords - list of words to be always considered incorrect
// This is useful for offensive words and common spelling errors.
// For example "hte" should be "the"
Expand Down
2 changes: 1 addition & 1 deletion docs/articles/background-dispatcher.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ The `options.msDelay` parameter is required and must be a valid non-zero number.
```ts
const backgroundDispatcher = new BackgroundDispatcher<TestEntry>(
dispatchFunction,
{ msDelay: 100 }
{ msDelay: 100 },
);
```

Expand Down
6 changes: 3 additions & 3 deletions docs/articles/hooks.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import { ZuploContext, ZuploRequest } from "@zuplo/runtime";
export async function tracingPlugin(
request: ZuploRequest,
context: ZuploContext,
policyName: string
policyName: string,
) {
// Get the trace header
let traceparent = request.headers.get("traceparent");
Expand Down Expand Up @@ -71,7 +71,7 @@ import { ZuploContext, ZuploRequest } from "@zuplo/runtime";
export async function pluginWithHook(
request: ZuploRequest,
context: ZuploContext,
policyName: string
policyName: string,
) {
const cloned = request.clone();
context.addResponseSendingFinalHook(
Expand All @@ -81,7 +81,7 @@ export async function pluginWithHook(
method: "GET",
body,
});
}
},
);

return request;
Expand Down
120 changes: 120 additions & 0 deletions docs/articles/monetization-dev-portal-setup.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
---
title: Step 1 - Set Up Developer Portal
sidebar_label: Step 1 - Set Up Developer Portal
---

This guide will walk you through the steps required to set up your Zuplo API's
Developer Portal. This is where your customers will see and subscribe to your
API plans.

If you don't already have a Zuplo API, you can create a new project using the
"ToDo" sample or following step 1 of the
[Getting Started quickstart](./step-1-setup-basic-gateway.md).

You'll also need a Stripe account. If you don't already have one, you'll need to
[register](https://dashboard.stripe.com/register) for one.

::: tip

You don't need to go through the full set up process in Stripe. You can follow
this tutorial using Stripe's test mode. We recommend using
[Test Mode](https://docs.stripe.com/test-mode) on your Stripe account. This will
allow you to test creating subscriptions with different plans without using real
money.

:::

Once you have a working Zuplo API and a Stripe account, we can move forward!

## 1/ Set Up in Stripe

You need to have a couple things set up in Stripe before enabling Zuplo
monetization.

Your API customers will subscribe to a "Plan" in Zuplo. Plans are associated
with [Stripe Products](https://docs.stripe.com/products-prices/getting-started).
The Stripe Product is what determines the price your customers will pay to use
your API.

![](https://cdn.zuplo.com/assets/aa34975a-2906-4869-9dd6-bd13e5b0dcda.png)

The "Plan" in Zuplo is where you will define the limits of each plan. For
example, the "Basic" plan might be limited to 10,000 requests per month while
the "Premium" plan may be limited to 1,000,000 requests per month.

### Creating Stripe Products

When you create your products in Stripe, set each product as **Recurring** on a
**Monthly** billing cycle.

![](https://cdn.zuplo.com/assets/0b7bc4e5-9e92-4b24-a4d5-16fe389bec8f.png)

### Creating a Stripe Pricing Table

Once you've created your products, you will need to create a Stripe Pricing
table. Follow
[Stripe's guide for creating a Pricing Table](https://docs.stripe.com/payments/checkout/pricing-table).

## 2/ Set Up in Zuplo Portal

In order for your Zuplo API to connect to Stripe, you'll need to set your Stripe
API Key as an environment variable.

1. In Stripe, navigate to the
[API keys section](https://dashboard.stripe.com/apikeys) of Stripe's
developer dashboard. Copy the value of the **Secret Key**.
2. Return to the Zuplo Portal, go to your project and open the **Environment
Variables** section in the **Settings** tab.
3. Click **Add Variable** and name your new environment variable
`STRIPE_SECRET_KEY`.
4. Set this variable as a **secret** and paste the Stripe API Key as the value.
Click **Save**.

![Save Environment Variable](../../public/media/monetization-dev-portal-setup/image-1a.png)

### Configure Your Developer Portal

Next, you will enable the monetization pages in your Developer Portal.

1. In your Zuplo project, go to the **Code** tab and select the file
`dev-portal.json`.
2. Scroll to the **Monetization Settings** section and select the checkbox
**Enable Monetization**.
3. The values for the fields **Pricing Table ID** and **Publishable Key** can be
found by opening the pricing table in the Stripe Dashboard
[Pricing Table](https://dashboard.stripe.com/pricing-tables) section.
4. Copy the value for `pricing-table-id` (it starts with `prctbl_`) and set it
as the value for **Pricing Table ID** in the form in the Zuplo Portal.
5. Copy the value for `publishable-key` (it starts with `pk_`) and set it as the
value for **Publishable Key** in the form in the Zuplo Portal.

![Pricing Table](../../public/media/monetization-dev-portal-setup/image-2a.png)

6. Finally, click the <EnvironmentVariablePicker/> icon next to the **Secret
Key** field and select the environment variable `STRIPE_SECRET_KEY` you
created earlier. This will prefill the form with the value
`$env(STRIPE_SECRET_KEY)`.
7. Click save to publish your changes.

## 3/ Preview Your Developer Portal

Now that your Developer Portal is configured for monetization you can open it
and view the pricing page. Click the toolbar on the bottom of the Zuplo Portal
to find the URL of your Developer Portal.

![Dev Portal Link](../../public/media/monetization-dev-portal-setup/image-3a.png)

Once you open your Developer Portal, you will find a link to the **Pricing**
page on the top right menu section. Open the **Pricing** page in your Developer
Portal to see the pricing table that you have configured as the **Pricing Table
ID** in the previous step.

![Pricing Table](../../public/media/monetization-dev-portal-setup/image.png)

:::caution

Don't go through the process of subscribing to an API plan yet. In the next step
we will configure the Stripe Webhook that will tell your API that a subscription
was created.

:::
4 changes: 4 additions & 0 deletions docs/articles/monetization-glossary.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
title: Monetization Glossary
sidebar_label: Glossary
---
103 changes: 103 additions & 0 deletions docs/articles/monetization-policy-setup.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
---
title: Step 3 - Configure Monetization Policy
sidebar_label: Step 3 - Configure Monetization Policy
---

In the previous steps you learned how to set up Stripe and configure your
Developer Portal for monetization. In this step, you will configure your Zuplo
API to ensure that users are allowed to use your API according to the plan they
subscribe to.

We will configure this using Zuplo's
[API Key Authentication Policy](/docs/policies/api-key-inbound) and the
[Monetization Inbound Policy](/docs/policies/monetization-inbound).

The Monetization Inbound Policy is what ensures that customers are only able to
call your API withing the quotas set in their Plan.

## 1/ Add API Key Authentication

In order to identify customers calling your API, you'll add the
[API Key Authentication Policy](/docs/policies/api-key-inbound). This policy
validates the API Key sent to your API and identifies the user in order to
associate them with the plan which they have subscribed.

:::tip

For a full tutorial on the API Key Auth Policy see the
[Getting Started guide](/docs/articles/step-2-add-api-key-auth).

:::

1. Open the **Code** section of the Zuplo Portal and select your
`routes.oas.json` file.

2. Open the route you want to add monetization to and click **Policies**, then
click **+ Add Policy**.

3. Type `API Key` in the search and select the **API Key Authentication**
policy.

4. Leave the default configuration and click **OK**.

Now, all requests to this route will require an API Key to successfully call the
endpoint.

## 2/ Add the Monetization Policy

With the user identified via the API Key Authentication policy, next you will
enforce that they are calling the API within the limits of their plan.

1. Open the **Policies** section of the same route and click **Add Policy**.

2. Type `Monetization` in the search and select **Monetization**.

The configuration of this policy is where you specify the meters that are
required in order to call this API. For now, leave the configuration as is, you
can always change it later.

3. Be sure to save your changes.

![Policy pipeline](../../public/media/monetization-policy-setup/image.png)

## 3/ Test the API

With both policies added to the route, you can now call the API with your
subscription's API Key.

1. Return to your Developer Portal and open the **API Reference** section.

2. Find the route you added the policies to in the previous steps.

3. Above the code sample, next to the route, click the **Test** button. This
will open up the API Playground that you can use to call the API.

:::note

The API Key for your subscription will be pre-populated in the `Authorization`
header.

:::

4. Edit the API Request if needed and click **Test**. You should see a
successful response. Click **Test** a few more times.

![Successful response](../../public/media/monetization-policy-setup/image-1.png)

5. Close the API Playground and click the **Subscription** link in the Dev
Portal header. Notice the Analytics section now shows that you have consumed
some of your request quota from your plan.

6. Return to the API Playground and click **Test** until you receive an error
response telling you that you have exhausted your quota.

:::note

If you set a large number in the **Max Value** of your request quota in the
earlier steps, this is going to take a while.

:::

![Max quota status](../../public/media/monetization-policy-setup/image-2.png)

Congratulations, you are now ready to monetize your API!
91 changes: 91 additions & 0 deletions docs/articles/monetization-programmatic-quotas.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
---
title: Monetization Programmatic Quotas
sidebar_label: Programmatic Quotas
---

Typically, when adding monetization to your API, you set the number of meters a
request will consume in the settings of the
[Monetization Policy](https://zuplo.com/docs/policies/monetization-inbound). For
example, the policy below specifies that the request will consume 1 `requests`
meter and 5 `computeUnits` meters.

```json
{
"export": "MonetizationInboundPolicy",
"module": "$import(@zuplo/runtime)",
"options": {
"allowRequestsOverQuota": false,
"allowedSubscriptionStatuses": ["active", "incomplete"],
"meterOnStatusCodes": "200-399",
"meters": {
"requests": 1,
"computeUnits": 5
}
}
}
```

However, in some cases, you may not know up front how many units of a particular
meter will be consumed until after the response is sent. For example, maybe your
backend is responsible for computing the `computeUnits` on a request and send
the result in the response in the `compute-units` header.

In Zuplo, you can support these dynamic meters by writing a little code. To make
the `computeUnits` meter dynamic, first update the policy by setting the
`computeUnits` meter to `0` as shown below.

```json
{
"export": "MonetizationInboundPolicy",
"module": "$import(@zuplo/runtime)",
"options": {
"allowRequestsOverQuota": false,
"allowedSubscriptionStatuses": ["active", "incomplete"],
"meterOnStatusCodes": "200-399",
"meters": {
"requests": 1,
"computeUnits": 0
}
}
}
```

Next you can create a
[custom code outbound policy](/docs/policies/custom-code-outbound) that reads
data from the Response (in this case the `compute-units` header) and sets the
meter programmatically.

```ts title="/modules/set-compute-units-outbound.ts"
import {
MonetizationInboundPolicy,
ZuploRequest,
ZuploContext,
} from "@zuplo/runtime";

export default async function (
response: Response,
request: ZuploRequest,
context: ZuploContext,
options: any,
policyName: string,
) {
const headerValue = response.headers.get("compute-units");
let computeUnits;
if (headerValue && typeof headerValue === "string") {
computeUnits = parseInt(headerValue);
}

// Throw an error if the server doesn't send compute units
// Alternatively, you could have a default value
if (!computeUnits) {
throw new Error("Invalid response, no compute units sent.");
}

// Set the compute units for the request
MonetizationInboundPolicy.setMeters(context, {
computeUnits,
});

return response;
}
```
Loading