recur-tw 0.10.8 → 0.11.0

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.
package/README.md CHANGED
@@ -211,6 +211,7 @@ function ProductsPage() {
211
211
 
212
212
  - `useProducts(options?)` - 取得商品列表
213
213
  - `useRecur()` - 取得 checkout 函式與狀態
214
+ - `useCustomer()` - 檢查客戶權限與訂閱狀態
214
215
 
215
216
  ```tsx
216
217
  // 篩選商品類型
@@ -218,6 +219,57 @@ const { data: subscriptions } = useProducts({ type: 'SUBSCRIPTION' });
218
219
  const { data: oneTimeProducts } = useProducts({ type: 'ONE_TIME' });
219
220
  ```
220
221
 
222
+ #### 權限檢查(useCustomer)
223
+
224
+ 檢查客戶是否有特定產品的訂閱權限,無需自建資料庫或處理 Webhook:
225
+
226
+ ```tsx
227
+ import { RecurProvider, useCustomer } from 'recur-tw';
228
+
229
+ // 1. Provider 需傳入 customer 識別資訊
230
+ function App() {
231
+ const user = useAuth(); // 你的認證系統
232
+ return (
233
+ <RecurProvider
234
+ config={{ publishableKey: 'pk_live_xxx' }}
235
+ customer={{ email: user?.email }}
236
+ >
237
+ <PremiumFeature />
238
+ </RecurProvider>
239
+ );
240
+ }
241
+
242
+ // 2. 使用 check() 檢查權限
243
+ function PremiumFeature() {
244
+ const { check, isLoading } = useCustomer();
245
+
246
+ if (isLoading) return <Spinner />;
247
+
248
+ const { allowed } = check({ product: 'pro-plan' });
249
+
250
+ if (!allowed) {
251
+ return <UpgradePrompt />;
252
+ }
253
+
254
+ return <PremiumContent />;
255
+ }
256
+
257
+ // 3. 顯示訂閱狀態
258
+ function AccountPage() {
259
+ const { customer, subscription, entitlements } = useCustomer();
260
+
261
+ return (
262
+ <div>
263
+ <p>方案:{subscription?.product.name}</p>
264
+ <p>到期日:{subscription?.currentPeriodEnd}</p>
265
+ <p>擁有權限:{entitlements.map(e => e.product).join(', ')}</p>
266
+ </div>
267
+ );
268
+ }
269
+ ```
270
+
271
+ > **安全提醒:** `check()` 從本地快取讀取,可被繞過。僅用於 UI 控制,敏感操作請在後端驗證。
272
+
221
273
  ---
222
274
 
223
275
  ### 方式五:`<recur-checkout>` Web Component
@@ -244,15 +296,13 @@ const { data: oneTimeProducts } = useProducts({ type: 'ONE_TIME' });
244
296
  後端驗證與管理:
245
297
 
246
298
  ```typescript
247
- import { RecurServer } from 'recur-tw/server';
299
+ import { Recur } from 'recur-tw/server';
248
300
 
249
- const recur = new RecurServer({
250
- secretKey: 'sk_live_xxx'
251
- });
301
+ const recur = new Recur(process.env.RECUR_SECRET_KEY!);
252
302
 
253
303
  // 建立 Portal Session(讓客戶管理訂閱)
254
- const session = await recur.portalSessions.create({
255
- customerId: 'cus_xxx',
304
+ const session = await recur.portal.sessions.create({
305
+ customer: 'cus_xxx',
256
306
  returnUrl: 'https://yoursite.com/account'
257
307
  });
258
308
 
@@ -260,6 +310,60 @@ const session = await recur.portalSessions.create({
260
310
  res.redirect(session.url);
261
311
  ```
262
312
 
313
+ ### 權限檢查(Server-side)
314
+
315
+ 在 API 路由中驗證客戶權限,確保安全:
316
+
317
+ ```typescript
318
+ import { Recur } from 'recur-tw/server';
319
+
320
+ const recur = new Recur(process.env.RECUR_SECRET_KEY!);
321
+
322
+ // API 路由範例
323
+ export async function GET(request: Request) {
324
+ const user = await getUser(request);
325
+
326
+ // 檢查單一產品權限
327
+ const { allowed } = await recur.entitlements.check({
328
+ product: 'pro-plan',
329
+ customer: { email: user.email },
330
+ });
331
+
332
+ if (!allowed) {
333
+ return Response.json(
334
+ { error: '請升級到 Pro 方案' },
335
+ { status: 403 }
336
+ );
337
+ }
338
+
339
+ return Response.json(protectedData);
340
+ }
341
+
342
+ // 列出所有權限
343
+ const { entitlements } = await recur.entitlements.list({
344
+ customer: { email: 'user@example.com' },
345
+ });
346
+
347
+ console.log(entitlements);
348
+ // [
349
+ // { product: 'pro-plan', status: 'active', subscriptionId: 'sub_xxx' },
350
+ // { product: 'addon-ai', status: 'active', subscriptionId: 'sub_yyy' },
351
+ // ]
352
+ ```
353
+
354
+ **客戶識別方式(擇一):**
355
+
356
+ ```typescript
357
+ // 方式一:Email(推薦)
358
+ customer: { email: 'user@example.com' }
359
+
360
+ // 方式二:External ID(你的系統 User ID)
361
+ customer: { externalId: 'usr_12345' }
362
+
363
+ // 方式三:Recur Customer ID
364
+ customer: { id: 'cus_xxx' }
365
+ ```
366
+
263
367
  ---
264
368
 
265
369
  ## API Key
package/dist/index.cjs CHANGED
@@ -2674,16 +2674,27 @@ function toCamelCase(obj) {
2674
2674
 
2675
2675
  // package.json
2676
2676
  var package_default = {
2677
- version: "0.10.8"};
2677
+ version: "0.11.0"};
2678
2678
  var SDK_VERSION = package_default.version;
2679
2679
  var SDK_TYPE = "react";
2680
2680
  var RecurContext = React.createContext(null);
2681
- function RecurProvider({ children, config: initialConfig = {} }) {
2681
+ var CustomerContext = React.createContext(null);
2682
+ function RecurProvider({ children, config: initialConfig = {}, customer: customerIdentifier }) {
2682
2683
  const [config, setConfig] = React.useState({
2683
2684
  checkoutMode: "embedded",
2684
2685
  ...initialConfig
2685
2686
  });
2686
2687
  const [isCheckingOut, setIsCheckingOut] = React.useState(false);
2688
+ const [customerData, setCustomerData] = React.useState({
2689
+ customer: null,
2690
+ subscription: null,
2691
+ entitlements: []
2692
+ });
2693
+ const [customerIsLoading, setCustomerIsLoading] = React.useState(false);
2694
+ const [customerError, setCustomerError] = React.useState(null);
2695
+ const lastSuccessfulCache = React__default.default.useRef(null);
2696
+ const isRefetching = React__default.default.useRef(false);
2697
+ const refetchDebounceTimer = React__default.default.useRef(null);
2687
2698
  React__default.default.useEffect(() => {
2688
2699
  setConfig({
2689
2700
  checkoutMode: "embedded",
@@ -2693,6 +2704,89 @@ function RecurProvider({ children, config: initialConfig = {} }) {
2693
2704
  React__default.default.useEffect(() => {
2694
2705
  setIsCheckingOut(false);
2695
2706
  }, [config.checkoutMode]);
2707
+ const fetchEntitlements = React.useCallback(async () => {
2708
+ if (!config.publishableKey) {
2709
+ console.warn("[Recur SDK] Cannot fetch entitlements: publishableKey is required");
2710
+ return;
2711
+ }
2712
+ if (!customerIdentifier?.email && !customerIdentifier?.externalId && !customerIdentifier?.id) {
2713
+ setCustomerData({ customer: null, subscription: null, entitlements: [] });
2714
+ return;
2715
+ }
2716
+ setCustomerIsLoading(true);
2717
+ setCustomerError(null);
2718
+ try {
2719
+ const baseUrl = config.baseUrl || "https://api.recur.tw";
2720
+ const params = new URLSearchParams();
2721
+ if (customerIdentifier.email) params.set("email", customerIdentifier.email);
2722
+ if (customerIdentifier.externalId) params.set("external_id", customerIdentifier.externalId);
2723
+ if (customerIdentifier.id) params.set("customer_id", customerIdentifier.id);
2724
+ const response = await fetch(`${baseUrl}/v1/customers/entitlements?${params.toString()}`, {
2725
+ method: "GET",
2726
+ headers: {
2727
+ "X-Recur-Publishable-Key": config.publishableKey,
2728
+ "X-Recur-SDK-Type": SDK_TYPE,
2729
+ "X-Recur-SDK-Version": SDK_VERSION
2730
+ }
2731
+ });
2732
+ if (!response.ok) {
2733
+ const errorData = await response.json().catch(() => ({}));
2734
+ throw new Error(errorData.error?.message || "Failed to fetch entitlements");
2735
+ }
2736
+ const rawResult = await response.json();
2737
+ const result = toCamelCase(rawResult);
2738
+ const newData = {
2739
+ customer: result.customer || null,
2740
+ subscription: result.subscription || null,
2741
+ entitlements: result.entitlements || []
2742
+ };
2743
+ setCustomerData(newData);
2744
+ lastSuccessfulCache.current = newData;
2745
+ } catch (error) {
2746
+ console.error("[Recur SDK] Failed to fetch entitlements:", error);
2747
+ setCustomerError(error instanceof Error ? error : new Error("Failed to fetch entitlements"));
2748
+ if (lastSuccessfulCache.current) {
2749
+ setCustomerData(lastSuccessfulCache.current);
2750
+ }
2751
+ } finally {
2752
+ setCustomerIsLoading(false);
2753
+ }
2754
+ }, [config.publishableKey, config.baseUrl, customerIdentifier?.email, customerIdentifier?.externalId, customerIdentifier?.id]);
2755
+ React__default.default.useEffect(() => {
2756
+ fetchEntitlements();
2757
+ }, [fetchEntitlements]);
2758
+ const refetch = React.useCallback(async () => {
2759
+ if (isRefetching.current) {
2760
+ console.log("[Recur SDK] Refetch skipped - already in progress");
2761
+ return;
2762
+ }
2763
+ if (refetchDebounceTimer.current) {
2764
+ clearTimeout(refetchDebounceTimer.current);
2765
+ }
2766
+ isRefetching.current = true;
2767
+ try {
2768
+ await fetchEntitlements();
2769
+ } finally {
2770
+ refetchDebounceTimer.current = setTimeout(() => {
2771
+ isRefetching.current = false;
2772
+ }, 300);
2773
+ }
2774
+ }, [fetchEntitlements]);
2775
+ React__default.default.useEffect(() => {
2776
+ return () => {
2777
+ if (refetchDebounceTimer.current) {
2778
+ clearTimeout(refetchDebounceTimer.current);
2779
+ }
2780
+ };
2781
+ }, []);
2782
+ const customerContextValue = React.useMemo(() => ({
2783
+ customer: customerData.customer,
2784
+ subscription: customerData.subscription,
2785
+ entitlements: customerData.entitlements,
2786
+ isLoading: customerIsLoading,
2787
+ error: customerError,
2788
+ refetch
2789
+ }), [customerData, customerIsLoading, customerError, refetch]);
2696
2790
  const updateConfig = React.useCallback((newConfig) => {
2697
2791
  setConfig((prev) => ({ ...prev, ...newConfig }));
2698
2792
  }, []);
@@ -3420,7 +3514,7 @@ function RecurProvider({ children, config: initialConfig = {} }) {
3420
3514
  }),
3421
3515
  [config, checkout, fetchProducts, fetchPlans, isCheckingOut, updateConfig, getCheckoutStatus]
3422
3516
  );
3423
- return /* @__PURE__ */ jsxRuntime.jsx(RecurContext.Provider, { value, children });
3517
+ return /* @__PURE__ */ jsxRuntime.jsx(RecurContext.Provider, { value, children: /* @__PURE__ */ jsxRuntime.jsx(CustomerContext.Provider, { value: customerContextValue, children }) });
3424
3518
  }
3425
3519
  function useRecur() {
3426
3520
  const context = React.useContext(RecurContext);
@@ -3510,8 +3604,47 @@ function useSubscribe(options = {}) {
3510
3604
  reset
3511
3605
  };
3512
3606
  }
3607
+ function useCustomer() {
3608
+ const context = React.useContext(CustomerContext);
3609
+ if (!context) {
3610
+ throw new Error("useCustomer must be used within a RecurProvider");
3611
+ }
3612
+ const { customer, subscription, entitlements, isLoading: isLoading2, error, refetch } = context;
3613
+ const check = React.useCallback(
3614
+ (options) => {
3615
+ if (!customer) {
3616
+ return { allowed: false, reason: "no_customer" };
3617
+ }
3618
+ if (entitlements.length === 0) {
3619
+ return { allowed: false, reason: "no_subscription" };
3620
+ }
3621
+ const entitlement = entitlements.find(
3622
+ (e) => e.product === options.product || e.productId === options.product
3623
+ );
3624
+ if (!entitlement) {
3625
+ return { allowed: false, reason: "wrong_product" };
3626
+ }
3627
+ let matchingSubscription;
3628
+ if (subscription && (subscription.product.slug === options.product || subscription.product.id === options.product)) {
3629
+ matchingSubscription = subscription;
3630
+ }
3631
+ return { allowed: true, subscription: matchingSubscription };
3632
+ },
3633
+ [customer, entitlements, subscription]
3634
+ );
3635
+ return {
3636
+ customer,
3637
+ subscription,
3638
+ entitlements,
3639
+ check,
3640
+ refetch,
3641
+ isLoading: isLoading2,
3642
+ error
3643
+ };
3644
+ }
3513
3645
 
3514
3646
  exports.RecurProvider = RecurProvider;
3647
+ exports.useCustomer = useCustomer;
3515
3648
  exports.usePlans = useProducts;
3516
3649
  exports.useProducts = useProducts;
3517
3650
  exports.useRecur = useRecur;
package/dist/index.d.cts CHANGED
@@ -551,6 +551,173 @@ interface UniPaymentInstance {
551
551
  }) => Promise<any>;
552
552
  onUpdate?: (callback: (data: any) => void) => void;
553
553
  }
554
+ /**
555
+ * Customer identifier for entitlements lookup
556
+ * At least one identifier must be provided
557
+ */
558
+ interface CustomerIdentifier {
559
+ /**
560
+ * Customer's email address
561
+ */
562
+ email?: string;
563
+ /**
564
+ * Customer's external ID from your system
565
+ */
566
+ externalId?: string;
567
+ /**
568
+ * Customer's internal ID in Recur
569
+ */
570
+ id?: string;
571
+ }
572
+ /**
573
+ * Entitlement status derived from subscription status
574
+ */
575
+ type EntitlementStatus = 'active' | 'trialing' | 'past_due' | 'canceled';
576
+ /**
577
+ * An entitlement represents a customer's access to a product
578
+ * Computed from active subscriptions
579
+ */
580
+ interface Entitlement {
581
+ /**
582
+ * Product slug (URL-friendly identifier)
583
+ * @example 'pro-plan'
584
+ */
585
+ product: string;
586
+ /**
587
+ * Product ID
588
+ * @example 'prod_abc123'
589
+ */
590
+ productId: string;
591
+ /**
592
+ * Current status of the entitlement
593
+ */
594
+ status: EntitlementStatus;
595
+ /**
596
+ * Associated subscription ID
597
+ * @example 'sub_xyz789'
598
+ */
599
+ subscriptionId: string;
600
+ }
601
+ /**
602
+ * Options for the check() method
603
+ */
604
+ interface CheckOptions {
605
+ /**
606
+ * Product slug or ID to check access for
607
+ * @example 'pro-plan' or 'prod_abc123'
608
+ */
609
+ product: string;
610
+ }
611
+ /**
612
+ * Reason why access was denied
613
+ */
614
+ type CheckDeniedReason = 'no_customer' | 'no_subscription' | 'expired' | 'wrong_product';
615
+ /**
616
+ * Result of the check() method
617
+ */
618
+ interface CheckResult {
619
+ /**
620
+ * Whether the customer has access to the product
621
+ */
622
+ allowed: boolean;
623
+ /**
624
+ * Reason for denial (only present when allowed is false)
625
+ */
626
+ reason?: CheckDeniedReason;
627
+ /**
628
+ * Associated subscription (only present when allowed is true)
629
+ */
630
+ subscription?: EntitlementSubscription;
631
+ }
632
+ /**
633
+ * Simplified subscription info returned in check result
634
+ */
635
+ interface EntitlementSubscription {
636
+ /**
637
+ * Subscription ID
638
+ */
639
+ id: string;
640
+ /**
641
+ * Subscription status
642
+ */
643
+ status: string;
644
+ /**
645
+ * Product information
646
+ */
647
+ product: {
648
+ id: string;
649
+ slug: string;
650
+ name: string;
651
+ };
652
+ /**
653
+ * End of current billing period (ISO 8601)
654
+ */
655
+ currentPeriodEnd: string;
656
+ }
657
+ /**
658
+ * Customer info returned from entitlements API
659
+ */
660
+ interface EntitlementCustomer {
661
+ /**
662
+ * Customer ID
663
+ */
664
+ id: string;
665
+ /**
666
+ * Customer email
667
+ */
668
+ email: string;
669
+ /**
670
+ * Customer name
671
+ */
672
+ name?: string | null;
673
+ /**
674
+ * External ID from your system
675
+ */
676
+ externalId?: string | null;
677
+ }
678
+ /**
679
+ * Return type of the useCustomer hook
680
+ */
681
+ interface UseCustomerResult {
682
+ /**
683
+ * Customer information (null if not found or loading)
684
+ */
685
+ customer: EntitlementCustomer | null;
686
+ /**
687
+ * The most recent active subscription (null if none)
688
+ */
689
+ subscription: EntitlementSubscription | null;
690
+ /**
691
+ * All active entitlements for this customer
692
+ */
693
+ entitlements: Entitlement[];
694
+ /**
695
+ * Check if customer has access to a specific product
696
+ * Synchronous - reads from local cache
697
+ *
698
+ * @example
699
+ * const { allowed } = check({ product: 'pro-plan' });
700
+ * if (!allowed) return <UpgradePrompt />;
701
+ */
702
+ check: (options: CheckOptions) => CheckResult;
703
+ /**
704
+ * Manually refresh entitlements from the API
705
+ * Call this after checkout to update permissions
706
+ *
707
+ * @example
708
+ * await checkout({ productId: 'prod_xxx' });
709
+ * await refetch(); // Update entitlements
710
+ */
711
+ refetch: () => Promise<void>;
712
+ /**
713
+ * Whether entitlements are currently being fetched
714
+ */
715
+ isLoading: boolean;
716
+ /**
717
+ * Error if the last fetch failed
718
+ */
719
+ error: Error | null;
720
+ }
554
721
 
555
722
  /**
556
723
  * Recur Payment Form Web Component (混合模式)
@@ -747,6 +914,21 @@ declare global {
747
914
  interface RecurProviderProps {
748
915
  children: React.ReactNode;
749
916
  config?: RecurConfig;
917
+ /**
918
+ * Customer identifier for entitlements lookup
919
+ * When provided, the SDK will fetch the customer's entitlements on mount
920
+ *
921
+ * @example
922
+ * ```tsx
923
+ * <RecurProvider
924
+ * config={{ publishableKey: 'pk_test_xxx' }}
925
+ * customer={{ email: user?.email }}
926
+ * >
927
+ * <App />
928
+ * </RecurProvider>
929
+ * ```
930
+ */
931
+ customer?: CustomerIdentifier;
750
932
  }
751
933
  /**
752
934
  * RecurProvider
@@ -760,7 +942,7 @@ interface RecurProviderProps {
760
942
  * </RecurProvider>
761
943
  * ```
762
944
  */
763
- declare function RecurProvider({ children, config: initialConfig }: RecurProviderProps): react_jsx_runtime.JSX.Element;
945
+ declare function RecurProvider({ children, config: initialConfig, customer: customerIdentifier }: RecurProviderProps): react_jsx_runtime.JSX.Element;
764
946
 
765
947
  /**
766
948
  * useRecur Hook
@@ -946,4 +1128,111 @@ interface UseSubscribeResult {
946
1128
  */
947
1129
  declare function useSubscribe(options?: UseSubscribeOptions): UseSubscribeResult;
948
1130
 
949
- export { type CheckoutError, type CheckoutOptions, type CheckoutResult, type Plan, type PlansResult, type Product, type ProductMetadata, type ProductsResult, type RecurConfig, type RecurContextValue, RecurProvider, type RecurProviderProps, type SubscriptionResult, type UseProductsOptions as UsePlansOptions, type UseProductsResult as UsePlansResult, type UseProductsOptions, type UseProductsResult, type UseSubscribeOptions, type UseSubscribeResult, useProducts as usePlans, useProducts, useRecur, useSubscribe };
1131
+ /**
1132
+ * useCustomer Hook
1133
+ *
1134
+ * Access customer entitlements and check product access without requiring
1135
+ * your own database or webhook integration. The hook fetches entitlements
1136
+ * from Recur on mount and provides synchronous access via the check() method.
1137
+ *
1138
+ * Must be used within a RecurProvider with a customer identifier:
1139
+ * ```tsx
1140
+ * <RecurProvider
1141
+ * config={{ publishableKey: 'pk_xxx' }}
1142
+ * customer={{ email: user?.email }}
1143
+ * >
1144
+ * <App />
1145
+ * </RecurProvider>
1146
+ * ```
1147
+ *
1148
+ * ## Basic Usage - Permission Check
1149
+ * @example
1150
+ * ```tsx
1151
+ * function PremiumFeature() {
1152
+ * const { check, isLoading } = useCustomer();
1153
+ *
1154
+ * if (isLoading) return <Spinner />;
1155
+ *
1156
+ * const { allowed } = check({ product: 'pro-plan' });
1157
+ *
1158
+ * if (!allowed) {
1159
+ * return <UpgradePrompt />;
1160
+ * }
1161
+ *
1162
+ * return <PremiumContent />;
1163
+ * }
1164
+ * ```
1165
+ *
1166
+ * ## Display Subscription Status
1167
+ * @example
1168
+ * ```tsx
1169
+ * function AccountPage() {
1170
+ * const { customer, subscription, entitlements, isLoading } = useCustomer();
1171
+ *
1172
+ * if (isLoading) return <Spinner />;
1173
+ *
1174
+ * return (
1175
+ * <div>
1176
+ * <h1>Hi, {customer?.name}</h1>
1177
+ * {subscription ? (
1178
+ * <>
1179
+ * <p>Plan: {subscription.product.name}</p>
1180
+ * <p>Renews: {new Date(subscription.currentPeriodEnd).toLocaleDateString()}</p>
1181
+ * </>
1182
+ * ) : (
1183
+ * <p>No active subscription</p>
1184
+ * )}
1185
+ * </div>
1186
+ * );
1187
+ * }
1188
+ * ```
1189
+ *
1190
+ * ## Refresh After Checkout
1191
+ * @example
1192
+ * ```tsx
1193
+ * function CheckoutButton({ productId }) {
1194
+ * const { checkout } = useRecur();
1195
+ * const { refetch } = useCustomer();
1196
+ *
1197
+ * const handleCheckout = async () => {
1198
+ * await checkout({
1199
+ * productId,
1200
+ * onPaymentComplete: async () => {
1201
+ * await refetch(); // Update entitlements after purchase
1202
+ * },
1203
+ * });
1204
+ * };
1205
+ *
1206
+ * return <button onClick={handleCheckout}>Upgrade</button>;
1207
+ * }
1208
+ * ```
1209
+ *
1210
+ * ## Security Note
1211
+ *
1212
+ * The check() method reads from a local cache and can be bypassed client-side.
1213
+ * Use it only for UI gating. For secure access control, always verify
1214
+ * permissions on your backend using the Server SDK:
1215
+ *
1216
+ * ```typescript
1217
+ * // Backend API route
1218
+ * const { allowed } = await recur.entitlements.check({
1219
+ * product: 'pro-plan',
1220
+ * customer: { email: user.email },
1221
+ * });
1222
+ * if (!allowed) return Response.json({ error: 'Forbidden' }, { status: 403 });
1223
+ * ```
1224
+ *
1225
+ * @returns {UseCustomerResult} Object containing:
1226
+ * - `customer` - Customer info (null if not found)
1227
+ * - `subscription` - Most recent active subscription (null if none)
1228
+ * - `entitlements` - Array of all active entitlements
1229
+ * - `check(options)` - Synchronous permission check
1230
+ * - `refetch()` - Manual refresh (debounced)
1231
+ * - `isLoading` - Whether data is being fetched
1232
+ * - `error` - Error from last fetch attempt
1233
+ *
1234
+ * @throws {Error} If used outside of RecurProvider
1235
+ */
1236
+ declare function useCustomer(): UseCustomerResult;
1237
+
1238
+ export { type CheckDeniedReason, type CheckOptions, type CheckResult, type CheckoutError, type CheckoutOptions, type CheckoutResult, type CustomerIdentifier, type Entitlement, type EntitlementCustomer, type EntitlementStatus, type EntitlementSubscription, type Plan, type PlansResult, type Product, type ProductMetadata, type ProductsResult, type RecurConfig, type RecurContextValue, RecurProvider, type RecurProviderProps, type SubscriptionResult, type UseCustomerResult, type UseProductsOptions as UsePlansOptions, type UseProductsResult as UsePlansResult, type UseProductsOptions, type UseProductsResult, type UseSubscribeOptions, type UseSubscribeResult, useCustomer, useProducts as usePlans, useProducts, useRecur, useSubscribe };