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

Feat: Stripe subscription tutorial #60

Merged
merged 13 commits into from
Jan 3, 2024
1 change: 1 addition & 0 deletions src/routes/docs/tutorials/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
android: 'icon-android',
apple: 'icon-apple',
flutter: 'icon-flutter',
stripe: 'icon-stripe',
refine: 'aw-icon-refine'
};

Expand Down
2 changes: 1 addition & 1 deletion src/routes/docs/tutorials/+page.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { base } from '$app/paths';
import type { Tutorial } from '$markdoc/layouts/Tutorial.svelte';

const framework_order = ['React', 'Vue', 'SvelteKit', 'Refine'];
const framework_order = ['React', 'Vue', 'SvelteKit', 'Stripe', 'Refine'];

export async function load() {
const tutorialsGlob = import.meta.glob('./**/step-1/+page.markdoc', {
Expand Down
10 changes: 10 additions & 0 deletions src/routes/docs/tutorials/subscriptions-with-stripe/+layout.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<script lang="ts">
import { globToTutorial } from '$lib/utils/tutorials.js';
import { setContext } from 'svelte';

export let data;
const tutorials = globToTutorial(data);
setContext('tutorials', tutorials);
</script>

<slot />
11 changes: 11 additions & 0 deletions src/routes/docs/tutorials/subscriptions-with-stripe/+layout.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import type { LayoutLoad } from './$types';

export const load: LayoutLoad = ({ url }) => {
const tutorials = import.meta.glob('./**/*.markdoc', {
eager: true
});
return {
tutorials,
pathname: url.pathname
};
};
6 changes: 6 additions & 0 deletions src/routes/docs/tutorials/subscriptions-with-stripe/+page.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { redirect } from '@sveltejs/kit';
import type { PageLoad } from './$types';

export const load: PageLoad = async () => {
throw redirect(303, '/docs/tutorials/subscriptions-with-stripe/step-1');
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
---
layout: tutorial
title: Add app subscriptions with Stripe
description: Add paid app subscription plans to your app with Stripe and Appwrite Functions.
step: 1
difficulty: easy
readtime: 10
framework: Stripe
---
As you app grows, you may start offering paid services or features.
This is an important part of growing your idea into a business.
This tutorial will show you how to accept payments and provide subscribers with premium features using **Stripe**, a popular payment platform.

{% only_dark %}
![Stripe subscription demo](/images/docs/tutorials/subscriptions-with-stripe/dark/stripe.png)
{% /only_dark %}
{% only_light %}
![Stripe subscription demo](/images/docs/tutorials/subscriptions-with-stripe/stripe.png)
{% /only_light %}

# Prerequisites {% #prerequisites %}
1. A [GitHub account](https://github.com/) and working knowledge with GitHub
1. A [Stripe account](https://stripe.com/en-ca).
1. An [Appwrite Cloud account](https://cloud.appwrite.io/).
1. Experience with [Appwrite Functions](/docs/products/functions).


Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
---
layout: tutorial
title: Setup Stripe
description: Add paid app subscription plans to your app with Stripe and Appwrite Functions.
step: 2
---

Start by visiting [Stripe](https://stripe.com/) and creating an account. When successful, you will see Stripe Dashboard.

This quick start will use test mode, but the same steps will also work for live mode.

{% only_dark %}
![Stripe dashboard](/images/docs/tutorials/subscriptions-with-stripe/dark/stripe-dashboard.png)
{% /only_dark %}
{% only_light %}
![Stripe dashboard](/images/docs/tutorials/subscriptions-with-stripe/stripe-dashboard.png)
{% /only_light %}

# API key {% #api-key %}

In the header, you can switch to the **Developers** page, where you can head to the **API Keys** section. On this page, reveal and copy the **Secret key**. Note it down, as you will need it later when setting the `STRIPE_SECRET_KEY` environment variable.

{% only_dark %}
![Stripe API key screen](/images/docs/tutorials/subscriptions-with-stripe/dark/stripe-api-key.png)
{% /only_dark %}
{% only_light %}
![Stripe API key screen](/images/docs/tutorials/subscriptions-with-stripe/stripe-api-key.png)
{% /only_light %}

# Webhooks {% #webhooks %}

Go to the **Webhooks** section and click the button to **Add an endpoint**.

Let's add a fake endpoint for now in order to get a webhook secret. Later, when you set up the Appwrite Function, you will update this endpoint with the Function's endpoint.

Enter `https://temporary-endpoint/` as **Endpoint URL** and feel free to write some **Description** for yourself.
Leave everything else as is, and continue down to **Select events**.
You need to toggle events `customer.subscription.created` and `customer.subscription.deleted`.
You can use the search bar to your advantage. By searching for `subscription`, you should easily find those two events.

Once both are selected, you can click a button to **Add events**. Finish off the form by clicking the **Add endpoint** button.


{% only_dark %}
![Stripe webhook setup screen](/images/docs/tutorials/subscriptions-with-stripe/dark/stripe-webhook-subscription.png)
{% /only_dark %}
{% only_light %}
![Stripe webhook setup screen](/images/docs/tutorials/subscriptions-with-stripe/stripe-webhook-subscription.png)
{% /only_light %}

Once created, click **Reveal** under **Signing secret** and copy the secret. Note it down, as you will need it later when setting the `STRIPE_WEBHOOK_SECRET` environment variable.

Keep this page open, as you will need it soon to update the endpoint with the real Function's endpoint.
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
---
layout: tutorial
title: Create function
description: Add paid app subscription plans to your app with Stripe and Appwrite Functions.
step: 3
---

Head to the [Appwrite Console](https://cloud.appwrite.io/console) and create a new project if you haven't already. If this is your first time using Appwrite, you will be asked to sign up first.

## Create a new function {% #create-function %}
Once inside your project overview, switch to the **Functions** page from the left sidebar. Under the **Templates** section, use the search bar and look for `subscriptions`. You will find the **Subscriptions with Stripe** template, which you can use by clicking the **Create function** button.


{% only_dark %}
![Project settings screen](/images/docs/tutorials/subscriptions-with-stripe/dark/templates-stripe-subscription.png)
{% /only_dark %}
{% only_light %}
![Project settings screen](/images/docs/tutorials/subscriptions-with-stripe/templates-stripe-subscription.png)
{% /only_light %}

# Create a new function {% #create-function %}

You will be asked to configure Function's **Name**, **Runtime**, and **Function ID**. Feel free to customise those or leave them as is. Click the **Next** button to continue to the environment variables definition.

# Configure variables {% #configure-variables %}

Toggle **Generate API key on completion** in the `APPWRITE_API_KEY` variable, so you don't need to go and generate it manually.

Fill the `STRIPE_SECRET_KEY` variable with the key you copied from Stripe's Dashboard under the **API keys** section.

Similarly, fill the `STRIPE_WEBHOOK_SECRET` variable with the secret copied after creating **Webhook**.

If you are self-hosting Appwrite, you will need to change `APPWRITE_ENDPOINT` as well, under the **Optional variables** section.

When done, click the **Next** button.

# Connect repository {% #connect-repository %}

In this section, you can decide whether to host your Function's source code in an existing repository or create a new one.

Larger projects will benefit from having all functions in one repository, but to keep this quick start simple, let's stick to **Create a new repository**.

Click the **Next** button to continue.

You will be asked to connect your project to Git provider. Click the one you want to use, and follow the authorization process. Once done, you will be redirected back to the Appwrite Console. Here, you can configure **Repository name** and toggle if you want to **Keep repository private**. Once done, click the **Next** button to continue to the final step.

# Select branch {% #select-branch %}

Finally, you can configure repository-related settings such as **Production branch**, **Root directory**, or **Silent mode**. Let's stick to the default values provided by the template and click the **Create** button to create the function.
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
---
layout: tutorial
title: Configure web platform
description: Add paid app subscription plans to your app with Stripe and Appwrite Functions.
step: 4
---

# Add platform {% #add-platform %}
To showcase the functionality, the template ships with a demo frontend that you can use. To allow this demo, you must add Function's domain as a trusted web platform. Head to your project's **Overview** page in Apwrite Console, and scroll down to the **Integrations** section. Click the **Add platform** button and select **Web App** from the dropdown.

{% only_dark %}
![Add a platform](/images/docs/quick-starts/dark/add-platform.png)
{% /only_dark %}
{% only_light %}
![Add a platform](/images/docs/quick-starts/add-platform.png)
{% /only_light %}

Set **Name** to `Subscription demo` and **Hostname** to your Function's domain.
You can find your function's domain under the **Domains** tab.

{% only_dark %}
![Functions domain](/images/docs/tutorials/subscriptions-with-stripe/dark/functions-stripe-domain.png)
{% /only_dark %}
{% only_light %}
![Functions domain](/images/docs/tutorials/subscriptions-with-stripe/functions-stripe-domain.png)
{% /only_light %}

Click the **Next** button to continue.

You don't need to follow the remaining screens, so you can click on **Skip optional steps**.

# Update webhook endpoint {% #update-webhook-endpoint %}

Navigate to your Stripe dashboard, under **Developers**, go to the **Webhooks** tab.
Click on the webhook we added earlier, under the three-dot menu, click **Update details**.

Update the **Endpoint URL** to `https://<YOUR_FUNCTION_DOMAIN>/webhook`. For example, `https://6579ea96aa28a5cfb66a.appwrite.global/webhook`
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
---
layout: tutorial
title: All set
description: Add paid app subscription plans to your app with Stripe and Appwrite Functions.
step: 5
---
You are now ready to use the Appwrite Function in your front end. You can initialize the payment process by redirecting your user to the `/subscribe` endpoint on the Function's domain.

# Visit demo {% #visit-demo %}

You can visit our Function's domain in the browser to see the demo application.

{% only_dark %}
![Stripe subscription demo](/images/docs/tutorials/subscriptions-with-stripe/dark/stripe.png)
{% /only_dark %}
{% only_light %}
![Stripe subscription demo](/images/docs/tutorials/subscriptions-with-stripe/stripe.png)
{% /only_light %}

In the demo app, click the **Register as anonymous** button to create a guest session. This will create a new user in your Appwrite Project. After registering, the demo app will show that you are **Not subscribed**. This means that your user does not have a `subscriber` label. You can click **Subscribe with Stripe** to start the payment process.

Once redirected to Stripe, fill out the payment form. If your Stripe account is in test mode, you can use test card number `4242 4242 4242 4242` with any future expiration date and any CVC code. When done, click the **Subscribe** button.

Once processed, you will be redirected back to the demo application, where **Subscription status** will say **Subscribed**. If it doesn't, please allow Stripe a few moments to process the payment. You can keep refreshing the website until the status is updated.

# Verify label {% #verify-label %}

Once someone subscribes, they should receive a special label that will grant them permissions.
To verify that a label was added successfully to the user,
you can switch to Appwrite Console and visit the **Auth** page of our Appwrite Project.
Here, you should see one anonymous user.

Let's click on this user to see the details.
After scrolling down to the **Labels** card, you can see the `subscriber` label has been added to this user.

{% only_dark %}
![Appwrite User screen](/images/docs/tutorials/subscriptions-with-stripe/dark/user-subscriber-label.png)
{% /only_dark %}
{% only_light %}
![Appwrite User screen](/images/docs/tutorials/subscriptions-with-stripe/user-subscriber-label.png)
{% /only_light %}


# Permissions {% #permissions %}

The label created will grant the user access to any resource with the following permissions.

| Description | Code Snippet |
| ------------------------------------------- | ------------------------------------------- |
| Read | `Permissions.read(Role.label('subscriber'))`|
| Update | `Permissions.update(Role.label('subscriber'))` |
| Delete | `Permissions.delete(Role.label('subscriber'))` |
| Create | `Permissions.create(Role.label('subscriber'))` |

{% arrow_link href="/docs/advanced/platform/permissions" %}
Learn more about permissions
{% /arrow_link %}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.