From 210ee3a9f2107ad9d04a0c1c68293928a2224e2c Mon Sep 17 00:00:00 2001 From: Marco Pedone Date: Wed, 14 Jan 2026 12:02:03 +0100 Subject: [PATCH] feat: update Stripe integration and add settings page with health check --- apps/infoalloggi/package-lock.json | 20 +- apps/infoalloggi/package.json | 276 +++++++++--------- apps/infoalloggi/src/i18n/en.ts | 1 + apps/infoalloggi/src/i18n/it.ts | 1 + .../area-riservata/admin/impostazioni.tsx | 87 ++++++ .../src/server/api/routers/stripe.ts | 4 + .../server/controllers/stripe.controller.ts | 10 + 7 files changed, 251 insertions(+), 148 deletions(-) create mode 100644 apps/infoalloggi/src/pages/area-riservata/admin/impostazioni.tsx diff --git a/apps/infoalloggi/package-lock.json b/apps/infoalloggi/package-lock.json index cc855c3..2919146 100644 --- a/apps/infoalloggi/package-lock.json +++ b/apps/infoalloggi/package-lock.json @@ -86,7 +86,7 @@ "react-use": "^17.6.0", "recharts": "^2.15.4", "sharp": "^0.34.3", - "stripe": "^18.4.0", + "stripe": "^20.1.2", "superjson": "2.2.2", "tailwind-merge": "3.3", "tailwind-scrollbar": "^4.0.2", @@ -15622,9 +15622,9 @@ } }, "node_modules/qs": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", - "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", + "version": "6.14.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz", + "integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==", "license": "BSD-3-Clause", "dependencies": { "side-channel": "^1.1.0" @@ -17861,18 +17861,18 @@ } }, "node_modules/stripe": { - "version": "18.4.0", - "resolved": "https://registry.npmjs.org/stripe/-/stripe-18.4.0.tgz", - "integrity": "sha512-LKFeDnDYo4U/YzNgx2Lc9PT9XgKN0JNF1iQwZxgkS4lOw5NunWCnzyH5RhTlD3clIZnf54h7nyMWkS8VXPmtTQ==", + "version": "20.1.2", + "resolved": "https://registry.npmjs.org/stripe/-/stripe-20.1.2.tgz", + "integrity": "sha512-qU+lQRRJnTxmyvglYBPE24/IepncmywsAg0GDTsTdP2pb+3e3RdREHJZjKgqCmv0phPxN/nmgNPnIPPH8w0P4A==", "license": "MIT", "dependencies": { - "qs": "^6.11.0" + "qs": "^6.14.1" }, "engines": { - "node": ">=12.*" + "node": ">=16" }, "peerDependencies": { - "@types/node": ">=12.x.x" + "@types/node": ">=16" }, "peerDependenciesMeta": { "@types/node": { diff --git a/apps/infoalloggi/package.json b/apps/infoalloggi/package.json index 40c7677..084fd74 100644 --- a/apps/infoalloggi/package.json +++ b/apps/infoalloggi/package.json @@ -1,139 +1,139 @@ { - "name": "infoalloggi", - "private": true, - "scripts": { - "build": "next build", - "d": "npm run-p dev stripe-dev -cl", - "dev": "next dev --turbopack", - "dev-https": "cross-env NEXT_PUBLIC_BASE_URL='https://localhost:3000' INTERNAL_BASE_URL='https://localhost:3000' BASE_URL='https://localhost:3000' next dev --experimental-https --turbopack", - "email": "cross-env NEXT_PUBLIC_BASE_URL='https://localhost:3000' email dev --port 3500", - "idev": "cross-env NODE_OPTIONS='--inspect' next dev --turbopack", - "knip": "knip --no-exit-code --no-config-hints", - "lint": "biome lint", - "format": "biome format --write", - "pg-typegen": "npx kanel", - "start": "next start", - "stripe-dev": "stripe listen --forward-to https://localhost:3000/api/stripe-webhook", - "test": "npm run lint && npm run types", - "types": "tsc --noEmit", - "analyze": "cross-env ANALYZE=true next build" - }, - "version": "0.1.0", - "dependencies": { - "@dnd-kit/core": "^6.3.1", - "@dnd-kit/sortable": "^10.0.0", - "@dnd-kit/utilities": "^3.2.2", - "@fattureincloud/fattureincloud-ts-sdk": "^2.1.1", - "@hookform/resolvers": "^5.2.1", - "@next/bundle-analyzer": "^16.0.2", - "@radix-ui/react-dialog": "^1.1.15", - "@radix-ui/react-popover": "^1.1.14", - "@radix-ui/react-scroll-area": "^1.2.10", - "@radix-ui/react-slot": "^1.2.3", - "@radix-ui/react-toggle-group": "^1.1.10", - "@radix-ui/react-tooltip": "^1.2.8", - "@react-email/components": "0.5.0", - "@react-email/render": "^1.1.4", - "@stripe/react-stripe-js": "^3.9.0", - "@stripe/stripe-js": "^7.8.0", - "@t3-oss/env-nextjs": "^0.13.8", - "@tailwindcss/forms": "^0.5.10", - "@tailwindcss/postcss": "^4.1.11", - "@tailwindcss/typography": "^0.5.16", - "@tanstack/react-query": "^5.74.4", - "@tanstack/react-table": "^8.21.3", - "@tanstack/react-virtual": "^3.13.12", - "@tiptap/core": "^3.0.9", - "@tiptap/extension-link": "^3.0.9", - "@tiptap/extension-underline": "^3.0.9", - "@tiptap/pm": "^3.0.9", - "@tiptap/react": "^3.0.9", - "@tiptap/starter-kit": "^3.0.9", - "@trpc/client": "^11.1.0", - "@trpc/next": "^11.4.3", - "@trpc/react-query": "^11.1.0", - "@trpc/server": "^11.1.0", - "class-variance-authority": "^0.7.1", - "clsx": "^2.1.1", - "cmdk": "^1.1.1", - "cookies-next": "^6.1.0", - "date-fns": "^4.1.0", - "embla-carousel-react": "^8.6.0", - "framer-motion": "^12.8.0", - "frimousse": "^0.3.0", - "html2canvas-pro": "^1.5.13", - "ioredis": "^5.7.0", - "jose": "^6.0.12", - "js-cookie": "^3.0.5", - "jspdf": "^3.0.4", - "kysely": "^0.28.2", - "kysely-plugin-serialize": "^0.8.2", - "leaflet": "^1.9.4", - "leaflet-defaulticon-compatibility": "^0.1.2", - "lucide-react": "^0.536.0", - "next": "15.4.10", - "next-themes": "^0.4.6", - "nextjs-progressbar": "^0.0.16", - "nodemailer": "^7.0.5", - "nodemailer-html-to-text": "^3.2.0", - "nuqs": "^2.4.3", - "pg": "^8.16.3", - "postcss": "^8.5.6", - "radix-ui": "^1.4.3", - "react": "^19.1.0", - "react-colorful": "^5.6.1", - "react-day-picker": "^9.11.1", - "react-dom": "^19.1.1", - "react-email": "^4.2.8", - "react-hook-form": "^7.62.0", - "react-hot-toast": "^2.5.2", - "react-is": "^19.1.1", - "react-leaflet": "^5.0.0", - "react-number-format": "^5.4.4", - "react-pdf": "^10.2.0", - "react-phone-number-input": "^3.4.12", - "react-reverse-portal": "^2.3.0", - "react-select": "^5.10.2", - "react-to-print": "^3.1.1", - "react-use": "^17.6.0", - "recharts": "^2.15.4", - "sharp": "^0.34.3", - "stripe": "^18.4.0", - "superjson": "2.2.2", - "tailwind-merge": "3.3", - "tailwind-scrollbar": "^4.0.2", - "tailwindcss": "^4.1.11", - "tunnel-rat": "^0.1.2", - "tw-animate-css": "^1.3.6", - "type-fest": "^4.41.0", - "uuid": "^11.1.0", - "vaul": "^1.1.2", - "zod": "^4.1.12" - }, - "devDependencies": { - "@biomejs/biome": "^2.3.1", - "@hookform/devtools": "^4.4.0", - "@react-email/preview-server": "^4.2.8", - "@total-typescript/ts-reset": "^0.6.1", - "@types/js-cookie": "^3.0.6", - "@types/leaflet": "^1.9.20", - "@types/node": "^24.2.0", - "@types/nodemailer": "^6.4.17", - "@types/nodemailer-html-to-text": "^3.1.3", - "@types/pg": "^8.15.5", - "@types/react": "^19.1.8", - "@types/react-dom": "^19.1.7", - "@types/react-is": "^19.0.0", - "@types/uuid": "^10.0.0", - "cross-env": "^10.0.0", - "jiti": "^2.6.1", - "kanel": "^3.14.2", - "kanel-kysely": "^0.7.1", - "knip": "^5.62.0", - "npm-run-all": "^4.1.5", - "typescript": "^5.9.2" - }, - "ct3aMetadata": { - "initVersion": "7.9.0" - } -} + "name": "infoalloggi", + "private": true, + "scripts": { + "build": "next build", + "d": "npm run-p dev stripe-dev -cl", + "dev": "next dev --turbopack", + "dev-https": "cross-env NEXT_PUBLIC_BASE_URL='https://localhost:3000' INTERNAL_BASE_URL='https://localhost:3000' BASE_URL='https://localhost:3000' next dev --experimental-https --turbopack", + "email": "cross-env NEXT_PUBLIC_BASE_URL='https://localhost:3000' email dev --port 3500", + "idev": "cross-env NODE_OPTIONS='--inspect' next dev --turbopack", + "knip": "knip --no-exit-code --no-config-hints", + "lint": "biome lint", + "format": "biome format --write", + "pg-typegen": "npx kanel", + "start": "next start", + "stripe-dev": "stripe listen --forward-to https://localhost:3000/api/stripe-webhook", + "test": "npm run lint && npm run types", + "types": "tsc --noEmit", + "analyze": "cross-env ANALYZE=true next build" + }, + "version": "0.1.0", + "dependencies": { + "@dnd-kit/core": "^6.3.1", + "@dnd-kit/sortable": "^10.0.0", + "@dnd-kit/utilities": "^3.2.2", + "@fattureincloud/fattureincloud-ts-sdk": "^2.1.1", + "@hookform/resolvers": "^5.2.1", + "@next/bundle-analyzer": "^16.0.2", + "@radix-ui/react-dialog": "^1.1.15", + "@radix-ui/react-popover": "^1.1.14", + "@radix-ui/react-scroll-area": "^1.2.10", + "@radix-ui/react-slot": "^1.2.3", + "@radix-ui/react-toggle-group": "^1.1.10", + "@radix-ui/react-tooltip": "^1.2.8", + "@react-email/components": "0.5.0", + "@react-email/render": "^1.1.4", + "@stripe/react-stripe-js": "^3.9.0", + "@stripe/stripe-js": "^7.8.0", + "@t3-oss/env-nextjs": "^0.13.8", + "@tailwindcss/forms": "^0.5.10", + "@tailwindcss/postcss": "^4.1.11", + "@tailwindcss/typography": "^0.5.16", + "@tanstack/react-query": "^5.74.4", + "@tanstack/react-table": "^8.21.3", + "@tanstack/react-virtual": "^3.13.12", + "@tiptap/core": "^3.0.9", + "@tiptap/extension-link": "^3.0.9", + "@tiptap/extension-underline": "^3.0.9", + "@tiptap/pm": "^3.0.9", + "@tiptap/react": "^3.0.9", + "@tiptap/starter-kit": "^3.0.9", + "@trpc/client": "^11.1.0", + "@trpc/next": "^11.4.3", + "@trpc/react-query": "^11.1.0", + "@trpc/server": "^11.1.0", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "cmdk": "^1.1.1", + "cookies-next": "^6.1.0", + "date-fns": "^4.1.0", + "embla-carousel-react": "^8.6.0", + "framer-motion": "^12.8.0", + "frimousse": "^0.3.0", + "html2canvas-pro": "^1.5.13", + "ioredis": "^5.7.0", + "jose": "^6.0.12", + "js-cookie": "^3.0.5", + "jspdf": "^3.0.4", + "kysely": "^0.28.2", + "kysely-plugin-serialize": "^0.8.2", + "leaflet": "^1.9.4", + "leaflet-defaulticon-compatibility": "^0.1.2", + "lucide-react": "^0.536.0", + "next": "15.4.10", + "next-themes": "^0.4.6", + "nextjs-progressbar": "^0.0.16", + "nodemailer": "^7.0.5", + "nodemailer-html-to-text": "^3.2.0", + "nuqs": "^2.4.3", + "pg": "^8.16.3", + "postcss": "^8.5.6", + "radix-ui": "^1.4.3", + "react": "^19.1.0", + "react-colorful": "^5.6.1", + "react-day-picker": "^9.11.1", + "react-dom": "^19.1.1", + "react-email": "^4.2.8", + "react-hook-form": "^7.62.0", + "react-hot-toast": "^2.5.2", + "react-is": "^19.1.1", + "react-leaflet": "^5.0.0", + "react-number-format": "^5.4.4", + "react-pdf": "^10.2.0", + "react-phone-number-input": "^3.4.12", + "react-reverse-portal": "^2.3.0", + "react-select": "^5.10.2", + "react-to-print": "^3.1.1", + "react-use": "^17.6.0", + "recharts": "^2.15.4", + "sharp": "^0.34.3", + "stripe": "^20.1.2", + "superjson": "2.2.2", + "tailwind-merge": "3.3", + "tailwind-scrollbar": "^4.0.2", + "tailwindcss": "^4.1.11", + "tunnel-rat": "^0.1.2", + "tw-animate-css": "^1.3.6", + "type-fest": "^4.41.0", + "uuid": "^11.1.0", + "vaul": "^1.1.2", + "zod": "^4.1.12" + }, + "devDependencies": { + "@biomejs/biome": "^2.3.1", + "@hookform/devtools": "^4.4.0", + "@react-email/preview-server": "^4.2.8", + "@total-typescript/ts-reset": "^0.6.1", + "@types/js-cookie": "^3.0.6", + "@types/leaflet": "^1.9.20", + "@types/node": "^24.2.0", + "@types/nodemailer": "^6.4.17", + "@types/nodemailer-html-to-text": "^3.1.3", + "@types/pg": "^8.15.5", + "@types/react": "^19.1.8", + "@types/react-dom": "^19.1.7", + "@types/react-is": "^19.0.0", + "@types/uuid": "^10.0.0", + "cross-env": "^10.0.0", + "jiti": "^2.6.1", + "kanel": "^3.14.2", + "kanel-kysely": "^0.7.1", + "knip": "^5.62.0", + "npm-run-all": "^4.1.5", + "typescript": "^5.9.2" + }, + "ct3aMetadata": { + "initVersion": "7.9.0" + } +} \ No newline at end of file diff --git a/apps/infoalloggi/src/i18n/en.ts b/apps/infoalloggi/src/i18n/en.ts index 9631296..9805876 100644 --- a/apps/infoalloggi/src/i18n/en.ts +++ b/apps/infoalloggi/src/i18n/en.ts @@ -628,6 +628,7 @@ export const en: LangDict = { }, { href: "/area-riservata/admin/storage", title: "Storage" }, { href: "/area-riservata/admin/blacklist", title: "Black-list" }, + { href: "/area-riservata/admin/impostazioni", title: "Settings" }, ], title: "Settings", }, diff --git a/apps/infoalloggi/src/i18n/it.ts b/apps/infoalloggi/src/i18n/it.ts index 334684f..63fdf5d 100644 --- a/apps/infoalloggi/src/i18n/it.ts +++ b/apps/infoalloggi/src/i18n/it.ts @@ -633,6 +633,7 @@ export const it: LangDict = { }, { href: "/area-riservata/admin/storage", title: "Allegati" }, { href: "/area-riservata/admin/blacklist", title: "Black-list" }, + { href: "/area-riservata/admin/impostazioni", title: "Impostazioni" }, ], title: "Impostazioni", }, diff --git a/apps/infoalloggi/src/pages/area-riservata/admin/impostazioni.tsx b/apps/infoalloggi/src/pages/area-riservata/admin/impostazioni.tsx new file mode 100644 index 0000000..b5ce30a --- /dev/null +++ b/apps/infoalloggi/src/pages/area-riservata/admin/impostazioni.tsx @@ -0,0 +1,87 @@ +import Head from "next/head"; +import { AreaRiservataLayout } from "~/components/Layout"; +import type { NextPageWithLayout } from "~/pages/_app"; +import { useTranslation } from "~/providers/I18nProvider"; +import { api } from "~/utils/api"; + +const Impostazioni: NextPageWithLayout = () => { + const { t } = useTranslation(); + + return ( + <> + + {t.heads.area_riservata_titolo} + + + +
+
+
+

Impostazioni

+
+
+ +
+ + ); +}; +export default Impostazioni; + +// export const getServerSideProps = (async (context) => { +// const access_token = context.req.cookies.access_token; + +// const helpers = await TrpcAuthedFetchingIstance({ access_token }); +// if (helpers) { + +// return { +// props: { +// trpcState: helpers.trpc.dehydrate(), +// }, +// }; +// } +// return { +// props: {}, +// }; +// }) satisfies GetServerSideProps; + +Impostazioni.getLayout = function getLayout(page) { + return {page}; +}; + +const StripeSection = () => { + const { data, isLoading } = api.stripe.health.useQuery(); + if (isLoading) { + return
Loading...
; + } + console.log(data); + return ( +
+

+ Stripe Status: {data?.success ? "Sano" : "Non sano"} +

+

Endpoint Webhook:

+ {data?.endpoints?.data.map((endpoint) => ( +
+

+ ID: {endpoint.id} +

+

+ URL: {endpoint.url} +

+ +

Eventi attivi:

+
    + {endpoint.enabled_events.map((event) => ( +
  • {event}
  • + ))} +
+

Versione Api: {endpoint.api_version}

+

+ Stato: {endpoint.status} +

+
+
+ ))} +
+ ); +}; diff --git a/apps/infoalloggi/src/server/api/routers/stripe.ts b/apps/infoalloggi/src/server/api/routers/stripe.ts index 6f4c057..ba4a345 100644 --- a/apps/infoalloggi/src/server/api/routers/stripe.ts +++ b/apps/infoalloggi/src/server/api/routers/stripe.ts @@ -6,6 +6,7 @@ import { } from "~/server/api/trpc"; import { createIntentHandler, + stripeHealthCheck, whIntentCreatedHandler, whIntentFailedHandler, whIntentSucceededHandler, @@ -13,6 +14,9 @@ import { import { zPagamentoId } from "~/server/utils/zod_types"; export const stripeRouter = createTRPCRouter({ + health: publicProcedure.query(async () => { + return await stripeHealthCheck(); + }), createIntent: protectedProcedure .input( z.object({ diff --git a/apps/infoalloggi/src/server/controllers/stripe.controller.ts b/apps/infoalloggi/src/server/controllers/stripe.controller.ts index c29e178..6ac8989 100644 --- a/apps/infoalloggi/src/server/controllers/stripe.controller.ts +++ b/apps/infoalloggi/src/server/controllers/stripe.controller.ts @@ -11,6 +11,16 @@ import { getUser } from "~/server/services/user.service"; const stripeApi = new Stripe(env.STRIPE_SECRET_KEY); +export const stripeHealthCheck = async () => { + try { + const endpoints = await stripeApi.webhookEndpoints.list(); + return { success: true, endpoints }; + } catch (e) { + console.error("Stripe health check failed:", e); + return { success: false, endpoints: null }; + } +}; + export const createIntentHandler = async ({ paymentId, }: {