+ Stripe connected 🎉 +
-- Your Stripe account has been successfully connected. - You can now receive payments. -
++ Your Stripe account is now linked. We can now automate payments and + reconciliation for you. +
- +diff --git a/stripe_to_invoice/app/api/stripe/callback/route.ts b/stripe_to_invoice/app/api/stripe/callback/route.ts index d4f6d79..e73565f 100644 --- a/stripe_to_invoice/app/api/stripe/callback/route.ts +++ b/stripe_to_invoice/app/api/stripe/callback/route.ts @@ -1,24 +1,26 @@ import { cookies } from "next/headers"; import { NextRequest, NextResponse } from "next/server"; +import { db } from "@/lib/db"; +import { stripeAccounts } from "@/lib/schema/stripeAccounts"; +import { eq } from "drizzle-orm"; type StripeOAuthResponse = { - access_token: string; - refresh_token: string; - stripe_user_id: string; - scope: string; + stripe_user_id: string; // acct_... }; export async function GET(req: NextRequest) { const cookieStore = await cookies(); const session = cookieStore.get("session"); - // Safety: user must still be logged in + // 🔒 Must be logged in if (!session) { return NextResponse.redirect( new URL("/login", process.env.NEXT_PUBLIC_BASE_URL) ); } + const userId = session.value; + const { searchParams } = new URL(req.url); const code = searchParams.get("code"); const error = searchParams.get("error"); @@ -26,7 +28,10 @@ export async function GET(req: NextRequest) { if (error) { console.error("Stripe OAuth error:", error); return NextResponse.redirect( - new URL("/connect/stripe?error=oauth_failed", process.env.NEXT_PUBLIC_BASE_URL) + new URL( + "/connect/stripe?error=oauth_failed", + process.env.NEXT_PUBLIC_BASE_URL + ) ); } @@ -37,7 +42,7 @@ export async function GET(req: NextRequest) { ); } - // Exchange code for access token + // 🔁 Exchange OAuth code const tokenRes = await fetch("https://connect.stripe.com/oauth/token", { method: "POST", headers: { @@ -55,34 +60,36 @@ export async function GET(req: NextRequest) { console.error("Stripe token exchange failed:", text); return NextResponse.redirect( - new URL("/connect/stripe?error=token_exchange_failed", process.env.NEXT_PUBLIC_BASE_URL) + new URL( + "/connect/stripe?error=token_exchange_failed", + process.env.NEXT_PUBLIC_BASE_URL + ) ); } const data = (await tokenRes.json()) as StripeOAuthResponse; - /** - * TODO (NEXT STEP): - * - Encrypt tokens - * - Persist to DB against the current user - * - * Required fields: - * - data.stripe_user_id (acct_...) - * - data.access_token - * - data.refresh_token - * - mode: "test" - */ - - console.log("Stripe OAuth success", { - stripe_account_id: data.stripe_user_id, - scope: data.scope, - has_access_token: Boolean(data.access_token), - has_refresh_token: Boolean(data.refresh_token), - access_token_preview: data.access_token?.slice(0, 8) + "...", + // ✅ Persist Stripe account → user (UPSERT) + await db + .insert(stripeAccounts) + .values({ + userId, + stripeAccountId: data.stripe_user_id, + }) + .onConflictDoUpdate({ + target: stripeAccounts.userId, + set: { + stripeAccountId: data.stripe_user_id, + }, }); - // MVP success redirect + console.log("Stripe connected", { + userId, + stripeAccountId: data.stripe_user_id, + }); + + // ✅ Success redirect return NextResponse.redirect( - new URL("/connect/stripe/success", process.env.APP_URL) + new URL("/connect/stripe/success", process.env.NEXT_PUBLIC_BASE_URL) ); } diff --git a/stripe_to_invoice/app/connect/stripe/refresh/page.tsx b/stripe_to_invoice/app/connect/stripe/refresh/page.tsx new file mode 100644 index 0000000..92af400 --- /dev/null +++ b/stripe_to_invoice/app/connect/stripe/refresh/page.tsx @@ -0,0 +1,23 @@ +export default function StripeRefreshPage() { + return ( +
+ Something interrupted the Stripe onboarding. + Please try again. +
+ + + Retry Stripe setup + +- Your Stripe account has been successfully connected. - You can now receive payments. -
++ Your Stripe account is now linked. We can now automate payments and + reconciliation for you. +
- +