From bf73dc42f612d4416fc3b8450d7e0fca3f3e2246 Mon Sep 17 00:00:00 2001 From: Rajan Maurya Date: Mon, 3 Aug 2026 16:18:09 +0530 Subject: [PATCH 1/2] =?UTF-8?q?chore:=20initialize=20session=20branch=20?= =?UTF-8?q?=E2=80=94=20fix(dashboard):=20native=20store=20as=20locked=20pr?= =?UTF-8?q?imary=20for=20iOS/Android?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit From 4d27383eea6c29a194dd99ed695c2a580c07b84d Mon Sep 17 00:00:00 2001 From: Rajan Maurya Date: Mon, 3 Aug 2026 16:18:23 +0530 Subject: [PATCH 2/2] chore(source): update dashboard/components/providers/platform-providers-panel.tsx --- .../providers/platform-providers-panel.tsx | 90 +++++++++++-------- 1 file changed, 55 insertions(+), 35 deletions(-) diff --git a/dashboard/components/providers/platform-providers-panel.tsx b/dashboard/components/providers/platform-providers-panel.tsx index a59a7c8..167e88b 100644 --- a/dashboard/components/providers/platform-providers-panel.tsx +++ b/dashboard/components/providers/platform-providers-panel.tsx @@ -56,12 +56,13 @@ const CATALOG: { { key: "direct_upi", lane: "web", href: "/providers/upi" }, ] -const PLATFORMS: { key: string; label: string; native?: string; nativeHref?: string }[] = [ - { key: "ios", label: "iOS", native: "App Store (StoreKit 2)", nativeHref: "/providers/app-store" }, - { key: "android", label: "Android", native: "Google Play Billing", nativeHref: "/providers/google-play" }, +const PLATFORMS: { key: string; label: string; nativeKey?: string; native?: string; nativeHref?: string }[] = [ + { key: "ios", label: "iOS", nativeKey: "app_store", native: "App Store (StoreKit 2)", nativeHref: "/providers/app-store" }, + { key: "android", label: "Android", nativeKey: "google_play", native: "Google Play Billing", nativeHref: "/providers/google-play" }, { key: "desktop", label: "Desktop" }, { key: "web", label: "Web" }, ] +const isNativePlatform = (key: string) => key === "ios" || key === "android" type Sel = { primary: string; fallback: string } @@ -102,9 +103,15 @@ export function PlatformProvidersPanel({ if (!plat || plat === "any") continue if (rule.country_code || rule.currency || rule.product_type) continue ruleIdByPlatform[plat] = rule.id - const primary = methodToProvider.get(rule.priority_methods?.[0] ?? "") ?? "" - const fallback = methodToProvider.get(rule.priority_methods?.[1] ?? "") ?? "" - initSel[plat] = { primary, fallback } + if (isNativePlatform(plat)) { + // native store is the fixed primary; the single stored web method is the web-lane fallback + const fallback = methodToProvider.get(rule.priority_methods?.[0] ?? "") ?? "" + initSel[plat] = { primary: "", fallback } + } else { + const primary = methodToProvider.get(rule.priority_methods?.[0] ?? "") ?? "" + const fallback = methodToProvider.get(rule.priority_methods?.[1] ?? "") ?? "" + initSel[plat] = { primary, fallback } + } } const [sel, setSel] = useState>(initSel) @@ -128,8 +135,10 @@ export function PlatformProvidersPanel({ return c }) } - // ordered [primary, fallback], drop empties/dupes, map to methods - const providers = [next.primary, next.fallback].filter((p, i, a) => p && connected.has(p) && a.indexOf(p) === i) + // ordered providers → methods. Native platforms: only the web-lane provider (native store is the + // fixed primary, not a routable method). Desktop/Web: [primary, fallback]. + const ordered = isNativePlatform(platform) ? [next.fallback] : [next.primary, next.fallback] + const providers = ordered.filter((p, i, a) => p && connected.has(p) && a.indexOf(p) === i) const methods = providers.map((p) => providerInfo.get(p)?.method).filter(Boolean) as string[] if (methods.length > 0) { const res = await fetch("/api/routing-rules", { @@ -228,10 +237,10 @@ export function PlatformProvidersPanel({ primary + fallback

- For each platform, pick a primary provider and an optional{" "} - fallback used when the primary can't serve a customer. Saves instantly. - iOS & Android digital subscriptions always use the native store; the dropdowns route the - web / physical-goods lane there, and full checkout on Desktop / Web. + iOS & Android use the native store (App Store / Google Play) as the + primary for digital subscriptions — mandatory per store policy — and the web-lane dropdown + handles physical goods there. Desktop & Web pick a primary + optional + fallback web provider. Saves instantly.

{noProviders ? ( @@ -246,44 +255,55 @@ export function PlatformProvidersPanel({
Platform Primary - Fallback + Fallback / web-lane
{PLATFORMS.map((p) => { const cur = sel[p.key] ?? { primary: "", fallback: "" } + const native = isNativePlatform(p.key) + const nativeConnected = p.nativeKey ? connected.has(p.nativeKey) : false return (
-
+
{p.label} - {p.native && ( - - {connected.has(p.key === "ios" ? "app_store" : "google_play") ? "" : "connect "}native - auto - - )}
{/* Primary */} - save(p.key, { primary: v, fallback: cur.fallback === v ? "" : cur.fallback })} - /> + {native ? ( + // native store is the mandatory primary for iOS/Android digital — locked, not routable +
+ {p.native} + native · auto + {nativeConnected ? ( + + connected + + ) : ( + connect → + )} +
+ ) : ( + save(p.key, { primary: v, fallback: cur.fallback === v ? "" : cur.fallback })} + /> + )} - {/* Fallback (excludes the chosen primary) */} + {/* Fallback / web-lane provider */}
o !== cur.primary)} + placeholder={native ? "Web lane — none" : "None"} + options={native ? options : options.filter((o) => o !== cur.primary)} pretty={pretty} fee={fee} - disabled={saving === p.key || !cur.primary} + disabled={saving === p.key || (!native && !cur.primary)} onChange={(v) => save(p.key, { primary: cur.primary, fallback: v })} /> {saving === p.key && saving…}