@txnod/sdk 1.0.1
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 +29 -0
- package/CHANGELOG.md +22 -0
- package/LICENSE +21 -0
- package/README.md +434 -0
- package/dist/_shared/index.d.ts +68 -0
- package/dist/client-sandbox.d.ts +396 -0
- package/dist/client-sandbox.d.ts.map +1 -0
- package/dist/client-sandbox.js +448 -0
- package/dist/client-sandbox.js.map +1 -0
- package/dist/client.d.ts +429 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +588 -0
- package/dist/client.js.map +1 -0
- package/dist/env.d.ts +29 -0
- package/dist/env.d.ts.map +1 -0
- package/dist/env.js +44 -0
- package/dist/env.js.map +1 -0
- package/dist/errors.d.ts +1887 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +2107 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +35 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +32 -0
- package/dist/index.js.map +1 -0
- package/dist/internals/error-ctor-map.d.ts +11 -0
- package/dist/internals/error-ctor-map.d.ts.map +1 -0
- package/dist/internals/error-ctor-map.js +75 -0
- package/dist/internals/error-ctor-map.js.map +1 -0
- package/dist/internals/fetch-with-retry.d.ts +34 -0
- package/dist/internals/fetch-with-retry.d.ts.map +1 -0
- package/dist/internals/fetch-with-retry.js +233 -0
- package/dist/internals/fetch-with-retry.js.map +1 -0
- package/dist/internals/hmac.d.ts +2 -0
- package/dist/internals/hmac.d.ts.map +1 -0
- package/dist/internals/hmac.js +10 -0
- package/dist/internals/hmac.js.map +1 -0
- package/dist/internals/logger.d.ts +9 -0
- package/dist/internals/logger.d.ts.map +1 -0
- package/dist/internals/logger.js +16 -0
- package/dist/internals/logger.js.map +1 -0
- package/dist/internals/parse-problem-details.d.ts +3 -0
- package/dist/internals/parse-problem-details.d.ts.map +1 -0
- package/dist/internals/parse-problem-details.js +76 -0
- package/dist/internals/parse-problem-details.js.map +1 -0
- package/dist/internals/synthetic-details.d.ts +12 -0
- package/dist/internals/synthetic-details.d.ts.map +1 -0
- package/dist/internals/synthetic-details.js +19 -0
- package/dist/internals/synthetic-details.js.map +1 -0
- package/dist/verify/chains/bsc.d.ts +17 -0
- package/dist/verify/chains/bsc.d.ts.map +1 -0
- package/dist/verify/chains/bsc.js +15 -0
- package/dist/verify/chains/bsc.js.map +1 -0
- package/dist/verify/chains/btc.d.ts +22 -0
- package/dist/verify/chains/btc.d.ts.map +1 -0
- package/dist/verify/chains/btc.js +55 -0
- package/dist/verify/chains/btc.js.map +1 -0
- package/dist/verify/chains/cardano.d.ts +73 -0
- package/dist/verify/chains/cardano.d.ts.map +1 -0
- package/dist/verify/chains/cardano.js +175 -0
- package/dist/verify/chains/cardano.js.map +1 -0
- package/dist/verify/chains/evm.d.ts +21 -0
- package/dist/verify/chains/evm.d.ts.map +1 -0
- package/dist/verify/chains/evm.js +46 -0
- package/dist/verify/chains/evm.js.map +1 -0
- package/dist/verify/chains/polygon.d.ts +17 -0
- package/dist/verify/chains/polygon.d.ts.map +1 -0
- package/dist/verify/chains/polygon.js +15 -0
- package/dist/verify/chains/polygon.js.map +1 -0
- package/dist/verify/chains/secp256k1-bip32.d.ts +20 -0
- package/dist/verify/chains/secp256k1-bip32.d.ts.map +1 -0
- package/dist/verify/chains/secp256k1-bip32.js +88 -0
- package/dist/verify/chains/secp256k1-bip32.js.map +1 -0
- package/dist/verify/chains/ton-cell.d.ts +179 -0
- package/dist/verify/chains/ton-cell.d.ts.map +1 -0
- package/dist/verify/chains/ton-cell.js +614 -0
- package/dist/verify/chains/ton-cell.js.map +1 -0
- package/dist/verify/chains/ton.d.ts +84 -0
- package/dist/verify/chains/ton.d.ts.map +1 -0
- package/dist/verify/chains/ton.js +131 -0
- package/dist/verify/chains/ton.js.map +1 -0
- package/dist/verify/chains/tron.d.ts +21 -0
- package/dist/verify/chains/tron.d.ts.map +1 -0
- package/dist/verify/chains/tron.js +42 -0
- package/dist/verify/chains/tron.js.map +1 -0
- package/dist/verify/config.d.ts +41 -0
- package/dist/verify/config.d.ts.map +1 -0
- package/dist/verify/config.js +120 -0
- package/dist/verify/config.js.map +1 -0
- package/dist/verify/errors.d.ts +56 -0
- package/dist/verify/errors.d.ts.map +1 -0
- package/dist/verify/errors.js +58 -0
- package/dist/verify/errors.js.map +1 -0
- package/dist/verify/index.d.ts +119 -0
- package/dist/verify/index.d.ts.map +1 -0
- package/dist/verify/index.js +166 -0
- package/dist/verify/index.js.map +1 -0
- package/dist/verify/xpub-safety.d.ts +33 -0
- package/dist/verify/xpub-safety.d.ts.map +1 -0
- package/dist/verify/xpub-safety.js +54 -0
- package/dist/verify/xpub-safety.js.map +1 -0
- package/dist/verify-webhook-signature.d.ts +30 -0
- package/dist/verify-webhook-signature.d.ts.map +1 -0
- package/dist/verify-webhook-signature.js +84 -0
- package/dist/verify-webhook-signature.js.map +1 -0
- package/docs/00-getting-started.md +135 -0
- package/docs/01-authentication.md +114 -0
- package/docs/02-invoices.md +216 -0
- package/docs/03-rates-and-quotes.md +82 -0
- package/docs/04-webhooks.md +126 -0
- package/docs/05-errors.md +199 -0
- package/docs/05-sandbox.md +159 -0
- package/docs/06-idempotency.md +132 -0
- package/docs/examples/express-webhook-receiver.md +97 -0
- package/docs/examples/nextjs-route-handler.md +206 -0
- package/docs/examples/sandbox-vitest-suite.md +263 -0
- package/docs/index.md +66 -0
- package/docs/reference/client.md +392 -0
- package/docs/reference/errors.md +161 -0
- package/docs/reference/types.md +400 -0
- package/package.json +53 -0
package/dist/client.js
ADDED
|
@@ -0,0 +1,588 @@
|
|
|
1
|
+
import { signedFetch, } from './internals/fetch-with-retry.js';
|
|
2
|
+
import { parseProblemDetails } from './internals/parse-problem-details.js';
|
|
3
|
+
import { parseXpubConfig, parseTonConfig } from './verify/config.js';
|
|
4
|
+
import { verifyAddress } from './verify/index.js';
|
|
5
|
+
import { assertNoTestnetXpubsInProduction } from './verify/xpub-safety.js';
|
|
6
|
+
import { TxnodEnvironmentUnknownError, TxnodExternalIdConflictError, TxnodSandboxKeyInProductionError, } from './errors.js';
|
|
7
|
+
import { getSdkEnv } from './env.js';
|
|
8
|
+
import { TxnodClientSandbox } from './client-sandbox.js';
|
|
9
|
+
const DEFAULT_BASE_URL = 'https://txnod.com';
|
|
10
|
+
function stripTrailingSlash(url) {
|
|
11
|
+
return url.endsWith('/') ? url.slice(0, -1) : url;
|
|
12
|
+
}
|
|
13
|
+
function toQueryParams(q) {
|
|
14
|
+
const out = {};
|
|
15
|
+
for (const [k, v] of Object.entries(q)) {
|
|
16
|
+
if (v === undefined)
|
|
17
|
+
continue;
|
|
18
|
+
out[k] = typeof v === 'boolean' ? String(v) : v;
|
|
19
|
+
}
|
|
20
|
+
return out;
|
|
21
|
+
}
|
|
22
|
+
const SANDBOX_API_SECRET_PREFIX = 'sk_sandbox_';
|
|
23
|
+
/**
|
|
24
|
+
* Implements spec §7.2 behavior matrix. Called from `TxnodClient.constructor`
|
|
25
|
+
* BEFORE any field assignment so a misconfiguration fails boot loudly.
|
|
26
|
+
*
|
|
27
|
+
* Per-constructor-invocation diagnostics: every call to `new TxnodClient(...)`
|
|
28
|
+
* with a sandbox secret emits exactly one `console.warn` (non-production env)
|
|
29
|
+
* or one `console.error` (production-detected with override) — module-level
|
|
30
|
+
* dedup is deliberately avoided so multiple clients in the same process all
|
|
31
|
+
* surface their config moment.
|
|
32
|
+
*/
|
|
33
|
+
function assertEnvironmentSafety(apiSecret, projectId, options) {
|
|
34
|
+
const isSandboxSecret = apiSecret.startsWith(SANDBOX_API_SECRET_PREFIX);
|
|
35
|
+
if (!isSandboxSecret) {
|
|
36
|
+
return { prodOverrideActive: false };
|
|
37
|
+
}
|
|
38
|
+
const env = getSdkEnv(options.environment);
|
|
39
|
+
if (env === 'production') {
|
|
40
|
+
if (options.iAcknowledgeRoutingRealCustomerFundsToSandboxAddresses === true) {
|
|
41
|
+
// Non-suppressible diagnostic per spec §7.2. The byte-exact string is
|
|
42
|
+
// load-bearing — partner runbooks may grep for it.
|
|
43
|
+
console.error(`[txnod-sdk] CRITICAL: Sandbox API secret active in production-detected environment for project_id=${projectId}. Real on-chain payments will NOT be observed. iAcknowledgeRoutingRealCustomerFundsToSandboxAddresses=true is set; X-Txnod-Client-Environment: production will be sent on every request.`);
|
|
44
|
+
return { prodOverrideActive: true };
|
|
45
|
+
}
|
|
46
|
+
const signal = options.environment !== undefined
|
|
47
|
+
? 'environment_option'
|
|
48
|
+
: process.env['TXNOD_ENVIRONMENT'] === 'production'
|
|
49
|
+
? 'TXNOD_ENVIRONMENT'
|
|
50
|
+
: 'NODE_ENV';
|
|
51
|
+
throw new TxnodSandboxKeyInProductionError(signal);
|
|
52
|
+
}
|
|
53
|
+
if (env === 'unknown') {
|
|
54
|
+
throw new TxnodEnvironmentUnknownError();
|
|
55
|
+
}
|
|
56
|
+
// env === 'non-production' — emit one warn per construction.
|
|
57
|
+
console.warn('[txnod-sdk] Sandbox API secret detected. Real on-chain payments will not be observed.');
|
|
58
|
+
return { prodOverrideActive: false };
|
|
59
|
+
}
|
|
60
|
+
const REQUEST_ID_HEADER = 'X-Txnod-Request-Id';
|
|
61
|
+
/**
|
|
62
|
+
* Typed client for the TxNod integration API. Every method signs outbound
|
|
63
|
+
* requests with HMAC-SHA256 using `apiSecret`, retries on 429 and 5xx, and
|
|
64
|
+
* throws `TxnodError` on non-2xx responses.
|
|
65
|
+
*
|
|
66
|
+
* @example
|
|
67
|
+
* ```ts
|
|
68
|
+
* import { TxnodClient } from '@txnod/sdk';
|
|
69
|
+
*
|
|
70
|
+
* const client = new TxnodClient({
|
|
71
|
+
* projectId: process.env.TXNOD_PROJECT_ID!,
|
|
72
|
+
* apiSecret: process.env.TXNOD_API_SECRET!,
|
|
73
|
+
* });
|
|
74
|
+
*
|
|
75
|
+
* const invoice = await client.createInvoice({
|
|
76
|
+
* amount_usd: 10.0,
|
|
77
|
+
* coin: 'btc',
|
|
78
|
+
* external_id: 'order-123',
|
|
79
|
+
* callback_url: 'https://my-site.com/webhooks/txnod',
|
|
80
|
+
* });
|
|
81
|
+
* console.log(invoice.id, invoice.address);
|
|
82
|
+
* ```
|
|
83
|
+
*/
|
|
84
|
+
export class TxnodClient {
|
|
85
|
+
#projectId;
|
|
86
|
+
#apiSecret;
|
|
87
|
+
#baseUrl;
|
|
88
|
+
#requestTimeoutMs;
|
|
89
|
+
#maxResponseBytes;
|
|
90
|
+
#requestLogger;
|
|
91
|
+
#prodOverrideActive;
|
|
92
|
+
#environmentOption;
|
|
93
|
+
#xpubConfig;
|
|
94
|
+
#tonConfig;
|
|
95
|
+
#sandboxNamespace;
|
|
96
|
+
#requestBound;
|
|
97
|
+
/**
|
|
98
|
+
* Server `X-Txnod-Request-Id` header from the most recently completed call,
|
|
99
|
+
* regardless of HTTP status. Reset to `undefined` at the start of every call,
|
|
100
|
+
* so a value here always corresponds to the call that just finished. Use it
|
|
101
|
+
* to correlate a successful response with TxNod-side logs (on failure paths
|
|
102
|
+
* the same id is also surfaced as `err.request_id` on the thrown
|
|
103
|
+
* `TxnodError`).
|
|
104
|
+
*
|
|
105
|
+
* Caveat: the field is per-instance, not per-call. Concurrent calls on a
|
|
106
|
+
* shared `TxnodClient` will overwrite each other; create a new client per
|
|
107
|
+
* request scope, or read the field synchronously after `await`-ing a single
|
|
108
|
+
* call.
|
|
109
|
+
*/
|
|
110
|
+
lastRequestId;
|
|
111
|
+
constructor(options) {
|
|
112
|
+
const safety = assertEnvironmentSafety(options.apiSecret, options.projectId, options);
|
|
113
|
+
this.#projectId = options.projectId;
|
|
114
|
+
this.#apiSecret = options.apiSecret;
|
|
115
|
+
this.#baseUrl = stripTrailingSlash(options.baseUrl ?? DEFAULT_BASE_URL);
|
|
116
|
+
this.#requestTimeoutMs = options.requestTimeoutMs;
|
|
117
|
+
this.#maxResponseBytes = options.maxResponseBytes;
|
|
118
|
+
this.#requestLogger = options.requestLogger;
|
|
119
|
+
this.#prodOverrideActive = safety.prodOverrideActive;
|
|
120
|
+
this.#environmentOption = options.environment;
|
|
121
|
+
this.#xpubConfig = parseXpubConfig();
|
|
122
|
+
this.#tonConfig = parseTonConfig();
|
|
123
|
+
assertNoTestnetXpubsInProduction(this.#xpubConfig, getSdkEnv(this.#environmentOption));
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Lazy accessor for the `client.sandbox.*` namespace exposing 14 thin HTTP
|
|
127
|
+
* wrappers around `/api/v1/sandbox/...` endpoints. The namespace class is
|
|
128
|
+
* only constructed on first access — when an integrator never references
|
|
129
|
+
* `client.sandbox`, the bundler's tree-shaker drops the entire
|
|
130
|
+
* `TxnodClientSandbox` class graph from the produced bundle.
|
|
131
|
+
*
|
|
132
|
+
* Methods throw the typed sandbox-specific error classes (e.g.
|
|
133
|
+
* `TxnodSandboxInvoiceTransitionInvalidError`) on 4xx/5xx server responses
|
|
134
|
+
* per FR55. See `docs/05-sandbox.md` in the bundled tarball for the full
|
|
135
|
+
* surface and code examples.
|
|
136
|
+
*
|
|
137
|
+
* @example
|
|
138
|
+
* ```ts
|
|
139
|
+
* import { TxnodClient, TxnodSandboxInvoiceTransitionInvalidError } from '@txnod/sdk';
|
|
140
|
+
*
|
|
141
|
+
* const client = new TxnodClient({
|
|
142
|
+
* projectId: process.env.TXNOD_PROJECT_ID!,
|
|
143
|
+
* apiSecret: process.env.TXNOD_API_SECRET!,
|
|
144
|
+
* environment: 'non-production',
|
|
145
|
+
* });
|
|
146
|
+
*
|
|
147
|
+
* try {
|
|
148
|
+
* const result = await client.sandbox.simulatePaid('01HK8MAR2QEXAMPLE000000000');
|
|
149
|
+
* console.log(result.event_id, result.status);
|
|
150
|
+
* } catch (err) {
|
|
151
|
+
* if (err instanceof TxnodSandboxInvoiceTransitionInvalidError) {
|
|
152
|
+
* console.error('forbidden simulate transition', err.request_id);
|
|
153
|
+
* } else {
|
|
154
|
+
* throw err;
|
|
155
|
+
* }
|
|
156
|
+
* }
|
|
157
|
+
* ```
|
|
158
|
+
*/
|
|
159
|
+
get sandbox() {
|
|
160
|
+
if (this.#sandboxNamespace === undefined) {
|
|
161
|
+
if (this.#requestBound === undefined) {
|
|
162
|
+
this.#requestBound = (input) => this.#request(input);
|
|
163
|
+
}
|
|
164
|
+
this.#sandboxNamespace = new TxnodClientSandbox(this.#requestBound);
|
|
165
|
+
}
|
|
166
|
+
return this.#sandboxNamespace;
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Re-read `TXNOD_<chain>_XPUB` env vars (and TON-specific siblings — the
|
|
170
|
+
* `TXNOD_TON_PUBKEY` family) into the client's xpub config. Useful after a
|
|
171
|
+
* wallet rotation in long-lived processes (Lambda containers, daemon-style
|
|
172
|
+
* workers) where the constructor ran with stale env values. Idempotent —
|
|
173
|
+
* calling it when env is unchanged is a no-op.
|
|
174
|
+
*
|
|
175
|
+
* @example
|
|
176
|
+
* ```ts
|
|
177
|
+
* import { TxnodClient } from '@txnod/sdk';
|
|
178
|
+
*
|
|
179
|
+
* const client = new TxnodClient({
|
|
180
|
+
* projectId: process.env.TXNOD_PROJECT_ID!,
|
|
181
|
+
* apiSecret: process.env.TXNOD_API_SECRET!,
|
|
182
|
+
* });
|
|
183
|
+
*
|
|
184
|
+
* // Operator rotated their xpub via the dashboard and your secrets manager
|
|
185
|
+
* // pushed the new TXNOD_BTC_XPUB into process.env. Pick it up without a restart:
|
|
186
|
+
* process.env.TXNOD_BTC_XPUB = 'zpub6r_NEW';
|
|
187
|
+
* client.refreshXpubConfig();
|
|
188
|
+
*
|
|
189
|
+
* // Subsequent createInvoice calls now verify the address against the new xpub.
|
|
190
|
+
* await client.createInvoice({
|
|
191
|
+
* external_id: 'after-rotation',
|
|
192
|
+
* coin: 'btc',
|
|
193
|
+
* amount_usd: 10,
|
|
194
|
+
* });
|
|
195
|
+
* ```
|
|
196
|
+
*/
|
|
197
|
+
refreshXpubConfig() {
|
|
198
|
+
this.#xpubConfig = parseXpubConfig();
|
|
199
|
+
this.#tonConfig = parseTonConfig();
|
|
200
|
+
assertNoTestnetXpubsInProduction(this.#xpubConfig, getSdkEnv(this.#environmentOption));
|
|
201
|
+
}
|
|
202
|
+
async #request(input) {
|
|
203
|
+
const fetchInput = {
|
|
204
|
+
baseUrl: this.#baseUrl,
|
|
205
|
+
projectId: this.#projectId,
|
|
206
|
+
apiSecret: this.#apiSecret,
|
|
207
|
+
method: input.method,
|
|
208
|
+
path: input.path,
|
|
209
|
+
};
|
|
210
|
+
if (input.query !== undefined)
|
|
211
|
+
fetchInput.query = input.query;
|
|
212
|
+
if (input.body !== undefined)
|
|
213
|
+
fetchInput.body = input.body;
|
|
214
|
+
if (this.#requestTimeoutMs !== undefined)
|
|
215
|
+
fetchInput.requestTimeoutMs = this.#requestTimeoutMs;
|
|
216
|
+
if (this.#maxResponseBytes !== undefined)
|
|
217
|
+
fetchInput.maxResponseBytes = this.#maxResponseBytes;
|
|
218
|
+
if (this.#requestLogger !== undefined)
|
|
219
|
+
fetchInput.requestLogger = this.#requestLogger;
|
|
220
|
+
if (this.#prodOverrideActive) {
|
|
221
|
+
fetchInput.extraHeaders = { 'X-Txnod-Client-Environment': 'production' };
|
|
222
|
+
}
|
|
223
|
+
this.lastRequestId = undefined;
|
|
224
|
+
const response = await signedFetch(fetchInput);
|
|
225
|
+
const headerRequestId = response.headers.get(REQUEST_ID_HEADER);
|
|
226
|
+
this.lastRequestId =
|
|
227
|
+
headerRequestId !== null && headerRequestId.length > 0
|
|
228
|
+
? headerRequestId
|
|
229
|
+
: undefined;
|
|
230
|
+
if (response.status >= 200 && response.status < 300) {
|
|
231
|
+
return (await response.json());
|
|
232
|
+
}
|
|
233
|
+
throw await parseProblemDetails(response);
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Create a new invoice. Idempotent on `(project_id, external_id)`. When the
|
|
237
|
+
* matching `TXNOD_<chain>_XPUB` env var is set, the SDK automatically
|
|
238
|
+
* verifies the returned address derives from the configured xpub at the
|
|
239
|
+
* invoice's `derivation_path` and throws `AddressVerificationError` on
|
|
240
|
+
* mismatch — no flag needed.
|
|
241
|
+
*
|
|
242
|
+
* @example
|
|
243
|
+
* ```ts
|
|
244
|
+
* import { TxnodClient, TxnodError, AddressVerificationError } from '@txnod/sdk';
|
|
245
|
+
*
|
|
246
|
+
* // Set TXNOD_BTC_XPUB=zpub6r... in env to enable address verification.
|
|
247
|
+
* const client = new TxnodClient({
|
|
248
|
+
* projectId: process.env.TXNOD_PROJECT_ID!,
|
|
249
|
+
* apiSecret: process.env.TXNOD_API_SECRET!,
|
|
250
|
+
* });
|
|
251
|
+
*
|
|
252
|
+
* try {
|
|
253
|
+
* const invoice = await client.createInvoice({
|
|
254
|
+
* amount_usd: 10.0,
|
|
255
|
+
* coin: 'btc',
|
|
256
|
+
* external_id: 'order-123',
|
|
257
|
+
* callback_url: 'https://my-site.com/webhooks/txnod',
|
|
258
|
+
* });
|
|
259
|
+
* console.log(invoice.id, invoice.address, invoice.payment_uri);
|
|
260
|
+
* } catch (err) {
|
|
261
|
+
* if (err instanceof AddressVerificationError) {
|
|
262
|
+
* console.error('address verify failed', err.expected_address, err.derived_address);
|
|
263
|
+
* } else if (err instanceof TxnodError) {
|
|
264
|
+
* console.error(err.error_code, err.status, err.request_id);
|
|
265
|
+
* }
|
|
266
|
+
* throw err;
|
|
267
|
+
* }
|
|
268
|
+
* ```
|
|
269
|
+
*/
|
|
270
|
+
async createInvoice(body) {
|
|
271
|
+
const invoice = await this.#request({
|
|
272
|
+
method: 'POST',
|
|
273
|
+
path: '/api/v1/invoices',
|
|
274
|
+
body,
|
|
275
|
+
});
|
|
276
|
+
const verifyInput = {
|
|
277
|
+
invoice,
|
|
278
|
+
config: this.#xpubConfig,
|
|
279
|
+
};
|
|
280
|
+
if (this.#tonConfig !== undefined)
|
|
281
|
+
verifyInput.tonConfig = this.#tonConfig;
|
|
282
|
+
verifyAddress(verifyInput);
|
|
283
|
+
return invoice;
|
|
284
|
+
}
|
|
285
|
+
/**
|
|
286
|
+
* Idempotent invoice creation. Tries `createInvoice`; on
|
|
287
|
+
* `TxnodExternalIdConflictError` fetches the pre-existing invoice via
|
|
288
|
+
* `searchInvoices({ external_id })` and returns it. Use this when your
|
|
289
|
+
* checkout flow may retry the same `external_id` (network glitches, idempotent
|
|
290
|
+
* upstream queues) and you want a single round-trip-then-fetch path.
|
|
291
|
+
*
|
|
292
|
+
* @example
|
|
293
|
+
* ```ts
|
|
294
|
+
* const invoice = await client.createOrGetInvoice({
|
|
295
|
+
* external_id: 'order-42',
|
|
296
|
+
* coin: 'usdt_trc20',
|
|
297
|
+
* amount_usd: 9.99,
|
|
298
|
+
* });
|
|
299
|
+
* ```
|
|
300
|
+
*/
|
|
301
|
+
async createOrGetInvoice(body) {
|
|
302
|
+
try {
|
|
303
|
+
return await this.createInvoice(body);
|
|
304
|
+
}
|
|
305
|
+
catch (err) {
|
|
306
|
+
if (!(err instanceof TxnodExternalIdConflictError))
|
|
307
|
+
throw err;
|
|
308
|
+
const page = await this.searchInvoices({
|
|
309
|
+
external_id: body.external_id,
|
|
310
|
+
limit: 1,
|
|
311
|
+
});
|
|
312
|
+
const existing = page.items[0];
|
|
313
|
+
if (existing === undefined) {
|
|
314
|
+
// Conflict raised but the row is not findable via search — surface the
|
|
315
|
+
// original error rather than silently masking it.
|
|
316
|
+
throw err;
|
|
317
|
+
}
|
|
318
|
+
return existing;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* Fetch an invoice by ULID.
|
|
323
|
+
*
|
|
324
|
+
* @example
|
|
325
|
+
* ```ts
|
|
326
|
+
* import { TxnodClient, TxnodError } from '@txnod/sdk';
|
|
327
|
+
*
|
|
328
|
+
* const client = new TxnodClient({
|
|
329
|
+
* projectId: process.env.TXNOD_PROJECT_ID!,
|
|
330
|
+
* apiSecret: process.env.TXNOD_API_SECRET!,
|
|
331
|
+
* });
|
|
332
|
+
*
|
|
333
|
+
* try {
|
|
334
|
+
* const invoice = await client.getInvoice('01HK8MAR2QEXAMPLE000000000');
|
|
335
|
+
* console.log(invoice.status);
|
|
336
|
+
* } catch (err) {
|
|
337
|
+
* if (err instanceof TxnodError) console.error(err.error_code);
|
|
338
|
+
* throw err;
|
|
339
|
+
* }
|
|
340
|
+
* ```
|
|
341
|
+
*/
|
|
342
|
+
getInvoice(id) {
|
|
343
|
+
return this.#request({
|
|
344
|
+
method: 'GET',
|
|
345
|
+
path: `/api/v1/invoices/${id}`,
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* Search invoices with snake_case query filters.
|
|
350
|
+
*
|
|
351
|
+
* @example
|
|
352
|
+
* ```ts
|
|
353
|
+
* import { TxnodClient, TxnodError } from '@txnod/sdk';
|
|
354
|
+
*
|
|
355
|
+
* const client = new TxnodClient({
|
|
356
|
+
* projectId: process.env.TXNOD_PROJECT_ID!,
|
|
357
|
+
* apiSecret: process.env.TXNOD_API_SECRET!,
|
|
358
|
+
* });
|
|
359
|
+
*
|
|
360
|
+
* try {
|
|
361
|
+
* const page = await client.searchInvoices({
|
|
362
|
+
* status: 'paid',
|
|
363
|
+
* limit: 20,
|
|
364
|
+
* });
|
|
365
|
+
* for (const invoice of page.items) console.log(invoice.id);
|
|
366
|
+
* } catch (err) {
|
|
367
|
+
* if (err instanceof TxnodError) console.error(err.error_code);
|
|
368
|
+
* throw err;
|
|
369
|
+
* }
|
|
370
|
+
* ```
|
|
371
|
+
*/
|
|
372
|
+
searchInvoices(query) {
|
|
373
|
+
return this.#request({
|
|
374
|
+
method: 'GET',
|
|
375
|
+
path: '/api/v1/invoices',
|
|
376
|
+
query: toQueryParams(query),
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
/**
|
|
380
|
+
* Cancel a pending invoice.
|
|
381
|
+
*
|
|
382
|
+
* @example
|
|
383
|
+
* ```ts
|
|
384
|
+
* import { TxnodClient, TxnodError } from '@txnod/sdk';
|
|
385
|
+
*
|
|
386
|
+
* const client = new TxnodClient({
|
|
387
|
+
* projectId: process.env.TXNOD_PROJECT_ID!,
|
|
388
|
+
* apiSecret: process.env.TXNOD_API_SECRET!,
|
|
389
|
+
* });
|
|
390
|
+
*
|
|
391
|
+
* try {
|
|
392
|
+
* const cancelled = await client.cancelInvoice('01HK8MAR2QEXAMPLE000000000');
|
|
393
|
+
* console.log(cancelled.status);
|
|
394
|
+
* } catch (err) {
|
|
395
|
+
* if (err instanceof TxnodError) console.error(err.error_code);
|
|
396
|
+
* throw err;
|
|
397
|
+
* }
|
|
398
|
+
* ```
|
|
399
|
+
*/
|
|
400
|
+
cancelInvoice(id) {
|
|
401
|
+
return this.#request({
|
|
402
|
+
method: 'POST',
|
|
403
|
+
path: `/api/v1/invoices/${id}/cancel`,
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
/**
|
|
407
|
+
* List on-chain receipts that did not match any invoice address.
|
|
408
|
+
*
|
|
409
|
+
* @example
|
|
410
|
+
* ```ts
|
|
411
|
+
* import { TxnodClient, TxnodError } from '@txnod/sdk';
|
|
412
|
+
*
|
|
413
|
+
* const client = new TxnodClient({
|
|
414
|
+
* projectId: process.env.TXNOD_PROJECT_ID!,
|
|
415
|
+
* apiSecret: process.env.TXNOD_API_SECRET!,
|
|
416
|
+
* });
|
|
417
|
+
*
|
|
418
|
+
* try {
|
|
419
|
+
* const page = await client.listOrphanPayments({ chain: 'btc', limit: 50 });
|
|
420
|
+
* for (const orphan of page.items) console.log(orphan.tx_hash);
|
|
421
|
+
* } catch (err) {
|
|
422
|
+
* if (err instanceof TxnodError) console.error(err.error_code);
|
|
423
|
+
* throw err;
|
|
424
|
+
* }
|
|
425
|
+
* ```
|
|
426
|
+
*/
|
|
427
|
+
listOrphanPayments(query) {
|
|
428
|
+
return this.#request({
|
|
429
|
+
method: 'GET',
|
|
430
|
+
path: '/api/v1/orphan-payments',
|
|
431
|
+
query: toQueryParams(query),
|
|
432
|
+
});
|
|
433
|
+
}
|
|
434
|
+
/**
|
|
435
|
+
* Attribute an orphan payment to an external invoice id.
|
|
436
|
+
*
|
|
437
|
+
* @example
|
|
438
|
+
* ```ts
|
|
439
|
+
* import { TxnodClient, TxnodError } from '@txnod/sdk';
|
|
440
|
+
*
|
|
441
|
+
* const client = new TxnodClient({
|
|
442
|
+
* projectId: process.env.TXNOD_PROJECT_ID!,
|
|
443
|
+
* apiSecret: process.env.TXNOD_API_SECRET!,
|
|
444
|
+
* });
|
|
445
|
+
*
|
|
446
|
+
* try {
|
|
447
|
+
* const invoice = await client.attributeOrphanPayment('0xabc123', {
|
|
448
|
+
* external_id: 'order-123',
|
|
449
|
+
* });
|
|
450
|
+
* console.log(invoice.id);
|
|
451
|
+
* } catch (err) {
|
|
452
|
+
* if (err instanceof TxnodError) console.error(err.error_code);
|
|
453
|
+
* throw err;
|
|
454
|
+
* }
|
|
455
|
+
* ```
|
|
456
|
+
*/
|
|
457
|
+
attributeOrphanPayment(txHash, body) {
|
|
458
|
+
return this.#request({
|
|
459
|
+
method: 'POST',
|
|
460
|
+
path: `/api/v1/orphan-payments/${txHash}/attribute`,
|
|
461
|
+
body,
|
|
462
|
+
});
|
|
463
|
+
}
|
|
464
|
+
/**
|
|
465
|
+
* List outbound webhook events for the authenticated project.
|
|
466
|
+
*
|
|
467
|
+
* @example
|
|
468
|
+
* ```ts
|
|
469
|
+
* import { TxnodClient, TxnodError } from '@txnod/sdk';
|
|
470
|
+
*
|
|
471
|
+
* const client = new TxnodClient({
|
|
472
|
+
* projectId: process.env.TXNOD_PROJECT_ID!,
|
|
473
|
+
* apiSecret: process.env.TXNOD_API_SECRET!,
|
|
474
|
+
* });
|
|
475
|
+
*
|
|
476
|
+
* try {
|
|
477
|
+
* const page = await client.listWebhookEvents({
|
|
478
|
+
* status: 'delivered',
|
|
479
|
+
* limit: 100,
|
|
480
|
+
* });
|
|
481
|
+
* for (const event of page.items) console.log(event.id, event.status);
|
|
482
|
+
* } catch (err) {
|
|
483
|
+
* if (err instanceof TxnodError) console.error(err.error_code);
|
|
484
|
+
* throw err;
|
|
485
|
+
* }
|
|
486
|
+
* ```
|
|
487
|
+
*/
|
|
488
|
+
listWebhookEvents(query) {
|
|
489
|
+
return this.#request({
|
|
490
|
+
method: 'GET',
|
|
491
|
+
path: '/api/v1/webhooks/events',
|
|
492
|
+
query: toQueryParams(query),
|
|
493
|
+
});
|
|
494
|
+
}
|
|
495
|
+
/**
|
|
496
|
+
* Resend a previously dispatched webhook event.
|
|
497
|
+
*
|
|
498
|
+
* @example
|
|
499
|
+
* ```ts
|
|
500
|
+
* import { TxnodClient, TxnodError } from '@txnod/sdk';
|
|
501
|
+
*
|
|
502
|
+
* const client = new TxnodClient({
|
|
503
|
+
* projectId: process.env.TXNOD_PROJECT_ID!,
|
|
504
|
+
* apiSecret: process.env.TXNOD_API_SECRET!,
|
|
505
|
+
* });
|
|
506
|
+
*
|
|
507
|
+
* try {
|
|
508
|
+
* const resent = await client.resendWebhookEvent(
|
|
509
|
+
* '01HK8MAR2QEXAMPLE000000000',
|
|
510
|
+
* );
|
|
511
|
+
* console.log(resent.event_id);
|
|
512
|
+
* } catch (err) {
|
|
513
|
+
* if (err instanceof TxnodError) console.error(err.error_code);
|
|
514
|
+
* throw err;
|
|
515
|
+
* }
|
|
516
|
+
* ```
|
|
517
|
+
*/
|
|
518
|
+
resendWebhookEvent(eventId) {
|
|
519
|
+
return this.#request({
|
|
520
|
+
method: 'POST',
|
|
521
|
+
path: `/api/v1/webhooks/events/${eventId}/resend`,
|
|
522
|
+
});
|
|
523
|
+
}
|
|
524
|
+
/**
|
|
525
|
+
* Returns an indicative rate for pre-invoice pricing. The binding rate is
|
|
526
|
+
* captured by `createInvoice` in `rate_snapshot`; any delta between this
|
|
527
|
+
* quote and the invoice is the partner's responsibility per FR33c.
|
|
528
|
+
*
|
|
529
|
+
* @example
|
|
530
|
+
* ```ts
|
|
531
|
+
* import { TxnodClient, TxnodError } from '@txnod/sdk';
|
|
532
|
+
*
|
|
533
|
+
* const client = new TxnodClient({
|
|
534
|
+
* projectId: process.env.TXNOD_PROJECT_ID!,
|
|
535
|
+
* apiSecret: process.env.TXNOD_API_SECRET!,
|
|
536
|
+
* });
|
|
537
|
+
*
|
|
538
|
+
* try {
|
|
539
|
+
* const rates = await client.getRates({ coins: 'btc,eth' });
|
|
540
|
+
* console.log(rates.rates.btc);
|
|
541
|
+
* } catch (err) {
|
|
542
|
+
* if (err instanceof TxnodError) console.error(err.error_code);
|
|
543
|
+
* throw err;
|
|
544
|
+
* }
|
|
545
|
+
* ```
|
|
546
|
+
*/
|
|
547
|
+
getRates(query) {
|
|
548
|
+
return this.#request({
|
|
549
|
+
method: 'GET',
|
|
550
|
+
path: '/api/v1/rates',
|
|
551
|
+
query: toQueryParams(query),
|
|
552
|
+
});
|
|
553
|
+
}
|
|
554
|
+
/**
|
|
555
|
+
* Returns an indicative rate for pre-invoice pricing. The binding rate is
|
|
556
|
+
* captured by `createInvoice` in `rate_snapshot`; any delta between this
|
|
557
|
+
* quote and the invoice is the partner's responsibility per FR33c.
|
|
558
|
+
*
|
|
559
|
+
* @example
|
|
560
|
+
* ```ts
|
|
561
|
+
* import { TxnodClient, TxnodError } from '@txnod/sdk';
|
|
562
|
+
*
|
|
563
|
+
* const client = new TxnodClient({
|
|
564
|
+
* projectId: process.env.TXNOD_PROJECT_ID!,
|
|
565
|
+
* apiSecret: process.env.TXNOD_API_SECRET!,
|
|
566
|
+
* });
|
|
567
|
+
*
|
|
568
|
+
* try {
|
|
569
|
+
* const quote = await client.quoteAmount({
|
|
570
|
+
* amount_usd: 10.0,
|
|
571
|
+
* coins: 'btc',
|
|
572
|
+
* });
|
|
573
|
+
* console.log(quote.quotes.btc);
|
|
574
|
+
* } catch (err) {
|
|
575
|
+
* if (err instanceof TxnodError) console.error(err.error_code);
|
|
576
|
+
* throw err;
|
|
577
|
+
* }
|
|
578
|
+
* ```
|
|
579
|
+
*/
|
|
580
|
+
quoteAmount(query) {
|
|
581
|
+
return this.#request({
|
|
582
|
+
method: 'GET',
|
|
583
|
+
path: '/api/v1/quote',
|
|
584
|
+
query: toQueryParams(query),
|
|
585
|
+
});
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
package/dist/env.d.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Single, concentrated reader for environment-detection signals consumed by
|
|
3
|
+
* `TxnodClient`'s sandbox-safety guards. Resolution order (per spec §7.2 + F-2):
|
|
4
|
+
*
|
|
5
|
+
* 1. `explicit` argument — the constructor's `environment` option wins
|
|
6
|
+
* everything when set. Use it for staging replicas where `NODE_ENV` is
|
|
7
|
+
* conventionally `'production'` but the secret is sandbox.
|
|
8
|
+
* 2. `process.env.TXNOD_ENVIRONMENT === 'production'` → `'production'`.
|
|
9
|
+
* 3. `process.env.TXNOD_ENVIRONMENT === 'non-production'` → `'non-production'`.
|
|
10
|
+
* 4. `process.env.NODE_ENV === 'production'` → `'production'`.
|
|
11
|
+
* 5. `process.env.NODE_ENV === 'development' | 'test'` → `'non-production'`.
|
|
12
|
+
* 6. Otherwise → `'unknown'`.
|
|
13
|
+
*
|
|
14
|
+
* **String comparisons are case-sensitive** to match Node.js convention
|
|
15
|
+
* (`NODE_ENV=Production` is treated as unrecognised by Express, Next.js, and
|
|
16
|
+
* Node's own runtime checks). A misconfigured `'Production'` therefore
|
|
17
|
+
* resolves to `'unknown'` and — paired with a sandbox secret — surfaces as
|
|
18
|
+
* `TxnodEnvironmentUnknownError` at construction, prompting the operator to
|
|
19
|
+
* fix the casing.
|
|
20
|
+
*
|
|
21
|
+
* The optional `env` parameter exists for testability — production callers
|
|
22
|
+
* always omit it. Mirrors the same shape as `parseXpubConfig`.
|
|
23
|
+
*
|
|
24
|
+
* **This is the only `process.env` reader in `packages/sdk/src/` for environment
|
|
25
|
+
* signals.** Do not introduce a second; it would split the resolution rules
|
|
26
|
+
* across files and bypass the sandbox-safety matrix in `TxnodClient`.
|
|
27
|
+
*/
|
|
28
|
+
export declare function getSdkEnv(explicit?: 'production' | 'non-production', env?: NodeJS.ProcessEnv): 'production' | 'non-production' | 'unknown';
|
|
29
|
+
//# sourceMappingURL=env.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../src/env.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,SAAS,CACvB,QAAQ,CAAC,EAAE,YAAY,GAAG,gBAAgB,EAC1C,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,YAAY,GAAG,gBAAgB,GAAG,SAAS,CAW7C"}
|
package/dist/env.js
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Single, concentrated reader for environment-detection signals consumed by
|
|
3
|
+
* `TxnodClient`'s sandbox-safety guards. Resolution order (per spec §7.2 + F-2):
|
|
4
|
+
*
|
|
5
|
+
* 1. `explicit` argument — the constructor's `environment` option wins
|
|
6
|
+
* everything when set. Use it for staging replicas where `NODE_ENV` is
|
|
7
|
+
* conventionally `'production'` but the secret is sandbox.
|
|
8
|
+
* 2. `process.env.TXNOD_ENVIRONMENT === 'production'` → `'production'`.
|
|
9
|
+
* 3. `process.env.TXNOD_ENVIRONMENT === 'non-production'` → `'non-production'`.
|
|
10
|
+
* 4. `process.env.NODE_ENV === 'production'` → `'production'`.
|
|
11
|
+
* 5. `process.env.NODE_ENV === 'development' | 'test'` → `'non-production'`.
|
|
12
|
+
* 6. Otherwise → `'unknown'`.
|
|
13
|
+
*
|
|
14
|
+
* **String comparisons are case-sensitive** to match Node.js convention
|
|
15
|
+
* (`NODE_ENV=Production` is treated as unrecognised by Express, Next.js, and
|
|
16
|
+
* Node's own runtime checks). A misconfigured `'Production'` therefore
|
|
17
|
+
* resolves to `'unknown'` and — paired with a sandbox secret — surfaces as
|
|
18
|
+
* `TxnodEnvironmentUnknownError` at construction, prompting the operator to
|
|
19
|
+
* fix the casing.
|
|
20
|
+
*
|
|
21
|
+
* The optional `env` parameter exists for testability — production callers
|
|
22
|
+
* always omit it. Mirrors the same shape as `parseXpubConfig`.
|
|
23
|
+
*
|
|
24
|
+
* **This is the only `process.env` reader in `packages/sdk/src/` for environment
|
|
25
|
+
* signals.** Do not introduce a second; it would split the resolution rules
|
|
26
|
+
* across files and bypass the sandbox-safety matrix in `TxnodClient`.
|
|
27
|
+
*/
|
|
28
|
+
export function getSdkEnv(explicit, env = process.env) {
|
|
29
|
+
if (explicit === 'production' || explicit === 'non-production') {
|
|
30
|
+
return explicit;
|
|
31
|
+
}
|
|
32
|
+
const txnodEnv = env['TXNOD_ENVIRONMENT'];
|
|
33
|
+
if (txnodEnv === 'production')
|
|
34
|
+
return 'production';
|
|
35
|
+
if (txnodEnv === 'non-production')
|
|
36
|
+
return 'non-production';
|
|
37
|
+
const nodeEnv = env['NODE_ENV'];
|
|
38
|
+
if (nodeEnv === 'production')
|
|
39
|
+
return 'production';
|
|
40
|
+
if (nodeEnv === 'development' || nodeEnv === 'test')
|
|
41
|
+
return 'non-production';
|
|
42
|
+
return 'unknown';
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=env.js.map
|
package/dist/env.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"env.js","sourceRoot":"","sources":["../src/env.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAM,UAAU,SAAS,CACvB,QAA0C,EAC1C,MAAyB,OAAO,CAAC,GAAG;IAEpC,IAAI,QAAQ,KAAK,YAAY,IAAI,QAAQ,KAAK,gBAAgB,EAAE,CAAC;QAC/D,OAAO,QAAQ,CAAC;IAClB,CAAC;IACD,MAAM,QAAQ,GAAG,GAAG,CAAC,mBAAmB,CAAC,CAAC;IAC1C,IAAI,QAAQ,KAAK,YAAY;QAAE,OAAO,YAAY,CAAC;IACnD,IAAI,QAAQ,KAAK,gBAAgB;QAAE,OAAO,gBAAgB,CAAC;IAC3D,MAAM,OAAO,GAAG,GAAG,CAAC,UAAU,CAAC,CAAC;IAChC,IAAI,OAAO,KAAK,YAAY;QAAE,OAAO,YAAY,CAAC;IAClD,IAAI,OAAO,KAAK,aAAa,IAAI,OAAO,KAAK,MAAM;QAAE,OAAO,gBAAgB,CAAC;IAC7E,OAAO,SAAS,CAAC;AACnB,CAAC"}
|