moltspay 1.6.0 → 2.4.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.
Files changed (57) hide show
  1. package/.env.example +23 -0
  2. package/CHANGELOG.md +551 -0
  3. package/README.md +466 -7
  4. package/dist/cdp/index.js.map +1 -1
  5. package/dist/cdp/index.mjs.map +1 -1
  6. package/dist/{cdp-DeohBe1o.d.ts → cdp-B5PhDUTC.d.ts} +1 -1
  7. package/dist/{cdp-p_eHuQpb.d.mts → cdp-D-5Hg3y_.d.mts} +1 -1
  8. package/dist/chains/index.d.mts +102 -1
  9. package/dist/chains/index.d.ts +102 -1
  10. package/dist/chains/index.js +66 -0
  11. package/dist/chains/index.js.map +1 -1
  12. package/dist/chains/index.mjs +57 -0
  13. package/dist/chains/index.mjs.map +1 -1
  14. package/dist/cli/index.js +6850 -2127
  15. package/dist/cli/index.js.map +1 -1
  16. package/dist/cli/index.mjs +6857 -2128
  17. package/dist/cli/index.mjs.map +1 -1
  18. package/dist/client/index.d.mts +348 -1
  19. package/dist/client/index.d.ts +348 -1
  20. package/dist/client/index.js +1462 -43
  21. package/dist/client/index.js.map +1 -1
  22. package/dist/client/index.mjs +1469 -51
  23. package/dist/client/index.mjs.map +1 -1
  24. package/dist/client/web/index.d.mts +17 -1
  25. package/dist/client/web/index.mjs +11 -0
  26. package/dist/client/web/index.mjs.map +1 -1
  27. package/dist/facilitators/index.d.mts +663 -4
  28. package/dist/facilitators/index.d.ts +663 -4
  29. package/dist/facilitators/index.js +1292 -10
  30. package/dist/facilitators/index.js.map +1 -1
  31. package/dist/facilitators/index.mjs +1268 -9
  32. package/dist/facilitators/index.mjs.map +1 -1
  33. package/dist/index.d.mts +78 -3
  34. package/dist/index.d.ts +78 -3
  35. package/dist/index.js +4229 -556
  36. package/dist/index.js.map +1 -1
  37. package/dist/index.mjs +4214 -548
  38. package/dist/index.mjs.map +1 -1
  39. package/dist/mcp/index.js +1462 -45
  40. package/dist/mcp/index.js.map +1 -1
  41. package/dist/mcp/index.mjs +1473 -56
  42. package/dist/mcp/index.mjs.map +1 -1
  43. package/dist/{registry-OsEO2dOu.d.mts → registry-DIo0WNoO.d.mts} +8 -1
  44. package/dist/{registry-OsEO2dOu.d.ts → registry-DIo0WNoO.d.ts} +8 -1
  45. package/dist/server/index.d.mts +336 -2
  46. package/dist/server/index.d.ts +336 -2
  47. package/dist/server/index.js +2516 -188
  48. package/dist/server/index.js.map +1 -1
  49. package/dist/server/index.mjs +2516 -188
  50. package/dist/server/index.mjs.map +1 -1
  51. package/dist/verify/index.js.map +1 -1
  52. package/dist/verify/index.mjs.map +1 -1
  53. package/dist/wallet/index.js.map +1 -1
  54. package/dist/wallet/index.mjs.map +1 -1
  55. package/package.json +20 -2
  56. package/schemas/moltspay.services.schema.json +211 -6
  57. package/scripts/postinstall.js +91 -0
@@ -1,4 +1,4 @@
1
- import { d as FacilitatorSelection } from '../registry-OsEO2dOu.js';
1
+ import { e as FacilitatorSelection } from '../registry-DIo0WNoO.js';
2
2
 
3
3
  /**
4
4
  * MoltsPay Server Types
@@ -22,6 +22,90 @@ interface ServiceConfig {
22
22
  command?: string;
23
23
  /** Function name to import from skill's index.js (new skill-based approach) */
24
24
  function?: string;
25
+ /**
26
+ * Alipay AI Pay per-service config (2.0.0+).
27
+ * Set when this service accepts CNY payments via the alipay rail.
28
+ * The `price`/`currency` fields above still describe USDC pricing
29
+ * for the x402 rails; `alipay.price_cny` is a separate CNY price.
30
+ */
31
+ alipay?: ServiceAlipayConfig;
32
+ /**
33
+ * WeChat Pay v3 Native per-service config (2.1.0+).
34
+ * Set when this service accepts CNY payments via the wechat rail.
35
+ * The `price`/`currency` fields above still describe USDC pricing for the
36
+ * x402 rails; `wechat.price_cny` is a separate CNY price.
37
+ */
38
+ wechat?: ServiceWechatConfig;
39
+ /**
40
+ * Custodial balance per-service config (2.2.0+).
41
+ * Set when this service accepts password-free deduction from
42
+ * server-custodied buyer balances. `balance.price` defaults to the
43
+ * service's top-level `price` when omitted.
44
+ */
45
+ balance?: ServiceBalanceConfig;
46
+ }
47
+ /**
48
+ * Per-service custodial balance configuration (2.2.0+).
49
+ *
50
+ * Sample:
51
+ * ```json
52
+ * "balance": {
53
+ * "price": "3.99"
54
+ * }
55
+ * ```
56
+ */
57
+ interface ServiceBalanceConfig {
58
+ /**
59
+ * Price in the ledger currency as a decimal string (e.g. `"3.99"`).
60
+ * Defaults to the service's top-level `price` when omitted.
61
+ * Must match `/^\d+(\.\d{1,2})?$/`.
62
+ */
63
+ price?: string;
64
+ }
65
+ /**
66
+ * Per-service WeChat Pay Native configuration (2.1.0+).
67
+ *
68
+ * Sample:
69
+ * ```json
70
+ * "wechat": {
71
+ * "price_cny": "10.00",
72
+ * "description": "Demo video - series one"
73
+ * }
74
+ * ```
75
+ */
76
+ interface ServiceWechatConfig {
77
+ /**
78
+ * CNY price as a decimal string in **yuan** (e.g. `"10.00"` = 10 CNY).
79
+ * NOT fen: `"100"` means 100 yuan, not 100 fen.
80
+ * Must match `/^\d+(\.\d{1,2})?$/`.
81
+ */
82
+ price_cny: string;
83
+ /** Order description shown to the payer in the WeChat app. */
84
+ description: string;
85
+ }
86
+ /**
87
+ * Per-service Alipay AI Pay configuration (2.0.0+).
88
+ *
89
+ * Sample:
90
+ * ```json
91
+ * "alipay": {
92
+ * "service_id": "API_0EA6DC4FC99A4DF7",
93
+ * "price_cny": "7.00",
94
+ * "goods_name": "Product demo video - series 1"
95
+ * }
96
+ * ```
97
+ */
98
+ interface ServiceAlipayConfig {
99
+ /** Per-service service_id (defaults to provider.alipay.service_id_default). */
100
+ service_id?: string;
101
+ /**
102
+ * CNY price as decimal string in **yuan** (e.g. `"7.00"` = 7 CNY).
103
+ * NOT cents: `"100"` means 100 yuan, not 100 fen.
104
+ * Must match `/^\d+(\.\d{1,2})?$/`.
105
+ */
106
+ price_cny: string;
107
+ /** Goods name shown to the user in the Alipay app. */
108
+ goods_name: string;
25
109
  }
26
110
  interface InputField {
27
111
  type: 'string' | 'number' | 'boolean' | 'object';
@@ -45,6 +129,129 @@ interface ProviderConfig {
45
129
  solana_wallet?: string;
46
130
  chain?: string;
47
131
  chains?: ChainConfig[];
132
+ /**
133
+ * Alipay AI Pay provider-level config (2.0.0+).
134
+ * Required when `chains` includes `"alipay"`.
135
+ * Server validates `private_key_path` / `alipay_public_key_path` are
136
+ * readable + parse as RSA PEM at startup; rejects start otherwise.
137
+ */
138
+ alipay?: ProviderAlipayConfig;
139
+ /**
140
+ * WeChat Pay v3 provider-level config (2.1.0+).
141
+ * Required when `chains` includes `"wechat"`. The server resolves the PEM
142
+ * file paths + validates them at startup; rejects start otherwise.
143
+ */
144
+ wechat?: ProviderWechatConfig;
145
+ /**
146
+ * Custodial balance rail provider-level config (2.2.0+).
147
+ * Required when `chains` includes `"balance"`. Requires Node >= 22.5
148
+ * (node:sqlite); the server rejects start otherwise.
149
+ */
150
+ balance?: ProviderBalanceConfig;
151
+ }
152
+ /**
153
+ * Provider-level custodial balance rail configuration (2.2.0+).
154
+ *
155
+ * Sample:
156
+ * ```json
157
+ * "balance": {
158
+ * "db_path": "./data/balance.sqlite",
159
+ * "currency": "USD",
160
+ * "single_limit": "5.00",
161
+ * "daily_limit": "10.00"
162
+ * }
163
+ * ```
164
+ */
165
+ interface ProviderBalanceConfig {
166
+ /** SQLite ledger file path (relative to moltspay.services.json). Created on first start. */
167
+ db_path: string;
168
+ /** Ledger quote currency. Defaults to `"USD"`. */
169
+ currency?: string;
170
+ /** Default per-transaction limit for new buyers, decimal string. Defaults to `"5.00"`. */
171
+ single_limit?: string;
172
+ /** Default daily limit for new buyers, decimal string. Defaults to `"10.00"`. */
173
+ daily_limit?: string;
174
+ /** Offered top-up pack amounts in ledger currency (2.3+), e.g. `["20.00","50.00"]`. */
175
+ topup_packs?: string[];
176
+ /** Pack the client auto-selects when a 402 finds an insufficient balance (2.3+). Must be one of `topup_packs`. */
177
+ default_pack?: string;
178
+ /** Ceiling on client auto-top-up without explicit pack selection (2.3+). */
179
+ auto_topup_max?: string;
180
+ /** User-auth rollout gate for deductions: `off` | `shadow` | `enforce`. Default `off`. */
181
+ auth_mode?: 'off' | 'shadow' | 'enforce';
182
+ }
183
+ /**
184
+ * Provider-level WeChat Pay v3 configuration (2.1.0+).
185
+ *
186
+ * The user-facing form uses FILE PATHS for the PEM keys; the server resolves
187
+ * them to PEM strings before constructing WechatFacilitator.
188
+ *
189
+ * Sample:
190
+ * ```json
191
+ * "wechat": {
192
+ * "mchid": "1900000001",
193
+ * "appid": "wx8888888888888888",
194
+ * "serial_no": "5157F09EFDC096DE15EBE81A47057A72...",
195
+ * "private_key_path": "./cert/wechat_apiclient_key.pem",
196
+ * "platform_public_key_path": "./cert/wechat_platform_cert.pem",
197
+ * "apiv3_key": "your32byteapiv3keyhere0123456789",
198
+ * "notify_url": "https://your.host/wechat/notify"
199
+ * }
200
+ * ```
201
+ */
202
+ interface ProviderWechatConfig {
203
+ /** Merchant id (mchid). */
204
+ mchid: string;
205
+ /** App id (official account / mini-program / app). */
206
+ appid: string;
207
+ /** Merchant API certificate serial number. */
208
+ serial_no: string;
209
+ /** Path to the merchant RSA private key PEM (relative to moltspay.services.json). */
210
+ private_key_path: string;
211
+ /** Path to the WeChat platform certificate public key PEM. Enables response verification. */
212
+ platform_public_key_path?: string;
213
+ /** APIv3 key (32 bytes). Only needed for callback decryption (Phase 2). */
214
+ apiv3_key?: string;
215
+ /** Async result notify URL. Required by Native order create even when polling. */
216
+ notify_url: string;
217
+ /** Open API base URL. Defaults to `"https://api.mch.weixin.qq.com"`. */
218
+ api_base?: string;
219
+ }
220
+ /**
221
+ * Provider-level Alipay AI Pay configuration (2.0.0+).
222
+ *
223
+ * The user-facing form uses FILE PATHS for both PEM keys; the server
224
+ * resolves them to PEM strings before constructing AlipayFacilitator.
225
+ *
226
+ * Sample:
227
+ * ```json
228
+ * "alipay": {
229
+ * "seller_id": "2088641494699428",
230
+ * "app_id": "2021006150642142",
231
+ * "seller_name": "Example Co., Ltd.",
232
+ * "service_id_default": "API_0EA6DC4FC99A4DF7",
233
+ * "private_key_path": "./cert/ALIPAY_PRIVATE_KEY.txt",
234
+ * "alipay_public_key_path": "./cert/ALIPAY_PUBLIC_KEY.txt"
235
+ * }
236
+ * ```
237
+ */
238
+ interface ProviderAlipayConfig {
239
+ /** Merchant Alipay ID (16 digits, e.g. `"2088641494699428"`). */
240
+ seller_id: string;
241
+ /** Application ID from Alipay Open Platform. */
242
+ app_id: string;
243
+ /** Merchant legal name; appears in `method.seller_name` of the 402 challenge. */
244
+ seller_name: string;
245
+ /** Default `service_id` when a `services[].alipay` entry doesn't override it. */
246
+ service_id_default: string;
247
+ /** Path to the RSA2 merchant private key PEM file (relative to moltspay.services.json). */
248
+ private_key_path: string;
249
+ /** Path to the Alipay platform public key PEM file (relative to moltspay.services.json). */
250
+ alipay_public_key_path: string;
251
+ /** Open API gateway URL. Defaults to `"https://openapi.alipay.com/gateway.do"`. */
252
+ gateway_url?: string;
253
+ /** Signature algorithm. Only `"RSA2"` is supported in 2.0.0. */
254
+ sign_type?: 'RSA2';
48
255
  }
49
256
  interface ServicesManifest {
50
257
  provider: ProviderConfig;
@@ -149,6 +356,29 @@ declare class MoltsPayServer {
149
356
  private registry;
150
357
  private networkId;
151
358
  private useMainnet;
359
+ /** Alipay AI Pay facilitator instance, set when `provider.alipay` is configured (2.0.0). */
360
+ private alipayFacilitator;
361
+ /** WeChat Pay Native facilitator instance, set when `provider.wechat` is configured (2.1.0). */
362
+ private wechatFacilitator;
363
+ /** Custodial balance facilitator instance, set when `provider.balance` is configured (2.2.0). */
364
+ private balanceFacilitator;
365
+ private balanceEndpoints;
366
+ /**
367
+ * Pending WeChat Native order cache — the double-charge fix.
368
+ *
369
+ * Every `buildWechatChallenge` used to place a NEW Native order, so a client
370
+ * that received two 402s (e.g. initial challenge + one poll re-request that
371
+ * raced ahead of payment) could surface two live QRs and a buyer could pay
372
+ * both (confirmed ¥0.07×2 on 2026-07-02). Now the unpaid order is cached
373
+ * under a content-derived key — sha256(service id | canonical params |
374
+ * price_cny) — and reused until it is paid or its `time_expire` window
375
+ * nears expiry, so any number of 402 emits for the same purchase intent
376
+ * share ONE order, even across separate client processes.
377
+ * Storing the in-flight promise also dedupes concurrent 402 builds.
378
+ * In-memory by design: on restart the worst case is one extra unpaid order,
379
+ * which expires server-side per `time_expire` — never a double charge.
380
+ */
381
+ private wechatPendingChallenges;
152
382
  constructor(servicesPath: string, options?: MoltsPayServerOptionsExtended);
153
383
  /**
154
384
  * Register a skill handler for a service
@@ -190,6 +420,29 @@ declare class MoltsPayServer {
190
420
  * Handle incoming request
191
421
  */
192
422
  private handleRequest;
423
+ /**
424
+ * Public base URL prefix for self-describing links, from PUBLIC_BASE_URL
425
+ * (trailing slash stripped). Empty when unset, so emitted paths stay
426
+ * root-relative — behavior is unchanged for local / no-prefix deploys.
427
+ *
428
+ * Needed because nginx rewrites the deployment prefix (e.g.
429
+ * `/t/moltspay-server`) away before proxying, so the process cannot infer
430
+ * its own public prefix; a root-relative `/services` would otherwise
431
+ * resolve against the domain root and hit the wrong backend.
432
+ */
433
+ private get publicBase();
434
+ /**
435
+ * Per-service pricing across every configured rail, for the discovery
436
+ * payloads. The top-level `price`/`currency` (crypto/USDC) stay unchanged
437
+ * for back-compat; this surfaces the fiat + balance rails (CNY) that were
438
+ * previously invisible in discovery even though the manifest defines them
439
+ * and the 402 challenge already quotes them. `acceptedCurrencies` becomes
440
+ * the union across rails so a client can see CNY is accepted without
441
+ * first triggering a 402.
442
+ */
443
+ private describeServicePricing;
444
+ /** Shared service-list entry for the discovery and /services endpoints. */
445
+ private buildDiscoveryService;
193
446
  /**
194
447
  * GET /.well-known/agent-services.json - Standard discovery endpoint
195
448
  */
@@ -206,6 +459,87 @@ declare class MoltsPayServer {
206
459
  * POST /execute - Execute service with x402 payment
207
460
  */
208
461
  private handleExecute;
462
+ /**
463
+ * Execute a service paid via the Alipay AI Pay fiat rail (2.0.0).
464
+ *
465
+ * Differs from the EVM/SVM path: no token detection, no EIP-3009/permit
466
+ * validation. Verify hits the Alipay Open API (`payment.verify`). Settlement
467
+ * (`fulfillment.confirm`) is FIRE-AND-FORGET per ALIPAY-INTEGRATION-DESIGN
468
+ * §5.1: a confirm failure is logged but does NOT fail the already-delivered
469
+ * response (the buyer's payment proof was already verified).
470
+ */
471
+ private handleAlipayExecute;
472
+ /**
473
+ * Build the Alipay 402 challenge for a service, or null when the alipay rail
474
+ * isn't configured for this server or this service. Returns the x402
475
+ * `accepts[]` entry plus the Base64URL `Payment-Needed` header value so the
476
+ * 402 responders can dual-emit both the x402 and legacy alipay-bot formats.
477
+ */
478
+ private buildAlipayChallenge;
479
+ /**
480
+ * Execute a service paid via the WeChat Pay v3 Native fiat rail (2.1.0).
481
+ *
482
+ * Differs from the EVM/SVM path: no token detection, no EIP-3009/permit
483
+ * validation. The buyer (a human) scanned the Native QR and paid; the
484
+ * client re-requests carrying `out_trade_no` in the X-Payment payload.
485
+ * Verify queries the order (`trade_state === SUCCESS`). Settlement is an
486
+ * idempotent re-confirm and is FIRE-AND-FORGET (mirrors the Alipay path):
487
+ * a confirm failure is logged but does NOT fail the delivered response —
488
+ * the order was already verified SUCCESS.
489
+ */
490
+ private handleWechatExecute;
491
+ /**
492
+ * Build the WeChat 402 challenge for a service, or null when the wechat rail
493
+ * isn't configured for this server or this service. Placing a Native order
494
+ * is a network call that returns a fresh `code_url` + `out_trade_no`; the
495
+ * x402 `accepts[]` entry carries both in `extra` so the client can render
496
+ * the QR and later echo `out_trade_no` back for verification.
497
+ *
498
+ * DOUBLE-CHARGE FIX: the unpaid order is cached per service id (see
499
+ * `wechatPendingChallenges`), so repeated 402 emits within the order's
500
+ * `time_expire` window return the SAME `code_url`/`out_trade_no` instead of
501
+ * minting a new payable order each time. The entry is dropped once the
502
+ * order is paid (`invalidateWechatChallenge`) or shortly before it expires
503
+ * (refresh margin, so clients never receive a nearly-dead QR). A build
504
+ * failure is not cached and degrades gracefully (the other rails'
505
+ * accepts[] still ship).
506
+ */
507
+ private buildWechatChallenge;
508
+ /**
509
+ * Get-or-create a pending WeChat Native order under `cacheKey`, deduping
510
+ * concurrent builds and reusing an unpaid order until it nears expiry.
511
+ * Shared by the 402 challenge path ({@link buildWechatChallenge}) and the
512
+ * balance top-up order path ({@link handleBalanceTopupOrder}). See the
513
+ * `wechatPendingChallenges` doc for the double-charge rationale.
514
+ */
515
+ private getOrCreatePendingWechatOrder;
516
+ /**
517
+ * Drop the cached pending WeChat order that matches a paid `out_trade_no`,
518
+ * so the next 402 mints a fresh order instead of re-serving a consumed one
519
+ * (Native is one-code-one-payment).
520
+ */
521
+ private invalidateWechatChallenge;
522
+ /**
523
+ * Handle /execute for the custodial balance rail (2.2.0).
524
+ *
525
+ * Execution order is INVERTED relative to the QR rails: the deduction IS
526
+ * the settlement, so it must land before the skill runs, and a skill
527
+ * failure refunds it. `settle()` is idempotent on the client's
528
+ * `request_id`, so a retried request never double-charges.
529
+ *
530
+ * QR rails: verify(paid?) → run skill → settle (confirm, fire-and-forget)
531
+ * balance: verify implicit in settle (atomic deduct) → run skill → [fail → refund]
532
+ */
533
+ private handleBalanceExecute;
534
+ /** The balance rail's requirements for a service (price defaults to `config.price`). */
535
+ private balanceRequirementsFor;
536
+ /**
537
+ * Build the balance 402 challenge for a service, or null when the rail
538
+ * isn't configured for this server or this service. Pure — nothing is
539
+ * minted, so unlike the QR rails a 402 emit has no side effects.
540
+ */
541
+ private buildBalanceChallenge;
542
+ /** GET /balance?buyer_id= — balance, limits, and today's spend. */
209
543
  /**
210
544
  * Handle MPP (Machine Payments Protocol) request
211
545
  * Supports both x402 and MPP protocols on service endpoints
@@ -284,4 +618,4 @@ declare class MoltsPayServer {
284
618
  private sendProxyPaymentRequired;
285
619
  }
286
620
 
287
- export { type ChainConfig, type Charge, type ChargeStatus, type CorsOptions, type InputField, MoltsPayServer, type MoltsPayServerOptions, type MoltsPayServerOptionsExtended, type OutputField, type PaymentRequest, type ProviderConfig, type RegisteredSkill, type ServiceConfig, type ServicesManifest, type SkillFunction, type TokenSymbol, type VerifyRequest };
621
+ export { type ChainConfig, type Charge, type ChargeStatus, type CorsOptions, type InputField, MoltsPayServer, type MoltsPayServerOptions, type MoltsPayServerOptionsExtended, type OutputField, type PaymentRequest, type ProviderAlipayConfig, type ProviderBalanceConfig, type ProviderConfig, type ProviderWechatConfig, type RegisteredSkill, type ServiceAlipayConfig, type ServiceBalanceConfig, type ServiceConfig, type ServiceWechatConfig, type ServicesManifest, type SkillFunction, type TokenSymbol, type VerifyRequest };