Skip to content

Commit

Permalink
fix: Stripe types and checks (#264)
Browse files Browse the repository at this point in the history
* throw error if not all products have a `default_price`

* define stripe types in ./stripe.ts

* fixes

* tweak

---------

Co-authored-by: Asher Gomez <ashersaupingomez@gmail.com>
  • Loading branch information
nicu-chiciuc and iuioiua authored Jul 2, 2023
1 parent 6567ffc commit 849df0f
Show file tree
Hide file tree
Showing 7 changed files with 49 additions and 10 deletions.
2 changes: 1 addition & 1 deletion components/Layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
SITE_WIDTH_STYLES,
} from "@/utils/constants.ts";
import Logo from "./Logo.tsx";
import { stripe } from "../utils/payments.ts";
import { stripe } from "@/utils/payments.ts";
import { Discord, GitHub } from "./Icons.tsx";

interface NavProps extends JSX.HTMLAttributes<HTMLElement> {
Expand Down
2 changes: 1 addition & 1 deletion deno.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
"@preact/signals-core": "https://esm.sh/*@preact/signals-core@1.2.3",
"twind-preset-tailwind/": "https://esm.sh/@twind/preset-tailwind@1.1.4/",
"std/": "https://deno.land/std@0.188.0/",
"stripe": "https://esm.sh/stripe@12.6.0",
"stripe": "./stripe.ts",
"feed": "https://esm.sh/feed@4.2.2",
"fresh_charts/": "https://deno.land/x/fresh_charts@0.2.2/",
"kv_oauth": "https://deno.land/x/deno_kv_oauth@v0.2.5/mod.ts",
Expand Down
4 changes: 3 additions & 1 deletion routes/account/manage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ import { redirect } from "@/utils/redirect.ts";
// deno-lint-ignore no-explicit-any
export const handler: Handlers<any, AccountState> = {
async GET(req, ctx) {
if (stripe === undefined) return ctx.renderNotFound();
if (stripe === undefined || ctx.state.user.stripeCustomerId === undefined) {
return ctx.renderNotFound();
}

const { url } = await stripe.billingPortal.sessions.create({
customer: ctx.state.user.stripeCustomerId,
Expand Down
2 changes: 1 addition & 1 deletion routes/api/stripe-webhooks.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright 2023 the Deno authors. All rights reserved. MIT license.
import type { Handlers } from "$fresh/server.ts";
import { stripe } from "@/utils/payments.ts";
import { Stripe } from "stripe";
import Stripe from "stripe";
import { getUserByStripeCustomer, updateUser } from "@/utils/db.ts";

const cryptoProvider = Stripe.createSubtleCryptoProvider();
Expand Down
26 changes: 20 additions & 6 deletions routes/pricing.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,12 @@ import type { Handlers, PageProps } from "$fresh/server.ts";
import Head from "@/components/Head.tsx";
import type { State } from "@/routes/_middleware.ts";
import { BUTTON_STYLES } from "@/utils/constants.ts";
import { formatAmountForDisplay, stripe } from "@/utils/payments.ts";
import {
formatAmountForDisplay,
isProductWithPrice,
stripe,
StripProductWithPrice,
} from "@/utils/payments.ts";
import Stripe from "stripe";
import { ComponentChild } from "preact";
import { getUserBySession, type User } from "@/utils/db.ts";
Expand All @@ -14,11 +19,11 @@ interface PricingPageData extends State {
}

function comparePrices(
productA: Stripe.Product,
productB: Stripe.Product,
productA: StripProductWithPrice,
productB: StripProductWithPrice,
) {
return ((productA.default_price as Stripe.Price).unit_amount || 0) -
((productB.default_price as Stripe.Price).unit_amount || 0);
return (productA.default_price.unit_amount || 0) -
(productB.default_price.unit_amount || 0);
}

export const handler: Handlers<PricingPageData, State> = {
Expand All @@ -29,7 +34,16 @@ export const handler: Handlers<PricingPageData, State> = {
expand: ["data.default_price"],
active: true,
});
const products = data.sort(comparePrices);

const productsWithPrice = data.filter(isProductWithPrice);

if (productsWithPrice.length !== data.length) {
throw new Error(
"Not all products have a default price. Please run the `deno task init:stripe` as the README instructs.",
);
}

const products = productsWithPrice.sort(comparePrices);

const user = ctx.state.sessionId
? await getUserBySession(ctx.state.sessionId)
Expand Down
7 changes: 7 additions & 0 deletions stripe.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// Copyright 2023 the Deno authors. All rights reserved. MIT license.

// Default types for Stripe don't yet work: https://github.com/stripe-samples/stripe-node-deno-samples/issues/2
// @deno-types="npm:stripe@12.6.0"
import Stripe from "https://esm.sh/stripe@12.6.0";

export default Stripe;
16 changes: 16 additions & 0 deletions utils/payments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,22 @@ if (stripe) {
);
}

/**
* We assume that the product has a default price.
* The official types allow for the default_price to be `undefined | null | string`
*/
export type StripProductWithPrice = Stripe.Product & {
default_price: Stripe.Price;
};

export function isProductWithPrice(
product: Stripe.Product,
): product is StripProductWithPrice {
return product.default_price !== undefined &&
product.default_price !== null &&
typeof product.default_price !== "string";
}

export function formatAmountForDisplay(
amount: number,
currency: string,
Expand Down

0 comments on commit 849df0f

Please sign in to comment.