import type { ActionFunctionArgs } from "react-router"; import prisma from "../db.server"; import { authenticate } from "../shopify.server"; import { forgetSubscriptions } from "../lib/partner.server"; import { bumpConfig } from "../services/config.server"; import { planFromSubscription } from "../services/plans"; /** * app/uninstalled: storefront behaviour stops immediately (proxy calls fail without a * session and the theme extension is removed by Shopify). Data is kept for 48 hours so a * quick reinstall keeps bookings; Shopify's shop/redact webhook then deletes everything. */ export const action = async ({ request }: ActionFunctionArgs) => { const { shop, topic, session, payload } = await authenticate.webhook(request); if (topic === "APP_UNINSTALLED") { forgetSubscriptions(); if (session) await prisma.session.deleteMany({ where: { shop } }); await prisma.shop.updateMany({ where: { domain: shop }, data: { uninstalledAt: new Date(), plan: "free" } }); // Shopify cancels app subscriptions on uninstall await prisma.hold.deleteMany({ where: { shop: { domain: shop } } }); await prisma.reminderJob.updateMany({ where: { shop: { domain: shop }, status: "pending" }, data: { status: "cancelled" } }); } // Managed pricing plan changes (upgrade, downgrade, cancellation), including ones made while the app is closed. if (topic === "APP_SUBSCRIPTIONS_UPDATE") { const sub = (payload as { app_subscription?: { name?: string; status?: string } }).app_subscription; const plan = sub?.status?.toUpperCase() === "ACTIVE" ? planFromSubscription(sub?.name) : "free"; const row = await prisma.shop.findUnique({ where: { domain: shop }, select: { id: true, plan: true } }); if (row && row.plan !== plan) { await prisma.shop.update({ where: { id: row.id }, data: { plan } }); await bumpConfig(row.id, shop); } } if (topic === "APP_SCOPES_UPDATE" && session) { const current = (payload as { current?: string[] }).current; if (current) await prisma.session.update({ where: { id: session.id }, data: { scope: current.toString() } }); } return new Response(); };