@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 +49 -0
- package/README.md +214 -58
- package/THIRD-PARTY-NOTICES.txt +45 -0
- package/dist/cli/payment-status.js +157 -46
- package/dist/cli/quote-status.js +117 -45
- package/dist/cli/request-quote.js +118 -46
- package/dist/cli/send-payment.js +332 -94
- package/dist/index.d.ts +180 -29
- package/dist/index.js +268 -71
- package/docs/idempotency.md +142 -0
- package/package.json +14 -4
- /package/{LICENSE.md → LICENSE} +0 -0
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
|
-
|
|
19
|
-
|
|
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
|
-
|
|
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>` -
|
|
42
|
-
- `--request-id-prefix <prefix>` -
|
|
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
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
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
|
-
|
|
84
|
-
|
|
85
|
-
|
|
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
|
-
###
|
|
109
|
-
|
|
110
|
-
To remove the global CLI command:
|
|
163
|
+
### Removing the command-line tools
|
|
111
164
|
|
|
112
165
|
```bash
|
|
113
|
-
npm
|
|
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-
|
|
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
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
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
|
-
|
|
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
|
-
|
|
171
|
-
|
|
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
|
-
|
|
176
|
-
|
|
177
|
-
console.error('
|
|
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
|
|
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
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
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
|
-
##
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
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
|
+
|