@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/edge.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,7 +2674,35 @@ 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'];
2702
+ type McpBootstrapPlanInput = NonNullable<components['schemas']['McpBootstrapDto']['plans']>[number];
2703
+ type ToolPlanMappingInput = NonNullable<components['schemas']['McpBootstrapDto']['tools']>[number];
2491
2704
  type McpBootstrapRequest = components['schemas']['McpBootstrapDto'];
2705
+ type McpToolPlanMappingInput = NonNullable<components['schemas']['ConfigureMcpPlansDto']['toolMapping']>[number];
2492
2706
  interface McpBootstrapResponse {
2493
2707
  product: components['schemas']['SdkProductResponse'];
2494
2708
  mcpServer: {
@@ -2548,11 +2762,41 @@ interface SolvaPayClient {
2548
2762
  createCustomer?(params: components['schemas']['CreateCustomerRequest']): Promise<{
2549
2763
  customerRef: string;
2550
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
+ }>;
2551
2780
  getCustomer(params: {
2552
2781
  customerRef?: string;
2553
2782
  externalRef?: string;
2554
2783
  email?: string;
2555
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>;
2556
2800
  listProducts?(): Promise<Array<{
2557
2801
  reference: string;
2558
2802
  name: string;
@@ -2631,6 +2875,9 @@ interface SolvaPayClient {
2631
2875
  createCheckoutSession(params: operations['CheckoutSessionSdkController_createCheckoutSession']['requestBody']['content']['application/json']): Promise<components['schemas']['CreateCheckoutSessionResponse']>;
2632
2876
  createCustomerSession(params: components['schemas']['CreateCustomerSessionRequest']): Promise<components['schemas']['CreateCustomerSessionResponse']>;
2633
2877
  activatePlan?(params: components['schemas']['ActivatePlanDto']): Promise<ActivatePlanResult>;
2878
+ getPaymentMethod?(params: {
2879
+ customerRef: string;
2880
+ }): Promise<PaymentMethodInfo>;
2634
2881
  }
2635
2882
 
2636
2883
  /**
@@ -2667,6 +2914,15 @@ type PaywallStructuredContent = {
2667
2914
  product: string;
2668
2915
  checkoutUrl: string;
2669
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;
2670
2926
  } | {
2671
2927
  kind: 'activation_required';
2672
2928
  /** Product ref from paywall metadata (or env default) */
@@ -2683,18 +2939,47 @@ type PaywallStructuredContent = {
2683
2939
  productDetails?: LimitActivationProduct;
2684
2940
  };
2685
2941
  /**
2686
- * 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
2687
2949
  */
2688
- interface PaywallToolResult {
2689
- content?: Array<{
2690
- type: 'text' | 'image' | 'resource';
2691
- text?: string;
2692
- data?: string;
2693
- mimeType?: string;
2694
- }>;
2695
- isError?: boolean;
2696
- structuredContent?: PaywallStructuredContent | Record<string, unknown>;
2697
- }
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
+ };
2698
2983
 
2699
2984
  /**
2700
2985
  * Configuration Options Type Definitions
@@ -2735,16 +3020,33 @@ interface RetryOptions {
2735
3020
  */
2736
3021
  onRetry?: (error: Error, attempt: number) => void;
2737
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
+ */
2738
3032
  interface McpToolExtra {
2739
3033
  authInfo?: {
2740
- token: string;
2741
- clientId: string;
2742
- scopes: string[];
3034
+ token?: string;
3035
+ clientId?: string;
3036
+ scopes?: string[];
2743
3037
  expiresAt?: number;
2744
3038
  extra?: Record<string, unknown>;
2745
3039
  };
2746
3040
  [key: string]: unknown;
2747
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
+ }
2748
3050
  /**
2749
3051
  * Options for configuring payable protection
2750
3052
  */
@@ -2800,19 +3102,6 @@ interface NextAdapterOptions {
2800
3102
  */
2801
3103
  transformResponse?: (result: any) => any;
2802
3104
  }
2803
- /**
2804
- * MCP adapter options for MCP servers
2805
- */
2806
- interface McpAdapterOptions {
2807
- /**
2808
- * Extract customer reference from MCP args
2809
- */
2810
- getCustomerRef?: (args: any, extra?: McpToolExtra) => string | Promise<string>;
2811
- /**
2812
- * Transform the response before wrapping in MCP format
2813
- */
2814
- transformResponse?: (result: any) => any;
2815
- }
2816
3105
 
2817
3106
  /**
2818
3107
  * SolvaPay Server SDK - API Client
@@ -3553,30 +3842,48 @@ declare function createSolvaPay(config?: CreateSolvaPayConfig): SolvaPay;
3553
3842
  */
3554
3843
 
3555
3844
  /**
3556
- * Error thrown when a paywall is triggered (purchase required or usage limit exceeded).
3845
+ * Error representing a paywall gate outcome (purchase required or usage
3846
+ * limit exceeded).
3557
3847
  *
3558
- * This error is automatically thrown by the paywall protection system when:
3559
- * - Customer doesn't have required purchase
3560
- * - Customer has exceeded usage limits
3561
- * - Customer needs to upgrade their plan
3848
+ * Soft-deprecated since 1.1.0 as the internal control-flow signal
3849
+ * `paywall.decide()` returns a typed `PaywallDecision<T>` union instead,
3850
+ * and adapters route gate outcomes through `formatGate` without
3851
+ * throwing. `PaywallError` is retained as a compat shim for three paths
3852
+ * that keep working without migration:
3562
3853
  *
3563
- * The error includes structured content with checkout URLs and metadata for
3564
- * building custom paywall UIs.
3854
+ * 1. `paywall.protect(handler, ...)` still throws `PaywallError` on
3855
+ * gate outcomes (the legacy throw-based API).
3856
+ * 2. Merchant code that `throw new PaywallError(...)` (or
3857
+ * `ctx.gate(reason)`, which is implemented on top of
3858
+ * `PaywallError`) — caught at the adapter boundary and routed
3859
+ * through `formatGate` so transport responses stay consistent.
3860
+ * 3. Custom third-party adapters that didn't implement `formatGate` —
3861
+ * `AbstractAdapter.formatGate` falls back to wrapping in
3862
+ * `PaywallError` and delegating to `formatError`.
3565
3863
  *
3566
- * @example
3864
+ * The error includes structured content with checkout URLs and metadata
3865
+ * for building custom paywall UIs.
3866
+ *
3867
+ * @example Preferred (decide()/formatGate)
3567
3868
  * ```typescript
3568
- * import { PaywallError } from '@solvapay/server';
3869
+ * const decision = await solvaPay.paywall.decide(args, { product })
3870
+ * if (decision.outcome === 'gate') {
3871
+ * return res.status(402).json(paywallErrorToClientPayload(
3872
+ * new PaywallError(decision.gate.message, decision.gate),
3873
+ * ))
3874
+ * }
3875
+ * ```
3569
3876
  *
3877
+ * @example Compat (try/catch on throw-based legacy path)
3878
+ * ```typescript
3570
3879
  * try {
3571
3880
  * const result = await payable.http(createTask)(req, res);
3572
3881
  * return result;
3573
3882
  * } catch (error) {
3574
3883
  * if (error instanceof PaywallError) {
3575
- * // Custom paywall handling
3576
3884
  * return res.status(402).json({
3577
3885
  * error: error.message,
3578
3886
  * checkoutUrl: error.structuredContent.checkoutUrl,
3579
- * // Additional metadata available in error.structuredContent
3580
3887
  * });
3581
3888
  * }
3582
3889
  * throw error;
@@ -3584,6 +3891,7 @@ declare function createSolvaPay(config?: CreateSolvaPayConfig): SolvaPay;
3584
3891
  * ```
3585
3892
  *
3586
3893
  * @see {@link PaywallStructuredContent} for the structured content format
3894
+ * @see {@link PaywallDecision} for the preferred decision-based API
3587
3895
  * @since 1.0.0
3588
3896
  */
3589
3897
  declare class PaywallError extends Error {
@@ -3599,6 +3907,86 @@ declare class PaywallError extends Error {
3599
3907
  /** JSON body shape for HTTP adapters and MCP text content (stable fields for clients). */
3600
3908
  declare function paywallErrorToClientPayload(error: PaywallError): Record<string, unknown>;
3601
3909
 
3910
+ /**
3911
+ * Pure state engine that classifies a `LimitResponseWithPlan` into a
3912
+ * recovery-tool-specific `PaywallState`, and produces the human-readable
3913
+ * gate / nudge message templates the MCP transport ships as
3914
+ * `content[0].text` on gate/nudge responses.
3915
+ *
3916
+ * This module has no UI dependencies — it's framework-neutral so
3917
+ * `@solvapay/server`, `@solvapay/mcp-core`, and any HTTP adapter can
3918
+ * reuse the classification + copy. The text-only paywall design puts
3919
+ * the full narration (including a clickable `checkoutUrl` for
3920
+ * terminal-first hosts) on `gate.message`, and names exactly one
3921
+ * primary recovery tool so LLMs chain naturally toward it.
3922
+ */
3923
+
3924
+ /**
3925
+ * Discriminated union describing which recovery path the customer
3926
+ * needs. Every state maps to exactly one primary recovery tool except
3927
+ * `reactivation_required`, which surfaces two alternatives (rare).
3928
+ */
3929
+ type PaywallState = {
3930
+ kind: 'activation_required';
3931
+ } | {
3932
+ kind: 'topup_required';
3933
+ } | {
3934
+ kind: 'upgrade_required';
3935
+ } | {
3936
+ kind: 'reactivation_required';
3937
+ };
3938
+ /**
3939
+ * Classify a `LimitResponseWithPlan` (or `null` on degraded paths) into
3940
+ * a `PaywallState`. Pure — safe to call multiple times per request.
3941
+ *
3942
+ * Precedence:
3943
+ * 1. `activationRequired === true` — trumps everything else; the
3944
+ * backend explicitly flagged that no plan is live yet.
3945
+ * 2. Usage-based plan out of credits — the customer has a plan but
3946
+ * ran out, so a topup is the right action. "Out of credits" is
3947
+ * determined from (in order): the nested
3948
+ * `balance.creditBalance === 0` block, the top-level
3949
+ * `creditBalance === 0` field, or `remaining === 0` as a
3950
+ * fallback for older backend responses that omit both credit
3951
+ * fields on usage-based plans.
3952
+ * 3. Everything else → `upgrade_required`, including:
3953
+ * - `limits === null` (defensive),
3954
+ * - no active plan on the product,
3955
+ * - recurring plan at period cap (`remaining <= 0`).
3956
+ *
3957
+ * `reactivation_required` is deferred — it needs a distinct backend
3958
+ * signal (future `LimitResponse.inactivePurchaseRef`) which isn't
3959
+ * emitted yet. Kept in the type so downstream code compiles against
3960
+ * the full discriminated union; `classifyPaywallState` will never
3961
+ * return it under current backend behaviour.
3962
+ */
3963
+ declare function classifyPaywallState(limits: LimitResponseWithPlan | null): PaywallState;
3964
+ /**
3965
+ * Produce the terminal-friendly gate message. Names exactly one
3966
+ * recovery tool (`upgrade` / `topup` / `activate_plan`), except for
3967
+ * the rare `reactivation_required` path which names two alternatives
3968
+ * (`manage_account` / `upgrade`). Inlines `gate.checkoutUrl` when
3969
+ * present so terminal-only MCP hosts (Claude Code, CLI clients) can
3970
+ * open a browser directly.
3971
+ *
3972
+ * Kept as a pure string so the adapter layer can concatenate it with
3973
+ * an optional narrator prefix without parsing structured copy.
3974
+ */
3975
+ declare function buildGateMessage(state: PaywallState, gate: PaywallStructuredContent): string;
3976
+ /**
3977
+ * Low-balance / approaching-cap nudge copy used as a plain text suffix
3978
+ * on a successful merchant response (no `structuredContent` switch, no
3979
+ * view switch). Mirrors the `buildGateMessage` surface so nudges feel
3980
+ * like a softer version of the same text-only nudge path.
3981
+ *
3982
+ * Receives the `PaywallState` the classifier would have produced if
3983
+ * the customer had tripped the gate. `upgrade_required` and
3984
+ * `topup_required` are the only kinds that currently produce nudge
3985
+ * copy; the others are no-ops (shouldn't happen — nudges only fire
3986
+ * on successful calls).
3987
+ */
3988
+ declare function buildNudgeMessage(state: PaywallState, limits: LimitResponseWithPlan | null): string;
3989
+
3602
3990
  /**
3603
3991
  * Utility functions for the SolvaPay Server SDK
3604
3992
  */
@@ -3693,8 +4081,16 @@ declare function handleRouteError(error: unknown, operationName: string, default
3693
4081
  * and name from authenticated requests. Works with any framework that uses
3694
4082
  * the standard Web API Request (Express, Fastify, Next.js, Edge Functions, etc.).
3695
4083
  *
3696
- * Uses dynamic imports to avoid requiring @solvapay/auth at build time,
3697
- * making it suitable for edge runtime environments.
4084
+ * Resolution order:
4085
+ * 1. `x-user-id` header set by Next.js-style middleware (unchanged).
4086
+ * 2. `Authorization: Bearer <jwt>` — verified via HS256 when a secret is
4087
+ * configured (`SOLVAPAY_JWT_SECRET` or `SUPABASE_JWT_SECRET`), or
4088
+ * unverified-decoded when no secret is configured. The unverified path
4089
+ * covers platforms that verify JWTs at the gateway (e.g. Supabase Edge
4090
+ * with `verify_jwt = true`, which is the default) and asymmetric signing
4091
+ * keys (ES256/RS256) that the SDK does not have keys for.
4092
+ * 3. Set `SOLVAPAY_AUTH_STRICT=true` to require cryptographic verification
4093
+ * inside the handler (the unverified-decode fallback is then disabled).
3698
4094
  *
3699
4095
  * @param request - Standard Web API Request object
3700
4096
  * @param options - Configuration options
@@ -4014,6 +4410,21 @@ declare function activatePlanCore(request: Request, body: {
4014
4410
  includeName?: boolean;
4015
4411
  }): Promise<ActivatePlanResult | ErrorResult>;
4016
4412
 
4413
+ /**
4414
+ * Payment-method helpers (core).
4415
+ *
4416
+ * Thin wrapper over `GET /v1/sdk/payment-method`. Extracts the authenticated
4417
+ * user from the request via `syncCustomerCore`, then asks the SolvaPay API
4418
+ * for the customer's default card. Returns `{ kind: 'none' }` gracefully
4419
+ * when no card is on file; any other failure surfaces as an `ErrorResult`.
4420
+ */
4421
+
4422
+ declare function getPaymentMethodCore(request: Request, options?: {
4423
+ solvaPay?: SolvaPay;
4424
+ includeEmail?: boolean;
4425
+ includeName?: boolean;
4426
+ }): Promise<PaymentMethodInfo | ErrorResult>;
4427
+
4017
4428
  /**
4018
4429
  * Plans Helper (Core)
4019
4430
  *
@@ -4024,13 +4435,42 @@ declare function activatePlanCore(request: Request, body: {
4024
4435
 
4025
4436
  type Plan = components['schemas']['Plan'];
4026
4437
  /**
4027
- * List plans - core implementation
4438
+ * List plans - core implementation.
4439
+ *
4440
+ * Pass `options.solvaPay` to route through a pre-configured SolvaPay instance
4441
+ * (e.g. stub-backed in examples). When omitted, the helper reads
4442
+ * `SOLVAPAY_SECRET_KEY` from environment and constructs a real API client.
4028
4443
  */
4029
- declare function listPlansCore(request: Request): Promise<{
4444
+ declare function listPlansCore(request: Request, options?: {
4445
+ solvaPay?: SolvaPay;
4446
+ }): Promise<{
4030
4447
  plans: Plan[];
4031
4448
  productRef: string;
4032
4449
  } | ErrorResult>;
4033
4450
 
4451
+ /**
4452
+ * Merchant Helper (Core)
4453
+ *
4454
+ * Generic helper for GET /api/merchant — returns the SDK-facing merchant
4455
+ * identity used by `<MandateText>`, `<CheckoutSummary>`, and trust signals.
4456
+ * Works with standard Web API Request (Express, Fastify, Next.js, Edge).
4457
+ */
4458
+
4459
+ declare function getMerchantCore(_request: Request, options?: {
4460
+ solvaPay?: SolvaPay;
4461
+ }): Promise<SdkMerchantResponse | ErrorResult>;
4462
+
4463
+ /**
4464
+ * Product Helper (Core)
4465
+ *
4466
+ * Generic helper for GET /api/get-product?productRef=...
4467
+ * Returns a single product by reference, used by the `useProduct` React hook.
4468
+ */
4469
+
4470
+ declare function getProductCore(request: Request, options?: {
4471
+ solvaPay?: SolvaPay;
4472
+ }): Promise<SdkProductResponse | ErrorResult>;
4473
+
4034
4474
  interface PurchaseCheckResult {
4035
4475
  customerRef: string;
4036
4476
  email?: string;
@@ -4062,6 +4502,33 @@ declare function checkPurchaseCore(request: Request, options?: {
4062
4502
  includeName?: boolean;
4063
4503
  }): Promise<PurchaseCheckResult | ErrorResult>;
4064
4504
 
4505
+ /**
4506
+ * Usage snapshot derived from the authenticated customer's active purchase.
4507
+ *
4508
+ * Shape matches the backend's `UserInfoUsageDto` so the React `useUsage`
4509
+ * hook gets a canonical set of fields regardless of transport.
4510
+ */
4511
+ interface GetUsageResult {
4512
+ meterRef: string | null;
4513
+ total: number | null;
4514
+ used: number;
4515
+ remaining: number | null;
4516
+ /** 0–100, rounded to 2dp. `null` when `total` is unknown. */
4517
+ percentUsed: number | null;
4518
+ periodStart?: string;
4519
+ periodEnd?: string;
4520
+ /** Raw purchase ref the usage belongs to (when a usage-based plan is active). */
4521
+ purchaseRef?: string;
4522
+ }
4523
+ /**
4524
+ * Fetch the authenticated customer's usage snapshot for the active purchase.
4525
+ *
4526
+ * Derives the values from `checkPurchaseCore` — no extra backend call.
4527
+ * Returns `null` values when no usage-based plan is active.
4528
+ */
4529
+ declare function getUsageCore(request: Request, options?: {
4530
+ solvaPay?: SolvaPay;
4531
+ }): Promise<GetUsageResult | ErrorResult>;
4065
4532
  declare function trackUsageCore(request: Request, body: {
4066
4533
  actionType?: 'transaction' | 'api_call' | 'hour' | 'email' | 'storage' | 'custom';
4067
4534
  units?: number;
@@ -4087,4 +4554,4 @@ declare function verifyWebhook({ body, signature, secret, }: {
4087
4554
  secret: string;
4088
4555
  }): Promise<WebhookEvent>;
4089
4556
 
4090
- export { type AuthenticatedUser, type CreateSolvaPayConfig, type CustomerBalanceResult, type CustomerWebhookObject, type ErrorResult, type HttpAdapterOptions, type LimitActivationBalance, type LimitActivationProduct, type LimitPlanSummary, type McpAdapterOptions, type NextAdapterOptions, type PayableFunction, type PayableOptions, type PaywallArgs, PaywallError, type PaywallMetadata, type PaywallStructuredContent, type PaywallToolResult, type PurchaseCheckResult, type RetryOptions, type ServerClientOptions, type SolvaPay, type SolvaPayClient, type WebhookEvent, type WebhookEventForType, type WebhookEventObjectMap, type WebhookEventType, type WebhookProduct, activatePlanCore, cancelPurchaseCore, checkPurchaseCore, createCheckoutSessionCore, createCustomerSessionCore, createPaymentIntentCore, createSolvaPay, createSolvaPayClient, createTopupPaymentIntentCore, getAuthenticatedUserCore, getCustomerBalanceCore, handleRouteError, isErrorResult, listPlansCore, paywallErrorToClientPayload, processPaymentIntentCore, reactivatePurchaseCore, syncCustomerCore, trackUsageCore, verifyWebhook, withRetry };
4557
+ 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 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 PurchaseCheckResult, type RetryOptions, type SdkMerchantResponse, type SdkProductResponse, type ServerClientOptions, type SolvaPay, type SolvaPayClient, type ToolPlanMappingInput, type WebhookEvent, type WebhookEventForType, type WebhookEventObjectMap, type WebhookEventType, type WebhookProduct, activatePlanCore, buildGateMessage, buildNudgeMessage, cancelPurchaseCore, checkPurchaseCore, classifyPaywallState, type components, createCheckoutSessionCore, createCustomerSessionCore, createPaymentIntentCore, createSolvaPay, createSolvaPayClient, createTopupPaymentIntentCore, getAuthenticatedUserCore, getCustomerBalanceCore, getMerchantCore, getPaymentMethodCore, getProductCore, getUsageCore, handleRouteError, isErrorResult, isPaywallStructuredContent, listPlansCore, paywallErrorToClientPayload, processPaymentIntentCore, reactivatePurchaseCore, syncCustomerCore, trackUsageCore, verifyWebhook, withRetry };