@feelflow/ffid-sdk 4.0.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.0.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.0.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,13 +45,16 @@ 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
- type FFIDWebhookEventType = 'subscription.created' | 'subscription.updated' | 'subscription.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';
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 {
49
52
  subscriptionId: string;
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,52 @@ 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;
100
+ }
101
+ interface FFIDSubscriptionDowngradeScheduledPayload {
102
+ /** FFID subscription UUID. */
103
+ subscriptionId: string;
104
+ /** Organization UUID that owns the subscription. */
105
+ organizationId: string;
106
+ /** Plan UUID currently active on the subscription. */
107
+ currentPlanId: string;
108
+ /** Plan UUID scheduled to become active at period end. */
109
+ newPlanId: string;
110
+ /** Billing interval carried by the target period, typically 'monthly' or 'yearly'. */
111
+ billingInterval: string;
112
+ /** ISO timestamp when the downgrade was scheduled. */
113
+ scheduledAt: string;
114
+ /** ISO timestamp when the scheduled downgrade should take effect. */
115
+ effectiveAt: string;
116
+ }
117
+ interface FFIDSubscriptionDowngradeAppliedPayload {
118
+ /** FFID subscription UUID. */
119
+ subscriptionId: string;
120
+ /** Organization UUID that owns the subscription. */
121
+ organizationId: string;
122
+ /** Plan UUID active before the pending downgrade was applied. */
123
+ previousPlanId: string;
124
+ /** Plan UUID that became active after the downgrade was applied. */
125
+ newPlanId: string;
126
+ /** Billing interval on the applied downgraded plan, typically 'monthly' or 'yearly'. */
127
+ billingInterval: string;
128
+ /** ISO timestamp when FFID applied the pending downgrade. */
129
+ appliedAt: string;
130
+ }
131
+ interface FFIDSubscriptionDowngradeCanceledPayload {
132
+ /** FFID subscription UUID. */
133
+ subscriptionId: string;
134
+ /** Organization UUID that owns the subscription. */
135
+ organizationId: string;
136
+ /** Plan UUID that had been pending but was canceled before taking effect. */
137
+ canceledPlanId: string;
138
+ /** Plan UUID that remains active after canceling the pending downgrade. */
139
+ currentPlanId: string;
140
+ /** ISO timestamp when the pending downgrade was canceled. */
141
+ canceledAt: string;
89
142
  }
90
143
  interface FFIDSubscriptionTrialEndingPayload {
91
144
  subscriptionId: string;
@@ -130,6 +183,13 @@ interface FFIDSubscriptionPaymentFailedPayload {
130
183
  * `null` means already blocking.
131
184
  */
132
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;
133
193
  /**
134
194
  * `true` when the FFID server could not resolve `stripeSubscriptionId` to an
135
195
  * FFID subscription row (e.g. race with a not-yet-synced sub, or invoice
@@ -221,6 +281,9 @@ interface FFIDWebhookEventMap {
221
281
  'subscription.created': FFIDSubscriptionCreatedPayload;
222
282
  'subscription.updated': FFIDSubscriptionUpdatedPayload;
223
283
  'subscription.canceled': FFIDSubscriptionCanceledPayload;
284
+ 'subscription.downgrade_scheduled': FFIDSubscriptionDowngradeScheduledPayload;
285
+ 'subscription.downgrade_applied': FFIDSubscriptionDowngradeAppliedPayload;
286
+ 'subscription.downgrade_canceled': FFIDSubscriptionDowngradeCanceledPayload;
224
287
  'subscription.trial_ending': FFIDSubscriptionTrialEndingPayload;
225
288
  'subscription.payment_failed': FFIDSubscriptionPaymentFailedPayload;
226
289
  'user.created': FFIDUserCreatedPayload;
@@ -363,4 +426,4 @@ declare function createFFIDWebhookHandler(config: FFIDWebhookHandlerConfig): {
363
426
  /** Type of the FFID Webhook handler */
364
427
  type FFIDWebhookHandler = ReturnType<typeof createFFIDWebhookHandler>;
365
428
 
366
- export { DEFAULT_TOLERANCE_SECONDS, type FFIDLegalAgreementRequiredPayload, type FFIDLegalDocumentUpdatedPayload, type FFIDOrganizationCreatedPayload, type FFIDOrganizationMemberAddedPayload, type FFIDOrganizationMemberRemovedPayload, type FFIDOrganizationMemberRoleChangedPayload, type FFIDOrganizationUpdatedPayload, type FFIDSubscriptionCanceledPayload, type FFIDSubscriptionCreatedPayload, type FFIDSubscriptionPaymentFailedPayload, type FFIDSubscriptionTrialEndingPayload, type FFIDSubscriptionUpdatedPayload, type FFIDSystemMaintenancePayload, type FFIDTestPingPayload, type FFIDUserCreatedPayload, type FFIDUserDeletedPayload, type FFIDUserDeletionRequestedPayload, type FFIDUserUpdatedPayload, FFIDWebhookError, type FFIDWebhookEvent, type FFIDWebhookEventHandler, type FFIDWebhookEventMap, type FFIDWebhookEventType, type FFIDWebhookHandler, type FFIDWebhookHandlerConfig, type FFIDWebhookLogger, FFIDWebhookPayloadError, FFIDWebhookSignatureError, FFIDWebhookTimestampError, FFID_WEBHOOK_EVENT_ID_HEADER, FFID_WEBHOOK_SIGNATURE_HEADER, FFID_WEBHOOK_SIGNATURE_VERSION, FFID_WEBHOOK_TIMESTAMP_HEADER, computeSignature, createFFIDWebhookHandler, parseSignatureHeader, verifyWebhookSignature };
429
+ export { DEFAULT_TOLERANCE_SECONDS, type FFIDLegalAgreementRequiredPayload, type FFIDLegalDocumentUpdatedPayload, type FFIDOrganizationCreatedPayload, type FFIDOrganizationMemberAddedPayload, type FFIDOrganizationMemberRemovedPayload, type FFIDOrganizationMemberRoleChangedPayload, type FFIDOrganizationUpdatedPayload, type FFIDSubscriptionCanceledPayload, type FFIDSubscriptionCreatedPayload, type FFIDSubscriptionDowngradeAppliedPayload, type FFIDSubscriptionDowngradeCanceledPayload, type FFIDSubscriptionDowngradeScheduledPayload, type FFIDSubscriptionPaymentFailedPayload, type FFIDSubscriptionTrialEndingPayload, type FFIDSubscriptionUpdatedPayload, type FFIDSystemMaintenancePayload, type FFIDTestPingPayload, type FFIDUserCreatedPayload, type FFIDUserDeletedPayload, type FFIDUserDeletionRequestedPayload, type FFIDUserUpdatedPayload, FFIDWebhookError, type FFIDWebhookEvent, type FFIDWebhookEventHandler, type FFIDWebhookEventMap, type FFIDWebhookEventType, type FFIDWebhookHandler, type FFIDWebhookHandlerConfig, type FFIDWebhookLogger, FFIDWebhookPayloadError, FFIDWebhookSignatureError, FFIDWebhookTimestampError, FFID_WEBHOOK_EVENT_ID_HEADER, FFID_WEBHOOK_SIGNATURE_HEADER, FFID_WEBHOOK_SIGNATURE_VERSION, FFID_WEBHOOK_TIMESTAMP_HEADER, computeSignature, createFFIDWebhookHandler, parseSignatureHeader, verifyWebhookSignature };
@@ -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,13 +45,16 @@ 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
- type FFIDWebhookEventType = 'subscription.created' | 'subscription.updated' | 'subscription.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';
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 {
49
52
  subscriptionId: string;
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,52 @@ 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;
100
+ }
101
+ interface FFIDSubscriptionDowngradeScheduledPayload {
102
+ /** FFID subscription UUID. */
103
+ subscriptionId: string;
104
+ /** Organization UUID that owns the subscription. */
105
+ organizationId: string;
106
+ /** Plan UUID currently active on the subscription. */
107
+ currentPlanId: string;
108
+ /** Plan UUID scheduled to become active at period end. */
109
+ newPlanId: string;
110
+ /** Billing interval carried by the target period, typically 'monthly' or 'yearly'. */
111
+ billingInterval: string;
112
+ /** ISO timestamp when the downgrade was scheduled. */
113
+ scheduledAt: string;
114
+ /** ISO timestamp when the scheduled downgrade should take effect. */
115
+ effectiveAt: string;
116
+ }
117
+ interface FFIDSubscriptionDowngradeAppliedPayload {
118
+ /** FFID subscription UUID. */
119
+ subscriptionId: string;
120
+ /** Organization UUID that owns the subscription. */
121
+ organizationId: string;
122
+ /** Plan UUID active before the pending downgrade was applied. */
123
+ previousPlanId: string;
124
+ /** Plan UUID that became active after the downgrade was applied. */
125
+ newPlanId: string;
126
+ /** Billing interval on the applied downgraded plan, typically 'monthly' or 'yearly'. */
127
+ billingInterval: string;
128
+ /** ISO timestamp when FFID applied the pending downgrade. */
129
+ appliedAt: string;
130
+ }
131
+ interface FFIDSubscriptionDowngradeCanceledPayload {
132
+ /** FFID subscription UUID. */
133
+ subscriptionId: string;
134
+ /** Organization UUID that owns the subscription. */
135
+ organizationId: string;
136
+ /** Plan UUID that had been pending but was canceled before taking effect. */
137
+ canceledPlanId: string;
138
+ /** Plan UUID that remains active after canceling the pending downgrade. */
139
+ currentPlanId: string;
140
+ /** ISO timestamp when the pending downgrade was canceled. */
141
+ canceledAt: string;
89
142
  }
90
143
  interface FFIDSubscriptionTrialEndingPayload {
91
144
  subscriptionId: string;
@@ -130,6 +183,13 @@ interface FFIDSubscriptionPaymentFailedPayload {
130
183
  * `null` means already blocking.
131
184
  */
132
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;
133
193
  /**
134
194
  * `true` when the FFID server could not resolve `stripeSubscriptionId` to an
135
195
  * FFID subscription row (e.g. race with a not-yet-synced sub, or invoice
@@ -221,6 +281,9 @@ interface FFIDWebhookEventMap {
221
281
  'subscription.created': FFIDSubscriptionCreatedPayload;
222
282
  'subscription.updated': FFIDSubscriptionUpdatedPayload;
223
283
  'subscription.canceled': FFIDSubscriptionCanceledPayload;
284
+ 'subscription.downgrade_scheduled': FFIDSubscriptionDowngradeScheduledPayload;
285
+ 'subscription.downgrade_applied': FFIDSubscriptionDowngradeAppliedPayload;
286
+ 'subscription.downgrade_canceled': FFIDSubscriptionDowngradeCanceledPayload;
224
287
  'subscription.trial_ending': FFIDSubscriptionTrialEndingPayload;
225
288
  'subscription.payment_failed': FFIDSubscriptionPaymentFailedPayload;
226
289
  'user.created': FFIDUserCreatedPayload;
@@ -363,4 +426,4 @@ declare function createFFIDWebhookHandler(config: FFIDWebhookHandlerConfig): {
363
426
  /** Type of the FFID Webhook handler */
364
427
  type FFIDWebhookHandler = ReturnType<typeof createFFIDWebhookHandler>;
365
428
 
366
- export { DEFAULT_TOLERANCE_SECONDS, type FFIDLegalAgreementRequiredPayload, type FFIDLegalDocumentUpdatedPayload, type FFIDOrganizationCreatedPayload, type FFIDOrganizationMemberAddedPayload, type FFIDOrganizationMemberRemovedPayload, type FFIDOrganizationMemberRoleChangedPayload, type FFIDOrganizationUpdatedPayload, type FFIDSubscriptionCanceledPayload, type FFIDSubscriptionCreatedPayload, type FFIDSubscriptionPaymentFailedPayload, type FFIDSubscriptionTrialEndingPayload, type FFIDSubscriptionUpdatedPayload, type FFIDSystemMaintenancePayload, type FFIDTestPingPayload, type FFIDUserCreatedPayload, type FFIDUserDeletedPayload, type FFIDUserDeletionRequestedPayload, type FFIDUserUpdatedPayload, FFIDWebhookError, type FFIDWebhookEvent, type FFIDWebhookEventHandler, type FFIDWebhookEventMap, type FFIDWebhookEventType, type FFIDWebhookHandler, type FFIDWebhookHandlerConfig, type FFIDWebhookLogger, FFIDWebhookPayloadError, FFIDWebhookSignatureError, FFIDWebhookTimestampError, FFID_WEBHOOK_EVENT_ID_HEADER, FFID_WEBHOOK_SIGNATURE_HEADER, FFID_WEBHOOK_SIGNATURE_VERSION, FFID_WEBHOOK_TIMESTAMP_HEADER, computeSignature, createFFIDWebhookHandler, parseSignatureHeader, verifyWebhookSignature };
429
+ export { DEFAULT_TOLERANCE_SECONDS, type FFIDLegalAgreementRequiredPayload, type FFIDLegalDocumentUpdatedPayload, type FFIDOrganizationCreatedPayload, type FFIDOrganizationMemberAddedPayload, type FFIDOrganizationMemberRemovedPayload, type FFIDOrganizationMemberRoleChangedPayload, type FFIDOrganizationUpdatedPayload, type FFIDSubscriptionCanceledPayload, type FFIDSubscriptionCreatedPayload, type FFIDSubscriptionDowngradeAppliedPayload, type FFIDSubscriptionDowngradeCanceledPayload, type FFIDSubscriptionDowngradeScheduledPayload, type FFIDSubscriptionPaymentFailedPayload, type FFIDSubscriptionTrialEndingPayload, type FFIDSubscriptionUpdatedPayload, type FFIDSystemMaintenancePayload, type FFIDTestPingPayload, type FFIDUserCreatedPayload, type FFIDUserDeletedPayload, type FFIDUserDeletionRequestedPayload, type FFIDUserUpdatedPayload, FFIDWebhookError, type FFIDWebhookEvent, type FFIDWebhookEventHandler, type FFIDWebhookEventMap, type FFIDWebhookEventType, type FFIDWebhookHandler, type FFIDWebhookHandlerConfig, type FFIDWebhookLogger, FFIDWebhookPayloadError, FFIDWebhookSignatureError, FFIDWebhookTimestampError, FFID_WEBHOOK_EVENT_ID_HEADER, FFID_WEBHOOK_SIGNATURE_HEADER, FFID_WEBHOOK_SIGNATURE_VERSION, FFID_WEBHOOK_TIMESTAMP_HEADER, computeSignature, createFFIDWebhookHandler, parseSignatureHeader, verifyWebhookSignature };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@feelflow/ffid-sdk",
3
- "version": "4.0.0",
3
+ "version": "4.2.0",
4
4
  "description": "FeelFlow ID Platform SDK for React/Next.js applications",
5
5
  "keywords": [
6
6
  "feelflow",