Skip to content

Commit

Permalink
add draft monetization docs (#335)
Browse files Browse the repository at this point in the history
* add draft monetization docs

* update image

* last touch

* new step 1

* fixed sitebar

* fixed image uploader

* updated image urls

* fix typo and icon

* step 2

* fixed sidebar

* Updates to intro and step2

* section 3

* Fix small typos

* minor fixes

* edit stripe note

* updated intro image

* updated url

* added missing image

* added new image

* images

* small changes

* More intro edits

* Minor tweaks to dev-portal-setup

* more intro tweaks

* tweak intro v3

* Update step 1 title

* edit step 1

* edit section 1 step 1

* More tweaks to step 1

* edit intro & zuplo setup in step 1

* step 1 fixes

* Change steps to begin with verbs

* tweaks to step 1 title and intro

* updated sidebar

* first edits step 2

* more edits step 2

* fix build

* Added programatic docs

* More tweaks to step 2

* step 3 initial changes

---------

Co-authored-by: Nathan Totten <nathan@ntotten.com>
Co-authored-by: Luis Campos Guajardo <luis@zuplo.com>
Co-authored-by: Nick Chen <nick@zuplo.com>
Co-authored-by: Josh Twist <josh@molmorg.com>
  • Loading branch information
5 people committed Feb 21, 2024
1 parent 2245356 commit 53a76c8
Show file tree
Hide file tree
Showing 49 changed files with 780 additions and 179 deletions.
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

0 comments on commit 53a76c8

Please sign in to comment.