fix(billing): register plans approved as test charges; reset plan on uninstall
App Store review 1.2.2: the reviewer's store got a test charge, but billing.check ran with isTest:false and ignored it, so the approved plan never registered. Checks now count test subscriptions too, and cancel uses each subscription's own test flag. Uninstall resets the stored plan to free (Shopify cancels the subscription), so a reinstall asks for approval again. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
799789ca28
commit
19b403f98f
@ -16,7 +16,9 @@ import { type CustomizationKind, findCustomizations, findGuard, gql, setCustomiz
|
||||
export const loader = async ({ request }: LoaderFunctionArgs) => {
|
||||
const { cfg, admin, billing } = await adminContext(request);
|
||||
// Keep the stored plan in sync with Shopify billing.
|
||||
const check = await billing.check({ plans: [PLAN_GROWTH, PLAN_PRO], isTest: await billingIsTest(admin) });
|
||||
// isTest: true counts test subscriptions too. Shopify makes the charge a test on stores that can't be billed
|
||||
// (including App Store review stores), and those must still unlock the plan.
|
||||
const check = await billing.check({ plans: [PLAN_GROWTH, PLAN_PRO], isTest: true });
|
||||
const plan = check.appSubscriptions.find((s) => s.status === "ACTIVE")?.name ?? "free";
|
||||
if (plan !== cfg.shop.plan) await prisma.shop.update({ where: { id: cfg.shop.id }, data: { plan } });
|
||||
const guard = await findGuard(admin).catch(() => null);
|
||||
@ -57,8 +59,8 @@ export const action = async ({ request }: ActionFunctionArgs) => {
|
||||
const plan = intent.slice(5);
|
||||
const isTest = await billingIsTest(admin);
|
||||
if (plan === "free") {
|
||||
const check = await billing.check({ plans: [PLAN_GROWTH, PLAN_PRO], isTest });
|
||||
for (const sub of check.appSubscriptions) await billing.cancel({ subscriptionId: sub.id, isTest, prorate: true });
|
||||
const check = await billing.check({ plans: [PLAN_GROWTH, PLAN_PRO], isTest: true });
|
||||
for (const sub of check.appSubscriptions) await billing.cancel({ subscriptionId: sub.id, isTest: sub.test, prorate: true });
|
||||
await prisma.shop.update({ where: { id: cfg.shop.id }, data: { plan: "free" } });
|
||||
return { ok: true, message: "Switched to Starter" };
|
||||
}
|
||||
|
||||
@ -11,7 +11,7 @@ export const action = async ({ request }: ActionFunctionArgs) => {
|
||||
const { shop, topic, session, payload } = await authenticate.webhook(request);
|
||||
if (topic === "APP_UNINSTALLED") {
|
||||
if (session) await prisma.session.deleteMany({ where: { shop } });
|
||||
await prisma.shop.updateMany({ where: { domain: shop }, data: { uninstalledAt: new Date() } });
|
||||
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" } });
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user