@txnod/sdk 1.0.1 → 1.0.2
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/AGENTS.md +1 -1
- package/CHANGELOG.md +17 -0
- package/dist/_shared/index.d.ts +14 -14
- package/dist/client-sandbox.d.ts +35 -6
- package/dist/client-sandbox.d.ts.map +1 -1
- package/dist/client-sandbox.js +37 -5
- package/dist/client-sandbox.js.map +1 -1
- package/dist/client.d.ts +57 -7
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +58 -6
- package/dist/client.js.map +1 -1
- package/dist/errors.d.ts +122 -4
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +136 -4
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/dist/internals/error-ctor-map.d.ts.map +1 -1
- package/dist/internals/error-ctor-map.js +2 -1
- package/dist/internals/error-ctor-map.js.map +1 -1
- package/dist/verify/chains/bsc.d.ts +1 -7
- package/dist/verify/chains/bsc.d.ts.map +1 -1
- package/dist/verify/chains/bsc.js +1 -7
- package/dist/verify/chains/bsc.js.map +1 -1
- package/dist/verify/chains/btc.d.ts +1 -8
- package/dist/verify/chains/btc.d.ts.map +1 -1
- package/dist/verify/chains/btc.js +1 -8
- package/dist/verify/chains/btc.js.map +1 -1
- package/dist/verify/chains/cardano.d.ts +6 -13
- package/dist/verify/chains/cardano.d.ts.map +1 -1
- package/dist/verify/chains/cardano.js +6 -13
- package/dist/verify/chains/cardano.js.map +1 -1
- package/dist/verify/chains/evm.d.ts +1 -6
- package/dist/verify/chains/evm.d.ts.map +1 -1
- package/dist/verify/chains/evm.js +1 -6
- package/dist/verify/chains/evm.js.map +1 -1
- package/dist/verify/chains/polygon.d.ts +1 -7
- package/dist/verify/chains/polygon.d.ts.map +1 -1
- package/dist/verify/chains/polygon.js +1 -7
- package/dist/verify/chains/polygon.js.map +1 -1
- package/dist/verify/chains/secp256k1-bip32.d.ts +2 -8
- package/dist/verify/chains/secp256k1-bip32.d.ts.map +1 -1
- package/dist/verify/chains/secp256k1-bip32.js +5 -13
- package/dist/verify/chains/secp256k1-bip32.js.map +1 -1
- package/dist/verify/chains/ton-cell.d.ts.map +1 -1
- package/dist/verify/chains/ton-cell.js +4 -7
- package/dist/verify/chains/ton-cell.js.map +1 -1
- package/dist/verify/chains/ton.d.ts +3 -7
- package/dist/verify/chains/ton.d.ts.map +1 -1
- package/dist/verify/chains/ton.js +1 -3
- package/dist/verify/chains/ton.js.map +1 -1
- package/dist/verify/chains/tron.d.ts +1 -7
- package/dist/verify/chains/tron.d.ts.map +1 -1
- package/dist/verify/chains/tron.js +1 -7
- package/dist/verify/chains/tron.js.map +1 -1
- package/dist/verify/errors.d.ts +6 -8
- package/dist/verify/errors.d.ts.map +1 -1
- package/dist/verify/errors.js +6 -8
- package/dist/verify/errors.js.map +1 -1
- package/dist/verify/index.d.ts +1 -1
- package/dist/verify/index.js +2 -2
- package/dist/verify/index.js.map +1 -1
- package/docs/02-invoices.md +46 -2
- package/docs/04-webhooks.md +2 -1
- package/docs/05-errors.md +24 -4
- package/docs/05-sandbox.md +4 -3
- package/docs/reference/client.md +32 -2
- package/docs/reference/errors.md +13 -2
- package/docs/reference/types.md +5 -4
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/verify/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,gBAAgB,EAAE,MAAM,mCAAmC,CAAC;AAGrE
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/verify/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,gBAAgB,EAAE,MAAM,mCAAmC,CAAC;AAGrE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,MAAM,OAAO,wBAAyB,SAAQ,UAAU;IACpC,IAAI,GAAG,sBAA+B,CAAC;IAChD,KAAK,CAAQ;IACb,eAAe,CAAS;IACxB,gBAAgB,CAAS;IACzB,eAAe,CAAS;IAEjC,YAAY,KAKX;QACC,KAAK,CACH,gBAAgB,CACd,cAAc,EACd,GAAG,EACH,0CAA0C,KAAK,CAAC,KAAK,gBAAgB,KAAK,CAAC,gBAAgB,eAAe,KAAK,CAAC,eAAe,gBAAgB,KAAK,CAAC,eAAe,IAAI,CACzK,CACF,CAAC;QACF,IAAI,CAAC,IAAI,GAAG,0BAA0B,CAAC;QACvC,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC;QACzB,IAAI,CAAC,eAAe,GAAG,KAAK,CAAC,eAAe,CAAC;QAC7C,IAAI,CAAC,gBAAgB,GAAG,KAAK,CAAC,gBAAgB,CAAC;QAC/C,IAAI,CAAC,eAAe,GAAG,KAAK,CAAC,eAAe,CAAC;IAC/C,CAAC;CACF"}
|
package/dist/verify/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Multi-chain address-verification dispatcher consumed by TxnodClient.createInvoice.
|
|
3
3
|
* Routes the invoice's coin → chain → per-chain verifier with multi-xpub
|
|
4
|
-
* iteration (newest-first
|
|
4
|
+
* iteration (newest-first) and selective error rethrow.
|
|
5
5
|
*/
|
|
6
6
|
import type { Chain, InvoiceResponse, WalletEdgeKind } from '../_shared/index.js';
|
|
7
7
|
import type { TonXpubConfig } from './config.js';
|
package/dist/verify/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Multi-chain address-verification dispatcher consumed by TxnodClient.createInvoice.
|
|
3
3
|
* Routes the invoice's coin → chain → per-chain verifier with multi-xpub
|
|
4
|
-
* iteration (newest-first
|
|
4
|
+
* iteration (newest-first) and selective error rethrow.
|
|
5
5
|
*/
|
|
6
6
|
import { logger } from '../internals/logger.js';
|
|
7
7
|
import { AddressVerificationError } from './errors.js';
|
|
@@ -12,7 +12,7 @@ import { verifyCardano } from './chains/cardano.js';
|
|
|
12
12
|
import { verifyPolygon } from './chains/polygon.js';
|
|
13
13
|
import { verifyBsc } from './chains/bsc.js';
|
|
14
14
|
import { verifyTonAddress } from './chains/ton.js';
|
|
15
|
-
//
|
|
15
|
+
// Coin → chain lookup. Kept in lockstep with the shared coin schema.
|
|
16
16
|
const COIN_TO_CHAIN_LITERAL = {
|
|
17
17
|
btc: 'btc',
|
|
18
18
|
eth: 'eth',
|
package/dist/verify/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/verify/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAChD,OAAO,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAC;AACvD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAGnD,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/verify/index.ts"],"names":[],"mappings":"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"}
|
package/docs/02-invoices.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: "Invoices"
|
|
3
|
-
description: "Create, fetch, search, and cancel invoices; the
|
|
4
|
-
sdk_version: 1.0.
|
|
3
|
+
description: "Create, fetch, search, and cancel invoices; the ten-state invoice lifecycle."
|
|
4
|
+
sdk_version: 1.0.2
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Invoices
|
|
@@ -21,6 +21,7 @@ An invoice moves through a state machine. Statuses are the `InvoiceStatus` union
|
|
|
21
21
|
| `expired_paid_late` | Payment arrived after `expires_at` | yes |
|
|
22
22
|
| `reverted` | Previously `paid`/`overpaid` but dropped by a chain reorg | yes |
|
|
23
23
|
| `cancelled` | Explicitly cancelled via `cancelInvoice` | yes |
|
|
24
|
+
| `ambiguous` | A deposit could not be attributed to a single open invoice (multiple candidates matched). Held until operator resolves via dashboard one-click flip | no (operator-resolved) |
|
|
24
25
|
|
|
25
26
|
**`paid` is not the end of the story.** A deeper reorg can still invalidate on-chain receipts. Only trust `paid` / `overpaid` for fulfilment once `confirmations` ≥ your project's finalization threshold, and always have a path to roll back on an `invoice.reverted` webhook — see [`06-idempotency.md`](./06-idempotency.md).
|
|
26
27
|
|
|
@@ -209,6 +210,49 @@ A receipt that lands on a derivation address not currently bound to any open inv
|
|
|
209
210
|
- [`listOrphanPayments`](./reference/client.md#listorphanpayments) — list and filter
|
|
210
211
|
- [`attributeOrphanPayment`](./reference/client.md#attributeorphanpayment) — post-hoc bind an orphan to an `external_id`; the returned invoice reflects the attribution
|
|
211
212
|
|
|
213
|
+
## Manual tx-hash claim
|
|
214
|
+
|
|
215
|
+
For the "I paid but the invoice is still open" recovery flow, the merchant's backend can forward an end-customer-submitted tx hash to `client.claimInvoiceByTx`. The server resolves the tx via the chain provider, verifies sender / recipient / amount / timestamp, and returns one of four outcomes:
|
|
216
|
+
|
|
217
|
+
- `auto_attributed` — facts matched; the invoice has been credited and transitioned to the corresponding terminal status (`terminal_status` field on the response carries which).
|
|
218
|
+
- `pending_review` — no automatic match; the claim is queued for operator review in the dashboard.
|
|
219
|
+
- `pending_finality` — the tx is not yet finalised against the per-chain finality threshold; the server will re-check asynchronously.
|
|
220
|
+
- `rejected` — `response.rejection_reason` is one of `tx_not_found_or_incomplete` (chain provider couldn't resolve the tx, or facts mismatch) or `tx_already_attributed` (the tx was previously credited).
|
|
221
|
+
|
|
222
|
+
```ts
|
|
223
|
+
import { TxnodClient, TxnodError } from '@txnod/sdk';
|
|
224
|
+
|
|
225
|
+
const client = new TxnodClient({
|
|
226
|
+
projectId: process.env.TXNOD_PROJECT_ID!,
|
|
227
|
+
apiSecret: process.env.TXNOD_API_SECRET!,
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
try {
|
|
231
|
+
const result = await client.claimInvoiceByTx({
|
|
232
|
+
invoiceId: '01HK8MAR2QEXAMPLE000000000',
|
|
233
|
+
txHash: '0xabc...def',
|
|
234
|
+
chain: 'eth',
|
|
235
|
+
});
|
|
236
|
+
if (result.status === 'auto_attributed') {
|
|
237
|
+
console.log('attributed; terminal_status:', result.terminal_status);
|
|
238
|
+
} else if (result.status === 'rejected') {
|
|
239
|
+
console.log('rejected:', result.rejection_reason);
|
|
240
|
+
} else {
|
|
241
|
+
console.log('queued; status:', result.status);
|
|
242
|
+
}
|
|
243
|
+
} catch (err) {
|
|
244
|
+
if (err instanceof TxnodError && err.error_code === 'manual_claim_rate_limit_exceeded') {
|
|
245
|
+
// 429; per-user hourly throttle (10/hour/user). Back off and retry later.
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
throw err;
|
|
249
|
+
}
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
Rejection paths come back in the response body — they do **not** throw. Throw-style consumers can opt in by inspecting `result.rejection_reason` and constructing `TxnodTxNotFoundError` / `TxnodTxAlreadyAttributedError` themselves; see [`reference/errors.md`](./reference/errors.md#manual-tx-hash-claim-flow).
|
|
253
|
+
|
|
254
|
+
The per-user throttle of 10 claims / hour / user surfaces as `error_code: 'manual_claim_rate_limit_exceeded'` (HTTP 429 with `Retry-After`); branch on `err.error_code` against the base `TxnodError`.
|
|
255
|
+
|
|
212
256
|
## Related
|
|
213
257
|
|
|
214
258
|
- [`04-webhooks.md`](./04-webhooks.md) — status changes you will observe from the outside
|
package/docs/04-webhooks.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: "Webhooks"
|
|
3
3
|
description: "Event types, verifyWebhookSignature, ±300-second window, event_id idempotency, retries, resend."
|
|
4
|
-
sdk_version: 1.0.
|
|
4
|
+
sdk_version: 1.0.2
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Webhooks
|
|
@@ -21,6 +21,7 @@ The `WebhookEvent` type is a discriminated union on `event_type`:
|
|
|
21
21
|
| `invoice.expired` | `expires_at` passed before `paid` | yes |
|
|
22
22
|
| `invoice.expired_paid_late` | Payment arrived after `expires_at` | yes |
|
|
23
23
|
| `invoice.reverted` | Previously `paid`/`overpaid` dropped by reorg | yes |
|
|
24
|
+
| `invoice.ambiguous` | Deposit could not be attributed to a single open invoice — multiple candidates matched (overlay-collision). Operator must resolve via one-click flip in the dashboard | no (operator-resolved) |
|
|
24
25
|
|
|
25
26
|
Every event shares this envelope:
|
|
26
27
|
|
package/docs/05-errors.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: "Error handling"
|
|
3
3
|
description: "Every typed TxnodError subclass, what raises it, and how to branch on it."
|
|
4
|
-
sdk_version: 1.0.
|
|
4
|
+
sdk_version: 1.0.2
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Error handling
|
|
@@ -111,12 +111,32 @@ Grouped by what the operator should do. Full `error_code` → class lookup is in
|
|
|
111
111
|
| `TxnodExternalIdConflictError` | `external_id_conflict` | Invoice with that `external_id` already exists; fetch via `searchInvoices({ external_id })` |
|
|
112
112
|
| `TxnodOrphanAlreadyAttributedError` | `orphan_already_attributed` | Orphan already bound to an invoice; treat as success |
|
|
113
113
|
|
|
114
|
+
### Overlay matching (pool routing)
|
|
115
|
+
|
|
116
|
+
The overlay allocator picks per-invoice addresses with a soft-reuse policy. Under saturation it falls back to a stricter mode that requires exact-amount matching — partner code must catch the resulting validation error if the project's matching mode is `at_least` or `any`.
|
|
117
|
+
|
|
118
|
+
| Class | `error_code` | Meaning |
|
|
119
|
+
|---|---|---|
|
|
120
|
+
| `TxnodOverlayMatchingModeError` | `overlay_matching_mode_required_exact` | Pool saturation forced the allocator into `required_exact` matching, but the project's `matching_mode` is `at_least` or `any`. Switch the project to `required_exact` (recommended) or wait for the pool to drain. HTTP 422 |
|
|
121
|
+
|
|
122
|
+
### Manual tx-hash claim flow
|
|
123
|
+
|
|
124
|
+
`client.claimInvoiceByTx` has an unusual error model: the two most common rejections (`tx_not_found_or_incomplete` and `tx_already_attributed`) are returned in the **response body** as `{ status: 'rejected', rejection_reason }` on HTTP 200 — they do **not** throw. The two error classes below are provided as helpers for consumers who prefer to convert those response shapes into throws themselves; they are **not** raised by the SDK's own `parseProblemDetails` path.
|
|
125
|
+
|
|
126
|
+
| Class | `error_code` | Meaning |
|
|
127
|
+
|---|---|---|
|
|
128
|
+
| `TxnodTxNotFoundError` | `tx_not_found_or_incomplete` | Chain provider could not resolve the submitted tx hash, or resolved facts do not match the invoice (recipient / amount / state). Surfaced as `response.rejection_reason` on HTTP 200 |
|
|
129
|
+
| `TxnodTxAlreadyAttributedError` | `tx_already_attributed` | The submitted tx was previously credited to an invoice. Surfaced as `response.rejection_reason` on HTTP 200 |
|
|
130
|
+
|
|
131
|
+
See [`02-invoices.md`](./02-invoices.md#manual-tx-hash-claim) for the full lifecycle.
|
|
132
|
+
|
|
114
133
|
### Transient — retry
|
|
115
134
|
|
|
116
135
|
| Class | `error_code` | Notes |
|
|
117
136
|
|---|---|---|
|
|
118
137
|
| `TxnodRateLimitError` | `rate_limit_exceeded` | `.retry_after_seconds` holds the `Retry-After` value. The SDK already retries once internally; this is what you see after retries are exhausted |
|
|
119
|
-
| `
|
|
138
|
+
| `TxnodError` (generic) | `manual_claim_rate_limit_exceeded` | HTTP 429 with `Retry-After`. Per-user hourly throttle on `client.claimInvoiceByTx` (10 claims / hour / user). Falls through to the base `TxnodError` — branch on `err.error_code` to distinguish from the standard API rate limit |
|
|
139
|
+
| `TxnodPoolExhaustedError` | `pool_exhausted` | Pool hit hard cap (`poolSizePerChain[chain]`). `.retry_after_seconds` holds the server-computed `Retry-After` (soonest `locked_until` or per-chain cooldown via `cooldownSecondsPerChain[chain]`). Wait it out, or raise the cap if it fires under steady load |
|
|
120
140
|
| `TxnodWebhookCapacityExhaustedError` | `webhook_capacity_exhausted` | Webhook fleet for this chain has no spare slots. `.retry_after_seconds` is always `0` because a partner retry alone won't free capacity — treat as transient outage and queue the invoice for a later retry |
|
|
121
141
|
| `TxnodServerError` | `internal_error` | 5xx classified as uncategorized — retryable with backoff |
|
|
122
142
|
|
|
@@ -129,7 +149,7 @@ Server-side TON failures that surface to partner code on `createInvoice` (or dow
|
|
|
129
149
|
| `TxnodTonOperatorWalletNotDeployedError` | `ton_operator_wallet_not_deployed` | TON operator wallet has not been deployed on-chain yet — operator must broadcast the wallet's `StateInit` (typical first send) before invoices can resolve. HTTP 422 |
|
|
130
150
|
| `TxnodTonInvalidWalletVersionError` | `ton_invalid_wallet_version` | Configured wallet version does not match the deployed one. Operator must reconcile `TXNOD_TON_WALLET_VERSION` with the actual deployed version (`v3R2` / `v4R2` / `v5`). HTTP 422 |
|
|
131
151
|
| `TxnodTonJettonResolveFailedError` | `ton_jetton_resolve_failed` | Toncenter v3 or TONAPI failed to resolve the jetton master/wallet pair (provider hiccup, typically transient). HTTP 503 — retry with backoff |
|
|
132
|
-
| `TxnodTonCommentParseFailedError` | `ton_comment_parse_failed` | Inbound TON tx carried a malformed `payment_token` comment that did not parse as the expected 8
|
|
152
|
+
| `TxnodTonCommentParseFailedError` | `ton_comment_parse_failed` | Inbound TON tx carried a malformed `payment_token` comment that did not parse as the expected memo shape (8 or 16 lowercase hex). Indicates a mis-formed wallet send; operator review required. HTTP 422 |
|
|
133
153
|
|
|
134
154
|
### TonConnect operator-onboarding
|
|
135
155
|
|
|
@@ -147,7 +167,7 @@ Raised by the operator's TonConnect signing flow during xpub onboarding — neve
|
|
|
147
167
|
|
|
148
168
|
### Sandbox lifecycle + simulation
|
|
149
169
|
|
|
150
|
-
Raised by the sandbox surface (`/api/v1/sandbox/*` REST + the
|
|
170
|
+
Raised by the sandbox surface (`/api/v1/sandbox/*` REST + the `client.sandbox.*` SDK methods + `sandbox:simulate` MCP scope). See [`05-sandbox.md`](./05-sandbox.md) for the full lifecycle.
|
|
151
171
|
|
|
152
172
|
| Class | `error_code` | Meaning |
|
|
153
173
|
|---|---|---|
|
package/docs/05-sandbox.md
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: "Sandbox"
|
|
3
3
|
description: "client.sandbox.* surface, environment-detection guards, layered defenses against routing real customer funds to sandbox addresses."
|
|
4
|
-
sdk_version: 1.0.
|
|
4
|
+
sdk_version: 1.0.2
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Sandbox
|
|
8
8
|
|
|
9
|
-
The `@txnod/sdk` ships a sandbox surface (`client.sandbox.*`
|
|
9
|
+
The `@txnod/sdk` ships a sandbox surface (`client.sandbox.*` with matching MCP tools on the server) for deterministic integration testing. **Sandbox secrets and sandbox projects exist in the same Postgres database as production — there is no separate environment.** Discrimination happens via:
|
|
10
10
|
|
|
11
11
|
1. The `sk_sandbox_` API-secret prefix.
|
|
12
12
|
2. A `projects.kind` column whose value is `'sandbox'` or `'production'`.
|
|
@@ -91,7 +91,7 @@ The override option is deliberately verbose — it must remain awkward to type.
|
|
|
91
91
|
|
|
92
92
|
## `client.sandbox.*` surface
|
|
93
93
|
|
|
94
|
-
All
|
|
94
|
+
All sandbox methods sign with the same HMAC scheme as the rest of the SDK and throw the typed sandbox-* error classes per the SDK's typed-error contract.
|
|
95
95
|
|
|
96
96
|
| Method | HTTP | Returns | Throws |
|
|
97
97
|
|---|---|---|---|
|
|
@@ -105,6 +105,7 @@ All 14 methods sign with the same HMAC scheme as the rest of the SDK and throw t
|
|
|
105
105
|
| `simulateReconfirm(invoiceId)` | `POST .../simulate-reconfirm` | `{ event_id, status: 'paid' }` | `TxnodSandboxInvoiceTransitionInvalidError` (invoice not in `reverted`) |
|
|
106
106
|
| `simulateDuplicateDelivery(invoiceId)` | `POST .../simulate-duplicate-delivery` | `{ event_id }` | `TxnodSandboxInvoiceTerminalError` (no terminal event yet) |
|
|
107
107
|
| `simulateEvent(invoiceId, eventInput)` | `POST .../simulate-event` | `{ event_id, status }` | `TxnodSandboxInvoiceTransitionInvalidError` (current status != `expectedCurrentStatus`) |
|
|
108
|
+
| `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 |
|
|
108
109
|
| `clockAdvance(projectId, params)` | `POST /api/v1/sandbox/{projectId}/clock/advance` | `{ advanced, remaining }` | `TxnodSandboxRateLimitExceededError` (>10/min/project) |
|
|
109
110
|
| `reset(projectId)` | `POST /api/v1/sandbox/{projectId}/reset` | `{ status: 'reset' }` | `TxnodSandboxResetFailedError` |
|
|
110
111
|
| `destroy(projectId)` | `DELETE /api/v1/sandbox/{projectId}` | `{ status: 'deleted' }` | `TxnodSandboxDeleteFailedError` |
|
package/docs/reference/client.md
CHANGED
|
@@ -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.
|
|
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
|
|
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({
|
package/docs/reference/errors.md
CHANGED
|
@@ -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.
|
|
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
|
|
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 |
|
package/docs/reference/types.md
CHANGED
|
@@ -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.
|
|
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 —
|
|
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.**
|
|
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.
|
|
3
|
+
"version": "1.0.2",
|
|
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",
|