@atumlabs/payment-gateway-client 3.0.1 → 4.0.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.
@@ -129,8 +129,7 @@ altogether is from a gateway predating it.
129
129
 
130
130
  ## Reading the outcome (payments)
131
131
 
132
- `status` is on every payment response, and the three endings mean different things for a
133
- retry:
132
+ `status` is on every payment response, and its endings mean different things for a retry:
134
133
 
135
134
  | `status` | What it means | What to do |
136
135
  | ---------------------- | --------------------------------- | ------------------------------------------------------ |
@@ -138,13 +137,16 @@ retry:
138
137
  | `pending` | Accepted, still settling. | Re-submit the **same** `request_id`, or read the status endpoint. Do not start a new payment. |
139
138
  | `finalizing` | Paid on chain, collecting confirmations. Not final. | As `pending`. Do not treat it as delivered. |
140
139
  | `failed` | Terminal — it can never complete. | A fresh attempt needs a **new** `request_id`. |
140
+ | `refunding` | Not delivered, the escrowed funds are being returned. Not final. | As `pending`. |
141
+ | `refund_available` | Not delivered, funds still in escrow. The status endpoint returns `refund_signature`. Not final. | Submit `refund_signature` to the source escrow to return the funds. Do not start a new payment until it is `refunded`: a late delivery can still complete it. |
142
+ | `refunded` | Terminal, not delivered. The funds were returned. | A fresh attempt needs a **new** `request_id`. |
141
143
 
142
144
  `pending` is not success. Cross-chain settlement can outrun the gateway's
143
145
  synchronous window, so a payment that has been accepted may still have no result
144
146
  when you get your response.
145
147
 
146
- A terminal failure **keeps** its `request_id`. Re-submitting that id returns the
147
- same failed payment for good, which is why recovering from one means minting a
148
+ A terminal payment **keeps** its `request_id`. Re-submitting that id returns the
149
+ same payment for good, which is why recovering from one means minting a
148
150
  new id — the opposite of what a pending payment needs. Getting these two the
149
151
  wrong way round is how a caller either pays twice or waits for something that
150
152
  will never happen.
package/docs/signing.md CHANGED
@@ -15,10 +15,9 @@ unidentified originator. The reverse identifies an originator who has authorized
15
15
  integrations need the first two; the third applies once the originator holds credentials about
16
16
  itself.
17
17
 
18
- > The two names are confusingly close, and that is not an accident of this client: the upstream
19
- > library calls its document signer `signPaymentRequest` too. This package exports it as
20
- > `signDeclaration` so the two cannot be confused for one another, matching `SignDeclaration`
21
- > in the Go client.
18
+ > The underlying declaration library also names its document signer `signPaymentRequest`. This
19
+ > package exports it as `signDeclaration`, matching `SignDeclaration` in the Go client, so the two
20
+ > cannot be confused.
22
21
 
23
22
  ## Keys never reach this package
24
23
 
@@ -83,18 +82,13 @@ await new PaymentGatewayClient({ BASE: gateway }).payments.submitPayment({ reque
83
82
 
84
83
  ## Check what you are signing
85
84
 
86
- `signPaymentRequest` signs **every** entry in `sender_auth.signed_messages[]` as given. It does
87
- not check that the terms inside those messages are the ones you agreed to, and neither does the
88
- Go client — the same is true of building the request in the first place, because the permit's
89
- spender, escrow and amounts come from the gateway's chain defaults.
85
+ `signPaymentRequest` signs **every** entry in `sender_auth.signed_messages[]` as given, in both
86
+ the TypeScript and Go clients. The permit's spender, escrow and amounts come from the gateway's
87
+ chain defaults, and the signer has no notion of what you intended.
90
88
 
91
- That is a deliberate division of labour, not an oversight: the SDK holds no notion of what you
92
- intended. But it means a gateway that returned hostile defaults would have your wallet sign a
93
- permit naming an address you never chose, and nothing in the signing path would object.
94
-
95
- If you are signing on behalf of someone else's funds, bind the signed permit to the terms **you**
96
- agreed before the request leaves your process. `verifyPaymentRequest` recovers every
97
- `signed_messages[]` entry and checks the terms it actually commits to:
89
+ Bind the signed permit to the terms **you** agreed before the request leaves your process.
90
+ `verifyPaymentRequest` recovers every `signed_messages[]` entry and checks the terms it commits
91
+ to:
98
92
 
99
93
  ```ts
100
94
  import { verifyPaymentRequest } from '@atum-labs/payment-gateway-client/signing';
@@ -107,22 +101,40 @@ const { recoveredAccount } = verifyPaymentRequest(signed, {
107
101
  });
108
102
  ```
109
103
 
110
- `sourceAsset` is the **bare token address**, not a CAIP-19 identifier — it differs in shape from
111
- the request's own `source.asset_identifier`, which is full CAIP-19. Passing the CAIP form here
112
- fails to match and rejects a request that was in fact correct.
104
+ `sourceAsset` is the **bare token address**, not the CAIP-19 identifier used in the request's
105
+ `source.asset_identifier`. Passing the CAIP form fails to match.
106
+
107
+ **Pass those three fields.** Without them, `verifyPaymentRequest` takes the token, network and
108
+ amount from the request itself, which proves only that the signatures are consistent with the
109
+ request's own fields.
110
+
111
+ It always checks the payee: the deposit must commit to the request's own `destination` and
112
+ `request_id`, so a request whose payee or request id was changed after signing is refused.
113
113
 
114
- **Pass those three fields.** Called bare, `verifyPaymentRequest` takes the token, network and
115
- amount *from the request itself*, so it proves only that the signatures are self-consistent with
116
- the request's own stated fields — which a hostile gateway controls. It is the `sourceAsset` /
117
- `network` / `maxSourceAmount` arguments that turn it into a check against your terms, and
118
- omitting them is the difference between a real check and a reassuring one.
114
+ If you hold the request as JSON text, call `verifyPaymentRequestJSON(json, expected)` instead. It
115
+ also refuses a repeated member name, including names that differ only in letter case, where it is
116
+ read: any top-level name, or inside `source`, `destination`, `request_id` or `sender_auth`.
117
+ On a refusal, `commitmentResultOf(error)` returns the outcome (for example
118
+ `destination_mismatch`) and both copies of the payee, ready to log. A verified entry carries the
119
+ same result as `results[i].commitment`.
119
120
 
120
- It is strict by design: every entry must be a bindable permit, and a malformed one throws rather
121
- than being skipped. A caller with deliberately mixed messages — say an EVM setup approval
122
- alongside the permit — must compose the per-entry adapter calls itself and own the skip policy.
121
+ **Contract-account payers.** `verifyPaymentRequest` recovers each signer, so it refuses a payer
122
+ that is a contract account (ERC-1271, such as a Safe): its signature comes from an owner key, never
123
+ from the account itself. For such a payer, call `verifyRequestCommitment(json)`. It checks the
124
+ payee and request id the deposit commits to without recovering the signer; Permit2 checks the
125
+ signature on chain when the deposit is made.
123
126
 
124
- In Go, `senderauth.GetChainAdapter(caip2).RecoverAndVerify(signed, source, expected)` returns the
125
- same signed terms for the same comparison.
127
+ Every entry must be a bindable permit; a malformed entry throws. A caller with mixed messages
128
+ (for example an EVM setup approval alongside the permit) composes the per-entry adapter calls
129
+ itself.
130
+
131
+ In Go, `senderauth.VerifyRequestCommitment(raw, solanaDomain)` is the payee check alone,
132
+ `senderauth.VerifyPaymentRequestJSON(raw, expected)` and
133
+ `senderauth.VerifyPaymentRequest(request, expected)` run the same checks,
134
+ `senderauth.CommitmentResultOf(err)` reads the outcome from an error, and
135
+ `senderauth.GetChainAdapter(caip2).RecoverAndVerify(signed, source, commitment, expected)` checks
136
+ one signed message against a `CommitmentExpectation` built from the request's `destination` and
137
+ `request_id`.
126
138
 
127
139
  A custodial signer is the natural place to enforce this: it is the component that can refuse.
128
140
 
@@ -170,21 +182,13 @@ loader in place of its built-in fetcher. A document whose `@context` names an un
170
182
  refused rather than resolved, since an attacker-served context could otherwise change what each
171
183
  term means, and therefore what the signature covers.
172
184
 
173
- One precision, because this page is about what your signatures cover: the embedded copies can be
174
- overridden by an environment variable intended for development against a modified snapshot. It
175
- reads from local disk, never the network, and nothing in normal operation sets it — but if it is
176
- set, the library now says so on first use rather than substituting the contexts silently.
177
-
178
- This is structural: there is no HTTP client in the signing libraries, and the JSON-LD processor
179
- is always handed the local loader in place of its built-in fetcher.
185
+ For development against a modified snapshot, an environment variable can override the embedded
186
+ copies. It reads from local disk only, and the library reports it on first use when it is set.
180
187
 
181
188
  ## The identity envelope
182
189
 
183
- **Read this before the example: you need an issuer-signed credential about yourself, and
184
- originators do not hold one yet.** The schema requires at least one credential and rejects an
185
- empty bag, so until issuance exists this surface cannot be exercised end to end. It
186
- is documented because the shape is stable and the signing half is done, not because it is
187
- callable today.
190
+ **Prerequisite: at least one issuer-signed credential about the originator.** The schema rejects
191
+ an envelope without one.
188
192
 
189
193
  `buildEnvelope` wraps the originator's issuer-signed credentials in a holder-signed
190
194
  PaymentRequestEnvelope. It takes the same kind of signer as the other surfaces, so a custodial
@@ -207,8 +211,7 @@ const envelope = await buildEnvelopeForAccount('eip155:1', wallet.address.toLowe
207
211
 
208
212
  Three things the verifier enforces, so they are not optional:
209
213
 
210
- - **At least one credential.** The schema requires it and an empty bag is rejected — see the
211
- prerequisite above.
214
+ - **At least one credential.** An empty bag is rejected.
212
215
  - **The challenge binds one request** and **the domain binds one environment**. Both are checked
213
216
  against the values the gateway expects, so an envelope cannot be replayed across either.
214
217
  - **The declaration type is bound into the signature**, not merely present in the document —
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "@atumlabs/payment-gateway-client",
3
- "version": "3.0.1",
3
+ "version": "4.0.0",
4
4
  "description": "TypeScript client for Atum Payment Gateway API",
5
5
  "author": "Atum Labs, Inc.",
6
- "homepage": "https://github.com/Atum-Labs/protocol/tree/main/payment-gateway-client#readme",
6
+ "homepage": "https://github.com/Atum-Labs/protocol/tree/main/schemas/apis/payment-gateway/v1/bindings/payment-gateway-client#readme",
7
7
  "repository": {
8
8
  "type": "git",
9
9
  "url": "git+https://github.com/Atum-Labs/protocol.git",
10
- "directory": "payment-gateway-client"
10
+ "directory": "schemas/apis/payment-gateway/v1/bindings/payment-gateway-client"
11
11
  },
12
12
  "bugs": {
13
13
  "url": "https://github.com/Atum-Labs/protocol/issues"
@@ -50,9 +50,9 @@
50
50
  "approve-permit2": "./dist/cli/approve-permit2.js"
51
51
  },
52
52
  "scripts": {
53
- "generate": "openapi-ts --input ../schemas/apis/payment-gateway/v1/openapi.yaml --output \"${PGC_GEN_OUT:-./src/generated}\" --client axios --name PaymentGatewayClient --useOptions",
53
+ "generate": "openapi-ts --input ../../openapi.yaml --output \"${PGC_GEN_OUT:-./src/generated}\" --client axios --name PaymentGatewayClient --useOptions",
54
54
  "lint": "eslint . --max-warnings 0",
55
- "prebuild:deps": "node -e \"if(!require('fs').existsSync('../pnpm-workspace.yaml')){console.error('protocol workspace not found at ../pnpm-workspace.yaml. Is this checked out inside the protocol monorepo?');process.exit(1)}\"",
55
+ "prebuild:deps": "node -e \"if(!require('fs').existsSync('../../../../../../pnpm-workspace.yaml')){console.error('protocol workspace not found at ../../../../../../pnpm-workspace.yaml. Is this checked out inside the protocol monorepo?');process.exit(1)}\"",
56
56
  "build:deps": "pnpm --filter \"@atum-labs/payment-gateway-client^...\" run build",
57
57
  "build": "tsup && chmod +x dist/cli/*.js && node scripts/stamp-entry-headers.mjs",
58
58
  "typecheck:tests": "tsc -p tsconfig.test.json",
@@ -73,6 +73,7 @@
73
73
  "dotenv": "^16.6.1",
74
74
  "ethers": "^6.11.0",
75
75
  "jsonld": "8.3.3",
76
+ "rdf-canonize": "3.4.0",
76
77
  "tweetnacl": "^1.0.3",
77
78
  "zod": "^3.25.0"
78
79
  },
@@ -85,6 +86,7 @@
85
86
  "@atum-labs/payment-request-sender-auth": "workspace:*",
86
87
  "@atum-labs/payment-request-token-approval": "workspace:*",
87
88
  "@atum-labs/publish-guard": "workspace:*",
89
+ "@atum-labs/schema-declarations": "workspace:*",
88
90
  "@atum-labs/solana-escrow-encoding": "workspace:*",
89
91
  "@atum-labs/turnkey-signer": "workspace:*",
90
92
  "@atum-labs/tvm-escrow-encoding": "workspace:*",
@@ -98,7 +100,7 @@
98
100
  "ts-jest": "^29.1.1",
99
101
  "tsup": "^8.5.1",
100
102
  "typescript": "^5.0.0",
101
- "uuid": "^9.0.0"
103
+ "uuid": "^11.1.1"
102
104
  },
103
105
  "files": [
104
106
  "dist/**/*",