@atumlabs/payment-gateway-client 2.0.0 → 2.2.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.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,55 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [2.2.0] - 2026-08-04
9
+
10
+ > **Read this before upgrading if you script `send-payment`.** Its exit codes now
11
+ > distinguish a settled payment from one still settling and one that failed, where every
12
+ > accepted submission previously exited `0`. See the first entry under Changed.
13
+ >
14
+ > One typed-API change: three fields on `PaymentResponse` are no longer optional. This makes
15
+ > reading a response simpler, not harder — see Changed.
16
+
17
+ ### Added
18
+ - `send-payment --wait` keeps checking a submitted payment until it settles or fails, instead of reporting it as still pending. `--wait-seconds <n>` sets how long (default 60). With `--wait` the JSON on stdout is printed once the wait ends and reports the payment as of the last check — so it carries the settled `status` and the `fulfillment_confirmation`, rather than the `pending` the submit returned. Without `--wait`, output is unchanged. The bound is a client-side convenience: giving up does not affect the payment, and re-submitting the same `request_id` or reading the status endpoint picks up wherever it got to. A settlement that stalls never becomes terminal on its own, so no bound can promise a terminal answer.
19
+ - `send-payment` and `payment-status` now print a one-line outcome summary to stderr saying what the payment's state means for a re-attempt: a pending payment is collected by re-submitting the **same** `request_id`, while a terminally failed one keeps its id and needs a **new** one. The JSON on stdout is unchanged.
20
+ - Documented in `docs/idempotency.md` and the README: what the `409` compares, that deadlines and the signature are exempt from it, `idempotent_replay`, how to read `status`, and that a direct caller may either poll the status endpoint or re-submit.
21
+
22
+ ### Changed
23
+ - **Behaviour change — `send-payment` exit codes.** Previously any accepted submission exited `0`. Now `0` means `completed`, `2` means `pending`, and `3` means `failed`/`cancelled`; `1` continues to mean the payment could not be submitted. Previously a still-settling payment and a terminally failed one both exited `0`, so `send-payment ... && <next step>` ran the next step for a payment that had not settled — including when re-submitting a `request_id` whose payment had already failed. Scripts that treat `0` as "submitted" should treat `0` and `2` as "submitted"; scripts that mean "settled" now get that from `0` alone. `payment-status` is unaffected: as a lookup, its exit code reports whether the lookup worked.
24
+ - `--request-id`'s help text now states the consequence of omitting it: an id is generated, which makes every bare re-run a separate payment rather than a retry.
25
+ - **`PaymentResponse.payment_id`, `.status` and `.idempotent_replay` are now required** in the typed API, matching what the gateway has always sent. They were typed optional, so TypeScript reported them as possibly `undefined` and reading `idempotent_replay` meant writing `=== true` to satisfy the compiler for a value that is always present. Existing code keeps compiling — a narrowed type only removes checks — unless it constructs a `PaymentResponse` itself, as a test fixture might, in which case all three now have to be supplied.
26
+
27
+
28
+
29
+ ### Fixed
30
+ - Corrected the idempotency documentation. The 2.1.0 note below said that reusing a `request_id` with different terms returns the original payment and silently drops the new terms. The gateway rejects it with `409` `IDEMPOTENCY_TERMS_MISMATCH`, so an integrator following the old text would neither expect nor handle that error.
31
+ - `IDEMPOTENCY_TERMS_MISMATCH`'s message described the compared terms as "source, destination, or fulfillment_amount", which had become incomplete — `max_source_amount` is compared too. It now names the categories ("accounts, assets, or amounts") rather than enumerating fields, so it stays accurate as the payment schema grows. The docs describe the comparison the same way, and point at the error's own `message` for which category differed.
32
+
33
+ ## [2.1.0] - 2026-07-31
34
+
35
+ ### Added
36
+ - The CLIs read an optional `.env` file from the working directory, so `cp .env.example .env` configures them. Resolution order: a CLI flag, then the environment, then `.env`, then the built-in default. This applies to the CLIs only; the SDK reads the environment but never modifies it, so an application embedding the SDK stays in control of its own configuration.
37
+ - `getErrorResponse(err)` returns the typed, public-facing gateway error (`ErrorResponse` — `code`, `message`, `request_id?`, `docs_url?`, `domain?`) from a caught `ApiError`, or `undefined` for anything else; `isApiError(err)` type guard for the transport-level error. No internal error taxonomy is exposed.
38
+ - Idempotency and retries documentation (`docs/idempotency.md`) plus a summary in the README: the two guarantees (gateway dedup and on-chain single-settlement) and the per-chain differences (EVM/Tron vs Solana).
39
+ - The published package now ships `LICENSE` and an autogenerated `THIRD-PARTY-NOTICES.txt` (attributing the third-party components statically bundled into `dist/`).
40
+ - The gateway base URL now honors a `GATEWAY_URL` environment variable. Precedence: an explicit URL (SDK `{ BASE }` or the CLI `--gateway` flag), then `GATEWAY_URL`, then the built-in production-testnet default. A blank or whitespace-only `GATEWAY_URL` counts as unset and falls through to the default. An explicit `{ BASE: '' }` is used verbatim, which is how a caller targets a same-origin proxy.
41
+ - The exported `OpenAPI` configuration now reports this package's default gateway (production-testnet), matching what a client resolves when no URL is supplied. The OpenAPI contract continues to list the mainnet server first, in line with the other Atum service contracts.
42
+
43
+ ### Changed
44
+ - README signing example now uses the shipped signer helpers (`createSenderSigner` + `signPaymentRequest`) instead of manual signing, and the error-handling example reads the typed `ErrorResponse` fields.
45
+ - The idempotency docs now state the consequence of reusing a `request_id` with different terms: the gateway returns the original payment, so the new terms are silently dropped and a genuinely new payment needs a new id.
46
+ - The `fulfillment_verifier.endpoint` example in the OpenAPI spec is now a bare origin (`https://veri-fill.production-testnet.atum.xyz`) and its description says so. The field is a base URL to which the caller appends the verifier's own paths, so an example carrying `/v1/fulfillment` would double the path for anyone copying its shape.
47
+ - Two documentation links in the OpenAPI contract now point at `schemas.atum.xyz`. Descriptive text only: JSON-LD `@context` prefixes, schema `$id` values, and `did:web:` identifiers are unchanged, since those take part in signature canonicalization.
48
+ - The package is marked `restricted` and its `license` field is `SEE LICENSE IN LICENSE`. The publish guard now also fails on any inlined copyleft dependency, stale/incomplete third-party notices, missing license materials in the tarball, or an incorrect `license` field.
49
+ - Default gateway hosts point at the canonical `payment-gw.production-{testnet,mainnet}.atum.xyz` endpoints (the SDK/CLI default and the generated client default), replacing the retired `atumlabs.xyz` / `api.atum.network` hosts.
50
+
51
+ ### Fixed
52
+ - The CLIs (`send-payment`, `request-quote`, `quote-status`, `payment-status`) report the real package version via `--version` (they previously hardcoded `1.0.0`).
53
+ - `docs/` is now included in the published package. The README links to `docs/idempotency.md`, which had not been shipped, so the link was dead for anyone installing from npm.
54
+ - README corrections: the install instructions now distinguish a global install (for the command-line tools) from a project install (for the SDK), and the removal instructions match; the `.env` file and the gateway-URL precedence are documented; monorepo build detail moved to `CONTRIBUTING.md`.
55
+ - A zero-config client (`new PaymentGatewayClient()` with no `BASE`) now uses a single, consistent gateway host; previously it fetched chain defaults from the testnet host but submitted payments to the mainnet host.
56
+
8
57
  ## [2.0.0] - 2026-07-27
9
58
 
10
59
  ### Breaking
package/README.md CHANGED
@@ -2,25 +2,38 @@
2
2
 
3
3
  TypeScript client for Atum Payment Gateway API with CLI support.
4
4
 
5
+ > **Proprietary software - not open source.**
6
+ >
7
+ > Copyright (c) 2026 Atum Labs, Inc. All rights reserved. Access to and use of `@atumlabs/payment-gateway-client` are limited to entities expressly authorized by Atum and are governed by the Atum Payment Gateway Client Private Repository License Agreement in the LICENSE file included with this package. Do not redistribute, publish, mirror, sublicense, or provide this package to any unauthorized person. By accessing, installing, copying, or using the package, you agree to the license terms.
8
+
5
9
  > NOTE: Payments from EVM chains require prior ERC-20 approval on the source token for the Permit2 contract (0x000000000022D473030F116dDEE9F6B43aC78BA3 on virtually all EVM chains).
6
10
  >
7
- > (And the same applies for Tron/TRC-20, with a Permit2 address of TJhMXTHQHeQyMD7TcKQFqAePNgG4b31H9m on mainnet or TPUqJPASUn1zLvbLBgRZ5pBYrx7WSe5ahp on Shasta)`)
11
+ > (And the same applies for Tron/TRC-20, with a Permit2 address of TJhMXTHQHeQyMD7TcKQFqAePNgG4b31H9m on mainnet or TPUqJPASUn1zLvbLBgRZ5pBYrx7WSe5ahp on Shasta).
8
12
 
9
13
  ## Installation
10
14
 
11
15
  `@atumlabs/payment-gateway-client` is a private npm package. Once your npm account
12
- has been granted access, install it with npm:
16
+ has been granted access, install it with npm.
17
+
18
+ To use the SDK in a project:
13
19
 
14
20
  ```bash
15
21
  npm install @atumlabs/payment-gateway-client
16
22
  ```
17
23
 
18
- This also installs the `send-payment`, `request-quote`, `quote-status`, and
19
- `payment-status` CLI commands.
24
+ To use the command-line tools, install globally so they are on your `PATH`:
25
+
26
+ ```bash
27
+ npm install -g @atumlabs/payment-gateway-client
28
+ ```
29
+
30
+ A project-local install also provides the commands, under `node_modules/.bin`, so
31
+ `npx send-payment ...` works without a global install.
20
32
 
21
33
  ## CLI Usage
22
34
 
23
- After linking, the `send-payment` command is available globally:
35
+ The package provides four commands: `send-payment`, `request-quote`,
36
+ `quote-status`, and `payment-status`. After a global install:
24
37
 
25
38
  ```bash
26
39
  send-payment [options] <sender> <amount> <sourceAsset> <receiver> <destinationAsset>
@@ -38,10 +51,39 @@ send-payment [options] <sender> <amount> <sourceAsset> <receiver> <destinationAs
38
51
 
39
52
  - `-V, --version` - Output the version number
40
53
  - `--gateway <url>` - Payment gateway URL (default: testnet)
41
- - `--request-id <id>` - Custom request ID (overrides prefix)
42
- - `--request-id-prefix <prefix>` - Custom request ID prefix (default: `pmt-gw-cli`)
54
+ - `--request-id <id>` - Idempotency key; seeds the on-chain deposit nonce. Re-run with the same id to re-attempt one payment. Omitted, an id is generated and printed, which makes every bare re-run a separate payment (see [Idempotency and retries](#idempotency-and-retries))
55
+ - `--request-id-prefix <prefix>` - Prefix for the auto-generated request id (default: `pmt-gw-cli`)
56
+ - `--wait` - After submitting, keep checking until the payment settles or fails instead of reporting it as still pending
57
+ - `--wait-seconds <n>` - How long `--wait` keeps checking (default: `60`). A convenience bound only; giving up does not affect the payment
43
58
  - `-h, --help` - Display help
44
59
 
60
+ ### Exit codes
61
+
62
+ `send-payment` reports the payment's ending, so `send-payment ... && <next step>`
63
+ runs the next step only for a settled payment:
64
+
65
+ | Code | Meaning |
66
+ | ---- | --------------------------------------------------------------------------- |
67
+ | `0` | `completed` — settled |
68
+ | `2` | `pending` — accepted, no result yet |
69
+ | `3` | `failed` / `cancelled` — terminal; a fresh attempt needs a new `--request-id` |
70
+ | `1` | no outcome obtained (rejected, gateway unreachable, bad arguments) |
71
+
72
+ On `2`, follow the payment with **`payment-status <payment_id>`** — the id is in the
73
+ output — or use `--wait` to have `send-payment` do it for you. Re-running
74
+ `send-payment` with the same `--request-id` also returns the current state, but it
75
+ rebuilds and re-sends the payment to do so, which is more work than a status
76
+ lookup.
77
+
78
+ `1` is the case that reverses that order. It means the outcome is **unknown**, not
79
+ that nothing happened: a request that timed out may still have been accepted, and
80
+ you may not have a `payment_id` to look up. Re-run with the same `--request-id`,
81
+ which resolves to the payment if one was created and creates it if not. The id is
82
+ printed on failure for exactly this.
83
+
84
+ `payment-status` is a lookup, so its exit code reports whether the lookup worked
85
+ (`0`) or not (`1`), never the payment's outcome — read `status` for that.
86
+
45
87
  ### CAIP-19 Asset Format Examples
46
88
 
47
89
  - **EVM ERC20**: `eip155:42161/erc20:0xaf88d065e77c8cC2239327C5EDb3A432268e5831`
@@ -75,19 +117,32 @@ export TURNKEY_API_PRIVATE_KEY=...
75
117
  send-payment --provider turnkey <args...>
76
118
  ```
77
119
 
78
- Turnkey signing uses the shared `@atum-labs/turnkey-signer` (and `@atum-labs/km-core`),
79
- which live in this monorepo under `key-management/` and are consumed as pnpm workspace
80
- packages (`workspace:*`), built locally by the `build:deps` step. `@solana/web3.js` and
81
- the other Turnkey deps are already declared, so no extra setup is required.
120
+ Everything Turnkey signing needs is already declared as a dependency, so no extra
121
+ setup is required. The Turnkey code is loaded on demand, so the private-key path
122
+ does not pull in Turnkey or `@solana/web3.js`.
123
+
124
+ ### Configuration file
125
+
126
+ The command-line tools also read an optional `.env` file from the directory you
127
+ run them in, so credentials and settings do not have to be retyped each time:
128
+
129
+ ```
130
+ PRIVATE_KEY=0x...
131
+ GATEWAY_URL=https://payment-gw.production-testnet.atum.xyz
132
+ SOLANA_RPC_URL=
133
+ TURNKEY_ORGANIZATION_ID=
134
+ TURNKEY_WALLET_ID=
135
+ TURNKEY_API_PUBLIC_KEY=
136
+ TURNKEY_API_PRIVATE_KEY=
137
+ ```
138
+
139
+ Where a setting has a matching flag, the flag takes precedence, then a value
140
+ already set in your environment, then `.env`, then the built-in default. Keep the
141
+ file out of version control — it holds signing keys.
82
142
 
83
- Notes:
84
- - The shared packages are workspace members, so a root `pnpm install` links them, and
85
- `pnpm run build` and `pnpm test` build them first (`build:deps`).
86
- - The Turnkey code is loaded lazily (`import('./turnkey-impl')`), so the private-key
87
- path never pulls in Turnkey or `@solana/web3.js`.
88
- - For publishing, the build (tsup) bundles the shared `@atum-labs/*` packages directly
89
- into `dist`, so the published package is self-contained with no `@atum-labs/*` runtime
90
- dependencies. The siblings stay `workspace:*` devDependencies used only at build time.
143
+ This applies to the command-line tools only. The SDK does not read `.env`; pass
144
+ configuration as arguments, or set `GATEWAY_URL` in your own application's
145
+ environment.
91
146
 
92
147
  ### Output
93
148
 
@@ -105,12 +160,10 @@ PRIVATE_KEY=0xabc... send-payment \
105
160
  tron:shasta/trc20:TG3XXyExBkPp9nzdajDZsozEu4BkaSJozs
106
161
  ```
107
162
 
108
- ### Unlinking
109
-
110
- To remove the global CLI command:
163
+ ### Removing the command-line tools
111
164
 
112
165
  ```bash
113
- npm unlink -g @atumlabs/payment-gateway-client
166
+ npm uninstall -g @atumlabs/payment-gateway-client
114
167
  ```
115
168
 
116
169
  ## SDK Usage
@@ -118,18 +171,31 @@ npm unlink -g @atumlabs/payment-gateway-client
118
171
  ### Basic Setup
119
172
 
120
173
  ```typescript
121
- import {
174
+ import {
122
175
  PaymentGatewayClient,
176
+ createSenderSigner,
177
+ signPaymentRequest,
178
+ getErrorResponse,
123
179
  type PaymentResponse,
124
- ApiError
180
+ ApiError,
125
181
  } from '@atumlabs/payment-gateway-client';
126
- import { ethers } from 'ethers';
127
182
 
128
183
  const client = new PaymentGatewayClient({
129
- BASE: 'https://payment-gateway.atumlabs.xyz' // or your gateway URL
184
+ BASE: 'https://payment-gw.production-testnet.atum.xyz' // or your gateway URL
130
185
  });
131
186
  ```
132
187
 
188
+ ### Gateway URL
189
+
190
+ The client uses the production-testnet gateway by default. To target another
191
+ environment, in order of precedence:
192
+
193
+ 1. Pass it explicitly — SDK `new PaymentGatewayClient({ BASE })`, or the CLI `--gateway` flag.
194
+ 2. Set the `GATEWAY_URL` environment variable.
195
+ 3. Otherwise the built-in default `https://payment-gw.production-testnet.atum.xyz` is used.
196
+
197
+ Mainnet access is granted by Atum; use the mainnet gateway URL provided to you.
198
+
133
199
  ### Example: Tron to Celo Sepolia Payment
134
200
 
135
201
  ```typescript
@@ -149,15 +215,22 @@ async function sendPayment() {
149
215
  fulfillmentAmount: '100000', // 0.10 USDT (6 decimals)
150
216
  sourceAsset: 'tron:shasta/trc20:TG3XXyExBkPp9nzdajDZsozEu4BkaSJozs', // USDT on Tron Shasta
151
217
  destinationAccount: '0xYourEthereumAddressHere',
152
- destinationAsset: 'eip155:11142220/erc20:0x456a3D042C0DbD3db53D5489e98dFb038553B0d0' // USDC on Celo Sepolia
218
+ destinationAsset: 'eip155:11142220/erc20:0x456a3D042C0DbD3db53D5489e98dFb038553B0d0', // USDC on Celo Sepolia
219
+ // Required. Your stable idempotency key — reuse the SAME value on retry so a
220
+ // retry never becomes a second payment (see "Idempotency and retries" below).
221
+ requestId: 'order-4711',
153
222
  });
154
223
 
155
- // Sign the message (Tron uses the same signing as Ethereum, but without the 0x private key prefix)
156
- const wallet = new ethers.Wallet(privateKey.replace(/^0x/, ''));
157
- paymentRequest.sender_auth.signed_messages[0].signature =
158
- wallet.signingKey.sign(
159
- paymentRequest.sender_auth.signed_messages[0].message
160
- ).serialized;
224
+ // Sign the prepared request with the SDK signer helper. It handles the
225
+ // per-chain signing differences internally (EVM typed-data, Tron hash,
226
+ // Solana Ed25519), so you never touch a chain library directly.
227
+ const sourceChainId = 'tron:shasta'; // CAIP-2 chain id, i.e. sourceAsset.split('/')[0]
228
+ const signer = createSenderSigner(sourceChainId, {
229
+ provider: 'raw',
230
+ privateKey,
231
+ pinnedAddress: tronSender,
232
+ });
233
+ await signPaymentRequest(paymentRequest, signer);
161
234
 
162
235
  try {
163
236
  // Submit the payment
@@ -165,16 +238,41 @@ async function sendPayment() {
165
238
  requestBody: paymentRequest
166
239
  }) as PaymentResponse;
167
240
 
168
- console.log('Payment successful!');
241
+ // A 2xx means the payment was accepted, NOT that it settled. Branch on `status`:
242
+ // cross-chain settlement can outrun the gateway's synchronous window, so an accepted
243
+ // payment may still have no result yet. See "Idempotency and retries" below.
169
244
  console.log('Payment ID:', response.payment_id);
170
- console.log('Source TX:', response.fulfillment_confirmation?.source_tx_hash);
171
- console.log('Destination TX:', response.fulfillment_confirmation?.destination_tx_hash);
172
-
245
+ if (response.idempotent_replay) {
246
+ // This request_id had been used before, so this is the payment your earlier attempt
247
+ // created. Nothing additional was charged.
248
+ console.log('Handed back an existing payment');
249
+ }
250
+
251
+ switch (response.status) {
252
+ case 'completed':
253
+ console.log('Settled.');
254
+ console.log('Source TX:', response.fulfillment_confirmation?.source_tx_hash);
255
+ console.log('Destination TX:', response.fulfillment_confirmation?.destination_tx_hash);
256
+ break;
257
+ case 'failed':
258
+ case 'cancelled':
259
+ // Terminal, and this request_id stays bound to it. A fresh attempt needs a NEW id.
260
+ console.error('Failed:', response.error?.code, response.error?.message);
261
+ break;
262
+ default:
263
+ // Still settling. Do not release goods, and do not pay again under a new id.
264
+ // Follow it with getPaymentStatus(payment_id); re-submitting this same request
265
+ // also returns the current state, but it re-sends the payment to do so.
266
+ console.log('Still settling — follow it with getPaymentStatus.');
267
+ }
268
+
173
269
  return response;
174
270
  } catch (error) {
175
- if (error instanceof ApiError) {
176
- console.error('API Error:', error.status, error.statusText);
177
- console.error('Response:', error.body);
271
+ const gwError = getErrorResponse(error);
272
+ if (gwError) {
273
+ console.error('Gateway error:', gwError.code, gwError.message, gwError.request_id);
274
+ } else if (error instanceof ApiError) {
275
+ console.error('HTTP', error.status, error.statusText, error.url);
178
276
  }
179
277
  throw error;
180
278
  }
@@ -183,19 +281,53 @@ async function sendPayment() {
183
281
 
184
282
  ### Error Handling
185
283
 
186
- The SDK provides detailed error information through the `ApiError` type:
284
+ The gateway returns a typed, public error body — `ErrorResponse`
285
+ (`{ code, message, request_id?, docs_url?, domain? }`). Use `getErrorResponse`
286
+ to narrow a caught error to it; it returns `undefined` for anything that is not
287
+ a gateway error with that shape. For transport-level detail (HTTP status, URL),
288
+ the thrown value is still an `ApiError`.
187
289
 
188
290
  ```typescript
189
- catch (error) {
190
- if (error instanceof ApiError) {
191
- console.error('Status:', error.status);
192
- console.error('Status Text:', error.statusText);
193
- console.error('URL:', error.url);
194
- console.error('Response Body:', error.body);
291
+ import { getErrorResponse, ApiError } from '@atumlabs/payment-gateway-client';
292
+
293
+ try {
294
+ await client.payments.submitPayment({ requestBody: paymentRequest });
295
+ } catch (error) {
296
+ const gwError = getErrorResponse(error);
297
+ if (gwError) {
298
+ console.error('Code:', gwError.code); // machine-readable category
299
+ console.error('Message:', gwError.message); // safe, human-readable text
300
+ console.error('Request ID:', gwError.request_id); // quote this to support
301
+ console.error('Docs:', gwError.docs_url);
302
+ } else if (error instanceof ApiError) {
303
+ // Transport-level failure with no typed body (e.g. a proxy/gateway error).
304
+ console.error('HTTP', error.status, error.statusText, error.url);
305
+ } else {
306
+ throw error;
195
307
  }
196
308
  }
197
309
  ```
198
310
 
311
+ A thrown error does **not** mean no payment was created — a request that timed out
312
+ may still have been accepted. Treat it as unknown rather than as a failure:
313
+
314
+ - If you already hold the `payment_id` from an earlier attempt, read
315
+ `getPaymentStatus` — the cheapest way to find out what happened.
316
+ - If you do not, re-submit the same `request_id`. It resolves to the payment if one
317
+ was created and creates it if not, which is the only recovery that works without
318
+ an id.
319
+
320
+ One code is worth handling by name:
321
+
322
+ ```typescript
323
+ const gwError = getErrorResponse(error);
324
+ if (gwError?.code === 'IDEMPOTENCY_TERMS_MISMATCH') {
325
+ // 409 — this request_id already identifies a payment with different economics.
326
+ // Re-submitting cannot amend that payment; a genuinely different one needs a new
327
+ // request_id. See "Idempotency and retries".
328
+ }
329
+ ```
330
+
199
331
  ### Asset Identifiers
200
332
 
201
333
  Assets are identified using the [CAIP-19](https://chainagnostic.org/CAIPs/caip-19) format:
@@ -203,13 +335,37 @@ Assets are identified using the [CAIP-19](https://chainagnostic.org/CAIPs/caip-1
203
335
  - Tron example: `tron:shasta/trc20:TG3XXyExBkPp9nzdajDZsozEu4BkaSJozs`
204
336
  - EVM example: `eip155:11142220/erc20:0x456a3D042C0DbD3db53D5489e98dFb038553B0d0`
205
337
 
206
- ## Package Structure
207
-
208
- - `src/` - Source TypeScript files
209
- - `generated/` - Auto-generated API client from OpenAPI spec
210
- - `cli/` - CLI implementation
211
- - `dist/` - Pre-compiled JavaScript (ready to use)
212
-
213
- ---
214
-
215
- This repository contains proprietary Atum Labs code. Access is restricted and governed by the Private Repository License included in this repo.
338
+ ## Idempotency and retries
339
+
340
+ Every payment carries a `request_id`. Pass a stable value and reuse the exact
341
+ same one on every retry (a timeout, a network blip, a restart) and you end up
342
+ with one payment, not two — and you get the same answer each time.
343
+
344
+ - Using this SDK, `request_id` is **required** on the payment path
345
+ (`preparePaymentRequest`), and it is the only thing you manage: the SDK derives
346
+ the on-chain deposit nonce deterministically from it, so retries line up on
347
+ their own. Reuse the same `request_id` to retry safely.
348
+ - On the CLI, `--request-id` is optional; when omitted an id is generated and
349
+ printed, so you can re-run with `--request-id <id>` to re-attempt the same
350
+ payment. A bare re-run, with no `--request-id`, is a **separate** payment.
351
+ - A genuinely new payment needs a **new** `request_id`. Reusing one with different
352
+ economics (a corrected amount, a different token) is rejected with `409`
353
+ `IDEMPOTENCY_TERMS_MISMATCH` — one id identifies one payment.
354
+ - Deadlines and the signature are exempt, so a re-submission of the original
355
+ bytes is fine even though its deadlines have expired by then.
356
+ - `idempotent_replay` on the response tells you whether you were handed a payment
357
+ that already existed.
358
+ - `pending` is not success. It means accepted and still settling: re-submit the
359
+ same `request_id` to collect the result. A `failed` payment is terminal and
360
+ keeps its id, so recovering from one needs a new id.
361
+
362
+ See [docs/idempotency.md](docs/idempotency.md) for the full model: the two
363
+ guarantees, when the `409` applies, how to read the outcome, and the per-chain
364
+ differences (EVM/Tron vs Solana).
365
+
366
+ ## What is in the package
367
+
368
+ - `dist/` — compiled JavaScript and TypeScript declarations, ready to use
369
+ - `dist/cli/` — the four command-line tools
370
+ - `docs/` — reference documentation, including [idempotency and retries](docs/idempotency.md)
371
+ - `LICENSE`, `THIRD-PARTY-NOTICES.txt` — license terms and third-party attribution
@@ -0,0 +1,45 @@
1
+ THIRD-PARTY SOFTWARE NOTICES AND INFORMATION
2
+ ==============================================================================
3
+ @atumlabs/payment-gateway-client
4
+
5
+ The published artifact of this package (the compiled code under dist/) statically
6
+ bundles the third-party open-source components listed below; their source is
7
+ redistributed within this package, so their license and copyright notices are
8
+ reproduced here. Components and versions were taken from the exact modules the
9
+ build inlined. This file does NOT cover packages the consumer installs separately
10
+ as dependencies of this package (for example @solana/web3.js, axios, ethers, bs58,
11
+ commander, dotenv, tweetnacl, and zod) — those ship under their own licenses and are not
12
+ redistributed here.
13
+
14
+ LICENSE SUMMARY (1 component)
15
+ ------------------------------------------------------------------------------
16
+ 1 MIT
17
+ ==============================================================================
18
+
19
+ ------------------------------------------------------------------------------
20
+ @noble/hashes 1.8.0 (MIT)
21
+ Author: Paul Miller (https://paulmillr.com)
22
+ ------------------------------------------------------------------------------
23
+
24
+ The MIT License (MIT)
25
+
26
+ Copyright (c) 2022 Paul Miller (https://paulmillr.com)
27
+
28
+ Permission is hereby granted, free of charge, to any person obtaining a copy
29
+ of this software and associated documentation files (the “Software”), to deal
30
+ in the Software without restriction, including without limitation the rights
31
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
32
+ copies of the Software, and to permit persons to whom the Software is
33
+ furnished to do so, subject to the following conditions:
34
+
35
+ The above copyright notice and this permission notice shall be included in
36
+ all copies or substantial portions of the Software.
37
+
38
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
39
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
40
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
41
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
42
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
43
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
44
+ THE SOFTWARE.
45
+