🔖

【NextJs + BetterAuth + Polar】Subcription サブスクサービスの実装【#14 Polar Route】

に公開

【#14 Polar tRPC Route】
YouTube: https://youtu.be/Nk07mpgtV8E
https://youtu.be/Nk07mpgtV8E

今回は、サーバーサイドでサブスクリプションの状態を管理する部分を実装します。

trpc/init.ts
import { cache } from "react";
import { headers } from "next/headers";

import { initTRPC, TRPCError } from "@trpc/server";

import { auth } from "@/lib/auth";
import { polarClient } from "@/lib/polar-client";

import superjson from "superjson";

export const createTRPCContext = cache(async () => {
  /**
   * @see: https://trpc.io/docs/server/context
   */
  return { userId: "user_123" };
});
// Avoid exporting the entire t-object
// since it's not very descriptive.
// For instance, the use of a t variable
// is common in i18n libraries.
const t = initTRPC.create({
  /**
   * @see https://trpc.io/docs/server/data-transformers
   */
  transformer: superjson,
});
// Base router and procedure helpers
export const createTRPCRouter = t.router;
export const createCallerFactory = t.createCallerFactory;
export const baseProcedure = t.procedure;

export const protectedProcedure = baseProcedure.use(async ({ ctx, next }) => {
  const session = await auth.api.getSession({
    headers: await headers(),
  });

  if (!session) {
    throw new TRPCError({ code: "UNAUTHORIZED", message: "Unauthorized" });
  }

  return next({
    ctx: { ...ctx, auth: session },
  });
});

export const subscribedProcedure = protectedProcedure.use(
  async ({ ctx, next }) => {
    const customer = await polarClient.customers.getStateExternal({
      externalId: ctx.auth.user.id,
    });

    if (
      !customer.activeSubscriptions ||
      customer.activeSubscriptions.length === 0
    ) {
      throw new TRPCError({ code: "FORBIDDEN", message: "Not subscribed" });
    }

    return next({ ctx: { ...ctx, customer } });
  }
);

Discussion