-
Notifications
You must be signed in to change notification settings - Fork 158
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: deno task init:stripe
and further documentation improvements
#93
Changes from 4 commits
4de9146
1df11ae
86037ee
4a3a7a0
78b166c
6686580
e311e0d
77136d1
beba6a8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -71,68 +71,39 @@ This will automatically configure the database tables and their settings for us. | |
file as `SUPABASE_URL`, `SUPABASE_ANON_KEY`, and `SUPABASE_SERVICE_KEY`, | ||
respectively. | ||
|
||
### Payments | ||
|
||
1. Create the “Premium tier” product in Stripe via the Stripe CLI: | ||
|
||
``` | ||
stripe products create \ | ||
--name="Premium tier" \ | ||
--default-price-data.unit-amount=500 \ | ||
--default-price-data.currency=usd \ | ||
--default-price-data.recurring.interval=month \ | ||
--description="Unlimited todos" | ||
``` | ||
### Payments and Subscriptions | ||
|
||
2. The resulting [product object](https://stripe.com/docs/api/products) will be | ||
printed in the terminal. Copy the `default_price` value as | ||
`STRIPE_PREMIUM_PLAN_PRICE_ID` in [`constants.ts`](constants.ts). | ||
1. Copy your Stripe secret key as `STRIPE_SECRET_KEY` into your `.env` file. We | ||
recommend using the test key for your development environment. | ||
2. Run `deno task init:stripe` and follow the instructions. This automatically | ||
creates your "Premium tier" product and configures the Stripe customer | ||
portal. | ||
|
||
3. Next, head over to | ||
[your Stripe dashboard settings](https://dashboard.stripe.com/test/apikeys) | ||
to copy the `STRIPE_SECRET_KEY` into your `.env` file. We recommend using the | ||
test key for your development environment. | ||
> Note: go to [init/stripe.ts] if you'd like to learn more about how the | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @niklasmtj, regarding #93 (comment), I'd rather have one or the other. I've added this comment to point people in the right direction if they want to learn more. At some point, I'll include technical details as JSDoc comments in the implementation to give an even more precise idea of what's happening. Do you think this addresses your concern? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sounds good to me 👍🏼 |
||
> `init:stripe` task works. | ||
|
||
4. Configure Stripe's customer portal settings: | ||
3. Listen locally to Stripe events: | ||
|
||
5. Ensure your users can manage their subscription via Stripe's Customer portal | ||
by going to | ||
[your Customer Portal settings](https://dashboard.stripe.com/test/settings/billing/portal) | ||
and toggling on: | ||
|
||
- Payment methods > Allow customers to view and update payment methods. | ||
- Cancellations > Cancel subscriptions | ||
- Subscriptions > Customers can switch plans (and select your relevant | ||
subscription tiers) | ||
|
||
Hit save. | ||
|
||
5. (Optional) | ||
[Set up your branding on Stripe](https://dashboard.stripe.com/settings/branding), | ||
as a user will be taken to Stripe's checkout page when they upgrade. | ||
``` | ||
stripe listen --forward-to localhost:8000/api/subscription | ||
``` | ||
|
||
#### Updating `customers` database via Stripe webhooks | ||
4. Copy the webhook signing secret to [.env](.env) as `STRIPE_WEBHOOK_SECRET`. | ||
|
||
Keep your `customers` database up to date with billing changes by | ||
[registering a webhook endpoint in Stripe](https://stripe.com/docs/development/dashboard/register-webhook). | ||
### Running the Server | ||
|
||
To test locally, use the Stripe CLI: | ||
Finally, start the server by running: | ||
|
||
``` | ||
stripe listen --forward-to localhost:8000/api/subscription | ||
deno task start | ||
``` | ||
|
||
You'll receive an output that includes your webhook signing secret. Copy that | ||
into your `.env` file as `STRIPE_WEBHOOK_SECRET`. | ||
|
||
Start the server with `deno task start` and test the webhook with | ||
`stripe trigger customer.subscription.created` or | ||
`stripe trigger customer.subscription.deleted`. | ||
Go to [http://localhost:8000](http://localhost:8000) to begin playing with your | ||
new SaaS app. | ||
|
||
#### Testing Payments | ||
|
||
You can use [Stripe's test credit cards](https://stripe.com/docs/testing) to | ||
make test payments while in Stripe's test mode. | ||
> Note: You can use | ||
> [Stripe's test credit cards](https://stripe.com/docs/testing) to make test | ||
> payments while in Stripe's test mode. | ||
|
||
### Global Constants | ||
|
||
|
@@ -155,7 +126,7 @@ Once Docker and Supabase services are running, start the project with: | |
deno task start | ||
``` | ||
|
||
Then, point your browser to `localhost:8000`. | ||
Then, point your browser to `http://localhost:8000`. | ||
|
||
## Deploying to Production | ||
|
||
|
@@ -167,6 +138,9 @@ TODO | |
|
||
### Payments | ||
|
||
[Set up your branding on Stripe](https://dashboard.stripe.com/settings/branding), | ||
as a user will be taken to Stripe's checkout page when they upgrade. | ||
|
||
Keep your `customers` database up to date with billing changes by | ||
[registering a webhook endpoint in Stripe](https://stripe.com/docs/development/dashboard/register-webhook). | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
// Copyright 2023 the Deno authors. All rights reserved. MIT license. | ||
import type { Stripe } from "stripe"; | ||
import { SITE_DESCRIPTION } from "@/constants.ts"; | ||
import "std/dotenv/load.ts"; | ||
import { stripe } from "@/utils/stripe.ts"; | ||
|
||
async function createPremiumTierProduct(stripe: Stripe) { | ||
/** | ||
* These values provide a set of default values for the demo. | ||
* However, these can be adjusted to fit your use case. | ||
*/ | ||
return await stripe.products.create({ | ||
name: "Premium tier", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm wondering if the stuff in this object should be configurable? Does that make any sense? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good suggestion. Though, we'll leave this as-is for now in the spirit of prioritising ease of setup over customizability. Either way, I've improved the comment. |
||
description: "Unlimited todos", | ||
default_price_data: { | ||
unit_amount: 500, | ||
currency: "usd", | ||
recurring: { | ||
interval: "month", | ||
}, | ||
}, | ||
}); | ||
} | ||
|
||
async function createDefaultPortalConfiguration( | ||
stripe: Stripe, | ||
product: | ||
Stripe.BillingPortal.ConfigurationCreateParams.Features.SubscriptionUpdate.Product, | ||
) { | ||
return await stripe.billingPortal.configurations.create({ | ||
features: { | ||
payment_method_update: { | ||
enabled: true, | ||
}, | ||
customer_update: { | ||
allowed_updates: ["email", "name"], | ||
enabled: true, | ||
}, | ||
subscription_cancel: { | ||
enabled: true, | ||
mode: "immediately", | ||
}, | ||
subscription_update: { | ||
enabled: true, | ||
default_allowed_updates: ["price"], | ||
products: [product], | ||
}, | ||
invoice_history: { enabled: true }, | ||
}, | ||
business_profile: { | ||
headline: SITE_DESCRIPTION, | ||
}, | ||
}); | ||
} | ||
|
||
async function main() { | ||
const product = await createPremiumTierProduct(stripe); | ||
|
||
if (typeof product.default_price !== "string") return; | ||
|
||
await createDefaultPortalConfiguration(stripe, { | ||
prices: [product.default_price], | ||
product: product.id, | ||
}); | ||
|
||
console.log( | ||
"Please copy and paste this value into the `STRIPE_PREMIUM_PLAN_PRICE_ID` constant in `constants.ts`: " + | ||
product.default_price, | ||
); | ||
} | ||
|
||
if (import.meta.main) { | ||
await main(); | ||
} |
This comment was marked as resolved.
Sorry, something went wrong.