@atumlabs/mppx-atum-escrow 0.2.2 → 0.4.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 +294 -53
- package/THIRD-PARTY-NOTICES.txt +7 -0
- package/dist/{chunk-IPZJXELQ.js → chunk-KRSFEITH.js} +3594 -469
- package/dist/{chunk-4P34CLTO.js → chunk-OEEC5P3E.js} +1323 -252
- package/dist/{chunk-NEM3HEZW.js → chunk-TPLD2L6B.js} +141 -13
- package/dist/client.d.ts +192 -52
- package/dist/client.js +11 -4
- package/dist/index.d.ts +3 -77
- package/dist/index.js +26 -5
- package/dist/{internal-DzEGXm14.d.ts → internal-CJEu9yUF.d.ts} +131 -24
- package/dist/server.d.ts +255 -50
- package/dist/server.js +18 -2
- package/package.json +10 -8
package/dist/index.js
CHANGED
|
@@ -6,31 +6,52 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { createRequire as __atumCreateRequire } from 'module'; const require = __atumCreateRequire(import.meta.url);
|
|
8
8
|
import {
|
|
9
|
-
|
|
9
|
+
import_payment_request_token_approval,
|
|
10
10
|
registerClient
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-KRSFEITH.js";
|
|
12
12
|
import {
|
|
13
|
+
PaymentRejectedError,
|
|
14
|
+
SettlementFailedError,
|
|
15
|
+
SettlementPendingError,
|
|
16
|
+
buildChargeChallenge,
|
|
13
17
|
buildChargeRequest,
|
|
14
18
|
corridorFromDefaults,
|
|
19
|
+
isPaymentRejected,
|
|
20
|
+
isSettlementFailed,
|
|
21
|
+
isSettlementPending,
|
|
15
22
|
registerServer,
|
|
16
23
|
validateCorridor
|
|
17
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-TPLD2L6B.js";
|
|
18
25
|
import {
|
|
19
26
|
ChargeRequestSchema,
|
|
20
27
|
CredentialPayloadSchema,
|
|
21
28
|
INTENT,
|
|
29
|
+
INTENT_ID_META_KEY,
|
|
22
30
|
METHOD_NAME,
|
|
23
31
|
atumEscrowChargeMethod
|
|
24
|
-
} from "./chunk-
|
|
32
|
+
} from "./chunk-OEEC5P3E.js";
|
|
33
|
+
var export_ensureSourceApproval = import_payment_request_token_approval.ensureSourceApproval;
|
|
34
|
+
var export_isUnconfirmed = import_payment_request_token_approval.isUnconfirmed;
|
|
35
|
+
var export_needsSourceApproval = import_payment_request_token_approval.needsSourceApproval;
|
|
25
36
|
export {
|
|
26
37
|
ChargeRequestSchema,
|
|
27
38
|
CredentialPayloadSchema,
|
|
28
39
|
INTENT,
|
|
40
|
+
INTENT_ID_META_KEY,
|
|
29
41
|
METHOD_NAME,
|
|
42
|
+
PaymentRejectedError,
|
|
43
|
+
SettlementFailedError,
|
|
44
|
+
SettlementPendingError,
|
|
30
45
|
atumEscrowChargeMethod,
|
|
46
|
+
buildChargeChallenge,
|
|
31
47
|
buildChargeRequest,
|
|
32
48
|
corridorFromDefaults,
|
|
33
|
-
ensureSourceApproval,
|
|
49
|
+
export_ensureSourceApproval as ensureSourceApproval,
|
|
50
|
+
isPaymentRejected,
|
|
51
|
+
isSettlementFailed,
|
|
52
|
+
isSettlementPending,
|
|
53
|
+
export_isUnconfirmed as isUnconfirmed,
|
|
54
|
+
export_needsSourceApproval as needsSourceApproval,
|
|
34
55
|
registerClient,
|
|
35
56
|
registerServer,
|
|
36
57
|
validateCorridor
|
|
@@ -3,35 +3,34 @@ import { PaymentRequest } from './generated/index.js';
|
|
|
3
3
|
import { Challenge, Credential } from 'mppx';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
* A message that has been cryptographically signed to prove authorization.
|
|
7
|
-
* The signature proves you control the wallet that's sending funds.
|
|
8
|
-
*
|
|
6
|
+
* A message that has been cryptographically signed to prove authorization. The signature proves you control the wallet that's sending funds.
|
|
9
7
|
*/
|
|
10
8
|
type SignedMessage = {
|
|
11
9
|
/**
|
|
12
10
|
* The data that was signed. Format depends on the blockchain:
|
|
13
11
|
* - EVM: Hex-encoded message hash (with 0x prefix)
|
|
14
|
-
* - Solana:
|
|
12
|
+
* - Solana: Hex-encoded message hash (with 0x prefix)
|
|
15
13
|
* - Tron: Hex-encoded message hash (possibly without 0x prefix)
|
|
16
|
-
*
|
|
17
14
|
*/
|
|
18
15
|
message: string;
|
|
19
16
|
/**
|
|
20
|
-
* Optional: If 'message' contains a hash, this field contains the original data
|
|
21
|
-
* before it was hashed. Useful for verification and debugging.
|
|
22
|
-
*
|
|
17
|
+
* Optional: If 'message' contains a hash, this field contains the original data before it was hashed. Useful for verification and debugging.
|
|
23
18
|
*/
|
|
24
19
|
message_prehash?: string;
|
|
25
20
|
/**
|
|
26
|
-
* The cryptographic signature proving you authorized this message.
|
|
27
|
-
* Format varies by blockchain (hex for EVM/Tron, base58 for Solana).
|
|
28
|
-
*
|
|
21
|
+
* The cryptographic signature proving you authorized this message. Hex-encoded with a 0x prefix on every supported chain. The signer's own public key is base58 on Solana, but the signature itself is not.
|
|
29
22
|
*/
|
|
30
23
|
signature: string;
|
|
24
|
+
/**
|
|
25
|
+
* Optional chain-specific payload containing additional signing context (e.g., delegate_signer for Solana). Preserved through the pipeline for downstream consumers.
|
|
26
|
+
*/
|
|
27
|
+
payload?: {
|
|
28
|
+
[key: string]: unknown;
|
|
29
|
+
};
|
|
31
30
|
};
|
|
32
31
|
|
|
33
32
|
/**
|
|
34
|
-
* Chain defaults returned by the payment gateway /defaults endpoint.
|
|
33
|
+
* Chain defaults returned by the payment gateway /v1/defaults endpoint.
|
|
35
34
|
*/
|
|
36
35
|
interface ChainDefaults {
|
|
37
36
|
escrowContract: string;
|
|
@@ -40,9 +39,22 @@ interface ChainDefaults {
|
|
|
40
39
|
fulfillmentVerifierEndpoint: string;
|
|
41
40
|
fulfillmentProxy: string;
|
|
42
41
|
permit2Contract?: string;
|
|
42
|
+
/**
|
|
43
|
+
* Which deposit witness the escrow deployed on this chain pins, surfaced by payment-gw
|
|
44
|
+
* `/v1/defaults` as `deposit_witness_version` (CORE-2033). EVM/Tron only — Solana carries the
|
|
45
|
+
* same fact in `svmSignatureDomainVersion` below.
|
|
46
|
+
*
|
|
47
|
+
* `4` and above select the CORE-2120 four-member witness
|
|
48
|
+
* (`depositRequestHash`, `destination`, `reserver`, `releaser`); `3` selects the two-member
|
|
49
|
+
* witness a not-yet-redeployed escrow pins. ABSENT means four — see the sender-auth package's
|
|
50
|
+
* `ChainDefaults.depositWitnessVersion` for why absence is the permissive default.
|
|
51
|
+
*
|
|
52
|
+
* Passed through to the sender_auth adapter verbatim; this type only has to carry it.
|
|
53
|
+
*/
|
|
54
|
+
depositWitnessVersion?: number;
|
|
43
55
|
/**
|
|
44
56
|
* V3 Solana escrow domain-separation fields, surfaced by payment-gw
|
|
45
|
-
* `/defaults` for Solana source chains. Used to build the EscrowDomain that the
|
|
57
|
+
* `/v1/defaults` for Solana source chains. Used to build the EscrowDomain that the
|
|
46
58
|
* V3 deposit hash binds to. Absent for EVM/Tron.
|
|
47
59
|
*/
|
|
48
60
|
svmSignatureClusterId?: string;
|
|
@@ -115,7 +127,8 @@ interface TurnkeyConfig {
|
|
|
115
127
|
/**
|
|
116
128
|
* Chain-native address the signer is expected to resolve to. Verified against
|
|
117
129
|
* the address Turnkey returns at initialize(); a mismatch fails fast rather
|
|
118
|
-
* than signing as the wrong depositor.
|
|
130
|
+
* than signing as the wrong depositor. Omit it to skip the check; an empty
|
|
131
|
+
* value is rejected rather than treated as omitted.
|
|
119
132
|
*/
|
|
120
133
|
pinnedAddress?: string;
|
|
121
134
|
/**
|
|
@@ -125,7 +138,10 @@ interface TurnkeyConfig {
|
|
|
125
138
|
logger?: Logger;
|
|
126
139
|
meter?: Meter;
|
|
127
140
|
}
|
|
128
|
-
/**
|
|
141
|
+
/**
|
|
142
|
+
* Options selecting a sender-signing provider. Defaults to the private-key provider.
|
|
143
|
+
* `pinnedAddress` behaves the same on both: omitted skips the sender check, empty is rejected.
|
|
144
|
+
*/
|
|
129
145
|
type SenderSignerOptions = {
|
|
130
146
|
provider?: 'raw';
|
|
131
147
|
privateKey: string;
|
|
@@ -134,6 +150,74 @@ type SenderSignerOptions = {
|
|
|
134
150
|
provider: 'turnkey';
|
|
135
151
|
} & TurnkeyConfig);
|
|
136
152
|
|
|
153
|
+
/**
|
|
154
|
+
* This file was automatically generated by json-schema-to-typescript.
|
|
155
|
+
* DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file,
|
|
156
|
+
* and run json-schema-to-typescript to regenerate this file.
|
|
157
|
+
*/
|
|
158
|
+
/**
|
|
159
|
+
* The `extra.atum` object carried inside an atum-escrow x402 PaymentRequirements entry (accepts[]): the merchant's receive-side plus the contract/role addresses and deadline budgets. Scheme-specific data the standard x402 `extra` bag treats as opaque, so it is owned here and shared by all role mechanisms. Addresses are chain-general: an EVM `0x`-hex address (20 bytes) or a base58-encoded address (Solana/Tron), per the field's CAIP-2 chain.
|
|
160
|
+
*/
|
|
161
|
+
interface AtumEscrowExtra$1 {
|
|
162
|
+
/**
|
|
163
|
+
* Where the merchant receives (CAIP-2 chain, token, address).
|
|
164
|
+
*/
|
|
165
|
+
destination: {
|
|
166
|
+
/**
|
|
167
|
+
* CAIP-2 destination chain id (e.g. eip155:42161).
|
|
168
|
+
*/
|
|
169
|
+
network: string;
|
|
170
|
+
/**
|
|
171
|
+
* Destination token contract address (EVM hex or base58, per the destination chain).
|
|
172
|
+
*/
|
|
173
|
+
asset: string;
|
|
174
|
+
/**
|
|
175
|
+
* Merchant receive address (EVM hex or base58, per the destination chain).
|
|
176
|
+
*/
|
|
177
|
+
address: string;
|
|
178
|
+
};
|
|
179
|
+
/**
|
|
180
|
+
* Exact amount the merchant receives, in atomic token units.
|
|
181
|
+
*/
|
|
182
|
+
fulfillmentAmount: string;
|
|
183
|
+
/**
|
|
184
|
+
* Source-chain escrow contract (the x402 payTo); EVM hex or base58, per the source chain.
|
|
185
|
+
*/
|
|
186
|
+
escrow: string;
|
|
187
|
+
/**
|
|
188
|
+
* Destination-chain fulfillment proxy contract (EVM hex or base58, per the destination chain).
|
|
189
|
+
*/
|
|
190
|
+
fulfillmentProxy: string;
|
|
191
|
+
/**
|
|
192
|
+
* Atum reserver role address (escrow deposit witness); EVM hex or base58, per the source chain.
|
|
193
|
+
*/
|
|
194
|
+
reserver: string;
|
|
195
|
+
/**
|
|
196
|
+
* Atum releaser role address (escrow deposit witness); EVM hex or base58, per the source chain.
|
|
197
|
+
*/
|
|
198
|
+
releaser: string;
|
|
199
|
+
/**
|
|
200
|
+
* Source-chain fulfillment-verifier endpoint the payment request carries. The verifier account and the quote_selector are the releaser and reserver respectively (the network derives the deposit witness roles from them), so only the endpoint is not otherwise present in this object.
|
|
201
|
+
*/
|
|
202
|
+
fulfillmentVerifierEndpoint: string;
|
|
203
|
+
/**
|
|
204
|
+
* Recommended quote-deadline budget in seconds, relative to signing time.
|
|
205
|
+
*/
|
|
206
|
+
quoteDeadlineSeconds: number;
|
|
207
|
+
/**
|
|
208
|
+
* Recommended fulfillment-deadline budget in seconds, relative to signing time.
|
|
209
|
+
*/
|
|
210
|
+
fulfillmentDeadlineSeconds: number;
|
|
211
|
+
/**
|
|
212
|
+
* Solana source only: cluster name used to build the 32-byte cluster_id in the V3 escrow signature domain. Optional; the facilitator requires it for a Solana source.
|
|
213
|
+
*/
|
|
214
|
+
svmSignatureClusterId?: string;
|
|
215
|
+
/**
|
|
216
|
+
* Solana source only: escrow signature-domain version (V3 domain separation). Optional; required for a Solana source.
|
|
217
|
+
*/
|
|
218
|
+
svmSignatureDomainVersion?: number;
|
|
219
|
+
}
|
|
220
|
+
|
|
137
221
|
/**
|
|
138
222
|
* This file was automatically generated by json-schema-to-typescript.
|
|
139
223
|
* DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file,
|
|
@@ -223,10 +307,6 @@ interface AtumEscrowExtra {
|
|
|
223
307
|
* Solana source only: escrow signature-domain version (V3 domain separation). Optional; required for a Solana source.
|
|
224
308
|
*/
|
|
225
309
|
svmSignatureDomainVersion?: number;
|
|
226
|
-
/**
|
|
227
|
-
* Solana source only: merchant-stamped issue time in epoch seconds. The merchant reads the cluster clock so the client makes no RPC and the 402 stays self-contained. Optional; required for a Solana source.
|
|
228
|
-
*/
|
|
229
|
-
issuedAt?: string;
|
|
230
310
|
}
|
|
231
311
|
|
|
232
312
|
/**
|
|
@@ -245,6 +325,24 @@ interface AtumEscrowExtra {
|
|
|
245
325
|
declare const METHOD_NAME = "atum-escrow";
|
|
246
326
|
/** The MPP intent this method implements. */
|
|
247
327
|
declare const INTENT = "charge";
|
|
328
|
+
/**
|
|
329
|
+
* Challenge-metadata key carrying the merchant's per-intent payment identifier.
|
|
330
|
+
*
|
|
331
|
+
* The payer derives the payment's `request_id` from this value, and the gateway
|
|
332
|
+
* de-duplicates on that `request_id`. So the key's lifetime defines what counts as "the same
|
|
333
|
+
* payment": one value per thing being bought, reused on every retry of that purchase, never
|
|
334
|
+
* shared between two purchases.
|
|
335
|
+
*
|
|
336
|
+
* It rides in challenge metadata rather than in the charge request because it identifies the
|
|
337
|
+
* purchase, not its terms — two sequential orders for the same item have identical terms and
|
|
338
|
+
* must still be two payments. Metadata is covered by the challenge-id HMAC, so a payer cannot
|
|
339
|
+
* substitute another intent's identifier to have a second purchase resolve onto an
|
|
340
|
+
* already-settled payment.
|
|
341
|
+
*
|
|
342
|
+
* Prefixed to keep it distinct from a merchant's own metadata and from the framework's
|
|
343
|
+
* reserved keys.
|
|
344
|
+
*/
|
|
345
|
+
declare const INTENT_ID_META_KEY = "atumIntentId";
|
|
248
346
|
/**
|
|
249
347
|
* Schema for the `charge` challenge request — the method-specific data a merchant
|
|
250
348
|
* publishes in an MPP `402` challenge. It describes what the merchant receives and the
|
|
@@ -286,10 +384,19 @@ declare const CredentialPayloadSchema: z.ZodMiniObject<{
|
|
|
286
384
|
}, z.core.$strip>;
|
|
287
385
|
/**
|
|
288
386
|
* The `charge` challenge request — the canonical `AtumEscrowRequest` shape (source option
|
|
289
|
-
* + the destination/corridor `extra`)
|
|
290
|
-
*
|
|
387
|
+
* + the destination/corridor `extra`), plus the Solana issue time this scheme stamps.
|
|
388
|
+
*
|
|
389
|
+
* `issuedAt` is declared here rather than borrowed, because the shared x402 `extra` contract
|
|
390
|
+
* does not carry it: an x402 resource server rebuilds its requirements on the paid request and
|
|
391
|
+
* compares them against what the payer echoed, which no clock reading survives. MPP has no such
|
|
392
|
+
* rebuild, so a merchant-stamped issue time remains coherent here — and is declared where it is
|
|
393
|
+
* emitted ({@link buildChargeRequest}) rather than inherited from a contract that dropped it.
|
|
291
394
|
*/
|
|
292
|
-
type ChargeRequest = AtumEscrowRequest
|
|
395
|
+
type ChargeRequest = Omit<AtumEscrowRequest, 'extra'> & {
|
|
396
|
+
extra: AtumEscrowExtra$1 & {
|
|
397
|
+
issuedAt?: string;
|
|
398
|
+
};
|
|
399
|
+
};
|
|
293
400
|
/** The `charge` credential payload, carrying the signed Atum payment request. */
|
|
294
401
|
interface ChargeCredentialPayload {
|
|
295
402
|
/** The signed Atum payment request: source/destination, amounts, and authorizations. */
|
|
@@ -300,7 +407,7 @@ interface ChargeCredentialPayload {
|
|
|
300
407
|
* (equal to {@link ChargeRequest} by the compile-time guard above, and compatible with
|
|
301
408
|
* the framework's `Record<string, unknown>` request constraint).
|
|
302
409
|
*/
|
|
303
|
-
type AtumEscrowChallenge = Challenge.Challenge<z.infer<typeof ChargeRequestSchema>,
|
|
410
|
+
type AtumEscrowChallenge = Challenge.Challenge<z.infer<typeof ChargeRequestSchema>, 'charge', 'atum-escrow'>;
|
|
304
411
|
/** The MPP credential for this method. */
|
|
305
412
|
type AtumEscrowCredential = Credential.Credential<ChargeCredentialPayload, AtumEscrowChallenge>;
|
|
306
413
|
/**
|
|
@@ -344,4 +451,4 @@ declare const atumEscrowChargeMethod: {
|
|
|
344
451
|
};
|
|
345
452
|
};
|
|
346
453
|
|
|
347
|
-
export { type AtumEscrowChallenge as A, type ChargeCredentialPayload as C, INTENT as I, METHOD_NAME as M, type SenderSigner as S, type AtumEscrowCredential as a, type
|
|
454
|
+
export { type AtumEscrowChallenge as A, type ChargeCredentialPayload as C, INTENT as I, METHOD_NAME as M, type SenderSigner as S, type AtumEscrowCredential as a, type AtumEscrowExtra$1 as b, type AtumEscrowRequest as c, type ChargeRequest as d, ChargeRequestSchema as e, CredentialPayloadSchema as f, INTENT_ID_META_KEY as g, type SenderSignerOptions as h, atumEscrowChargeMethod as i, type ChainDefaults as j, type SolanaClusterUnixTimeReader as k };
|
package/dist/server.d.ts
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
export { A as AtumEscrowChallenge, a as AtumEscrowCredential, C as ChargeCredentialPayload, d as ChargeRequestSchema, e as CredentialPayloadSchema, I as INTENT, M as METHOD_NAME, g as atumEscrowChargeMethod } from './internal-DzEGXm14.js';
|
|
5
|
-
import { Receipt, Method } from 'mppx';
|
|
1
|
+
import { i as atumEscrowChargeMethod, j as ChainDefaults, d as ChargeRequest } from './internal-CJEu9yUF.js';
|
|
2
|
+
export { A as AtumEscrowChallenge, a as AtumEscrowCredential, C as ChargeCredentialPayload, e as ChargeRequestSchema, f as CredentialPayloadSchema, I as INTENT, g as INTENT_ID_META_KEY, M as METHOD_NAME } from './internal-CJEu9yUF.js';
|
|
3
|
+
import { Errors, Receipt, Method } from 'mppx';
|
|
6
4
|
import { PaymentRequest } from './generated/index.js';
|
|
7
5
|
export { PaymentRequest } from './generated/index.js';
|
|
6
|
+
import 'zod/mini';
|
|
8
7
|
|
|
9
8
|
/**
|
|
10
9
|
* This file was automatically generated by json-schema-to-typescript.
|
|
@@ -53,6 +52,151 @@ interface FulfillmentConfirmation {
|
|
|
53
52
|
destination_block_number?: number;
|
|
54
53
|
}
|
|
55
54
|
|
|
55
|
+
/**
|
|
56
|
+
* Settlement outcomes that are not a receipt.
|
|
57
|
+
*
|
|
58
|
+
* MPP models a completed payment and nothing else: a receipt's status is the literal
|
|
59
|
+
* `"success"`, so a payment that has not settled cannot be expressed as one and has to leave
|
|
60
|
+
* `verify()` as a throw. What matters is that the two non-success outcomes stay
|
|
61
|
+
* distinguishable, because they call for opposite responses:
|
|
62
|
+
*
|
|
63
|
+
* - {@link SettlementPendingError} — the payment is still settling. Retrying the SAME purchase
|
|
64
|
+
* is safe and is how the payer collects the result: the identifier it derives from resolves to
|
|
65
|
+
* this same payment, so the gateway returns the original rather than charging again.
|
|
66
|
+
* - {@link SettlementFailedError} — the payment reached a terminal failure. Retrying the same
|
|
67
|
+
* purchase resolves to the dead payment forever, so recovering means starting a NEW purchase
|
|
68
|
+
* under a new identifier.
|
|
69
|
+
*
|
|
70
|
+
* Both carry the gateway's payment id when one was issued, so a merchant can reconcile the
|
|
71
|
+
* attempt against the gateway without parsing a message.
|
|
72
|
+
*
|
|
73
|
+
* Both extend the framework's error types, so a merchant that does not distinguish them still
|
|
74
|
+
* gets the standard `402` + problem-details response.
|
|
75
|
+
*
|
|
76
|
+
* @packageDocumentation
|
|
77
|
+
*/
|
|
78
|
+
|
|
79
|
+
/** Fields shared by both settlement outcomes. */
|
|
80
|
+
interface SettlementErrorDetails {
|
|
81
|
+
/** The gateway's payment id, when the payment was accepted and given one. */
|
|
82
|
+
readonly paymentId: string | undefined;
|
|
83
|
+
}
|
|
84
|
+
/** True if the payment is still settling — a retry of the same purchase is safe. */
|
|
85
|
+
declare function isSettlementPending(err: unknown): err is SettlementPendingError;
|
|
86
|
+
/** True if the payment reached a terminal failure — the same purchase can never settle. */
|
|
87
|
+
declare function isSettlementFailed(err: unknown): err is SettlementFailedError;
|
|
88
|
+
/** True if the gateway refused the request outright — nothing was charged. */
|
|
89
|
+
declare function isPaymentRejected(err: unknown): err is PaymentRejectedError;
|
|
90
|
+
/**
|
|
91
|
+
* The payment was accepted but had not settled when the merchant's synchronous window closed.
|
|
92
|
+
*
|
|
93
|
+
* Not a failure: the payment is in flight. Re-attempting the same purchase is safe — it resolves to
|
|
94
|
+
* this payment and returns its result once settled.
|
|
95
|
+
*
|
|
96
|
+
* **What re-attempting means.** Run the purchase again: request a fresh challenge and sign it again,
|
|
97
|
+
* keeping the same intent id. The intent id is what makes the re-attempt resolve onto the payment
|
|
98
|
+
* already in flight rather than taking a second one, so it must not change — while everything
|
|
99
|
+
* time-bound about the authorization must, because the deadlines are absolute timestamps fixed when
|
|
100
|
+
* it was signed. A stored credential therefore cannot simply be presented a second time; once its
|
|
101
|
+
* quote window has closed it is refused, and on Solana the deposit authorization carries its own
|
|
102
|
+
* replay window that expires with it.
|
|
103
|
+
*
|
|
104
|
+
* A payment-enabled `fetch` does this for the caller, since each attempt fetches a new challenge.
|
|
105
|
+
* Code that drives the client directly has to rebuild.
|
|
106
|
+
*/
|
|
107
|
+
declare class SettlementPendingError extends Errors.PaymentActionRequiredError implements SettlementErrorDetails {
|
|
108
|
+
/** Discriminant; test it with {@link isSettlementPending}. */
|
|
109
|
+
readonly isSettlementPending: true;
|
|
110
|
+
readonly paymentId: string | undefined;
|
|
111
|
+
constructor(options?: {
|
|
112
|
+
paymentId?: string | undefined;
|
|
113
|
+
});
|
|
114
|
+
/**
|
|
115
|
+
* Adds `paymentId` as its own field, not just interpolated into `detail`'s prose. A merchant
|
|
116
|
+
* catching this in-process already has `.paymentId`; a real cross-network payer only ever
|
|
117
|
+
* sees this JSON body, and without a dedicated field would have to parse it out of a sentence
|
|
118
|
+
* to reconcile the attempt against the gateway. The return type is declared (not left to
|
|
119
|
+
* inference) so a TypeScript merchant that re-serializes through the base type still sees
|
|
120
|
+
* `paymentId` in intellisense.
|
|
121
|
+
*/
|
|
122
|
+
toProblemDetails(challengeId?: string): Errors.PaymentError.ProblemDetails & {
|
|
123
|
+
paymentId?: string;
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* The payment reached a terminal failure state and will not settle.
|
|
128
|
+
*
|
|
129
|
+
* A retry of the same purchase cannot recover it: that purchase's identifier now resolves to
|
|
130
|
+
* this failed payment. Recovering means a new purchase under a new identifier.
|
|
131
|
+
*/
|
|
132
|
+
declare class SettlementFailedError extends Errors.VerificationFailedError implements SettlementErrorDetails {
|
|
133
|
+
/** Discriminant; test it with {@link isSettlementFailed}. */
|
|
134
|
+
readonly isSettlementFailed: true;
|
|
135
|
+
readonly paymentId: string | undefined;
|
|
136
|
+
/** The terminal state the gateway reported. */
|
|
137
|
+
readonly state: string;
|
|
138
|
+
constructor(options: {
|
|
139
|
+
paymentId?: string | undefined;
|
|
140
|
+
state: string;
|
|
141
|
+
});
|
|
142
|
+
/**
|
|
143
|
+
* Adds `paymentId` and `state` as their own fields, not just interpolated into `detail`'s
|
|
144
|
+
* prose — see {@link SettlementPendingError.toProblemDetails} for why a payer needs this on
|
|
145
|
+
* the wire, not only a same-process merchant. Return type declared for the same reason: so a
|
|
146
|
+
* TypeScript merchant sees `paymentId`/`state` in intellisense, not only at runtime.
|
|
147
|
+
*/
|
|
148
|
+
toProblemDetails(challengeId?: string): Errors.PaymentError.ProblemDetails & {
|
|
149
|
+
paymentId?: string;
|
|
150
|
+
state: string;
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* The gateway refused the payment outright — it never entered settlement.
|
|
155
|
+
*
|
|
156
|
+
* Distinct from {@link SettlementFailedError}: nothing was charged and nothing is in flight, so
|
|
157
|
+
* the fault is in the request and fixing it makes the same purchase payable. The most common
|
|
158
|
+
* cause is reusing one purchase identifier for two different sets of terms, which the gateway
|
|
159
|
+
* rejects rather than silently resolving to the first payment.
|
|
160
|
+
*
|
|
161
|
+
* Thrown by a {@link ../server!PaymentSubmitter | PaymentSubmitter} — the submitter owns the
|
|
162
|
+
* gateway call, so it is the only place that can tell a refusal (the request was rejected) from
|
|
163
|
+
* a transport or server failure (the outcome is unknown, and reporting it as a refusal would
|
|
164
|
+
* invite a second payment). Classify on the response status rather than on the body, so an
|
|
165
|
+
* intermediary that rewrites the error payload cannot turn a refusal into an unknown outcome.
|
|
166
|
+
* Raising it preserves the gateway's error code through to the payer instead of flattening every
|
|
167
|
+
* cause into one generic verification failure.
|
|
168
|
+
*/
|
|
169
|
+
declare class PaymentRejectedError extends Errors.BadRequestError {
|
|
170
|
+
/** Discriminant; test it with {@link isPaymentRejected}. */
|
|
171
|
+
readonly isPaymentRejected: true;
|
|
172
|
+
/** The gateway's error code, for programmatic handling. */
|
|
173
|
+
readonly code: string;
|
|
174
|
+
constructor(options: {
|
|
175
|
+
code: string;
|
|
176
|
+
detail: string;
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Merchant-side entry point for the Atum escrow MPP payment method.
|
|
182
|
+
*
|
|
183
|
+
* Register the returned method into your `mppx` server to accept `atum-escrow`
|
|
184
|
+
* payments, and use {@link buildChargeRequest} / {@link corridorFromDefaults} to construct
|
|
185
|
+
* the `402` challenges. Verification recovers the source-chain deposit authorization per
|
|
186
|
+
* chain (EVM, Tron, Solana) and settles through the Atum Payment Gateway.
|
|
187
|
+
*
|
|
188
|
+
* @example
|
|
189
|
+
* ```ts
|
|
190
|
+
* import { Mppx } from 'mppx/server'
|
|
191
|
+
* import { registerServer } from '@atumlabs/mppx-atum-escrow/server'
|
|
192
|
+
*
|
|
193
|
+
* const method = registerServer({ submitter })
|
|
194
|
+
* const mppx = Mppx.create({ realm: 'api.example.com', secretKey, methods: [method] })
|
|
195
|
+
* ```
|
|
196
|
+
*
|
|
197
|
+
* @packageDocumentation
|
|
198
|
+
*/
|
|
199
|
+
|
|
56
200
|
/** One source chain/token the merchant accepts payment from. */
|
|
57
201
|
interface AtumEscrowSource {
|
|
58
202
|
/** CAIP-2 source chain id (e.g. `eip155:8453`, `tron:mainnet`, `solana:<genesis>`). */
|
|
@@ -95,15 +239,37 @@ interface AtumEscrowCorridor {
|
|
|
95
239
|
/** Accepted source options; selected per charge by `(network, asset)`. */
|
|
96
240
|
sources: AtumEscrowSource[];
|
|
97
241
|
}
|
|
242
|
+
/** Lifecycle state the gateway reports for a payment. */
|
|
243
|
+
type PaymentSettlementStatus = 'pending' | 'completed' | 'failed' | 'cancelled';
|
|
244
|
+
/** What a {@link PaymentSubmitter} resolves with. */
|
|
245
|
+
interface PaymentSubmitResult {
|
|
246
|
+
/** The gateway's payment id. Present whenever the payment was accepted. */
|
|
247
|
+
payment_id?: string;
|
|
248
|
+
/**
|
|
249
|
+
* The payment's state. When absent, an unsettled payment is treated as still in flight —
|
|
250
|
+
* the conservative reading, since calling an in-flight payment failed would tell the payer to
|
|
251
|
+
* start a second one.
|
|
252
|
+
*/
|
|
253
|
+
status?: PaymentSettlementStatus;
|
|
254
|
+
/** The settlement confirmation, present once the payment has completed. */
|
|
255
|
+
fulfillment_confirmation?: FulfillmentConfirmation;
|
|
256
|
+
}
|
|
98
257
|
/**
|
|
99
258
|
* Submits a signed payment request to the Atum Payment Gateway and resolves with the
|
|
100
259
|
* settlement result. Satisfied by an adapter over `@atum-labs/payment-gateway-client`.
|
|
260
|
+
*
|
|
261
|
+
* Submit and return what the gateway said; do not poll for completion inside the submitter.
|
|
262
|
+
* A payment that outlives the gateway's synchronous window surfaces to the payer as
|
|
263
|
+
* {@link SettlementPendingError}, and the payer's retry of the same purchase is what collects the
|
|
264
|
+
* result — the gateway resolves that retry to the same payment. Polling inside the submitter
|
|
265
|
+
* instead holds the merchant's request open for the full settlement window and hides the pending
|
|
266
|
+
* state that makes the retry safe.
|
|
267
|
+
*
|
|
268
|
+
* Throw {@link PaymentRejectedError} when the gateway refuses the request, so the refusal keeps
|
|
269
|
+
* its code. Any other throw is treated as an unknown outcome.
|
|
101
270
|
*/
|
|
102
271
|
interface PaymentSubmitter {
|
|
103
|
-
submit(request: PaymentRequest): Promise<
|
|
104
|
-
payment_id?: string;
|
|
105
|
-
fulfillment_confirmation?: FulfillmentConfirmation;
|
|
106
|
-
}>;
|
|
272
|
+
submit(request: PaymentRequest): Promise<PaymentSubmitResult>;
|
|
107
273
|
}
|
|
108
274
|
/** Configuration for the merchant side of the method. */
|
|
109
275
|
interface AtumEscrowServerConfig {
|
|
@@ -122,6 +288,18 @@ type AtumEscrowReceipt = Receipt.Receipt & {
|
|
|
122
288
|
/** The full settlement confirmation. */
|
|
123
289
|
fulfillmentConfirmation: FulfillmentConfirmation;
|
|
124
290
|
};
|
|
291
|
+
/**
|
|
292
|
+
* The merchant-side method {@link registerServer} returns, ready to register into an
|
|
293
|
+
* `mppx` server via `Mppx.create({ methods: [method] })`.
|
|
294
|
+
*
|
|
295
|
+
* Name this type when the method is built in one place and registered in another — for
|
|
296
|
+
* example a factory that assembles the corridor and submitter for your environment. Its
|
|
297
|
+
* `verify()` resolves with an {@link AtumEscrowReceipt}, so the settlement confirmation is
|
|
298
|
+
* available directly on the result.
|
|
299
|
+
*/
|
|
300
|
+
type AtumEscrowServer = Omit<Method.Server<typeof atumEscrowChargeMethod, {}, undefined, {}, undefined>, 'verify'> & {
|
|
301
|
+
verify: (parameters: Method.VerifyContext<typeof atumEscrowChargeMethod>) => Promise<AtumEscrowReceipt>;
|
|
302
|
+
};
|
|
125
303
|
/**
|
|
126
304
|
* Configure the merchant side of the method.
|
|
127
305
|
*
|
|
@@ -137,42 +315,7 @@ type AtumEscrowReceipt = Receipt.Receipt & {
|
|
|
137
315
|
*
|
|
138
316
|
* @param config - the gateway submitter and options.
|
|
139
317
|
*/
|
|
140
|
-
declare function registerServer(config: AtumEscrowServerConfig):
|
|
141
|
-
readonly name: "atum-escrow";
|
|
142
|
-
readonly intent: "charge";
|
|
143
|
-
readonly schema: {
|
|
144
|
-
readonly request: z.ZodMiniObject<{
|
|
145
|
-
source: z.ZodMiniObject<{
|
|
146
|
-
network: z.ZodMiniString<string>;
|
|
147
|
-
asset: z.ZodMiniString<string>;
|
|
148
|
-
amount: z.ZodMiniString<string>;
|
|
149
|
-
}, zod_v4_core.$strip>;
|
|
150
|
-
extra: z.ZodMiniObject<{
|
|
151
|
-
destination: z.ZodMiniObject<{
|
|
152
|
-
network: z.ZodMiniString<string>;
|
|
153
|
-
asset: z.ZodMiniString<string>;
|
|
154
|
-
address: z.ZodMiniString<string>;
|
|
155
|
-
}, zod_v4_core.$strip>;
|
|
156
|
-
fulfillmentAmount: z.ZodMiniString<string>;
|
|
157
|
-
escrow: z.ZodMiniString<string>;
|
|
158
|
-
fulfillmentProxy: z.ZodMiniString<string>;
|
|
159
|
-
reserver: z.ZodMiniString<string>;
|
|
160
|
-
releaser: z.ZodMiniString<string>;
|
|
161
|
-
fulfillmentVerifierEndpoint: z.ZodMiniString<string>;
|
|
162
|
-
quoteDeadlineSeconds: z.ZodMiniNumber<number>;
|
|
163
|
-
fulfillmentDeadlineSeconds: z.ZodMiniNumber<number>;
|
|
164
|
-
svmSignatureClusterId: z.ZodMiniOptional<z.ZodMiniString<string>>;
|
|
165
|
-
svmSignatureDomainVersion: z.ZodMiniOptional<z.ZodMiniNumber<number>>;
|
|
166
|
-
issuedAt: z.ZodMiniOptional<z.ZodMiniString<string>>;
|
|
167
|
-
}, zod_v4_core.$strip>;
|
|
168
|
-
}, zod_v4_core.$strip>;
|
|
169
|
-
readonly credential: {
|
|
170
|
-
readonly payload: z.ZodMiniObject<{
|
|
171
|
-
paymentRequest: z.ZodMiniRecord<z.ZodMiniString<string>, z.ZodMiniUnknown>;
|
|
172
|
-
}, zod_v4_core.$strip>;
|
|
173
|
-
};
|
|
174
|
-
};
|
|
175
|
-
}, {}, undefined, {}, undefined>;
|
|
318
|
+
declare function registerServer(config: AtumEscrowServerConfig): AtumEscrowServer;
|
|
176
319
|
/**
|
|
177
320
|
* Validate a corridor's shape and per-source addresses. Run automatically by
|
|
178
321
|
* {@link buildChargeRequest}; call it directly to fail fast at merchant startup.
|
|
@@ -182,10 +325,16 @@ declare function registerServer(config: AtumEscrowServerConfig): Method.Server<{
|
|
|
182
325
|
*/
|
|
183
326
|
declare function validateCorridor(corridor: AtumEscrowCorridor): void;
|
|
184
327
|
/**
|
|
185
|
-
* Build the `charge` challenge request for a specific source option from a corridor
|
|
186
|
-
*
|
|
187
|
-
*
|
|
188
|
-
*
|
|
328
|
+
* Build the `charge` challenge request for a specific source option from a corridor — the
|
|
329
|
+
* payment terms alone. The source cap is `fulfillmentAmount + markup`, quoted in the source
|
|
330
|
+
* token. The corridor is validated first, so a misconfiguration fails here rather than at
|
|
331
|
+
* signing time.
|
|
332
|
+
*
|
|
333
|
+
* Prefer {@link buildChargeChallenge}, which pairs these terms with the per-purchase identifier
|
|
334
|
+
* the payer requires. A challenge built from this function alone carries no such identifier and
|
|
335
|
+
* the payer's client refuses it — at signing time, not at build time, so code that calls this
|
|
336
|
+
* directly and worked before only surfaces the problem when a real payer's client refuses the
|
|
337
|
+
* payment. Reach for it directly only when supplying the per-purchase metadata by hand.
|
|
189
338
|
*
|
|
190
339
|
* @param corridor - the corridor to serve.
|
|
191
340
|
* @param select - which configured source to fund from, by `(network, asset)`.
|
|
@@ -205,6 +354,62 @@ declare function buildChargeRequest(corridor: AtumEscrowCorridor, select: {
|
|
|
205
354
|
}, fulfillmentAmount: string, options?: {
|
|
206
355
|
issuedAt?: string;
|
|
207
356
|
}): ChargeRequest;
|
|
357
|
+
/**
|
|
358
|
+
* A charge challenge's two halves, ready to hand to the framework.
|
|
359
|
+
*
|
|
360
|
+
* They are separate because the framework keeps them separate: `request` is the
|
|
361
|
+
* method-specific payment terms, `meta` is challenge metadata. Both are covered by the
|
|
362
|
+
* challenge-id HMAC, but nesting the metadata inside the request would bury the purchase
|
|
363
|
+
* identifier in the terms blob where the payer does not look for it — so this type keeps them in
|
|
364
|
+
* the shape the framework expects.
|
|
365
|
+
*/
|
|
366
|
+
interface ChargeChallenge {
|
|
367
|
+
/** The method-specific charge request (the payment terms). */
|
|
368
|
+
request: ChargeRequest;
|
|
369
|
+
/** Challenge metadata carrying the per-intent identifier. */
|
|
370
|
+
meta: Record<string, string>;
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* Build a charge challenge for a specific source option from a corridor.
|
|
374
|
+
*
|
|
375
|
+
* This is the entry point a merchant should use: it pairs the payment terms with the per-purchase
|
|
376
|
+
* identifier the payer needs to make the payment retry-safe. A challenge built without that
|
|
377
|
+
* identifier is refused by the payer's client rather than paid unsafely.
|
|
378
|
+
*
|
|
379
|
+
* Pass the result straight through to the framework:
|
|
380
|
+
*
|
|
381
|
+
* ```ts
|
|
382
|
+
* const { request, meta } = buildChargeChallenge(corridor, source, amount, { intentId: order.id })
|
|
383
|
+
*
|
|
384
|
+
* // Route-handler style:
|
|
385
|
+
* mppx.compose(['atum-escrow/charge', { ...request, meta }])(input)
|
|
386
|
+
*
|
|
387
|
+
* // Or building the challenge directly:
|
|
388
|
+
* Challenge.fromMethod(atumEscrowChargeMethod, { request, meta, realm, secretKey })
|
|
389
|
+
* ```
|
|
390
|
+
*
|
|
391
|
+
* @param corridor - the corridor to serve.
|
|
392
|
+
* @param select - which configured source to fund from, by `(network, asset)`.
|
|
393
|
+
* @param fulfillmentAmount - exact amount the merchant receives, atomic units of the
|
|
394
|
+
* destination token.
|
|
395
|
+
* @param options.intentId - identifies the thing being bought. Use the merchant's own order,
|
|
396
|
+
* invoice, or cart identifier: one value per purchase, the SAME value every time that purchase
|
|
397
|
+
* is re-offered or retried, and never shared between two purchases. The payer derives the
|
|
398
|
+
* payment's identity from it and the gateway de-duplicates on that, so its lifetime is what
|
|
399
|
+
* decides whether a retry is recognized (a value that changes per attempt causes a second
|
|
400
|
+
* charge) and whether two purchases stay distinct (a value shared between them leaves the
|
|
401
|
+
* second unpaid). Do not derive it from the terms: two orders for the same item have identical
|
|
402
|
+
* terms.
|
|
403
|
+
* @param options.issuedAt - Solana-source only; see {@link buildChargeRequest}.
|
|
404
|
+
* @throws if `intentId` is empty, or the corridor/source selection is invalid.
|
|
405
|
+
*/
|
|
406
|
+
declare function buildChargeChallenge(corridor: AtumEscrowCorridor, select: {
|
|
407
|
+
network: string;
|
|
408
|
+
asset: string;
|
|
409
|
+
}, fulfillmentAmount: string, options: {
|
|
410
|
+
intentId: string;
|
|
411
|
+
issuedAt?: string;
|
|
412
|
+
}): ChargeChallenge;
|
|
208
413
|
/** The minimal defaults source `corridorFromDefaults` needs; satisfied by the gateway client. */
|
|
209
414
|
interface ChainDefaultsSource {
|
|
210
415
|
fetchChainDefaults(chainId: string): Promise<ChainDefaults>;
|
|
@@ -235,4 +440,4 @@ declare function corridorFromDefaults(defaults: ChainDefaultsSource, params: {
|
|
|
235
440
|
fulfillmentDeadlineSeconds: number;
|
|
236
441
|
}): Promise<AtumEscrowCorridor>;
|
|
237
442
|
|
|
238
|
-
export { type AtumEscrowCorridor, type AtumEscrowReceipt, type AtumEscrowServerConfig, type AtumEscrowSource, type ChainDefaultsSource, ChargeRequest, type FulfillmentConfirmation, type PaymentSubmitter, buildChargeRequest, corridorFromDefaults, registerServer, validateCorridor };
|
|
443
|
+
export { type AtumEscrowCorridor, type AtumEscrowReceipt, type AtumEscrowServer, type AtumEscrowServerConfig, type AtumEscrowSource, type ChainDefaultsSource, type ChargeChallenge, ChargeRequest, type FulfillmentConfirmation, PaymentRejectedError, type PaymentSettlementStatus, type PaymentSubmitResult, type PaymentSubmitter, type SettlementErrorDetails, SettlementFailedError, SettlementPendingError, atumEscrowChargeMethod, buildChargeChallenge, buildChargeRequest, corridorFromDefaults, isPaymentRejected, isSettlementFailed, isSettlementPending, registerServer, validateCorridor };
|