@txnod/sdk 1.0.1 → 1.1.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 (74) hide show
  1. package/AGENTS.md +1 -1
  2. package/CHANGELOG.md +33 -0
  3. package/dist/_shared/index.d.ts +26 -16
  4. package/dist/client-sandbox.d.ts +181 -6
  5. package/dist/client-sandbox.d.ts.map +1 -1
  6. package/dist/client-sandbox.js +216 -5
  7. package/dist/client-sandbox.js.map +1 -1
  8. package/dist/client.d.ts +59 -9
  9. package/dist/client.d.ts.map +1 -1
  10. package/dist/client.js +58 -6
  11. package/dist/client.js.map +1 -1
  12. package/dist/errors.d.ts +187 -4
  13. package/dist/errors.d.ts.map +1 -1
  14. package/dist/errors.js +208 -4
  15. package/dist/errors.js.map +1 -1
  16. package/dist/index.d.ts +4 -4
  17. package/dist/index.d.ts.map +1 -1
  18. package/dist/index.js +3 -3
  19. package/dist/index.js.map +1 -1
  20. package/dist/internals/error-ctor-map.d.ts +4 -3
  21. package/dist/internals/error-ctor-map.d.ts.map +1 -1
  22. package/dist/internals/error-ctor-map.js +5 -1
  23. package/dist/internals/error-ctor-map.js.map +1 -1
  24. package/dist/verify/chains/bsc.d.ts +1 -7
  25. package/dist/verify/chains/bsc.d.ts.map +1 -1
  26. package/dist/verify/chains/bsc.js +1 -7
  27. package/dist/verify/chains/bsc.js.map +1 -1
  28. package/dist/verify/chains/btc.d.ts +1 -8
  29. package/dist/verify/chains/btc.d.ts.map +1 -1
  30. package/dist/verify/chains/btc.js +1 -8
  31. package/dist/verify/chains/btc.js.map +1 -1
  32. package/dist/verify/chains/cardano.d.ts +6 -13
  33. package/dist/verify/chains/cardano.d.ts.map +1 -1
  34. package/dist/verify/chains/cardano.js +6 -13
  35. package/dist/verify/chains/cardano.js.map +1 -1
  36. package/dist/verify/chains/evm.d.ts +1 -6
  37. package/dist/verify/chains/evm.d.ts.map +1 -1
  38. package/dist/verify/chains/evm.js +8 -7
  39. package/dist/verify/chains/evm.js.map +1 -1
  40. package/dist/verify/chains/polygon.d.ts +1 -7
  41. package/dist/verify/chains/polygon.d.ts.map +1 -1
  42. package/dist/verify/chains/polygon.js +1 -7
  43. package/dist/verify/chains/polygon.js.map +1 -1
  44. package/dist/verify/chains/secp256k1-bip32.d.ts +2 -8
  45. package/dist/verify/chains/secp256k1-bip32.d.ts.map +1 -1
  46. package/dist/verify/chains/secp256k1-bip32.js +5 -13
  47. package/dist/verify/chains/secp256k1-bip32.js.map +1 -1
  48. package/dist/verify/chains/ton-cell.d.ts.map +1 -1
  49. package/dist/verify/chains/ton-cell.js +4 -7
  50. package/dist/verify/chains/ton-cell.js.map +1 -1
  51. package/dist/verify/chains/ton.d.ts +3 -7
  52. package/dist/verify/chains/ton.d.ts.map +1 -1
  53. package/dist/verify/chains/ton.js +1 -3
  54. package/dist/verify/chains/ton.js.map +1 -1
  55. package/dist/verify/chains/tron.d.ts +1 -7
  56. package/dist/verify/chains/tron.d.ts.map +1 -1
  57. package/dist/verify/chains/tron.js +1 -7
  58. package/dist/verify/chains/tron.js.map +1 -1
  59. package/dist/verify/errors.d.ts +6 -8
  60. package/dist/verify/errors.d.ts.map +1 -1
  61. package/dist/verify/errors.js +6 -8
  62. package/dist/verify/errors.js.map +1 -1
  63. package/dist/verify/index.d.ts +1 -1
  64. package/dist/verify/index.js +2 -2
  65. package/dist/verify/index.js.map +1 -1
  66. package/docs/02-invoices.md +46 -2
  67. package/docs/04-webhooks.md +4 -1
  68. package/docs/05-errors.md +24 -4
  69. package/docs/05-sandbox.md +13 -5
  70. package/docs/examples/sandbox-vitest-suite.md +2 -2
  71. package/docs/reference/client.md +39 -2
  72. package/docs/reference/errors.md +13 -2
  73. package/docs/reference/types.md +5 -4
  74. package/package.json +13 -13
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  title: "TxnodClient reference"
3
3
  description: "Every method of TxnodClient and the verifyWebhookSignature helper: signature, behaviour, and a minimal example."
4
- sdk_version: 1.0.0
4
+ sdk_version: 1.0.2
5
5
  ---
6
6
 
7
7
  # `TxnodClient` reference
@@ -242,6 +242,36 @@ attributeOrphanPayment(
242
242
 
243
243
  ---
244
244
 
245
+ ## `claimInvoiceByTx`
246
+
247
+ Submit a manual tx-hash claim for an open invoice — the "I paid but it's not showing up" recovery path. The server resolves the tx via the chain provider, verifies sender / recipient / amount / timestamp, and either auto-attributes, queues for operator review, defers pending finality, or rejects.
248
+
249
+ **Signature**
250
+
251
+ ```ts
252
+ claimInvoiceByTx(args: {
253
+ invoiceId: string;
254
+ txHash: string;
255
+ chain: Chain;
256
+ }): Promise<ClaimByTxResponse>
257
+ ```
258
+
259
+ **Response (`ClaimByTxResponse`)**
260
+
261
+ | Field | Type | Notes |
262
+ |---|---|---|
263
+ | `request_id` | ULID | Request correlation id |
264
+ | `status` | `'auto_attributed' \| 'pending_review' \| 'pending_finality' \| 'rejected'` | Outcome discriminator |
265
+ | `terminal_status` | `'paid' \| 'overpaid' \| 'partial' \| 'expired_paid_late' \| null` | Present when `status === 'auto_attributed'` |
266
+ | `resolved_facts` | `ResolvedFacts \| null` | Sender / recipient / amount / confirmations / timestamp as resolved from the chain provider |
267
+ | `rejection_reason` | `'tx_not_found_or_incomplete' \| 'tx_already_attributed' \| null` | Present when `status === 'rejected'` |
268
+
269
+ **Throws**: generic `TxnodError` with `error_code: 'manual_claim_rate_limit_exceeded'` (HTTP 429, per-user 10/hour throttle). The `tx_not_found_or_incomplete` and `tx_already_attributed` rejection paths are returned in the response body, not thrown.
270
+
271
+ See [`../02-invoices.md#manual-tx-hash-claim`](../02-invoices.md#manual-tx-hash-claim) for the full lifecycle and a worked example.
272
+
273
+ ---
274
+
245
275
  ## `listWebhookEvents`
246
276
 
247
277
  Paginated audit list of outbound webhook events for the calling project.
@@ -291,7 +321,7 @@ resendWebhookEvent(eventId: string): Promise<WebhookEventResendResponse>
291
321
 
292
322
  ## Sandbox surface (`client.sandbox.*`)
293
323
 
294
- Lazy accessor (`client.sandbox` is constructed on first use; bundlers tree-shake the entire namespace when never referenced) onto the 14 sandbox-mode endpoints introduced by Story 37.2. Use it from a sandbox-API-secret-authenticated client (`apiSecret: 'sk_sandbox_...'` + `environment: 'non-production'`) — production secrets cannot reach these routes (the server returns `production_key_against_sandbox_project`). See [`../05-sandbox.md`](../05-sandbox.md) for the lifecycle picture and per-method worked examples.
324
+ Lazy accessor (`client.sandbox` is constructed on first use; bundlers tree-shake the entire namespace when never referenced) onto the sandbox-mode endpoints. Use it from a sandbox-API-secret-authenticated client (`apiSecret: 'sk_sandbox_...'` + `environment: 'non-production'`) — production secrets cannot reach these routes (the server returns `production_key_against_sandbox_project`). See [`../05-sandbox.md`](../05-sandbox.md) for the lifecycle picture and per-method worked examples.
295
325
 
296
326
  ```ts
297
327
  const client = new TxnodClient({
@@ -308,6 +338,12 @@ const r = await client.sandbox.simulatePaid('01HK8MAR2QEXAMPLE000000000');
308
338
 
309
339
  | Method | HTTP | Returns | Throws |
310
340
  |---|---|---|---|
341
+ | `createInvoice(body)` | `POST /api/v1/sandbox/invoices` | `InvoiceResponse` | `TxnodSandboxActiveInvoiceCapReachedError` (10k active cap), `TxnodCoinNotEnabledError`, `TxnodRateQuoteUnavailableError` |
342
+ | `getInvoice(invoiceId)` | `GET /api/v1/sandbox/invoices/{invoiceId}` | `InvoiceDetailResponse` | `TxnodInvoiceNotFoundError` |
343
+ | `listInvoices(query?)` | `GET /api/v1/sandbox/invoices` | `CursorPaginatedInvoiceResponse` | `TxnodValidationError` |
344
+ | `cancelInvoice(invoiceId)` | `POST /api/v1/sandbox/invoices/{invoiceId}/cancel` | `InvoiceDetailResponse` | `TxnodInvoiceNotFoundError`, `TxnodInvoiceNotCancellableError` |
345
+ | `listWebhookEvents(query?)` | `GET /api/v1/sandbox/webhooks/events` | `WebhookEventListResponse` | `TxnodValidationError` |
346
+ | `resendWebhookEvent(eventId)` | `POST /api/v1/sandbox/webhooks/events/{eventId}/resend` | `WebhookEventResendResponse` | `TxnodEventNotFoundError`, `TxnodEventNotResendableError` (in-flight or dead-lettered) |
311
347
  | `simulateDetect(invoiceId, opts?)` | `POST /api/v1/sandbox/invoices/{invoiceId}/simulate-detect` | `{ event_id, status: 'detected' }` | `TxnodSandboxInvoiceTransitionInvalidError` (invoice not in `pending`), `TxnodSandboxInvoiceNotFoundError` |
312
348
  | `simulatePaid(invoiceId, opts?)` | `POST .../simulate-paid` | `{ event_id, status: 'paid' }` | `TxnodSandboxInvoiceTransitionInvalidError` (invoice not in `detected`) |
313
349
  | `simulateOverpaid(invoiceId, params)` | `POST .../simulate-overpaid` | `{ event_id, status: 'paid' }` | `TxnodSandboxInvoiceTransitionInvalidError` |
@@ -318,6 +354,7 @@ const r = await client.sandbox.simulatePaid('01HK8MAR2QEXAMPLE000000000');
318
354
  | `simulateReconfirm(invoiceId)` | `POST .../simulate-reconfirm` | `{ event_id, status: 'paid' }` | `TxnodSandboxInvoiceTransitionInvalidError` (invoice not in `reverted`) |
319
355
  | `simulateDuplicateDelivery(invoiceId)` | `POST .../simulate-duplicate-delivery` | `{ event_id }` | `TxnodSandboxInvoiceTerminalError` (no terminal event yet) |
320
356
  | `simulateEvent(invoiceId, eventInput)` | `POST .../simulate-event` | `{ event_id, status }` | `TxnodSandboxInvoiceTransitionInvalidError` (current status != `expectedCurrentStatus`) |
357
+ | `simulateClaimByTx(invoiceId, args)` | `POST .../claim-by-tx` | `ClaimByTxResponse` | Generic `TxnodError` with `error_code: 'manual_claim_rate_limit_exceeded'` (per-user 10/hour throttle); rejection paths return in the response body, not thrown |
321
358
  | `clockAdvance(projectId, params)` | `POST /api/v1/sandbox/{projectId}/clock/advance` | `{ advanced, remaining }` | `TxnodSandboxRateLimitExceededError` (>10/min/project) |
322
359
  | `reset(projectId)` | `POST /api/v1/sandbox/{projectId}/reset` | `{ status: 'reset' }` | `TxnodSandboxResetFailedError` |
323
360
  | `destroy(projectId)` | `DELETE /api/v1/sandbox/{projectId}` | `{ status: 'deleted' }` | `TxnodSandboxDeleteFailedError` |
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  title: "Error class catalogue"
3
3
  description: "Complete error_code → class lookup and the narrative groups."
4
- sdk_version: 1.0.0
4
+ sdk_version: 1.0.2
5
5
  ---
6
6
 
7
7
  # Error class catalogue
@@ -56,6 +56,7 @@ The 55-row lookup below covers every server-emitted code in `ERROR_CODES` (58 en
56
56
  |---|---|---|---|
57
57
  | `external_id_conflict` | `TxnodExternalIdConflictError` | 409 | **Idempotent** — fetch existing |
58
58
  | `orphan_already_attributed` | `TxnodOrphanAlreadyAttributedError` | 409 | **Idempotent** — treat as success |
59
+ | `overlay_matching_mode_required_exact` | `TxnodOverlayMatchingModeError` | 422 | Pool saturation forced the allocator into `required_exact`; project's `matching_mode` must be flipped or the pool must drain |
59
60
  | `wallet_not_bound` | `TxnodWalletNotBoundError` | 422 | Operator must bind a verified wallet of matching kind for the requested chain before invoice creation succeeds |
60
61
  | `wallet_not_owned` | `TxnodWalletNotOwnedError` | 403 | **Dashboard-only** — never reaches partner API |
61
62
  | `wallet_has_active_bindings` | `TxnodWalletHasActiveBindingsError` | 409 | **Dashboard-only** — never reaches partner API |
@@ -71,6 +72,15 @@ The 55-row lookup below covers every server-emitted code in `ERROR_CODES` (58 en
71
72
  | `ton_jetton_resolve_failed` | `TxnodTonJettonResolveFailedError` | 503 | Toncenter/TONAPI hiccup resolving the jetton master/wallet pair — retry with backoff |
72
73
  | `ton_comment_parse_failed` | `TxnodTonCommentParseFailedError` | 422 | Inbound TON tx carried a malformed `payment_token` comment |
73
74
 
75
+ ### Manual tx-hash claim flow
76
+
77
+ Returned in the **response body** as `{ status: 'rejected', rejection_reason }` on HTTP 200 — these two codes are not raised by `parseProblemDetails`. The classes below exist as helpers for partners who prefer to throw on the rejection paths themselves.
78
+
79
+ | `error_code` | Class | HTTP status | Notes |
80
+ |---|---|---|---|
81
+ | `tx_not_found_or_incomplete` | `TxnodTxNotFoundError` | 200 (body) | Chain provider could not resolve the tx, or facts (recipient / amount / state) do not match the invoice |
82
+ | `tx_already_attributed` | `TxnodTxAlreadyAttributedError` | 200 (body) | The submitted tx was previously credited to an invoice |
83
+
74
84
  ### TonConnect (operator-onboarding)
75
85
 
76
86
  These never reach partner methods — they fire on the operator's TonConnect signing flow during xpub onboarding. Exported so the dashboard's typed-error catch can branch correctly.
@@ -87,7 +97,7 @@ These never reach partner methods — they fire on the operator's TonConnect sig
87
97
 
88
98
  ### Sandbox (lifecycle + simulation)
89
99
 
90
- Surfaced by `/api/v1/sandbox/*` REST endpoints, the 14 `client.sandbox.*` SDK methods, and the `sandbox:simulate`-scoped MCP tools.
100
+ Surfaced by `/api/v1/sandbox/*` REST endpoints, the `client.sandbox.*` SDK methods, and the `sandbox:simulate`-scoped MCP tools.
91
101
 
92
102
  | `error_code` | Class | HTTP status | Notes |
93
103
  |---|---|---|---|
@@ -109,6 +119,7 @@ Surfaced by `/api/v1/sandbox/*` REST endpoints, the 14 `client.sandbox.*` SDK me
109
119
  | `error_code` | Class | HTTP status | Notes |
110
120
  |---|---|---|---|
111
121
  | `rate_limit_exceeded` | `TxnodRateLimitError` | 429 | `.retry_after_seconds` |
122
+ | `manual_claim_rate_limit_exceeded` | `TxnodError` (generic) | 429 | Per-user hourly throttle on `client.claimInvoiceByTx` (10/hour/user); falls through to base `TxnodError` — branch on `err.error_code` |
112
123
  | `pool_exhausted` | `TxnodPoolExhaustedError` | 503 | `.retry_after_seconds` — hard-cap hit, wait for cooldown |
113
124
  | `webhook_capacity_exhausted` | `TxnodWebhookCapacityExhaustedError` | 503 | `.retry_after_seconds` always `0` — partner retry alone won't free capacity, treat as transient outage |
114
125
  | `internal_error` | `TxnodServerError` | 5xx | Log `request_id`, retryable |
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  title: "Type reference"
3
3
  description: "Authoritative type shapes for requests, responses, and shared primitives."
4
- sdk_version: 1.0.0
4
+ sdk_version: 1.0.2
5
5
  ---
6
6
 
7
7
  # Type reference
@@ -200,7 +200,8 @@ type WebhookEventType =
200
200
  | 'invoice.partial'
201
201
  | 'invoice.expired'
202
202
  | 'invoice.expired_paid_late'
203
- | 'invoice.reverted';
203
+ | 'invoice.reverted'
204
+ | 'invoice.ambiguous';
204
205
 
205
206
  type WebhookEventData = {
206
207
  invoice_id: string;
@@ -212,7 +213,7 @@ type WebhookEventData = {
212
213
  amount_units: string; // integer string in smallest unit
213
214
  confirmations: number;
214
215
  block_height: number | null;
215
- payment_token: string | null; // TON only — 8-hex memo token
216
+ payment_token: string | null; // TON only — lowercase hex memo token (8 or 16 chars)
216
217
  matched_payment_token: string | null;
217
218
  chain_specific: { ton?: { tx_lt: string; mc_block_seqno: number; block_ref: { workchain: number; shard: string; seqno: number; }; jetton_master?: string; } } | null;
218
219
  reason?: 'reorg' | 'late_arrival'; // present on reverted/expired_paid_late
@@ -372,7 +373,7 @@ type CursorPaginatedOrphanPaymentResponse = {
372
373
  };
373
374
  ```
374
375
 
375
- > **TRON activation events never appear here.** Story 31.3 server-side filter excludes inbound activation transactions (operator-funded TRX/USDT sends that activate a fresh address) from orphan classification — partners do not need to dedupe them client-side.
376
+ > **TRON activation events never appear here.** A server-side filter excludes inbound activation transactions (operator-funded TRX/USDT sends that activate a fresh address) from orphan classification — partners do not need to dedupe them client-side.
376
377
 
377
378
  ## `ProblemDetails`
378
379
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@txnod/sdk",
3
- "version": "1.0.1",
3
+ "version": "1.1.0",
4
4
  "description": "TypeScript SDK for the txnod non-custodial crypto payment gateway — sign requests, verify webhook signatures, and verify invoice deposit-address derivation across BTC, ETH, TRON, Cardano, Polygon PoS, BNB Smart Chain, and TON. Pure Node >= 20; runs in any server-side framework (Express, Fastify, Hono, Next.js, Nuxt, SvelteKit, etc.).",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -20,6 +20,17 @@
20
20
  "CHANGELOG.md",
21
21
  "LICENSE"
22
22
  ],
23
+ "scripts": {
24
+ "build": "pnpm --filter @txnod/shared build && tsc --build && node scripts/bundle-shared-types.mjs",
25
+ "typecheck": "tsc --build",
26
+ "lint": "eslint src",
27
+ "test": "vitest run --config vitest.unit.config.ts",
28
+ "test:contract": "pnpm run build && vitest run --config vitest.contract.config.ts",
29
+ "check:bundle-size": "bash ../../scripts/ci/sdk-bundle-size.sh",
30
+ "check:dep-allowlist": "bash ../../scripts/ci/sdk-dep-allowlist.sh",
31
+ "check:pack-whitelist": "bash ../../scripts/ci/sdk-pack-whitelist.sh",
32
+ "check:dts-self-contained": "bash ../../scripts/ci/sdk-dts-self-contained.sh"
33
+ },
23
34
  "publishConfig": {
24
35
  "access": "public"
25
36
  },
@@ -38,16 +49,5 @@
38
49
  "devDependencies": {
39
50
  "@testcontainers/postgresql": "11.14.0",
40
51
  "@ton/ton": "16.2.4"
41
- },
42
- "scripts": {
43
- "build": "pnpm --filter @txnod/shared build && tsc --build && node scripts/bundle-shared-types.mjs",
44
- "typecheck": "tsc --build",
45
- "lint": "eslint src",
46
- "test": "vitest run --config vitest.unit.config.ts",
47
- "test:contract": "pnpm run build && vitest run --config vitest.contract.config.ts",
48
- "check:bundle-size": "bash ../../scripts/ci/sdk-bundle-size.sh",
49
- "check:dep-allowlist": "bash ../../scripts/ci/sdk-dep-allowlist.sh",
50
- "check:pack-whitelist": "bash ../../scripts/ci/sdk-pack-whitelist.sh",
51
- "check:dts-self-contained": "bash ../../scripts/ci/sdk-dts-self-contained.sh"
52
52
  }
53
- }
53
+ }