@solvapay/server 1.0.9-preview.1 → 1.0.9

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/dist/index.d.ts CHANGED
@@ -49,6 +49,58 @@ type WebhookEvent = {
49
49
 
50
50
  interface components {
51
51
  schemas: {
52
+ SdkMerchantResponseDto: {
53
+ /**
54
+ * Brand name shown in UI
55
+ * @example Acme
56
+ */
57
+ displayName: string;
58
+ /**
59
+ * Legal entity name used in SCA mandate copy
60
+ * @example Acme Inc.
61
+ */
62
+ legalName: string;
63
+ /** @example support@acme.com */
64
+ supportEmail?: string;
65
+ /** @example https://acme.com/support */
66
+ supportUrl?: string;
67
+ /** @example https://acme.com/terms */
68
+ termsUrl?: string;
69
+ /** @example https://acme.com/privacy */
70
+ privacyUrl?: string;
71
+ /**
72
+ * ISO-3166 alpha-2 country code of the merchant
73
+ * @example US
74
+ */
75
+ country?: string;
76
+ /**
77
+ * ISO-4217 default settlement currency
78
+ * @example usd
79
+ */
80
+ defaultCurrency?: string;
81
+ /**
82
+ * Descriptor appearing on the customer card statement
83
+ * @example ACME INC
84
+ */
85
+ statementDescriptor?: string;
86
+ /**
87
+ * Absolute URL to the merchant logo
88
+ * @example https://cdn.acme.com/logo.png
89
+ */
90
+ logoUrl?: string;
91
+ /**
92
+ * Absolute URL to the square app icon / logomark. Consumed by MCP host chromes, mobile avatar slots, and any surface where the landscape `logoUrl` would need letterboxing.
93
+ * @example https://cdn.acme.com/icon.png
94
+ */
95
+ iconUrl?: string;
96
+ };
97
+ SdkPlatformConfigResponseDto: {
98
+ /**
99
+ * SolvaPay's platform Stripe publishable key for the authenticated provider's environment. Safe to expose browser-side; paired with the connected `accountId` returned from `create-payment-intent` for Stripe Connect direct charges. Omitted when not configured so callers can fall back cleanly to a hosted flow.
100
+ * @example pk_test_...
101
+ */
102
+ stripePublishableKey?: string;
103
+ };
52
104
  CreatePaymentIntentDto: {
53
105
  productRef?: string;
54
106
  customerRef: string;
@@ -1047,6 +1099,14 @@ interface components {
1047
1099
  /** @description Active purchases */
1048
1100
  purchases?: components["schemas"]["PurchaseInfo"][];
1049
1101
  };
1102
+ UpdateCustomerRequest: {
1103
+ /** Format: email */
1104
+ email?: string;
1105
+ name?: string;
1106
+ telephone?: string;
1107
+ metadata?: unknown;
1108
+ externalRef?: string;
1109
+ };
1050
1110
  CreateCustomerSessionResponse: {
1051
1111
  /**
1052
1112
  * Customer session ID/token
@@ -1180,6 +1240,53 @@ interface components {
1180
1240
  pathItems: never;
1181
1241
  }
1182
1242
  interface operations {
1243
+ getMerchant: {
1244
+ parameters: {
1245
+ query?: never;
1246
+ header?: never;
1247
+ path?: never;
1248
+ cookie?: never;
1249
+ };
1250
+ requestBody?: never;
1251
+ responses: {
1252
+ /** @description Merchant identity */
1253
+ 200: {
1254
+ headers: {
1255
+ [name: string]: unknown;
1256
+ };
1257
+ content: {
1258
+ "application/json": components["schemas"]["SdkMerchantResponseDto"];
1259
+ };
1260
+ };
1261
+ /** @description Provider not found */
1262
+ 404: {
1263
+ headers: {
1264
+ [name: string]: unknown;
1265
+ };
1266
+ content?: never;
1267
+ };
1268
+ };
1269
+ };
1270
+ getPlatformConfig: {
1271
+ parameters: {
1272
+ query?: never;
1273
+ header?: never;
1274
+ path?: never;
1275
+ cookie?: never;
1276
+ };
1277
+ requestBody?: never;
1278
+ responses: {
1279
+ /** @description Platform config */
1280
+ 200: {
1281
+ headers: {
1282
+ [name: string]: unknown;
1283
+ };
1284
+ content: {
1285
+ "application/json": components["schemas"]["SdkPlatformConfigResponseDto"];
1286
+ };
1287
+ };
1288
+ };
1289
+ };
1183
1290
  PaymentIntentSdkController_getPaymentIntents: {
1184
1291
  parameters: {
1185
1292
  query?: {
@@ -2258,7 +2365,7 @@ interface operations {
2258
2365
  };
2259
2366
  };
2260
2367
  };
2261
- CustomerSdkController_getCustomerBalance: {
2368
+ CustomerSdkController_getCustomer: {
2262
2369
  parameters: {
2263
2370
  query?: never;
2264
2371
  header?: never;
@@ -2270,13 +2377,13 @@ interface operations {
2270
2377
  };
2271
2378
  requestBody?: never;
2272
2379
  responses: {
2273
- /** @description Customer balance retrieved successfully */
2380
+ /** @description Customer retrieved successfully */
2274
2381
  200: {
2275
2382
  headers: {
2276
2383
  [name: string]: unknown;
2277
2384
  };
2278
2385
  content: {
2279
- "application/json": unknown;
2386
+ "application/json": components["schemas"]["CustomerResponse"];
2280
2387
  };
2281
2388
  };
2282
2389
  /** @description Customer not found */
@@ -2290,7 +2397,7 @@ interface operations {
2290
2397
  };
2291
2398
  };
2292
2399
  };
2293
- CustomerSdkController_getCustomer: {
2400
+ CustomerSdkController_updateCustomer: {
2294
2401
  parameters: {
2295
2402
  query?: never;
2296
2403
  header?: never;
@@ -2300,9 +2407,14 @@ interface operations {
2300
2407
  };
2301
2408
  cookie?: never;
2302
2409
  };
2303
- requestBody?: never;
2410
+ /** @description Fields to update (all optional) */
2411
+ requestBody: {
2412
+ content: {
2413
+ "application/json": components["schemas"]["UpdateCustomerRequest"];
2414
+ };
2415
+ };
2304
2416
  responses: {
2305
- /** @description Customer retrieved successfully */
2417
+ /** @description Customer updated successfully */
2306
2418
  200: {
2307
2419
  headers: {
2308
2420
  [name: string]: unknown;
@@ -2311,6 +2423,43 @@ interface operations {
2311
2423
  "application/json": components["schemas"]["CustomerResponse"];
2312
2424
  };
2313
2425
  };
2426
+ /** @description Invalid update payload */
2427
+ 400: {
2428
+ headers: {
2429
+ [name: string]: unknown;
2430
+ };
2431
+ content?: never;
2432
+ };
2433
+ /** @description Customer not found */
2434
+ 404: {
2435
+ headers: {
2436
+ [name: string]: unknown;
2437
+ };
2438
+ content?: never;
2439
+ };
2440
+ };
2441
+ };
2442
+ CustomerSdkController_getCustomerBalance: {
2443
+ parameters: {
2444
+ query?: never;
2445
+ header?: never;
2446
+ path: {
2447
+ /** @description Customer reference identifier */
2448
+ reference: string;
2449
+ };
2450
+ cookie?: never;
2451
+ };
2452
+ requestBody?: never;
2453
+ responses: {
2454
+ /** @description Customer balance retrieved successfully */
2455
+ 200: {
2456
+ headers: {
2457
+ [name: string]: unknown;
2458
+ };
2459
+ content: {
2460
+ "application/json": unknown;
2461
+ };
2462
+ };
2314
2463
  /** @description Customer not found */
2315
2464
  404: {
2316
2465
  headers: {
@@ -2435,6 +2584,43 @@ interface operations {
2435
2584
  };
2436
2585
  };
2437
2586
  };
2587
+ PaymentMethodSdkController_getPaymentMethod: {
2588
+ parameters: {
2589
+ query: {
2590
+ /** @description Customer reference (e.g. `customer_...`). */
2591
+ customerRef: string;
2592
+ };
2593
+ header?: never;
2594
+ path?: never;
2595
+ cookie?: never;
2596
+ };
2597
+ requestBody?: never;
2598
+ responses: {
2599
+ /** @description The customer's default card, or `{ kind: 'none' }` when no card is on file. */
2600
+ 200: {
2601
+ headers: {
2602
+ [name: string]: unknown;
2603
+ };
2604
+ content: {
2605
+ "application/json": {
2606
+ /** @enum {string} */
2607
+ kind: "card";
2608
+ /** @example visa */
2609
+ brand: string;
2610
+ /** @example 4242 */
2611
+ last4: string;
2612
+ /** @example 12 */
2613
+ expMonth: number;
2614
+ /** @example 2030 */
2615
+ expYear: number;
2616
+ } | {
2617
+ /** @enum {string} */
2618
+ kind: "none";
2619
+ };
2620
+ };
2621
+ };
2622
+ };
2623
+ };
2438
2624
  }
2439
2625
 
2440
2626
  /**
@@ -2488,6 +2674,31 @@ interface ProcessPaymentResult {
2488
2674
  status: 'completed';
2489
2675
  }
2490
2676
  type ActivatePlanResult = components['schemas']['ActivatePlanResponseDto'];
2677
+ /**
2678
+ * SDK-facing payment-method projection returned by
2679
+ * `GET /v1/sdk/payment-method?customerRef=...`.
2680
+ *
2681
+ * Derived from the generated operation response so any backend shape
2682
+ * change propagates through a single `npm run generate:types` run. The
2683
+ * inline `oneOf` schema on the backend controller translates to a clean
2684
+ * `{ kind: 'card', ... } | { kind: 'none' }` discriminated union here.
2685
+ */
2686
+ type PaymentMethodInfo = operations['PaymentMethodSdkController_getPaymentMethod']['responses']['200']['content']['application/json'];
2687
+ /**
2688
+ * SDK-facing merchant identity (source: GET /v1/sdk/merchant).
2689
+ */
2690
+ type SdkMerchantResponse = components['schemas']['SdkMerchantResponseDto'];
2691
+ /**
2692
+ * SDK-facing platform config (source: GET /v1/sdk/platform-config).
2693
+ *
2694
+ * Environment-aware platform values resolved against the authenticated
2695
+ * provider. Primary consumer today is the MCP checkout app, which uses
2696
+ * `stripePublishableKey` to boot Stripe.js for a CSP probe before a
2697
+ * PaymentIntent exists.
2698
+ */
2699
+ type SdkPlatformConfigResponse = components['schemas']['SdkPlatformConfigResponseDto'];
2700
+ /** SDK-facing product projection. Sourced from the existing OpenAPI spec. */
2701
+ type SdkProductResponse = components['schemas']['SdkProductResponse'];
2491
2702
  type McpBootstrapPlanInput = NonNullable<components['schemas']['McpBootstrapDto']['plans']>[number];
2492
2703
  type ToolPlanMappingInput = NonNullable<components['schemas']['McpBootstrapDto']['tools']>[number];
2493
2704
  type McpBootstrapRequest = components['schemas']['McpBootstrapDto'];
@@ -2551,11 +2762,41 @@ interface SolvaPayClient {
2551
2762
  createCustomer?(params: components['schemas']['CreateCustomerRequest']): Promise<{
2552
2763
  customerRef: string;
2553
2764
  }>;
2765
+ /**
2766
+ * PATCH: /v1/sdk/customers/{customerRef}
2767
+ * Update mutable customer fields. Used by `ensureCustomer` to backfill
2768
+ * `externalRef` on an existing email-matched customer, and exposed
2769
+ * directly for integrators who need it.
2770
+ */
2771
+ updateCustomer?(customerRef: string, params: {
2772
+ email?: string;
2773
+ name?: string;
2774
+ telephone?: string;
2775
+ metadata?: Record<string, unknown>;
2776
+ externalRef?: string;
2777
+ }): Promise<{
2778
+ customerRef: string;
2779
+ }>;
2554
2780
  getCustomer(params: {
2555
2781
  customerRef?: string;
2556
2782
  externalRef?: string;
2557
2783
  email?: string;
2558
2784
  }): Promise<CustomerResponseMapped>;
2785
+ /**
2786
+ * SDK-facing merchant identity (GET /v1/sdk/merchant).
2787
+ * Returns the subset of provider fields safe for browser consumption —
2788
+ * used by `<MandateText>`, `<CheckoutSummary>`, and trust signals.
2789
+ */
2790
+ getMerchant?(): Promise<SdkMerchantResponse>;
2791
+ /**
2792
+ * SDK-facing platform config (GET /v1/sdk/platform-config).
2793
+ * Returns environment-aware browser-safe values (resolved sandbox/live
2794
+ * against the authenticated provider). Primary consumer today is the
2795
+ * MCP checkout app, which uses `stripePublishableKey` to boot Stripe.js
2796
+ * for a CSP probe before a PaymentIntent exists.
2797
+ */
2798
+ getPlatformConfig?(): Promise<SdkPlatformConfigResponse>;
2799
+ getProduct?(productRef: string): Promise<SdkProductResponse>;
2559
2800
  listProducts?(): Promise<Array<{
2560
2801
  reference: string;
2561
2802
  name: string;
@@ -2634,6 +2875,9 @@ interface SolvaPayClient {
2634
2875
  createCheckoutSession(params: operations['CheckoutSessionSdkController_createCheckoutSession']['requestBody']['content']['application/json']): Promise<components['schemas']['CreateCheckoutSessionResponse']>;
2635
2876
  createCustomerSession(params: components['schemas']['CreateCustomerSessionRequest']): Promise<components['schemas']['CreateCustomerSessionResponse']>;
2636
2877
  activatePlan?(params: components['schemas']['ActivatePlanDto']): Promise<ActivatePlanResult>;
2878
+ getPaymentMethod?(params: {
2879
+ customerRef: string;
2880
+ }): Promise<PaymentMethodInfo>;
2637
2881
  }
2638
2882
 
2639
2883
  /**
@@ -2670,6 +2914,15 @@ type PaywallStructuredContent = {
2670
2914
  product: string;
2671
2915
  checkoutUrl: string;
2672
2916
  message: string;
2917
+ /**
2918
+ * Quota balance at the moment the paywall tripped. Optional so
2919
+ * older server versions (pre-balance-on-payment_required) stay
2920
+ * compatible; the React `PaywallNotice.Message` prefers this
2921
+ * structured data over the raw `message` when available.
2922
+ */
2923
+ balance?: LimitActivationBalance;
2924
+ /** Rich product context from checkLimits (name, ref, provider slug/id) */
2925
+ productDetails?: LimitActivationProduct;
2673
2926
  } | {
2674
2927
  kind: 'activation_required';
2675
2928
  /** Product ref from paywall metadata (or env default) */
@@ -2686,18 +2939,47 @@ type PaywallStructuredContent = {
2686
2939
  productDetails?: LimitActivationProduct;
2687
2940
  };
2688
2941
  /**
2689
- * MCP tool result with optional paywall information
2942
+ * Runtime type guard for `PaywallStructuredContent`.
2943
+ *
2944
+ * Used by the MCP registration layer to detect paywall-shaped tool results
2945
+ * coming out of `.mcp()` so it knows whether to attach the `_meta.ui`
2946
+ * envelope that tells MCP hosts which UI resource to open.
2947
+ *
2948
+ * @since 1.0.9
2690
2949
  */
2691
- interface PaywallToolResult {
2692
- content?: Array<{
2693
- type: 'text' | 'image' | 'resource';
2694
- text?: string;
2695
- data?: string;
2696
- mimeType?: string;
2697
- }>;
2698
- isError?: boolean;
2699
- structuredContent?: PaywallStructuredContent | Record<string, unknown>;
2700
- }
2950
+ declare function isPaywallStructuredContent(value: unknown): value is PaywallStructuredContent;
2951
+ /**
2952
+ * Discriminated union returned from `paywall.decide(args, metadata)` that
2953
+ * surfaces the pre-check outcome as data instead of an exception.
2954
+ *
2955
+ * Replaces the throw-based `PaywallError` control flow adapters previously
2956
+ * relied on: `allow` carries the resolved customer ref + fresh
2957
+ * `LimitResponseWithPlan` so the handler can run, while `gate` carries the
2958
+ * ready-to-serialise `PaywallStructuredContent` so the adapter's
2959
+ * `formatGate` can emit the transport-specific paywall response.
2960
+ *
2961
+ * The `args` field on `allow` is the same shape passed in — threaded
2962
+ * through so callers can hand it directly to the handler without having
2963
+ * to hold onto a reference across the call.
2964
+ *
2965
+ * @since 1.1.0
2966
+ */
2967
+ type PaywallDecision<T> = {
2968
+ outcome: 'allow';
2969
+ args: T;
2970
+ limits: LimitResponseWithPlan;
2971
+ customerRef: string;
2972
+ } | {
2973
+ outcome: 'gate';
2974
+ gate: PaywallStructuredContent;
2975
+ /**
2976
+ * The `LimitResponseWithPlan` consulted when the gate tripped. May
2977
+ * be `null` on degraded paths that couldn't produce a fresh
2978
+ * response (defensive — normal flow always populates this).
2979
+ */
2980
+ limits: LimitResponseWithPlan | null;
2981
+ customerRef: string;
2982
+ };
2701
2983
 
2702
2984
  /**
2703
2985
  * Configuration Options Type Definitions
@@ -2738,16 +3020,33 @@ interface RetryOptions {
2738
3020
  */
2739
3021
  onRetry?: (error: Error, attempt: number) => void;
2740
3022
  }
3023
+ /**
3024
+ * Extra context passed into MCP tool handlers — structural copy of
3025
+ * `McpToolExtra` from `@solvapay/mcp` kept here to avoid a build-time
3026
+ * circular dependency between `@solvapay/server` and `@solvapay/mcp`.
3027
+ *
3028
+ * The canonical version lives in `@solvapay/mcp/types.ts`. Keep the two
3029
+ * in lockstep — a `vitest` snapshot in `@solvapay/mcp` guards the
3030
+ * shape.
3031
+ */
2741
3032
  interface McpToolExtra {
2742
3033
  authInfo?: {
2743
- token: string;
2744
- clientId: string;
2745
- scopes: string[];
3034
+ token?: string;
3035
+ clientId?: string;
3036
+ scopes?: string[];
2746
3037
  expiresAt?: number;
2747
3038
  extra?: Record<string, unknown>;
2748
3039
  };
2749
3040
  [key: string]: unknown;
2750
3041
  }
3042
+ /**
3043
+ * MCP adapter options — structural copy of `McpAdapterOptions` from
3044
+ * `@solvapay/mcp`.
3045
+ */
3046
+ interface McpAdapterOptions {
3047
+ getCustomerRef?: (args: any, extra?: McpToolExtra) => string | Promise<string>;
3048
+ transformResponse?: (result: any) => any;
3049
+ }
2751
3050
  /**
2752
3051
  * Options for configuring payable protection
2753
3052
  */
@@ -2803,19 +3102,6 @@ interface NextAdapterOptions {
2803
3102
  */
2804
3103
  transformResponse?: (result: any) => any;
2805
3104
  }
2806
- /**
2807
- * MCP adapter options for MCP servers
2808
- */
2809
- interface McpAdapterOptions {
2810
- /**
2811
- * Extract customer reference from MCP args
2812
- */
2813
- getCustomerRef?: (args: any, extra?: McpToolExtra) => string | Promise<string>;
2814
- /**
2815
- * Transform the response before wrapping in MCP format
2816
- */
2817
- transformResponse?: (result: any) => any;
2818
- }
2819
3105
 
2820
3106
  /**
2821
3107
  * Virtual Tools for MCP Server Monetization
@@ -3575,30 +3861,48 @@ declare function createSolvaPayClient(opts: ServerClientOptions): SolvaPayClient
3575
3861
  */
3576
3862
 
3577
3863
  /**
3578
- * Error thrown when a paywall is triggered (purchase required or usage limit exceeded).
3864
+ * Error representing a paywall gate outcome (purchase required or usage
3865
+ * limit exceeded).
3579
3866
  *
3580
- * This error is automatically thrown by the paywall protection system when:
3581
- * - Customer doesn't have required purchase
3582
- * - Customer has exceeded usage limits
3583
- * - Customer needs to upgrade their plan
3867
+ * Soft-deprecated since 1.1.0 as the internal control-flow signal
3868
+ * `paywall.decide()` returns a typed `PaywallDecision<T>` union instead,
3869
+ * and adapters route gate outcomes through `formatGate` without
3870
+ * throwing. `PaywallError` is retained as a compat shim for three paths
3871
+ * that keep working without migration:
3584
3872
  *
3585
- * The error includes structured content with checkout URLs and metadata for
3586
- * building custom paywall UIs.
3873
+ * 1. `paywall.protect(handler, ...)` still throws `PaywallError` on
3874
+ * gate outcomes (the legacy throw-based API).
3875
+ * 2. Merchant code that `throw new PaywallError(...)` (or
3876
+ * `ctx.gate(reason)`, which is implemented on top of
3877
+ * `PaywallError`) — caught at the adapter boundary and routed
3878
+ * through `formatGate` so transport responses stay consistent.
3879
+ * 3. Custom third-party adapters that didn't implement `formatGate` —
3880
+ * `AbstractAdapter.formatGate` falls back to wrapping in
3881
+ * `PaywallError` and delegating to `formatError`.
3587
3882
  *
3588
- * @example
3883
+ * The error includes structured content with checkout URLs and metadata
3884
+ * for building custom paywall UIs.
3885
+ *
3886
+ * @example Preferred (decide()/formatGate)
3589
3887
  * ```typescript
3590
- * import { PaywallError } from '@solvapay/server';
3888
+ * const decision = await solvaPay.paywall.decide(args, { product })
3889
+ * if (decision.outcome === 'gate') {
3890
+ * return res.status(402).json(paywallErrorToClientPayload(
3891
+ * new PaywallError(decision.gate.message, decision.gate),
3892
+ * ))
3893
+ * }
3894
+ * ```
3591
3895
  *
3896
+ * @example Compat (try/catch on throw-based legacy path)
3897
+ * ```typescript
3592
3898
  * try {
3593
3899
  * const result = await payable.http(createTask)(req, res);
3594
3900
  * return result;
3595
3901
  * } catch (error) {
3596
3902
  * if (error instanceof PaywallError) {
3597
- * // Custom paywall handling
3598
3903
  * return res.status(402).json({
3599
3904
  * error: error.message,
3600
3905
  * checkoutUrl: error.structuredContent.checkoutUrl,
3601
- * // Additional metadata available in error.structuredContent
3602
3906
  * });
3603
3907
  * }
3604
3908
  * throw error;
@@ -3606,6 +3910,7 @@ declare function createSolvaPayClient(opts: ServerClientOptions): SolvaPayClient
3606
3910
  * ```
3607
3911
  *
3608
3912
  * @see {@link PaywallStructuredContent} for the structured content format
3913
+ * @see {@link PaywallDecision} for the preferred decision-based API
3609
3914
  * @since 1.0.0
3610
3915
  */
3611
3916
  declare class PaywallError extends Error {
@@ -3620,76 +3925,114 @@ declare class PaywallError extends Error {
3620
3925
  }
3621
3926
  /** JSON body shape for HTTP adapters and MCP text content (stable fields for clients). */
3622
3927
  declare function paywallErrorToClientPayload(error: PaywallError): Record<string, unknown>;
3623
-
3624
3928
  /**
3625
- * MCP OAuth helper utilities.
3929
+ * Handler-scoped context passed as the optional second positional
3930
+ * argument to handlers registered via `paywall.protect(...)`.
3931
+ *
3932
+ * Backwards-compatible: existing one-arg handlers `(args) => ...`
3933
+ * ignore the second argument and continue to work.
3626
3934
  *
3627
- * These helpers are intentionally lightweight and do not verify JWT signatures.
3628
- * Use them after token validation (for example via /v1/customer/auth/userinfo).
3935
+ * Consumed by `@solvapay/mcp`'s `buildPayableHandler` to construct the
3936
+ * `ResponseContext` merchant tools receive as their second argument.
3629
3937
  */
3630
- declare class McpBearerAuthError extends Error {
3631
- constructor(message: string);
3938
+ interface ProtectHandlerContext {
3939
+ /** Resolved backend customer ref (`cus_...`). */
3940
+ customerRef: string;
3941
+ /**
3942
+ * The `LimitResponseWithPlan` consulted at pre-check time. Sourced
3943
+ * from either the fresh `checkLimits` call on cache-miss or the
3944
+ * cached entry on cache-hit. `null` only when the paywall is
3945
+ * operating in a degraded mode that couldn't produce a limit
3946
+ * response (defensive; normal flow always populates this).
3947
+ */
3948
+ limits: LimitResponseWithPlan | null;
3949
+ /**
3950
+ * Transport-level extra passed through adapter chains (e.g. the MCP
3951
+ * adapter's `extra` bag holding `authInfo`). Opaque to `protect()`
3952
+ * itself; forwarded verbatim so adapter-aware handlers can use it.
3953
+ */
3954
+ extra?: unknown;
3632
3955
  }
3633
- type McpBearerCustomerRefOptions = {
3634
- claimPriority?: string[];
3635
- };
3636
- declare function extractBearerToken(authorization?: string | null): string | null;
3637
- declare function decodeJwtPayload(token: string): Record<string, unknown>;
3638
- declare function getCustomerRefFromJwtPayload(payload: Record<string, unknown>, options?: McpBearerCustomerRefOptions): string;
3639
- declare function getCustomerRefFromBearerAuthHeader(authorization?: string | null, options?: McpBearerCustomerRefOptions): string;
3640
3956
 
3641
- interface BuildAuthInfoFromBearerOptions extends McpBearerCustomerRefOptions {
3642
- clientId?: string;
3643
- defaultScopes?: string[];
3644
- includePayload?: boolean;
3645
- }
3646
- declare function buildAuthInfoFromBearer(authorization?: string | null, options?: BuildAuthInfoFromBearerOptions): McpToolExtra['authInfo'] | null;
3957
+ /**
3958
+ * Pure state engine that classifies a `LimitResponseWithPlan` into a
3959
+ * recovery-tool-specific `PaywallState`, and produces the human-readable
3960
+ * gate / nudge message templates the MCP transport ships as
3961
+ * `content[0].text` on gate/nudge responses.
3962
+ *
3963
+ * This module has no UI dependencies — it's framework-neutral so
3964
+ * `@solvapay/server`, `@solvapay/mcp-core`, and any HTTP adapter can
3965
+ * reuse the classification + copy. The text-only paywall design puts
3966
+ * the full narration (including a clickable `checkoutUrl` for
3967
+ * terminal-first hosts) on `gate.message`, and names exactly one
3968
+ * primary recovery tool so LLMs chain naturally toward it.
3969
+ */
3647
3970
 
3648
- type RequestLike = {
3649
- method?: string;
3650
- path?: string;
3651
- headers?: Record<string, string | string[] | undefined>;
3652
- body?: unknown;
3653
- auth?: unknown;
3654
- };
3655
- type ResponseLike = {
3656
- status: (code: number) => ResponseLike;
3657
- json: (payload: unknown) => void;
3658
- setHeader: (name: string, value: string) => void;
3659
- };
3660
- type NextLike = () => void;
3661
- type Middleware = (req: RequestLike, res: ResponseLike, next: NextLike) => void;
3662
- interface OAuthAuthorizationServerOptions {
3663
- apiBaseUrl: string;
3664
- productRef: string;
3665
- }
3666
- interface McpOAuthBridgeOptions {
3667
- publicBaseUrl: string;
3668
- apiBaseUrl: string;
3669
- productRef: string;
3670
- mcpPath?: string;
3671
- requireAuth?: boolean;
3672
- authInfo?: BuildAuthInfoFromBearerOptions;
3673
- protectedResourcePath?: string;
3674
- authorizationServerPath?: string;
3675
- }
3676
- declare function getOAuthProtectedResourceResponse(publicBaseUrl: string): {
3677
- resource: string;
3678
- authorization_servers: string[];
3679
- scopes_supported: string[];
3680
- };
3681
- declare function getOAuthAuthorizationServerResponse({ apiBaseUrl, productRef, }: OAuthAuthorizationServerOptions): {
3682
- issuer: string;
3683
- authorization_endpoint: string;
3684
- token_endpoint: string;
3685
- registration_endpoint: string;
3686
- token_endpoint_auth_methods_supported: string[];
3687
- response_types_supported: string[];
3688
- grant_types_supported: string[];
3689
- scopes_supported: string[];
3690
- code_challenge_methods_supported: string[];
3971
+ /**
3972
+ * Discriminated union describing which recovery path the customer
3973
+ * needs. Every state maps to exactly one primary recovery tool except
3974
+ * `reactivation_required`, which surfaces two alternatives (rare).
3975
+ */
3976
+ type PaywallState = {
3977
+ kind: 'activation_required';
3978
+ } | {
3979
+ kind: 'topup_required';
3980
+ } | {
3981
+ kind: 'upgrade_required';
3982
+ } | {
3983
+ kind: 'reactivation_required';
3691
3984
  };
3692
- declare function createMcpOAuthBridge(options: McpOAuthBridgeOptions): Middleware[];
3985
+ /**
3986
+ * Classify a `LimitResponseWithPlan` (or `null` on degraded paths) into
3987
+ * a `PaywallState`. Pure — safe to call multiple times per request.
3988
+ *
3989
+ * Precedence:
3990
+ * 1. `activationRequired === true` — trumps everything else; the
3991
+ * backend explicitly flagged that no plan is live yet.
3992
+ * 2. Usage-based plan out of credits — the customer has a plan but
3993
+ * ran out, so a topup is the right action. "Out of credits" is
3994
+ * determined from (in order): the nested
3995
+ * `balance.creditBalance === 0` block, the top-level
3996
+ * `creditBalance === 0` field, or `remaining === 0` as a
3997
+ * fallback for older backend responses that omit both credit
3998
+ * fields on usage-based plans.
3999
+ * 3. Everything else → `upgrade_required`, including:
4000
+ * - `limits === null` (defensive),
4001
+ * - no active plan on the product,
4002
+ * - recurring plan at period cap (`remaining <= 0`).
4003
+ *
4004
+ * `reactivation_required` is deferred — it needs a distinct backend
4005
+ * signal (future `LimitResponse.inactivePurchaseRef`) which isn't
4006
+ * emitted yet. Kept in the type so downstream code compiles against
4007
+ * the full discriminated union; `classifyPaywallState` will never
4008
+ * return it under current backend behaviour.
4009
+ */
4010
+ declare function classifyPaywallState(limits: LimitResponseWithPlan | null): PaywallState;
4011
+ /**
4012
+ * Produce the terminal-friendly gate message. Names exactly one
4013
+ * recovery tool (`upgrade` / `topup` / `activate_plan`), except for
4014
+ * the rare `reactivation_required` path which names two alternatives
4015
+ * (`manage_account` / `upgrade`). Inlines `gate.checkoutUrl` when
4016
+ * present so terminal-only MCP hosts (Claude Code, CLI clients) can
4017
+ * open a browser directly.
4018
+ *
4019
+ * Kept as a pure string so the adapter layer can concatenate it with
4020
+ * an optional narrator prefix without parsing structured copy.
4021
+ */
4022
+ declare function buildGateMessage(state: PaywallState, gate: PaywallStructuredContent): string;
4023
+ /**
4024
+ * Low-balance / approaching-cap nudge copy used as a plain text suffix
4025
+ * on a successful merchant response (no `structuredContent` switch, no
4026
+ * view switch). Mirrors the `buildGateMessage` surface so nudges feel
4027
+ * like a softer version of the same text-only nudge path.
4028
+ *
4029
+ * Receives the `PaywallState` the classifier would have produced if
4030
+ * the customer had tripped the gate. `upgrade_required` and
4031
+ * `topup_required` are the only kinds that currently produce nudge
4032
+ * copy; the others are no-ops (shouldn't happen — nudges only fire
4033
+ * on successful calls).
4034
+ */
4035
+ declare function buildNudgeMessage(state: PaywallState, limits: LimitResponseWithPlan | null): string;
3693
4036
 
3694
4037
  /**
3695
4038
  * Utility functions for the SolvaPay Server SDK
@@ -3785,8 +4128,16 @@ declare function handleRouteError(error: unknown, operationName: string, default
3785
4128
  * and name from authenticated requests. Works with any framework that uses
3786
4129
  * the standard Web API Request (Express, Fastify, Next.js, Edge Functions, etc.).
3787
4130
  *
3788
- * Uses dynamic imports to avoid requiring @solvapay/auth at build time,
3789
- * making it suitable for edge runtime environments.
4131
+ * Resolution order:
4132
+ * 1. `x-user-id` header set by Next.js-style middleware (unchanged).
4133
+ * 2. `Authorization: Bearer <jwt>` — verified via HS256 when a secret is
4134
+ * configured (`SOLVAPAY_JWT_SECRET` or `SUPABASE_JWT_SECRET`), or
4135
+ * unverified-decoded when no secret is configured. The unverified path
4136
+ * covers platforms that verify JWTs at the gateway (e.g. Supabase Edge
4137
+ * with `verify_jwt = true`, which is the default) and asymmetric signing
4138
+ * keys (ES256/RS256) that the SDK does not have keys for.
4139
+ * 3. Set `SOLVAPAY_AUTH_STRICT=true` to require cryptographic verification
4140
+ * inside the handler (the unverified-decode fallback is then disabled).
3790
4141
  *
3791
4142
  * @param request - Standard Web API Request object
3792
4143
  * @param options - Configuration options
@@ -4106,6 +4457,21 @@ declare function activatePlanCore(request: Request, body: {
4106
4457
  includeName?: boolean;
4107
4458
  }): Promise<ActivatePlanResult | ErrorResult>;
4108
4459
 
4460
+ /**
4461
+ * Payment-method helpers (core).
4462
+ *
4463
+ * Thin wrapper over `GET /v1/sdk/payment-method`. Extracts the authenticated
4464
+ * user from the request via `syncCustomerCore`, then asks the SolvaPay API
4465
+ * for the customer's default card. Returns `{ kind: 'none' }` gracefully
4466
+ * when no card is on file; any other failure surfaces as an `ErrorResult`.
4467
+ */
4468
+
4469
+ declare function getPaymentMethodCore(request: Request, options?: {
4470
+ solvaPay?: SolvaPay;
4471
+ includeEmail?: boolean;
4472
+ includeName?: boolean;
4473
+ }): Promise<PaymentMethodInfo | ErrorResult>;
4474
+
4109
4475
  /**
4110
4476
  * Plans Helper (Core)
4111
4477
  *
@@ -4116,13 +4482,42 @@ declare function activatePlanCore(request: Request, body: {
4116
4482
 
4117
4483
  type Plan = components['schemas']['Plan'];
4118
4484
  /**
4119
- * List plans - core implementation
4485
+ * List plans - core implementation.
4486
+ *
4487
+ * Pass `options.solvaPay` to route through a pre-configured SolvaPay instance
4488
+ * (e.g. stub-backed in examples). When omitted, the helper reads
4489
+ * `SOLVAPAY_SECRET_KEY` from environment and constructs a real API client.
4120
4490
  */
4121
- declare function listPlansCore(request: Request): Promise<{
4491
+ declare function listPlansCore(request: Request, options?: {
4492
+ solvaPay?: SolvaPay;
4493
+ }): Promise<{
4122
4494
  plans: Plan[];
4123
4495
  productRef: string;
4124
4496
  } | ErrorResult>;
4125
4497
 
4498
+ /**
4499
+ * Merchant Helper (Core)
4500
+ *
4501
+ * Generic helper for GET /api/merchant — returns the SDK-facing merchant
4502
+ * identity used by `<MandateText>`, `<CheckoutSummary>`, and trust signals.
4503
+ * Works with standard Web API Request (Express, Fastify, Next.js, Edge).
4504
+ */
4505
+
4506
+ declare function getMerchantCore(_request: Request, options?: {
4507
+ solvaPay?: SolvaPay;
4508
+ }): Promise<SdkMerchantResponse | ErrorResult>;
4509
+
4510
+ /**
4511
+ * Product Helper (Core)
4512
+ *
4513
+ * Generic helper for GET /api/get-product?productRef=...
4514
+ * Returns a single product by reference, used by the `useProduct` React hook.
4515
+ */
4516
+
4517
+ declare function getProductCore(request: Request, options?: {
4518
+ solvaPay?: SolvaPay;
4519
+ }): Promise<SdkProductResponse | ErrorResult>;
4520
+
4126
4521
  interface PurchaseCheckResult {
4127
4522
  customerRef: string;
4128
4523
  email?: string;
@@ -4154,6 +4549,33 @@ declare function checkPurchaseCore(request: Request, options?: {
4154
4549
  includeName?: boolean;
4155
4550
  }): Promise<PurchaseCheckResult | ErrorResult>;
4156
4551
 
4552
+ /**
4553
+ * Usage snapshot derived from the authenticated customer's active purchase.
4554
+ *
4555
+ * Shape matches the backend's `UserInfoUsageDto` so the React `useUsage`
4556
+ * hook gets a canonical set of fields regardless of transport.
4557
+ */
4558
+ interface GetUsageResult {
4559
+ meterRef: string | null;
4560
+ total: number | null;
4561
+ used: number;
4562
+ remaining: number | null;
4563
+ /** 0–100, rounded to 2dp. `null` when `total` is unknown. */
4564
+ percentUsed: number | null;
4565
+ periodStart?: string;
4566
+ periodEnd?: string;
4567
+ /** Raw purchase ref the usage belongs to (when a usage-based plan is active). */
4568
+ purchaseRef?: string;
4569
+ }
4570
+ /**
4571
+ * Fetch the authenticated customer's usage snapshot for the active purchase.
4572
+ *
4573
+ * Derives the values from `checkPurchaseCore` — no extra backend call.
4574
+ * Returns `null` values when no usage-based plan is active.
4575
+ */
4576
+ declare function getUsageCore(request: Request, options?: {
4577
+ solvaPay?: SolvaPay;
4578
+ }): Promise<GetUsageResult | ErrorResult>;
4157
4579
  declare function trackUsageCore(request: Request, body: {
4158
4580
  actionType?: 'transaction' | 'api_call' | 'hour' | 'email' | 'storage' | 'custom';
4159
4581
  units?: number;
@@ -4221,4 +4643,4 @@ declare function verifyWebhook({ body, signature, secret, }: {
4221
4643
  secret: string;
4222
4644
  }): WebhookEvent;
4223
4645
 
4224
- export { type ActivatePlanResult, type AuthenticatedUser, type ConfigureMcpPlansRequest, type ConfigureMcpPlansResponse, type CreateSolvaPayConfig, type CustomerBalanceResult, type CustomerResponseMapped, type CustomerWebhookObject, type ErrorResult, type HttpAdapterOptions, type LimitActivationBalance, type LimitActivationProduct, type LimitPlanSummary, type McpAdapterOptions, McpBearerAuthError, type McpBootstrapPlanInput, type McpBootstrapRequest, type McpBootstrapResponse, type McpServerLike, type McpToolExtra, type McpToolPlanMappingInput, type NextAdapterOptions, type OneTimePurchaseInfo, type PayableFunction, type PayableOptions, type PaywallArgs, PaywallError, type PaywallMetadata, type PaywallStructuredContent, type PaywallToolResult, type ProcessPaymentResult, type PurchaseCheckResult, type RegisterVirtualToolsMcpOptions, type RetryOptions, type ServerClientOptions, type SolvaPay, type SolvaPayClient, type ToolPlanMappingInput, VIRTUAL_TOOL_DEFINITIONS, type VirtualToolDefinition, type VirtualToolsOptions, type WebhookEvent, type WebhookEventForType, type WebhookEventObjectMap, type WebhookEventType, type WebhookProduct, activatePlanCore, buildAuthInfoFromBearer, cancelPurchaseCore, checkPurchaseCore, type components, createCheckoutSessionCore, createCustomerSessionCore, createMcpOAuthBridge, createPaymentIntentCore, createSolvaPay, createSolvaPayClient, createTopupPaymentIntentCore, createVirtualTools, decodeJwtPayload, extractBearerToken, getAuthenticatedUserCore, getCustomerBalanceCore, getCustomerRefFromBearerAuthHeader, getCustomerRefFromJwtPayload, getOAuthAuthorizationServerResponse, getOAuthProtectedResourceResponse, handleRouteError, isErrorResult, jsonSchemaToZodRawShape, listPlansCore, paywallErrorToClientPayload, processPaymentIntentCore, reactivatePurchaseCore, registerVirtualToolsMcpImpl, syncCustomerCore, trackUsageCore, verifyWebhook, withRetry };
4646
+ export { type ActivatePlanResult, type AuthenticatedUser, type ConfigureMcpPlansRequest, type ConfigureMcpPlansResponse, type CreateSolvaPayConfig, type CustomerBalanceResult, type CustomerResponseMapped, type CustomerWebhookObject, type ErrorResult, type GetUsageResult, type HttpAdapterOptions, type LimitActivationBalance, type LimitActivationProduct, type LimitPlanSummary, type LimitResponseWithPlan, type McpBootstrapPlanInput, type McpBootstrapRequest, type McpBootstrapResponse, type McpServerLike, type McpToolPlanMappingInput, type NextAdapterOptions, type OneTimePurchaseInfo, type PayableFunction, type PayableOptions, type PaymentMethodInfo, type PaywallArgs, type PaywallDecision, PaywallError, type PaywallMetadata, type PaywallState, type PaywallStructuredContent, type ProcessPaymentResult, type ProtectHandlerContext, type PurchaseCheckResult, type RegisterVirtualToolsMcpOptions, type RetryOptions, type SdkMerchantResponse, type SdkProductResponse, type ServerClientOptions, type SolvaPay, type SolvaPayClient, type ToolPlanMappingInput, VIRTUAL_TOOL_DEFINITIONS, type VirtualToolDefinition, type VirtualToolsOptions, type WebhookEvent, type WebhookEventForType, type WebhookEventObjectMap, type WebhookEventType, type WebhookProduct, activatePlanCore, buildGateMessage, buildNudgeMessage, cancelPurchaseCore, checkPurchaseCore, classifyPaywallState, type components, createCheckoutSessionCore, createCustomerSessionCore, createPaymentIntentCore, createSolvaPay, createSolvaPayClient, createTopupPaymentIntentCore, createVirtualTools, getAuthenticatedUserCore, getCustomerBalanceCore, getMerchantCore, getPaymentMethodCore, getProductCore, getUsageCore, handleRouteError, isErrorResult, isPaywallStructuredContent, jsonSchemaToZodRawShape, listPlansCore, paywallErrorToClientPayload, processPaymentIntentCore, reactivatePurchaseCore, registerVirtualToolsMcpImpl, syncCustomerCore, trackUsageCore, verifyWebhook, withRetry };