@lukoweb/apitogo-plugin-dev-portal-billing 0.1.60 → 0.1.61

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.mjs +112 -15
  2. package/package.json +2 -2
package/dist/index.mjs CHANGED
@@ -243,9 +243,13 @@ function useDevPortalApiBaseUrl() {
243
243
  if (providerData?.type === "dev-portal" && providerData.apiBaseUrl) return providerData.apiBaseUrl;
244
244
  return resolveDevPortalApiBaseUrl();
245
245
  }
246
+ function useIsDemoSession() {
247
+ return useAuth().isDemoSession === true;
248
+ }
246
249
  function useDevPortalPreviewMode() {
247
250
  const auth = useAuth();
248
251
  const apiBaseUrl = useDevPortalApiBaseUrl();
252
+ if (auth.isDemoSession) return false;
249
253
  if (auth.authMode === "preview" || auth.isBackendAvailable === false) return true;
250
254
  return isPlaceholderDevPortalApiUrl(apiBaseUrl);
251
255
  }
@@ -279,15 +283,49 @@ function useCancelSubscription() {
279
283
  });
280
284
  }
281
285
 
286
+ //#endregion
287
+ //#region src/utils/demo-session.ts
288
+ const DEMO_SUBSCRIPTION_KEY = "demo-subscription-key";
289
+ function resolveDemoPlanSku(plans) {
290
+ return plans?.[0]?.sku ?? "free";
291
+ }
292
+ function createDemoMeResponse(plans) {
293
+ const planSku = resolveDemoPlanSku(plans);
294
+ const periodEnd = /* @__PURE__ */ new Date();
295
+ periodEnd.setMonth(periodEnd.getMonth() + 1);
296
+ return {
297
+ userId: "00000000-0000-0000-0000-000000000001",
298
+ displayName: "Demo User",
299
+ subscriptions: [{
300
+ planSku,
301
+ status: "Active",
302
+ entitlementState: "active",
303
+ currentPeriodEnd: periodEnd.toISOString()
304
+ }]
305
+ };
306
+ }
307
+ function createDemoCredentials(plans) {
308
+ return {
309
+ idToken: "demo-id-token",
310
+ accessToken: null,
311
+ subscriptionKey: DEMO_SUBSCRIPTION_KEY,
312
+ gatewayBaseUrl: null,
313
+ planSku: resolveDemoPlanSku(plans),
314
+ expiresAt: null
315
+ };
316
+ }
317
+
282
318
  //#endregion
283
319
  //#region src/hooks/useMySubscriptions.ts
284
320
  function useMySubscriptions() {
285
321
  const auth = useAuth();
286
322
  const apiBaseUrl = useDevPortalApiBaseUrl();
287
323
  const isPreview = useDevPortalPreviewMode();
288
- return useQuery({
324
+ const isDemoSession = useIsDemoSession();
325
+ const { previewPlans } = useDevPortalBillingConfig();
326
+ const liveQuery = useQuery({
289
327
  queryKey: ["dev-portal-me", apiBaseUrl],
290
- enabled: auth.isAuthenticated && !isPreview && Boolean(apiBaseUrl),
328
+ enabled: auth.isAuthenticated && !isPreview && !isDemoSession && Boolean(apiBaseUrl),
291
329
  queryFn: async () => {
292
330
  const response = await fetch(`${apiBaseUrl}/api/v1/auth/me`, {
293
331
  method: "GET",
@@ -298,6 +336,18 @@ function useMySubscriptions() {
298
336
  return await response.json();
299
337
  }
300
338
  });
339
+ if (isDemoSession && auth.isAuthenticated) return {
340
+ ...liveQuery,
341
+ data: createDemoMeResponse(previewPlans),
342
+ error: null,
343
+ isError: false,
344
+ isFetching: false,
345
+ isLoading: false,
346
+ isPending: false,
347
+ isSuccess: true,
348
+ status: "success"
349
+ };
350
+ return liveQuery;
301
351
  }
302
352
 
303
353
  //#endregion
@@ -305,10 +355,11 @@ function useMySubscriptions() {
305
355
  function usePlans() {
306
356
  const apiBaseUrl = useDevPortalApiBaseUrl();
307
357
  const isPreview = useDevPortalPreviewMode();
358
+ const isDemoSession = useIsDemoSession();
308
359
  const { previewPlans } = useDevPortalBillingConfig();
309
360
  const liveQuery = useQuery({
310
361
  queryKey: ["dev-portal-plans", apiBaseUrl],
311
- enabled: !isPreview && Boolean(apiBaseUrl),
362
+ enabled: !isPreview && !isDemoSession && Boolean(apiBaseUrl),
312
363
  queryFn: async () => {
313
364
  if (!apiBaseUrl) return { plans: [] };
314
365
  const response = await fetch(`${apiBaseUrl}/api/v1/plans`, { headers: { Accept: "application/json" } });
@@ -316,7 +367,7 @@ function usePlans() {
316
367
  return await response.json();
317
368
  }
318
369
  });
319
- if (isPreview) {
370
+ if (isPreview || isDemoSession) {
320
371
  const data = parseManifestPlans(previewPlans);
321
372
  return {
322
373
  ...liveQuery,
@@ -340,6 +391,7 @@ function usePlans() {
340
391
  const AccountPlansPage = () => {
341
392
  const { previewPlans } = useDevPortalBillingConfig();
342
393
  const isPreview = useDevPortalPreviewMode();
394
+ const isDemoSession = useIsDemoSession();
343
395
  const livePlans = usePlans();
344
396
  const catalog = isPreview ? parseManifestPlans(previewPlans) : livePlans.data;
345
397
  const isLoading = isPreview ? false : livePlans.isLoading;
@@ -376,7 +428,7 @@ const AccountPlansPage = () => {
376
428
  })]
377
429
  })]
378
430
  }),
379
- isPreview ? /* @__PURE__ */ jsxs("div", {
431
+ isPreview && !isDemoSession ? /* @__PURE__ */ jsxs("div", {
380
432
  className: "mb-8 rounded-xl border border-dashed bg-muted/20 p-6 text-center text-sm text-muted-foreground",
381
433
  children: [
382
434
  "Local preview uses plans from",
@@ -395,6 +447,10 @@ const AccountPlansPage = () => {
395
447
  "). Subscribe and unsubscribe activate after the portal is published."
396
448
  ]
397
449
  }) : null,
450
+ isDemoSession ? /* @__PURE__ */ jsx("div", {
451
+ className: "mb-8 rounded-xl border border-dashed bg-muted/20 p-6 text-center text-sm text-muted-foreground",
452
+ children: "Local demo mode shows plans from your manifest with a sample active subscription. Billing actions activate after publish."
453
+ }) : null,
398
454
  cancelMutation.isError ? /* @__PURE__ */ jsx("div", {
399
455
  className: "mb-6 rounded-xl border border-destructive/30 bg-destructive/10 p-4 text-sm text-destructive",
400
456
  children: cancelMutation.error instanceof Error ? cancelMutation.error.message : "Failed to cancel subscription."
@@ -417,9 +473,9 @@ const AccountPlansPage = () => {
417
473
  mode: "account",
418
474
  subscriptionStatus: subscription?.status,
419
475
  isSubscribed,
420
- onUnsubscribe: isPreview ? void 0 : handleUnsubscribe,
476
+ onUnsubscribe: isPreview || isDemoSession ? void 0 : handleUnsubscribe,
421
477
  isCanceling: cancelingSku === plan.sku,
422
- cancelDisabled: isPreview
478
+ cancelDisabled: isPreview || isDemoSession
423
479
  }, plan.sku);
424
480
  })
425
481
  }) : !isLoading && !isError ? /* @__PURE__ */ jsx("div", {
@@ -497,6 +553,7 @@ const CheckoutPage = () => {
497
553
  const auth = useAuth();
498
554
  const apiBaseUrl = useDevPortalApiBaseUrl();
499
555
  const isPreview = useDevPortalPreviewMode();
556
+ const isDemoSession = useIsDemoSession();
500
557
  const plansPath = auth.isAuthenticated ? ACCOUNT_PLANS_PATH : PRICING_PATH;
501
558
  const checkoutQuery = useQuery({
502
559
  queryKey: [
@@ -505,7 +562,7 @@ const CheckoutPage = () => {
505
562
  planSku,
506
563
  auth.profile?.sub
507
564
  ],
508
- enabled: Boolean(planSku) && !isPreview && auth.isAuthenticated && Boolean(apiBaseUrl),
565
+ enabled: Boolean(planSku) && !isPreview && !isDemoSession && auth.isAuthenticated && Boolean(apiBaseUrl),
509
566
  retry: false,
510
567
  queryFn: async () => {
511
568
  const response = await fetch(`${apiBaseUrl}/api/v1/subscriptions/checkout`, {
@@ -540,6 +597,27 @@ const CheckoutPage = () => {
540
597
  })
541
598
  ]
542
599
  });
600
+ if (isDemoSession) return /* @__PURE__ */ jsxs("div", {
601
+ className: "mx-auto flex min-h-[50vh] max-w-lg flex-col items-center justify-center gap-4 px-4 text-center",
602
+ children: [
603
+ /* @__PURE__ */ jsx("h1", {
604
+ className: "text-2xl font-semibold tracking-tight",
605
+ children: "Demo checkout"
606
+ }),
607
+ /* @__PURE__ */ jsx("p", {
608
+ className: "text-muted-foreground",
609
+ children: "Checkout is simulated in local demo mode. Publish your portal to APItoGo to complete real subscriptions and billing."
610
+ }),
611
+ /* @__PURE__ */ jsx(Button$1, {
612
+ asChild: true,
613
+ variant: "outline",
614
+ children: /* @__PURE__ */ jsx(Link, {
615
+ to: "/account/subscriptions",
616
+ children: "View demo subscriptions"
617
+ })
618
+ })
619
+ ]
620
+ });
543
621
  if (isPreview) return /* @__PURE__ */ jsxs("div", {
544
622
  className: "mx-auto flex min-h-[50vh] max-w-lg flex-col items-center justify-center gap-4 px-4 text-center",
545
623
  children: [
@@ -591,9 +669,11 @@ function useCredentials() {
591
669
  const auth = useAuth();
592
670
  const apiBaseUrl = useDevPortalApiBaseUrl();
593
671
  const isPreview = useDevPortalPreviewMode();
594
- return useQuery({
672
+ const isDemoSession = useIsDemoSession();
673
+ const { previewPlans } = useDevPortalBillingConfig();
674
+ const liveQuery = useQuery({
595
675
  queryKey: ["dev-portal-credentials", apiBaseUrl],
596
- enabled: auth.isAuthenticated && !isPreview && Boolean(apiBaseUrl),
676
+ enabled: auth.isAuthenticated && !isPreview && !isDemoSession && Boolean(apiBaseUrl),
597
677
  queryFn: async () => {
598
678
  const response = await fetch(`${apiBaseUrl}/api/v1/credentials`, {
599
679
  method: "GET",
@@ -606,6 +686,18 @@ function useCredentials() {
606
686
  },
607
687
  retry: (failureCount, error) => error instanceof Error && error.message === "NO_ENTITLEMENT" ? false : failureCount < 2
608
688
  });
689
+ if (isDemoSession && auth.isAuthenticated) return {
690
+ ...liveQuery,
691
+ data: createDemoCredentials(previewPlans),
692
+ error: null,
693
+ isError: false,
694
+ isFetching: false,
695
+ isLoading: false,
696
+ isPending: false,
697
+ isSuccess: true,
698
+ status: "success"
699
+ };
700
+ return liveQuery;
609
701
  }
610
702
 
611
703
  //#endregion
@@ -613,6 +705,7 @@ function useCredentials() {
613
705
  const CredentialsPage = () => {
614
706
  const auth = useAuth();
615
707
  const isPreview = useDevPortalPreviewMode();
708
+ const isDemoSession = useIsDemoSession();
616
709
  const { data: credentials, isLoading, error } = useCredentials();
617
710
  const { data: catalog } = usePlans();
618
711
  const [revealed, setRevealed] = useState(false);
@@ -637,14 +730,18 @@ const CredentialsPage = () => {
637
730
  })]
638
731
  })]
639
732
  }),
640
- isPreview ? /* @__PURE__ */ jsx("div", {
733
+ isDemoSession ? /* @__PURE__ */ jsx("div", {
734
+ className: "mb-6 rounded-xl border border-dashed bg-muted/20 p-4 text-sm text-muted-foreground",
735
+ children: "Local demo credentials. Publish the portal to receive live subscription keys from your dev-portal backend."
736
+ }) : null,
737
+ isPreview && !isDemoSession ? /* @__PURE__ */ jsx("div", {
641
738
  className: "rounded-xl border border-dashed p-8 text-center",
642
739
  children: /* @__PURE__ */ jsx("p", {
643
740
  className: "text-muted-foreground",
644
741
  children: "API credentials are available after the developer portal is published."
645
742
  })
646
743
  }) : null,
647
- !isPreview && !auth.isAuthenticated ? /* @__PURE__ */ jsxs("div", {
744
+ !isPreview && !isDemoSession && !auth.isAuthenticated ? /* @__PURE__ */ jsxs("div", {
648
745
  className: "rounded-xl border border-dashed p-8 text-center",
649
746
  children: [/* @__PURE__ */ jsx("p", {
650
747
  className: "text-muted-foreground",
@@ -658,11 +755,11 @@ const CredentialsPage = () => {
658
755
  })
659
756
  })]
660
757
  }) : null,
661
- !isPreview && auth.isAuthenticated && isLoading ? /* @__PURE__ */ jsx("p", {
758
+ !isPreview && !isDemoSession && auth.isAuthenticated && isLoading ? /* @__PURE__ */ jsx("p", {
662
759
  className: "text-muted-foreground",
663
760
  children: "Loading credentials…"
664
761
  }) : null,
665
- !isPreview && auth.isAuthenticated && error ? /* @__PURE__ */ jsxs("div", {
762
+ !isPreview && !isDemoSession && auth.isAuthenticated && error ? /* @__PURE__ */ jsxs("div", {
666
763
  className: "rounded-xl border border-dashed p-8 text-center",
667
764
  children: [/* @__PURE__ */ jsx("p", {
668
765
  className: "text-muted-foreground",
@@ -676,7 +773,7 @@ const CredentialsPage = () => {
676
773
  })
677
774
  })]
678
775
  }) : null,
679
- !isPreview && credentials ? /* @__PURE__ */ jsxs("div", {
776
+ (isDemoSession || !isPreview) && credentials ? /* @__PURE__ */ jsxs("div", {
680
777
  className: "space-y-6",
681
778
  children: [
682
779
  /* @__PURE__ */ jsx("article", {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lukoweb/apitogo-plugin-dev-portal-billing",
3
- "version": "0.1.60",
3
+ "version": "0.1.61",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/zuplo/zudoku",
@@ -46,7 +46,7 @@
46
46
  "react": "19.2.4",
47
47
  "react-dom": "19.2.4",
48
48
  "tsdown": "0.20.3",
49
- "@lukoweb/apitogo": "0.1.60"
49
+ "@lukoweb/apitogo": "0.1.61"
50
50
  },
51
51
  "peerDependencies": {
52
52
  "react": ">=19.2.0",