@feelflow/ffid-sdk 1.17.0 → 1.19.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.
@@ -21,6 +21,193 @@ interface FFIDCacheConfig {
21
21
  ttl: number;
22
22
  }
23
23
 
24
+ /** Billing interval for subscriptions */
25
+ type FFIDBillingInterval = 'monthly' | 'yearly';
26
+ /** Service information returned by plans endpoint */
27
+ interface FFIDServiceInfo {
28
+ id: string;
29
+ code: string;
30
+ name: string;
31
+ description: string | null;
32
+ isActive: boolean;
33
+ createdAt: string;
34
+ plans: FFIDPlanInfo[];
35
+ }
36
+ /** Plan information returned by plans endpoint */
37
+ interface FFIDPlanInfo {
38
+ id: string;
39
+ serviceId: string;
40
+ code: string;
41
+ name: string;
42
+ description: string | null;
43
+ minSeats: number;
44
+ maxSeats: number | null;
45
+ trialDays: number;
46
+ priceMonthly: number;
47
+ priceYearly: number | null;
48
+ currency: string;
49
+ isActive: boolean;
50
+ displayOrder: number;
51
+ customPricing: boolean;
52
+ createdAt: string;
53
+ }
54
+ /** Response from list plans endpoint */
55
+ interface FFIDListPlansResponse {
56
+ services: FFIDServiceInfo[];
57
+ count: number;
58
+ }
59
+ /** Subscription details returned by ext API (sanitized, no Stripe IDs) */
60
+ interface FFIDSubscriptionDetail {
61
+ id: string;
62
+ organizationId: string;
63
+ organizationName: string;
64
+ serviceCode: string;
65
+ serviceName: string;
66
+ planCode: string;
67
+ planName: string;
68
+ status: FFIDSubscriptionStatus;
69
+ paymentMethod: string | null;
70
+ billingInterval: FFIDBillingInterval;
71
+ quantity: number;
72
+ currentPeriodStart: string | null;
73
+ currentPeriodEnd: string | null;
74
+ trialStart: string | null;
75
+ trialEnd: string | null;
76
+ canceledAt: string | null;
77
+ cancelAtPeriodEnd: boolean;
78
+ createdAt: string;
79
+ }
80
+ /** Parameters for subscribing to a plan */
81
+ interface FFIDSubscribeParams {
82
+ /** Organization ID (UUID) */
83
+ organizationId: string;
84
+ /** Plan code (e.g., 'free', 'basic', 'pro') */
85
+ planCode: string;
86
+ /** Billing interval (default: 'monthly') */
87
+ billingInterval?: FFIDBillingInterval;
88
+ /** Number of seats (default: plan's minSeats) */
89
+ quantity?: number;
90
+ /** User ID for seat auto-assignment (only needed for service-key mode) */
91
+ userId?: string;
92
+ }
93
+ /** Sanitized subscription fields returned by subscribe/reactivate endpoints (no Stripe IDs) */
94
+ interface FFIDSubscriptionSummary {
95
+ id: string;
96
+ organizationId: string;
97
+ serviceId: string;
98
+ planId: string;
99
+ status: FFIDSubscriptionStatus;
100
+ paymentMethod: string | null;
101
+ billingInterval: FFIDBillingInterval;
102
+ quantity: number;
103
+ currentPeriodStart: string | null;
104
+ currentPeriodEnd: string | null;
105
+ trialStart: string | null;
106
+ trialEnd: string | null;
107
+ canceledAt: string | null;
108
+ cancelAtPeriodEnd: boolean;
109
+ createdAt: string;
110
+ }
111
+ /** Response from subscribe endpoint */
112
+ interface FFIDSubscribeResponse {
113
+ subscription: FFIDSubscriptionSummary;
114
+ service: {
115
+ id: string;
116
+ code: string;
117
+ name: string;
118
+ };
119
+ plan: {
120
+ id: string;
121
+ code: string;
122
+ name: string;
123
+ priceMonthly: number;
124
+ priceYearly: number | null;
125
+ trialDays: number;
126
+ };
127
+ /** True if a canceled subscription was reactivated */
128
+ reactivated?: boolean;
129
+ /** True if Stripe checkout is needed (canceled paid plan reactivation) */
130
+ needsCheckout?: boolean;
131
+ }
132
+ /** Parameters for changing plan */
133
+ interface FFIDChangePlanParams {
134
+ /** Subscription ID (UUID) */
135
+ subscriptionId: string;
136
+ /** New plan code */
137
+ planCode: string;
138
+ /** New billing interval (default: keeps current) */
139
+ billingInterval?: FFIDBillingInterval;
140
+ }
141
+ /** Response from change plan endpoint */
142
+ interface FFIDChangePlanResponse {
143
+ message: string;
144
+ subscription: {
145
+ id: string;
146
+ organizationId: string;
147
+ serviceCode: string;
148
+ planCode: string;
149
+ planName: string;
150
+ billingInterval: FFIDBillingInterval;
151
+ status: FFIDSubscriptionStatus;
152
+ isUpgrade: boolean;
153
+ };
154
+ }
155
+ /** Parameters for canceling subscription */
156
+ interface FFIDCancelSubscriptionParams {
157
+ /** Subscription ID (UUID) */
158
+ subscriptionId: string;
159
+ /** Cancellation reason code */
160
+ reason?: 'too_expensive' | 'missing_features' | 'switched_service' | 'rarely_used' | 'customer_service' | 'other';
161
+ /** Additional cancellation details */
162
+ reasonDetails?: string;
163
+ }
164
+ /** Response from cancel subscription endpoint */
165
+ interface FFIDCancelSubscriptionResponse {
166
+ message: string;
167
+ subscription: {
168
+ id: string;
169
+ organizationId: string;
170
+ status: FFIDSubscriptionStatus;
171
+ canceledAt: string | null;
172
+ };
173
+ }
174
+ /** Parameters for previewing plan change */
175
+ interface FFIDPreviewPlanChangeParams {
176
+ /** Subscription ID (UUID) */
177
+ subscriptionId: string;
178
+ /** New plan code */
179
+ planCode: string;
180
+ /** New billing interval (default: keeps current) */
181
+ billingInterval?: FFIDBillingInterval;
182
+ }
183
+ /** Plan change preview line item */
184
+ interface FFIDPlanChangeLineItem {
185
+ description: string;
186
+ amount: number;
187
+ }
188
+ /** Response from plan change preview endpoint */
189
+ interface FFIDPlanChangePreviewResponse {
190
+ preview: {
191
+ currentPlan: {
192
+ name: string;
193
+ price: number;
194
+ };
195
+ newPlan: {
196
+ name: string;
197
+ price: number;
198
+ };
199
+ billingInterval: FFIDBillingInterval;
200
+ isUpgrade: boolean;
201
+ proratedAmount: number;
202
+ nextInvoiceAmount: number;
203
+ nextInvoiceDate: string | null;
204
+ currency: string;
205
+ isEstimate: boolean;
206
+ estimateReason?: string;
207
+ lineItems: FFIDPlanChangeLineItem[];
208
+ };
209
+ }
210
+
24
211
  /**
25
212
  * FFID SDK Type Definitions
26
213
  *
@@ -103,7 +290,7 @@ interface FFIDSubscription {
103
290
  /** Plan display name */
104
291
  planName: string;
105
292
  /** Subscription status */
106
- status: 'trialing' | 'active' | 'past_due' | 'canceled' | 'paused';
293
+ status: FFIDSubscriptionStatus;
107
294
  /** Current billing period end date */
108
295
  currentPeriodEnd: string | null;
109
296
  /** Trial end date (null if not a trial or no trial_end set) */
@@ -118,7 +305,7 @@ interface FFIDSubscription {
118
305
  /** OAuth userinfo subscription summary */
119
306
  interface FFIDOAuthUserInfoSubscription {
120
307
  subscriptionId: string | null;
121
- status: 'trialing' | 'active' | 'past_due' | 'canceled' | 'paused' | null;
308
+ status: FFIDSubscriptionStatus | null;
122
309
  planCode: string | null;
123
310
  seatModel: FFIDSeatModel | null;
124
311
  memberRole: FFIDOAuthUserInfoMemberRole | null;
@@ -255,9 +442,6 @@ type FFIDApiResponse<T> = {
255
442
  data?: undefined;
256
443
  error: FFIDError;
257
444
  };
258
- /**
259
- * Subscription check response from ext/check endpoint
260
- */
261
445
  /** Subscription status values matching the FFID platform's SubscriptionStatus type */
262
446
  type FFIDSubscriptionStatus = 'trialing' | 'active' | 'past_due' | 'canceled' | 'pending_invoice' | 'paused' | 'incomplete' | 'incomplete_expired' | 'unpaid';
263
447
  interface FFIDSubscriptionCheckResponse {
@@ -308,6 +492,45 @@ interface FFIDCreatePortalParams {
308
492
  /** URL to redirect when user exits the portal */
309
493
  returnUrl: string;
310
494
  }
495
+
496
+ /** Member role in an organization */
497
+ type FFIDMemberRole = 'owner' | 'admin' | 'member' | 'viewer';
498
+ /** Member status in an organization */
499
+ type FFIDMemberStatus = 'active' | 'invited' | 'suspended';
500
+ /** Organization member returned by the ext members API */
501
+ interface FFIDOrganizationMember {
502
+ /** User ID (UUID) */
503
+ userId: string;
504
+ /** Email address */
505
+ email: string;
506
+ /** Display name */
507
+ displayName: string | null;
508
+ /** Avatar URL */
509
+ avatarUrl: string | null;
510
+ /** Role in the organization */
511
+ role: FFIDMemberRole;
512
+ /** Membership status */
513
+ status: FFIDMemberStatus;
514
+ /** When the user joined */
515
+ joinedAt: string | null;
516
+ /** When the user was invited */
517
+ invitedAt: string | null;
518
+ /** Last sign-in timestamp */
519
+ lastSignInAt: string | null;
520
+ }
521
+ /** Response from listMembers (ext) */
522
+ interface FFIDListMembersResponse {
523
+ organizationId: string;
524
+ members: FFIDOrganizationMember[];
525
+ }
526
+ /** Response from updateMemberRole (ext) */
527
+ interface FFIDUpdateMemberRoleResponse {
528
+ member: FFIDOrganizationMember;
529
+ }
530
+ /** Response from removeMember (ext) */
531
+ interface FFIDRemoveMemberResponse {
532
+ message: string;
533
+ }
311
534
  /**
312
535
  * Result of a redirect operation (redirectToLogin / redirectToAuthorize / redirectToLogout)
313
536
  *
@@ -424,8 +647,26 @@ declare function createFFIDClient(config: FFIDConfig): {
424
647
  userId: string;
425
648
  organizationId: string;
426
649
  }) => Promise<FFIDApiResponse<FFIDSubscriptionCheckResponse>>;
650
+ listMembers: (params: {
651
+ organizationId: string;
652
+ }) => Promise<FFIDApiResponse<FFIDListMembersResponse>>;
653
+ updateMemberRole: (params: {
654
+ organizationId: string;
655
+ userId: string;
656
+ role: FFIDMemberRole;
657
+ }) => Promise<FFIDApiResponse<FFIDUpdateMemberRoleResponse>>;
658
+ removeMember: (params: {
659
+ organizationId: string;
660
+ userId: string;
661
+ }) => Promise<FFIDApiResponse<FFIDRemoveMemberResponse>>;
427
662
  createCheckoutSession: (params: FFIDCreateCheckoutParams) => Promise<FFIDApiResponse<FFIDCheckoutSessionResponse>>;
428
663
  createPortalSession: (params: FFIDCreatePortalParams) => Promise<FFIDApiResponse<FFIDPortalSessionResponse>>;
664
+ listPlans: () => Promise<FFIDApiResponse<FFIDListPlansResponse>>;
665
+ getSubscription: (subscriptionId: string) => Promise<FFIDApiResponse<FFIDSubscriptionDetail>>;
666
+ subscribe: (params: FFIDSubscribeParams) => Promise<FFIDApiResponse<FFIDSubscribeResponse>>;
667
+ changePlan: (params: FFIDChangePlanParams) => Promise<FFIDApiResponse<FFIDChangePlanResponse>>;
668
+ cancelSubscription: (params: FFIDCancelSubscriptionParams) => Promise<FFIDApiResponse<FFIDCancelSubscriptionResponse>>;
669
+ previewPlanChange: (params: FFIDPreviewPlanChangeParams) => Promise<FFIDApiResponse<FFIDPlanChangePreviewResponse>>;
429
670
  verifyAccessToken: (accessToken: string, options?: FFIDVerifyAccessTokenOptions) => Promise<FFIDApiResponse<FFIDOAuthUserInfo>>;
430
671
  requestPasswordReset: (email: string) => Promise<FFIDApiResponse<FFIDPasswordResetResponse>>;
431
672
  verifyPasswordResetToken: (accessToken: string) => Promise<FFIDApiResponse<FFIDPasswordResetVerifyResponse>>;
@@ -120,7 +120,7 @@ function normalizeUserinfo(raw) {
120
120
  seatModel: raw.subscription.seat_model ?? null,
121
121
  memberRole: raw.subscription.member_role ?? null,
122
122
  organizationId: raw.subscription.organization_id ?? null,
123
- hasSeatAssignment: raw.subscription.has_seat_assignment ?? null
123
+ hasSeatAssignment: raw.subscription.has_seat_assignment ?? false
124
124
  } : void 0
125
125
  };
126
126
  }
@@ -138,6 +138,7 @@ function mapUserinfoSubscriptionToSession(userinfo, serviceCode) {
138
138
  planName: subscription.planCode,
139
139
  status: subscription.status,
140
140
  currentPeriodEnd: null,
141
+ trialEnd: null,
141
142
  seatModel: subscription.seatModel ?? void 0,
142
143
  memberRole: subscription.memberRole ?? void 0,
143
144
  organizationId: subscription.organizationId
@@ -441,8 +442,160 @@ function createBillingMethods(deps) {
441
442
  return { createCheckoutSession, createPortalSession };
442
443
  }
443
444
 
445
+ // src/client/subscription-methods.ts
446
+ var EXT_PLANS_ENDPOINT = "/api/v1/subscriptions/ext/plans";
447
+ var EXT_SUBSCRIPTION_ENDPOINT = "/api/v1/subscriptions/ext";
448
+ var EXT_SUBSCRIBE_ENDPOINT = "/api/v1/subscriptions/ext/subscribe";
449
+ function createSubscriptionMethods(deps) {
450
+ const { fetchWithAuth, createError } = deps;
451
+ async function listPlans() {
452
+ return fetchWithAuth(EXT_PLANS_ENDPOINT);
453
+ }
454
+ async function getSubscription(subscriptionId) {
455
+ if (!subscriptionId) {
456
+ return {
457
+ error: createError("VALIDATION_ERROR", "subscriptionId \u306F\u5FC5\u9808\u3067\u3059")
458
+ };
459
+ }
460
+ return fetchWithAuth(
461
+ `${EXT_SUBSCRIPTION_ENDPOINT}/${encodeURIComponent(subscriptionId)}`
462
+ );
463
+ }
464
+ async function subscribe(params) {
465
+ if (!params.organizationId || !params.planCode) {
466
+ return {
467
+ error: createError("VALIDATION_ERROR", "organizationId \u3068 planCode \u306F\u5FC5\u9808\u3067\u3059")
468
+ };
469
+ }
470
+ return fetchWithAuth(
471
+ EXT_SUBSCRIBE_ENDPOINT,
472
+ {
473
+ method: "POST",
474
+ body: JSON.stringify({
475
+ organizationId: params.organizationId,
476
+ planCode: params.planCode,
477
+ ...params.billingInterval ? { billingInterval: params.billingInterval } : {},
478
+ ...params.quantity !== void 0 ? { quantity: params.quantity } : {},
479
+ ...params.userId ? { userId: params.userId } : {}
480
+ })
481
+ }
482
+ );
483
+ }
484
+ async function changePlan(params) {
485
+ if (!params.subscriptionId || !params.planCode) {
486
+ return {
487
+ error: createError("VALIDATION_ERROR", "subscriptionId \u3068 planCode \u306F\u5FC5\u9808\u3067\u3059")
488
+ };
489
+ }
490
+ return fetchWithAuth(
491
+ `${EXT_SUBSCRIPTION_ENDPOINT}/${encodeURIComponent(params.subscriptionId)}/plan`,
492
+ {
493
+ method: "PUT",
494
+ body: JSON.stringify({
495
+ planCode: params.planCode,
496
+ ...params.billingInterval ? { billingInterval: params.billingInterval } : {}
497
+ })
498
+ }
499
+ );
500
+ }
501
+ async function cancelSubscription(params) {
502
+ if (!params.subscriptionId) {
503
+ return {
504
+ error: createError("VALIDATION_ERROR", "subscriptionId \u306F\u5FC5\u9808\u3067\u3059")
505
+ };
506
+ }
507
+ const body = {};
508
+ if (params.reason) body.reason = params.reason;
509
+ if (params.reasonDetails) body.reasonDetails = params.reasonDetails;
510
+ return fetchWithAuth(
511
+ `${EXT_SUBSCRIPTION_ENDPOINT}/${encodeURIComponent(params.subscriptionId)}`,
512
+ {
513
+ method: "DELETE",
514
+ ...Object.keys(body).length > 0 ? { body: JSON.stringify(body) } : {}
515
+ }
516
+ );
517
+ }
518
+ async function previewPlanChange(params) {
519
+ if (!params.subscriptionId || !params.planCode) {
520
+ return {
521
+ error: createError("VALIDATION_ERROR", "subscriptionId \u3068 planCode \u306F\u5FC5\u9808\u3067\u3059")
522
+ };
523
+ }
524
+ return fetchWithAuth(
525
+ `${EXT_SUBSCRIPTION_ENDPOINT}/${encodeURIComponent(params.subscriptionId)}/plan/preview`,
526
+ {
527
+ method: "POST",
528
+ body: JSON.stringify({
529
+ planCode: params.planCode,
530
+ ...params.billingInterval ? { billingInterval: params.billingInterval } : {}
531
+ })
532
+ }
533
+ );
534
+ }
535
+ return {
536
+ listPlans,
537
+ getSubscription,
538
+ subscribe,
539
+ changePlan,
540
+ cancelSubscription,
541
+ previewPlanChange
542
+ };
543
+ }
544
+
545
+ // src/client/members-methods.ts
546
+ var EXT_MEMBERS_ENDPOINT = "/api/v1/organizations/ext/members";
547
+ function createMembersMethods(deps) {
548
+ const { fetchWithAuth, createError, serviceCode } = deps;
549
+ function buildQuery(organizationId) {
550
+ return new URLSearchParams({ organizationId, serviceCode }).toString();
551
+ }
552
+ async function listMembers(params) {
553
+ if (!params.organizationId) {
554
+ return {
555
+ error: createError("VALIDATION_ERROR", "organizationId \u306F\u5FC5\u9808\u3067\u3059")
556
+ };
557
+ }
558
+ return fetchWithAuth(
559
+ `${EXT_MEMBERS_ENDPOINT}?${buildQuery(params.organizationId)}`
560
+ );
561
+ }
562
+ async function updateMemberRole(params) {
563
+ if (!params.organizationId || !params.userId) {
564
+ return {
565
+ error: createError("VALIDATION_ERROR", "organizationId \u3068 userId \u306F\u5FC5\u9808\u3067\u3059")
566
+ };
567
+ }
568
+ if (!params.role) {
569
+ return {
570
+ error: createError("VALIDATION_ERROR", "role \u306F\u5FC5\u9808\u3067\u3059")
571
+ };
572
+ }
573
+ return fetchWithAuth(
574
+ `${EXT_MEMBERS_ENDPOINT}/${encodeURIComponent(params.userId)}?${buildQuery(params.organizationId)}`,
575
+ {
576
+ method: "PUT",
577
+ body: JSON.stringify({ role: params.role })
578
+ }
579
+ );
580
+ }
581
+ async function removeMember(params) {
582
+ if (!params.organizationId || !params.userId) {
583
+ return {
584
+ error: createError("VALIDATION_ERROR", "organizationId \u3068 userId \u306F\u5FC5\u9808\u3067\u3059")
585
+ };
586
+ }
587
+ return fetchWithAuth(
588
+ `${EXT_MEMBERS_ENDPOINT}/${encodeURIComponent(params.userId)}?${buildQuery(params.organizationId)}`,
589
+ {
590
+ method: "DELETE"
591
+ }
592
+ );
593
+ }
594
+ return { listMembers, updateMemberRole, removeMember };
595
+ }
596
+
444
597
  // src/client/version-check.ts
445
- var SDK_VERSION = "1.17.0";
598
+ var SDK_VERSION = "1.19.0";
446
599
  var SDK_USER_AGENT = `FFID-SDK/${SDK_VERSION} (TypeScript)`;
447
600
  var SDK_VERSION_HEADER = "X-FFID-SDK-Version";
448
601
  function sdkHeaders() {
@@ -1502,6 +1655,22 @@ function createFFIDClient(config) {
1502
1655
  fetchWithAuth,
1503
1656
  createError
1504
1657
  });
1658
+ const {
1659
+ listPlans,
1660
+ getSubscription,
1661
+ subscribe,
1662
+ changePlan,
1663
+ cancelSubscription,
1664
+ previewPlanChange
1665
+ } = createSubscriptionMethods({
1666
+ fetchWithAuth,
1667
+ createError
1668
+ });
1669
+ const { listMembers, updateMemberRole, removeMember } = createMembersMethods({
1670
+ fetchWithAuth,
1671
+ createError,
1672
+ serviceCode: config.serviceCode
1673
+ });
1505
1674
  const {
1506
1675
  requestPasswordReset,
1507
1676
  verifyPasswordResetToken,
@@ -1545,8 +1714,17 @@ function createFFIDClient(config) {
1545
1714
  exchangeCodeForTokens,
1546
1715
  refreshAccessToken,
1547
1716
  checkSubscription,
1717
+ listMembers,
1718
+ updateMemberRole,
1719
+ removeMember,
1548
1720
  createCheckoutSession,
1549
1721
  createPortalSession,
1722
+ listPlans,
1723
+ getSubscription,
1724
+ subscribe,
1725
+ changePlan,
1726
+ cancelSubscription,
1727
+ previewPlanChange,
1550
1728
  verifyAccessToken,
1551
1729
  requestPasswordReset,
1552
1730
  verifyPasswordResetToken,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@feelflow/ffid-sdk",
3
- "version": "1.17.0",
3
+ "version": "1.19.0",
4
4
  "description": "FeelFlow ID Platform SDK for React/Next.js applications",
5
5
  "keywords": [
6
6
  "feelflow",