@feelflow/ffid-sdk 4.1.0 → 4.2.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.
@@ -803,7 +803,7 @@ function createProfileMethods(deps) {
803
803
  }
804
804
 
805
805
  // src/client/version-check.ts
806
- var SDK_VERSION = "4.1.0";
806
+ var SDK_VERSION = "4.2.0";
807
807
  var SDK_USER_AGENT = `FFID-SDK/${SDK_VERSION} (TypeScript)`;
808
808
  var SDK_VERSION_HEADER = "X-FFID-SDK-Version";
809
809
  function sdkHeaders() {
@@ -2180,6 +2180,73 @@ var FFID_ERROR_CODES = {
2180
2180
  TOKEN_VERIFICATION_ERROR: "TOKEN_VERIFICATION_ERROR"
2181
2181
  };
2182
2182
  var EXT_CHECK_ENDPOINT = "/api/v1/subscriptions/ext/check";
2183
+ var DEFAULT_ALLOW_GRACE = true;
2184
+ var DEFAULT_SERVICE_ACCESS_FAIL_POLICY = "failClosed";
2185
+ var SERVICE_ACCESS_DENIED_CODE = "SERVICE_ACCESS_DENIED";
2186
+ var ACCESS_GRANTING_EFFECTIVE_STATUSES = ["active", "past_due_grace"];
2187
+ var BLOCKING_EFFECTIVE_STATUSES = [
2188
+ "blocked",
2189
+ "canceled",
2190
+ "expired",
2191
+ "trial_expired"
2192
+ ];
2193
+ function resolveServiceAccessDenialReason(params, allowGrace) {
2194
+ if (params.hasAccess) {
2195
+ return null;
2196
+ }
2197
+ if (params.effectiveStatus === null) {
2198
+ return "no_subscription";
2199
+ }
2200
+ if (params.isGrace && !allowGrace) {
2201
+ return "grace_disallowed";
2202
+ }
2203
+ if (params.effectiveStatus === "active" || params.effectiveStatus === "past_due_grace") {
2204
+ return "blocked";
2205
+ }
2206
+ return params.effectiveStatus;
2207
+ }
2208
+ function toServiceAccessDecision(response, allowGrace) {
2209
+ const effectiveStatus = response.effectiveStatus ?? null;
2210
+ const serverHasAccess = response.hasAccess ?? (effectiveStatus !== null && ACCESS_GRANTING_EFFECTIVE_STATUSES.includes(effectiveStatus));
2211
+ const isGrace = response.isGrace ?? effectiveStatus === "past_due_grace";
2212
+ const hasAccess = serverHasAccess && (allowGrace || !isGrace);
2213
+ const isBlocked = response.isBlocked ?? (effectiveStatus === null || effectiveStatus !== null && BLOCKING_EFFECTIVE_STATUSES.includes(effectiveStatus));
2214
+ return {
2215
+ hasAccess,
2216
+ effectiveStatus,
2217
+ isGrace,
2218
+ isBlocked: isBlocked || !hasAccess,
2219
+ allowGrace,
2220
+ failPolicy: DEFAULT_SERVICE_ACCESS_FAIL_POLICY,
2221
+ denialReason: resolveServiceAccessDenialReason({ effectiveStatus, hasAccess, isGrace }, allowGrace),
2222
+ organizationId: response.organizationId,
2223
+ subscriptionId: response.subscriptionId,
2224
+ status: response.status,
2225
+ planCode: response.planCode,
2226
+ currentPeriodEnd: response.currentPeriodEnd,
2227
+ gracePeriodEndsAt: response.gracePeriodEndsAt ?? null,
2228
+ reactivatable: response.reactivatable ?? false
2229
+ };
2230
+ }
2231
+ function failClosedServiceAccessDecision(params, error) {
2232
+ return {
2233
+ hasAccess: false,
2234
+ effectiveStatus: null,
2235
+ isGrace: false,
2236
+ isBlocked: true,
2237
+ allowGrace: params.allowGrace ?? DEFAULT_ALLOW_GRACE,
2238
+ failPolicy: DEFAULT_SERVICE_ACCESS_FAIL_POLICY,
2239
+ denialReason: "ffid_unreachable",
2240
+ organizationId: params.organizationId || null,
2241
+ subscriptionId: null,
2242
+ status: null,
2243
+ planCode: null,
2244
+ currentPeriodEnd: null,
2245
+ gracePeriodEndsAt: null,
2246
+ reactivatable: false,
2247
+ error
2248
+ };
2249
+ }
2183
2250
  function resolveRedirectUri(raw, logger) {
2184
2251
  if (raw === null) return null;
2185
2252
  try {
@@ -2418,6 +2485,57 @@ function createFFIDClient(config) {
2418
2485
  `${EXT_CHECK_ENDPOINT}?${query.toString()}`
2419
2486
  );
2420
2487
  }
2488
+ async function checkServiceAccess(params) {
2489
+ const failPolicy = params.failPolicy ?? DEFAULT_SERVICE_ACCESS_FAIL_POLICY;
2490
+ if (failPolicy !== DEFAULT_SERVICE_ACCESS_FAIL_POLICY) {
2491
+ return {
2492
+ error: createError(
2493
+ "VALIDATION_ERROR",
2494
+ `failPolicy \u306F ${DEFAULT_SERVICE_ACCESS_FAIL_POLICY} \u306E\u307F\u6307\u5B9A\u3067\u304D\u307E\u3059`
2495
+ )
2496
+ };
2497
+ }
2498
+ const subscriptionParams = {
2499
+ organizationId: params.organizationId
2500
+ };
2501
+ if (params.userId !== void 0) {
2502
+ subscriptionParams.userId = params.userId;
2503
+ }
2504
+ const subscriptionResult = await checkSubscription(subscriptionParams);
2505
+ if (subscriptionResult.error) {
2506
+ if (subscriptionResult.error.code === "VALIDATION_ERROR") {
2507
+ return { error: subscriptionResult.error };
2508
+ }
2509
+ return {
2510
+ data: failClosedServiceAccessDecision(params, subscriptionResult.error)
2511
+ };
2512
+ }
2513
+ return {
2514
+ data: toServiceAccessDecision(
2515
+ subscriptionResult.data,
2516
+ params.allowGrace ?? DEFAULT_ALLOW_GRACE
2517
+ )
2518
+ };
2519
+ }
2520
+ async function requireServiceAccess(params) {
2521
+ const result = await checkServiceAccess(params);
2522
+ if (result.error) {
2523
+ return result;
2524
+ }
2525
+ if (!result.data.hasAccess) {
2526
+ const error = createError(
2527
+ SERVICE_ACCESS_DENIED_CODE,
2528
+ `FFID service access denied: ${result.data.denialReason ?? "unknown"}`
2529
+ );
2530
+ if (result.data.error) {
2531
+ error.details = { cause: result.data.error };
2532
+ }
2533
+ return {
2534
+ error
2535
+ };
2536
+ }
2537
+ return result;
2538
+ }
2421
2539
  const { createCheckoutSession, createPortalSession } = createBillingMethods({
2422
2540
  fetchWithAuth,
2423
2541
  createError
@@ -2516,6 +2634,8 @@ function createFFIDClient(config) {
2516
2634
  exchangeCodeForTokens,
2517
2635
  refreshAccessToken,
2518
2636
  checkSubscription,
2637
+ checkServiceAccess,
2638
+ requireServiceAccess,
2519
2639
  listMembers,
2520
2640
  updateMemberRole,
2521
2641
  removeMember,
@@ -1,6 +1,7 @@
1
- import { F as FFIDLogger, a as FFIDError, b as FFIDCacheAdapter, c as FFIDVerifyAccessTokenOptions, d as FFIDApiResponse, e as FFIDOAuthUserInfo } from '../ffid-client-ZcJhRbD6.cjs';
2
- export { f as FFIDCacheConfig, g as FFIDClient, h as FFIDConfig, i as FFIDOrganization, j as FFIDOtpSendResponse, k as FFIDOtpVerifyResponse, l as FFIDPasswordResetConfirmResponse, m as FFIDPasswordResetResponse, n as FFIDPasswordResetVerifyResponse, o as FFIDProfileCallOptions, p as FFIDResetSessionResponse, q as FFIDSubscription, r as FFIDUpdateUserProfileRequest, s as FFIDUser, t as FFIDUserProfile, T as TokenData, u as TokenStore, v as createFFIDClient, w as createTokenStore } from '../ffid-client-ZcJhRbD6.cjs';
1
+ import { F as FFIDLogger, a as FFIDError, b as FFIDCacheAdapter, c as FFIDVerifyAccessTokenOptions, d as FFIDApiResponse, e as FFIDOAuthUserInfo } from '../ffid-client-CKMGqqPi.cjs';
2
+ export { f as FFIDCacheConfig, g as FFIDClient, h as FFIDConfig, i as FFIDOrganization, j as FFIDOtpSendResponse, k as FFIDOtpVerifyResponse, l as FFIDPasswordResetConfirmResponse, m as FFIDPasswordResetResponse, n as FFIDPasswordResetVerifyResponse, o as FFIDProfileCallOptions, p as FFIDResetSessionResponse, q as FFIDSubscription, r as FFIDUpdateUserProfileRequest, s as FFIDUser, t as FFIDUserProfile, T as TokenData, u as TokenStore, v as createFFIDClient, w as createTokenStore } from '../ffid-client-CKMGqqPi.cjs';
3
3
  export { D as DEFAULT_API_BASE_URL, a as DEFAULT_OAUTH_SCOPES } from '../constants-D61jqRIO.cjs';
4
+ import '../types-5g_Bg6Ey.cjs';
4
5
 
5
6
  /** Token verification - verifyAccessToken() implementation */
6
7
 
@@ -1,6 +1,7 @@
1
- import { F as FFIDLogger, a as FFIDError, b as FFIDCacheAdapter, c as FFIDVerifyAccessTokenOptions, d as FFIDApiResponse, e as FFIDOAuthUserInfo } from '../ffid-client-ZcJhRbD6.js';
2
- export { f as FFIDCacheConfig, g as FFIDClient, h as FFIDConfig, i as FFIDOrganization, j as FFIDOtpSendResponse, k as FFIDOtpVerifyResponse, l as FFIDPasswordResetConfirmResponse, m as FFIDPasswordResetResponse, n as FFIDPasswordResetVerifyResponse, o as FFIDProfileCallOptions, p as FFIDResetSessionResponse, q as FFIDSubscription, r as FFIDUpdateUserProfileRequest, s as FFIDUser, t as FFIDUserProfile, T as TokenData, u as TokenStore, v as createFFIDClient, w as createTokenStore } from '../ffid-client-ZcJhRbD6.js';
1
+ import { F as FFIDLogger, a as FFIDError, b as FFIDCacheAdapter, c as FFIDVerifyAccessTokenOptions, d as FFIDApiResponse, e as FFIDOAuthUserInfo } from '../ffid-client-CUOFknXy.js';
2
+ export { f as FFIDCacheConfig, g as FFIDClient, h as FFIDConfig, i as FFIDOrganization, j as FFIDOtpSendResponse, k as FFIDOtpVerifyResponse, l as FFIDPasswordResetConfirmResponse, m as FFIDPasswordResetResponse, n as FFIDPasswordResetVerifyResponse, o as FFIDProfileCallOptions, p as FFIDResetSessionResponse, q as FFIDSubscription, r as FFIDUpdateUserProfileRequest, s as FFIDUser, t as FFIDUserProfile, T as TokenData, u as TokenStore, v as createFFIDClient, w as createTokenStore } from '../ffid-client-CUOFknXy.js';
3
3
  export { D as DEFAULT_API_BASE_URL, a as DEFAULT_OAUTH_SCOPES } from '../constants-D61jqRIO.js';
4
+ import '../types-5g_Bg6Ey.js';
4
5
 
5
6
  /** Token verification - verifyAccessToken() implementation */
6
7
 
@@ -802,7 +802,7 @@ function createProfileMethods(deps) {
802
802
  }
803
803
 
804
804
  // src/client/version-check.ts
805
- var SDK_VERSION = "4.1.0";
805
+ var SDK_VERSION = "4.2.0";
806
806
  var SDK_USER_AGENT = `FFID-SDK/${SDK_VERSION} (TypeScript)`;
807
807
  var SDK_VERSION_HEADER = "X-FFID-SDK-Version";
808
808
  function sdkHeaders() {
@@ -2179,6 +2179,73 @@ var FFID_ERROR_CODES = {
2179
2179
  TOKEN_VERIFICATION_ERROR: "TOKEN_VERIFICATION_ERROR"
2180
2180
  };
2181
2181
  var EXT_CHECK_ENDPOINT = "/api/v1/subscriptions/ext/check";
2182
+ var DEFAULT_ALLOW_GRACE = true;
2183
+ var DEFAULT_SERVICE_ACCESS_FAIL_POLICY = "failClosed";
2184
+ var SERVICE_ACCESS_DENIED_CODE = "SERVICE_ACCESS_DENIED";
2185
+ var ACCESS_GRANTING_EFFECTIVE_STATUSES = ["active", "past_due_grace"];
2186
+ var BLOCKING_EFFECTIVE_STATUSES = [
2187
+ "blocked",
2188
+ "canceled",
2189
+ "expired",
2190
+ "trial_expired"
2191
+ ];
2192
+ function resolveServiceAccessDenialReason(params, allowGrace) {
2193
+ if (params.hasAccess) {
2194
+ return null;
2195
+ }
2196
+ if (params.effectiveStatus === null) {
2197
+ return "no_subscription";
2198
+ }
2199
+ if (params.isGrace && !allowGrace) {
2200
+ return "grace_disallowed";
2201
+ }
2202
+ if (params.effectiveStatus === "active" || params.effectiveStatus === "past_due_grace") {
2203
+ return "blocked";
2204
+ }
2205
+ return params.effectiveStatus;
2206
+ }
2207
+ function toServiceAccessDecision(response, allowGrace) {
2208
+ const effectiveStatus = response.effectiveStatus ?? null;
2209
+ const serverHasAccess = response.hasAccess ?? (effectiveStatus !== null && ACCESS_GRANTING_EFFECTIVE_STATUSES.includes(effectiveStatus));
2210
+ const isGrace = response.isGrace ?? effectiveStatus === "past_due_grace";
2211
+ const hasAccess = serverHasAccess && (allowGrace || !isGrace);
2212
+ const isBlocked = response.isBlocked ?? (effectiveStatus === null || effectiveStatus !== null && BLOCKING_EFFECTIVE_STATUSES.includes(effectiveStatus));
2213
+ return {
2214
+ hasAccess,
2215
+ effectiveStatus,
2216
+ isGrace,
2217
+ isBlocked: isBlocked || !hasAccess,
2218
+ allowGrace,
2219
+ failPolicy: DEFAULT_SERVICE_ACCESS_FAIL_POLICY,
2220
+ denialReason: resolveServiceAccessDenialReason({ effectiveStatus, hasAccess, isGrace }, allowGrace),
2221
+ organizationId: response.organizationId,
2222
+ subscriptionId: response.subscriptionId,
2223
+ status: response.status,
2224
+ planCode: response.planCode,
2225
+ currentPeriodEnd: response.currentPeriodEnd,
2226
+ gracePeriodEndsAt: response.gracePeriodEndsAt ?? null,
2227
+ reactivatable: response.reactivatable ?? false
2228
+ };
2229
+ }
2230
+ function failClosedServiceAccessDecision(params, error) {
2231
+ return {
2232
+ hasAccess: false,
2233
+ effectiveStatus: null,
2234
+ isGrace: false,
2235
+ isBlocked: true,
2236
+ allowGrace: params.allowGrace ?? DEFAULT_ALLOW_GRACE,
2237
+ failPolicy: DEFAULT_SERVICE_ACCESS_FAIL_POLICY,
2238
+ denialReason: "ffid_unreachable",
2239
+ organizationId: params.organizationId || null,
2240
+ subscriptionId: null,
2241
+ status: null,
2242
+ planCode: null,
2243
+ currentPeriodEnd: null,
2244
+ gracePeriodEndsAt: null,
2245
+ reactivatable: false,
2246
+ error
2247
+ };
2248
+ }
2182
2249
  function resolveRedirectUri(raw, logger) {
2183
2250
  if (raw === null) return null;
2184
2251
  try {
@@ -2417,6 +2484,57 @@ function createFFIDClient(config) {
2417
2484
  `${EXT_CHECK_ENDPOINT}?${query.toString()}`
2418
2485
  );
2419
2486
  }
2487
+ async function checkServiceAccess(params) {
2488
+ const failPolicy = params.failPolicy ?? DEFAULT_SERVICE_ACCESS_FAIL_POLICY;
2489
+ if (failPolicy !== DEFAULT_SERVICE_ACCESS_FAIL_POLICY) {
2490
+ return {
2491
+ error: createError(
2492
+ "VALIDATION_ERROR",
2493
+ `failPolicy \u306F ${DEFAULT_SERVICE_ACCESS_FAIL_POLICY} \u306E\u307F\u6307\u5B9A\u3067\u304D\u307E\u3059`
2494
+ )
2495
+ };
2496
+ }
2497
+ const subscriptionParams = {
2498
+ organizationId: params.organizationId
2499
+ };
2500
+ if (params.userId !== void 0) {
2501
+ subscriptionParams.userId = params.userId;
2502
+ }
2503
+ const subscriptionResult = await checkSubscription(subscriptionParams);
2504
+ if (subscriptionResult.error) {
2505
+ if (subscriptionResult.error.code === "VALIDATION_ERROR") {
2506
+ return { error: subscriptionResult.error };
2507
+ }
2508
+ return {
2509
+ data: failClosedServiceAccessDecision(params, subscriptionResult.error)
2510
+ };
2511
+ }
2512
+ return {
2513
+ data: toServiceAccessDecision(
2514
+ subscriptionResult.data,
2515
+ params.allowGrace ?? DEFAULT_ALLOW_GRACE
2516
+ )
2517
+ };
2518
+ }
2519
+ async function requireServiceAccess(params) {
2520
+ const result = await checkServiceAccess(params);
2521
+ if (result.error) {
2522
+ return result;
2523
+ }
2524
+ if (!result.data.hasAccess) {
2525
+ const error = createError(
2526
+ SERVICE_ACCESS_DENIED_CODE,
2527
+ `FFID service access denied: ${result.data.denialReason ?? "unknown"}`
2528
+ );
2529
+ if (result.data.error) {
2530
+ error.details = { cause: result.data.error };
2531
+ }
2532
+ return {
2533
+ error
2534
+ };
2535
+ }
2536
+ return result;
2537
+ }
2420
2538
  const { createCheckoutSession, createPortalSession } = createBillingMethods({
2421
2539
  fetchWithAuth,
2422
2540
  createError
@@ -2515,6 +2633,8 @@ function createFFIDClient(config) {
2515
2633
  exchangeCodeForTokens,
2516
2634
  refreshAccessToken,
2517
2635
  checkSubscription,
2636
+ checkServiceAccess,
2637
+ requireServiceAccess,
2518
2638
  listMembers,
2519
2639
  updateMemberRole,
2520
2640
  removeMember,
@@ -1,4 +1,5 @@
1
- import { g as FFIDClient, e as FFIDOAuthUserInfo } from '../../ffid-client-ZcJhRbD6.cjs';
1
+ import { g as FFIDClient, e as FFIDOAuthUserInfo } from '../../ffid-client-CKMGqqPi.cjs';
2
+ import '../../types-5g_Bg6Ey.cjs';
2
3
 
3
4
  /**
4
5
  * FFID SDK - Test mode client (E2E / integration test bypass)
@@ -1,4 +1,5 @@
1
- import { g as FFIDClient, e as FFIDOAuthUserInfo } from '../../ffid-client-ZcJhRbD6.js';
1
+ import { g as FFIDClient, e as FFIDOAuthUserInfo } from '../../ffid-client-CUOFknXy.js';
2
+ import '../../types-5g_Bg6Ey.js';
2
3
 
3
4
  /**
4
5
  * FFID SDK - Test mode client (E2E / integration test bypass)
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Subscription access-control types (Chain of Pacts / Issue #2443).
3
+ *
4
+ * Defines `EffectiveSubscriptionStatus` — the semantic status surfaced by
5
+ * `/api/v1/subscriptions/ext/check` for external services that need a single
6
+ * value to drive access-control decisions. Mirrors the server-side type in
7
+ * `src/lib/common/subscription-helpers.ts` so the FFID backend, SDK, and
8
+ * consuming services agree on the same vocabulary.
9
+ */
10
+ /**
11
+ * Semantic subscription status used for access-control decisions by external
12
+ * services.
13
+ *
14
+ * Unlike the raw DB `FFIDSubscriptionStatus` (which keeps the Stripe-aligned
15
+ * status machine, e.g. `past_due` / `incomplete` / `incomplete_expired`), this
16
+ * narrowed union flattens dunning-window vs. hard-block cases so callers can
17
+ * branch on a single value:
18
+ *
19
+ * - `active` — normal paying subscription; grant full access.
20
+ * - `past_due_grace` — payment failed but FFID is still retrying. Service
21
+ * SHOULD keep access and surface a recovery banner.
22
+ * - `blocked` — payment dunning exhausted (`past_due` over grace,
23
+ * `unpaid`, `incomplete_expired`, `paused`, `incomplete`). Deny access.
24
+ * - `canceled` — contract ended (voluntary or auto-cancel). Deny access;
25
+ * check the accompanying `reactivatable` flag before showing a restart CTA.
26
+ * - `trial_expired` — `status = 'trialing'` but the trial end is in the past.
27
+ * DB row is still `trialing`; deny access and prompt for a paid plan.
28
+ * - `expired` — `status = 'active'` but `current_period_end` is in the past.
29
+ * This is the FFID-internal runtime-expired case (see
30
+ * `getEffectiveSubscriptionStatus` on the server). Deny access.
31
+ *
32
+ * @see /api/v1/subscriptions/ext/check
33
+ * @see docs/03-implementation/EXPIRED_CONTRACT_HANDLING.md
34
+ */
35
+ type EffectiveSubscriptionStatus = 'active' | 'past_due_grace' | 'blocked' | 'canceled' | 'trial_expired' | 'expired';
36
+
37
+ export type { EffectiveSubscriptionStatus as E };
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Subscription access-control types (Chain of Pacts / Issue #2443).
3
+ *
4
+ * Defines `EffectiveSubscriptionStatus` — the semantic status surfaced by
5
+ * `/api/v1/subscriptions/ext/check` for external services that need a single
6
+ * value to drive access-control decisions. Mirrors the server-side type in
7
+ * `src/lib/common/subscription-helpers.ts` so the FFID backend, SDK, and
8
+ * consuming services agree on the same vocabulary.
9
+ */
10
+ /**
11
+ * Semantic subscription status used for access-control decisions by external
12
+ * services.
13
+ *
14
+ * Unlike the raw DB `FFIDSubscriptionStatus` (which keeps the Stripe-aligned
15
+ * status machine, e.g. `past_due` / `incomplete` / `incomplete_expired`), this
16
+ * narrowed union flattens dunning-window vs. hard-block cases so callers can
17
+ * branch on a single value:
18
+ *
19
+ * - `active` — normal paying subscription; grant full access.
20
+ * - `past_due_grace` — payment failed but FFID is still retrying. Service
21
+ * SHOULD keep access and surface a recovery banner.
22
+ * - `blocked` — payment dunning exhausted (`past_due` over grace,
23
+ * `unpaid`, `incomplete_expired`, `paused`, `incomplete`). Deny access.
24
+ * - `canceled` — contract ended (voluntary or auto-cancel). Deny access;
25
+ * check the accompanying `reactivatable` flag before showing a restart CTA.
26
+ * - `trial_expired` — `status = 'trialing'` but the trial end is in the past.
27
+ * DB row is still `trialing`; deny access and prompt for a paid plan.
28
+ * - `expired` — `status = 'active'` but `current_period_end` is in the past.
29
+ * This is the FFID-internal runtime-expired case (see
30
+ * `getEffectiveSubscriptionStatus` on the server). Deny access.
31
+ *
32
+ * @see /api/v1/subscriptions/ext/check
33
+ * @see docs/03-implementation/EXPIRED_CONTRACT_HANDLING.md
34
+ */
35
+ type EffectiveSubscriptionStatus = 'active' | 'past_due_grace' | 'blocked' | 'canceled' | 'trial_expired' | 'expired';
36
+
37
+ export type { EffectiveSubscriptionStatus as E };
@@ -1,3 +1,5 @@
1
+ import { E as EffectiveSubscriptionStatus } from '../types-5g_Bg6Ey.cjs';
2
+
1
3
  /**
2
4
  * FFID Webhook SDK Constants
3
5
  *
@@ -43,6 +45,7 @@ declare class FFIDWebhookPayloadError extends FFIDWebhookError {
43
45
  * Types for receiving and verifying FeelFlow ID webhook events.
44
46
  * Used by external services to handle real-time event notifications.
45
47
  */
48
+
46
49
  /** All supported webhook event types */
47
50
  type FFIDWebhookEventType = 'subscription.created' | 'subscription.updated' | 'subscription.canceled' | 'subscription.downgrade_scheduled' | 'subscription.downgrade_applied' | 'subscription.downgrade_canceled' | 'subscription.trial_ending' | 'subscription.payment_failed' | 'user.created' | 'user.updated' | 'user.deleted' | 'user.deletion_requested' | 'organization.created' | 'organization.updated' | 'organization.member.added' | 'organization.member.removed' | 'organization.member.role_changed' | 'legal.document.updated' | 'legal.agreement.required' | 'system.maintenance.scheduled' | 'system.maintenance.started' | 'system.maintenance.completed' | 'announcement.published' | 'test.ping';
48
51
  interface FFIDSubscriptionCreatedPayload {
@@ -50,6 +53,8 @@ interface FFIDSubscriptionCreatedPayload {
50
53
  organizationId?: string;
51
54
  plan?: string;
52
55
  status?: string;
56
+ effectiveStatus?: EffectiveSubscriptionStatus;
57
+ gracePeriodEndsAt?: string | null;
53
58
  }
54
59
  interface FFIDSubscriptionUpdatedPayload {
55
60
  subscriptionId: string;
@@ -57,6 +62,8 @@ interface FFIDSubscriptionUpdatedPayload {
57
62
  plan?: string;
58
63
  previousPlan?: string;
59
64
  status?: string;
65
+ effectiveStatus?: EffectiveSubscriptionStatus;
66
+ gracePeriodEndsAt?: string | null;
60
67
  }
61
68
  interface FFIDSubscriptionCanceledPayload {
62
69
  /** FFID subscription UUID. */
@@ -86,6 +93,10 @@ interface FFIDSubscriptionCanceledPayload {
86
93
  * when Stripe runs out the paid period before emitting the deletion event.
87
94
  */
88
95
  expiredAt?: string;
96
+ /** Cancellation always leaves the canonical access-control status as `canceled`. */
97
+ effectiveStatus?: EffectiveSubscriptionStatus;
98
+ /** Always null for cancellation events because no grace window remains. */
99
+ gracePeriodEndsAt?: string | null;
89
100
  }
90
101
  interface FFIDSubscriptionDowngradeScheduledPayload {
91
102
  /** FFID subscription UUID. */
@@ -172,6 +183,13 @@ interface FFIDSubscriptionPaymentFailedPayload {
172
183
  * `null` means already blocking.
173
184
  */
174
185
  graceUntil?: string | null;
186
+ /** Canonical FFID access-control status derived from payment-failure severity. */
187
+ effectiveStatus?: EffectiveSubscriptionStatus;
188
+ /**
189
+ * Alias of `graceUntil` for canonical access payloads; services should prefer
190
+ * this field when sharing code with `/subscriptions/ext/check`.
191
+ */
192
+ gracePeriodEndsAt?: string | null;
175
193
  /**
176
194
  * `true` when the FFID server could not resolve `stripeSubscriptionId` to an
177
195
  * FFID subscription row (e.g. race with a not-yet-synced sub, or invoice
@@ -1,3 +1,5 @@
1
+ import { E as EffectiveSubscriptionStatus } from '../types-5g_Bg6Ey.js';
2
+
1
3
  /**
2
4
  * FFID Webhook SDK Constants
3
5
  *
@@ -43,6 +45,7 @@ declare class FFIDWebhookPayloadError extends FFIDWebhookError {
43
45
  * Types for receiving and verifying FeelFlow ID webhook events.
44
46
  * Used by external services to handle real-time event notifications.
45
47
  */
48
+
46
49
  /** All supported webhook event types */
47
50
  type FFIDWebhookEventType = 'subscription.created' | 'subscription.updated' | 'subscription.canceled' | 'subscription.downgrade_scheduled' | 'subscription.downgrade_applied' | 'subscription.downgrade_canceled' | 'subscription.trial_ending' | 'subscription.payment_failed' | 'user.created' | 'user.updated' | 'user.deleted' | 'user.deletion_requested' | 'organization.created' | 'organization.updated' | 'organization.member.added' | 'organization.member.removed' | 'organization.member.role_changed' | 'legal.document.updated' | 'legal.agreement.required' | 'system.maintenance.scheduled' | 'system.maintenance.started' | 'system.maintenance.completed' | 'announcement.published' | 'test.ping';
48
51
  interface FFIDSubscriptionCreatedPayload {
@@ -50,6 +53,8 @@ interface FFIDSubscriptionCreatedPayload {
50
53
  organizationId?: string;
51
54
  plan?: string;
52
55
  status?: string;
56
+ effectiveStatus?: EffectiveSubscriptionStatus;
57
+ gracePeriodEndsAt?: string | null;
53
58
  }
54
59
  interface FFIDSubscriptionUpdatedPayload {
55
60
  subscriptionId: string;
@@ -57,6 +62,8 @@ interface FFIDSubscriptionUpdatedPayload {
57
62
  plan?: string;
58
63
  previousPlan?: string;
59
64
  status?: string;
65
+ effectiveStatus?: EffectiveSubscriptionStatus;
66
+ gracePeriodEndsAt?: string | null;
60
67
  }
61
68
  interface FFIDSubscriptionCanceledPayload {
62
69
  /** FFID subscription UUID. */
@@ -86,6 +93,10 @@ interface FFIDSubscriptionCanceledPayload {
86
93
  * when Stripe runs out the paid period before emitting the deletion event.
87
94
  */
88
95
  expiredAt?: string;
96
+ /** Cancellation always leaves the canonical access-control status as `canceled`. */
97
+ effectiveStatus?: EffectiveSubscriptionStatus;
98
+ /** Always null for cancellation events because no grace window remains. */
99
+ gracePeriodEndsAt?: string | null;
89
100
  }
90
101
  interface FFIDSubscriptionDowngradeScheduledPayload {
91
102
  /** FFID subscription UUID. */
@@ -172,6 +183,13 @@ interface FFIDSubscriptionPaymentFailedPayload {
172
183
  * `null` means already blocking.
173
184
  */
174
185
  graceUntil?: string | null;
186
+ /** Canonical FFID access-control status derived from payment-failure severity. */
187
+ effectiveStatus?: EffectiveSubscriptionStatus;
188
+ /**
189
+ * Alias of `graceUntil` for canonical access payloads; services should prefer
190
+ * this field when sharing code with `/subscriptions/ext/check`.
191
+ */
192
+ gracePeriodEndsAt?: string | null;
175
193
  /**
176
194
  * `true` when the FFID server could not resolve `stripeSubscriptionId` to an
177
195
  * FFID subscription row (e.g. race with a not-yet-synced sub, or invoice
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@feelflow/ffid-sdk",
3
- "version": "4.1.0",
3
+ "version": "4.2.0",
4
4
  "description": "FeelFlow ID Platform SDK for React/Next.js applications",
5
5
  "keywords": [
6
6
  "feelflow",