// app/routes/app.billing.success.jsx import React, { useMemo } from "react"; import { json } from "@remix-run/node"; import { useLoaderData } from "@remix-run/react"; import { Page, Layout, Card, BlockStack, InlineStack, Text, Badge, Divider, Button, Banner, Box, } from "@shopify/polaris"; import { TitleBar } from "@shopify/app-bridge-react"; import { authenticate } from "../shopify.server"; /** =========================== * LOADER * =========================== */ export const loader = async ({ request }) => { const { admin, session } = await authenticate.admin(request); const shop = session.shop; const shopDomain = shop.split(".")[0]; // Pull richer subscription details: interval + price const resp = await admin.graphql(` query ActiveSubForSuccess { currentAppInstallation { activeSubscriptions { id name status trialDays createdAt currentPeriodEnd test lineItems { plan { appRecurringPricingDetails { interval price { amount currencyCode } } } } } } } `); const result = await resp.json(); const subscription = result?.data?.currentAppInstallation?.activeSubscriptions?.[0] || null; // Detect recent activation (today or last 2 days) let recentActivation = false; if (subscription?.createdAt) { const created = new Date(subscription.createdAt); const now = new Date(); const diffMs = now.getTime() - created.getTime(); const TWO_DAYS_MS = 2 * 24 * 60 * 60 * 1000; recentActivation = diffMs >= 0 && diffMs <= TWO_DAYS_MS && (subscription.status === "ACTIVE" || subscription.status === "TRIAL"); } return json({ subscription, shop, shopDomain, recentActivation }); }; /** =========================== * HELPERS * =========================== */ function formatDate(d) { if (!d) return "N/A"; return new Date(d).toLocaleString(undefined, { year: "numeric", month: "short", day: "numeric", }); } function getRecurringLine(subscription) { const items = subscription?.lineItems || []; for (const li of items) { const r = li?.plan?.appRecurringPricingDetails; if (r) return r; } return null; } function intervalToLabel(interval) { switch (interval) { case "ANNUAL": return "Annual"; case "EVERY_30_DAYS": return "Monthly"; default: return interval || "N/A"; } } /** =========================== * PAGE * =========================== */ export default function BillingSuccess() { const { subscription, shop, shopDomain, recentActivation } = useLoaderData(); const recurring = getRecurringLine(subscription); const cadenceLabel = intervalToLabel(recurring?.interval); const priceText = recurring?.price?.amount && recurring?.price?.currencyCode ? `${recurring.price.amount} ${recurring.price.currencyCode}` : "N/A"; // Trial end const trialEndStr = useMemo(() => { if (!subscription?.trialDays || !subscription?.createdAt) return "N/A"; const start = new Date(subscription.createdAt); const end = new Date(start); end.setDate(end.getDate() + subscription.trialDays); return formatDate(end); }, [subscription?.trialDays, subscription?.createdAt]); // Trial days left (if still on TRIAL) const trialDaysLeft = useMemo(() => { if ( subscription?.status !== "TRIAL" || !subscription?.trialDays || !subscription?.createdAt ) return null; const start = new Date(subscription.createdAt); const trialEnd = new Date(start); trialEnd.setDate(trialEnd.getDate() + subscription.trialDays); const now = new Date(); const left = Math.ceil( (trialEnd.getTime() - now.getTime()) / (1000 * 60 * 60 * 24) ); return Math.max(0, left); }, [subscription?.status, subscription?.trialDays, subscription?.createdAt]); const showError = !subscription; const nextRenewal = formatDate(subscription?.currentPeriodEnd); const createdAt = formatDate(subscription?.createdAt); return ( {showError ? (

We couldn’t find an active subscription for this shop. If you just approved billing, it may not be visible yet. You can proceed to the billing screen to create or refresh your subscription.

) : recentActivation ? (

Congratulations! Your plan is now active. You’re all set to sync brands, build collections, and automate your Turn14 catalog.

Activated: {createdAt}  •  Status:{" "} {subscription.status}

) : (

Your subscription is active. Below are the full details of your plan, trial, and renewal.

)}
Plan Overview Plan Name {subscription?.name || "Starter Sync"} Billing Cadence {cadenceLabel} Price {priceText} Billing & Trial Status {subscription?.status || "N/A"} {subscription?.test && Test} Trial {subscription?.trialDays ? `${subscription.trialDays} days` : "N/A"} {trialDaysLeft != null && — {trialDaysLeft} day(s) left} Trial Ends {trialEndStr} Next Renewal / Period End {nextRenewal} Subscription Metadata Subscription ID {subscription?.id || "N/A"} Created / Activated {createdAt} Shop {shop}
); }