Refactor code and improve usage of package dependencies

This commit updates the naming convention of icons from Lucide-React, moving some package dependencies to "peerDependencies" in 'team-accounts', 'admin' and 'auth'. Additionally, it includes tweaks to the development server command in apps/web package.json and adds a logger reference to the shared package. Furthermore, cleanup work has been performed within the features and UI packages, and new scripts to interact with Stripe have been added to the root package.json.
This commit is contained in:
giancarlo
2024-03-26 01:34:19 +08:00
parent 95793c42b4
commit ee507e0816
92 changed files with 1691 additions and 1270 deletions

View File

@@ -7,7 +7,8 @@
"clean": "git clean -xdf .turbo node_modules",
"format": "prettier --check \"**/*.{ts,tsx}\"",
"lint": "eslint .",
"typecheck": "tsc --noEmit"
"typecheck": "tsc --noEmit",
"start": "docker run --rm -it --name=stripe -v ~/.config/stripe:/root/.config/stripe stripe/stripe-cli:latest listen --forward-to http://host.docker.internal:3000/api/billing/webhook"
},
"prettier": "@kit/prettier-config",
"exports": {

View File

@@ -2,6 +2,7 @@
import { useState } from 'react';
import { invariant } from '@epic-web/invariant';
import {
EmbeddedCheckout,
EmbeddedCheckoutProvider,
@@ -14,15 +15,10 @@ import {
DialogHeader,
DialogTitle,
} from '@kit/ui/dialog';
import { cn } from '@kit/ui/utils';
const STRIPE_PUBLISHABLE_KEY = process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY;
if (!STRIPE_PUBLISHABLE_KEY) {
throw new Error(
'Missing NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY environment variable. Did you forget to add it to your .env file?',
);
}
invariant(STRIPE_PUBLISHABLE_KEY, 'Stripe publishable key is required');
const stripePromise = loadStripe(STRIPE_PUBLISHABLE_KEY);
@@ -52,11 +48,7 @@ function EmbeddedCheckoutPopup({
onClose?: () => void;
}>) {
const [open, setOpen] = useState(true);
const className = cn({
[`bg-white p-4 max-h-[98vh] overflow-y-auto shadow-transparent border border-gray-200 dark:border-dark-700`]:
true,
});
const className = `bg-white p-4 max-h-[98vh] overflow-y-auto shadow-transparent border`;
return (
<Dialog

View File

@@ -4,20 +4,19 @@ import { StripeServerEnvSchema } from '../schema/stripe-server-env.schema';
const STRIPE_API_VERSION = '2023-10-16';
// Parse the environment variables and validate them
const stripeServerEnv = StripeServerEnvSchema.parse({
STRIPE_SECRET_KEY: process.env.STRIPE_SECRET_KEY,
STRIPE_WEBHOOK_SECRET: process.env.STRIPE_WEBHOOK_SECRET,
});
/**
* @description returns a Stripe instance
*/
export async function createStripeClient() {
const { default: Stripe } = await import('stripe');
const key = stripeServerEnv.STRIPE_SECRET_KEY;
return new Stripe(key, {
// Parse the environment variables and validate them
const stripeServerEnv = StripeServerEnvSchema.parse({
STRIPE_SECRET_KEY: process.env.STRIPE_SECRET_KEY,
STRIPE_WEBHOOK_SECRET: process.env.STRIPE_WEBHOOK_SECRET,
});
return new Stripe(stripeServerEnv.STRIPE_SECRET_KEY, {
apiVersion: STRIPE_API_VERSION,
});
}

View File

@@ -29,9 +29,11 @@ export class StripeWebhookHandlerService
*/
async verifyWebhookSignature(request: Request) {
const body = await request.clone().text();
const signature = `stripe-signature`;
const signatureKey = `stripe-signature`;
const signature = request.headers.get(signatureKey) as string;
const { STRIPE_WEBHOOK_SECRET } = StripeServerEnvSchema.parse({
STRIPE_SECRET_KEY: process.env.STRIPE_SECRET_KEY,
STRIPE_WEBHOOK_SECRET: process.env.STRIPE_WEBHOOK_SECRET,
});
@@ -109,7 +111,7 @@ export class StripeWebhookHandlerService
private async handleCheckoutSessionCompleted(
event: Stripe.CheckoutSessionCompletedEvent,
onCheckoutCompletedCallback: (
data: InsertSubscriptionParams,
data: Omit<Subscription['Insert'], 'billing_customer_id'>,
customerId: string,
) => Promise<unknown>,
) {
@@ -128,11 +130,11 @@ export class StripeWebhookHandlerService
// TODO: convert or store the amount in cents?
const amount = session.amount_total ?? 0;
const payload = this.buildSubscriptionPayload<typeof accountId>({
const payload = this.buildSubscriptionPayload({
subscription,
accountId,
amount,
});
}) as InsertSubscriptionParams;
return onCheckoutCompletedCallback(payload, customerId);
}
@@ -149,7 +151,7 @@ export class StripeWebhookHandlerService
return (acc + (item.plan.amount ?? 0)) * (item.quantity ?? 1);
}, 0);
const payload = this.buildSubscriptionPayload<undefined>({
const payload = this.buildSubscriptionPayload({
subscription,
amount,
});
@@ -166,27 +168,27 @@ export class StripeWebhookHandlerService
return onSubscriptionDeletedCallback(subscription.id);
}
private buildSubscriptionPayload<
AccountId extends string | undefined,
>(params: {
private buildSubscriptionPayload(params: {
subscription: Stripe.Subscription;
amount: number;
// we only need the account id if we
// are creating a subscription for an account
accountId?: AccountId;
}): AccountId extends string
? InsertSubscriptionParams
: Subscription['Update'] {
accountId?: string;
}) {
const { subscription } = params;
const lineItem = subscription.items.data[0];
const price = lineItem?.price;
const priceId = price?.id!;
const interval = price?.recurring?.interval ?? null;
const active =
subscription.status === 'active' || subscription.status === 'trialing';
const data = {
billing_provider: this.provider,
id: subscription.id,
status: subscription.status,
active,
price_amount: params.amount,
cancel_at_period_end: subscription.cancel_at_period_end ?? false,
interval: interval as string,
@@ -194,15 +196,27 @@ export class StripeWebhookHandlerService
product_id: price?.product as string,
variant_id: priceId,
interval_count: price?.recurring?.interval_count ?? 1,
};
period_starts_at: getISOString(subscription.current_period_start),
period_ends_at: getISOString(subscription.current_period_end),
trial_starts_at: getISOString(subscription.trial_start),
trial_ends_at: getISOString(subscription.trial_end),
} satisfies Omit<InsertSubscriptionParams, 'account_id'>;
// when we are creating a subscription for an account
// we need to include the account id
if (params.accountId !== undefined) {
return {
...data,
account_id: params.accountId,
} satisfies InsertSubscriptionParams;
};
}
return data as Subscription['Update'];
// otherwise we are updating a subscription
// and we only need to return the update payload
return data;
}
}
function getISOString(date: number | null) {
return date ? new Date(date * 1000).toISOString() : null;
}