@piprail/sdk 1.20.0 → 1.20.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +22 -0
- package/dist/index.cjs +18 -3
- package/dist/index.js +18 -3
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,28 @@ All notable changes to `@piprail/sdk` are documented here. The format
|
|
|
4
4
|
follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the
|
|
5
5
|
versions follow [Semantic Versioning](https://semver.org/).
|
|
6
6
|
|
|
7
|
+
## [1.20.1] — 2026-06-11 — gate replay store: bounded + exception-safe
|
|
8
|
+
|
|
9
|
+
Patch — internal robustness on the gate's built-in replay protection. No API change, no visible
|
|
10
|
+
behaviour change, defaults identical.
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
- **Bounded the default used-proof set.** It's now evicted past the replay window
|
|
14
|
+
(`maxTimeoutSeconds`) instead of growing for the life of the process — safe because the driver's
|
|
15
|
+
recency check rejects any proof that old anyway, so a dropped entry still can't be replayed. A
|
|
16
|
+
long-lived gate no longer slowly leaks memory. Custom `isUsed`/`markUsed` stores are unaffected
|
|
17
|
+
(give them a TTL = the window).
|
|
18
|
+
- **`onchain-proof` verification is now claim-release exception-safe.** If a driver's `verify()`
|
|
19
|
+
*throws* (an unexpected RPC exception) rather than returning a rejection, the gate now releases the
|
|
20
|
+
proof reservation before rethrowing — so a transient blip can't permanently burn an otherwise-valid
|
|
21
|
+
proof. This matches the `exact` path, which already did it.
|
|
22
|
+
|
|
23
|
+
### Docs
|
|
24
|
+
- Rewrote **[Replay protection & recovery](https://docs.piprail.com/accepting-payments/replay-protection/)**
|
|
25
|
+
with the full "paid but didn't receive — what happens to the payment?" model (a recoverability
|
|
26
|
+
matrix, the at-most-once-by-design rationale, the bounded-memory behaviour, and the client's
|
|
27
|
+
never-re-pay `.ref` recovery).
|
|
28
|
+
|
|
7
29
|
## [1.20.0] — 2026-06-11 — discovery hardening: conformance-locked, accurate timing, PipRail-attributed
|
|
8
30
|
|
|
9
31
|
A minor release focused on the discovery/registration subsystem — verified live against the real
|
package/dist/index.cjs
CHANGED
|
@@ -4215,14 +4215,23 @@ function createPaymentGate(options) {
|
|
|
4215
4215
|
};
|
|
4216
4216
|
}
|
|
4217
4217
|
const hasCustomStore = Boolean(options.isUsed || options.markUsed);
|
|
4218
|
-
const localUsed = /* @__PURE__ */ new
|
|
4218
|
+
const localUsed = /* @__PURE__ */ new Map();
|
|
4219
|
+
const replayWindowMs = maxTimeoutSeconds * 1e3;
|
|
4220
|
+
function pruneUsed(now) {
|
|
4221
|
+
for (const [key, expiry] of localUsed) {
|
|
4222
|
+
if (expiry > now) break;
|
|
4223
|
+
localUsed.delete(key);
|
|
4224
|
+
}
|
|
4225
|
+
}
|
|
4219
4226
|
async function claimTx(ref) {
|
|
4220
4227
|
if (hasCustomStore) {
|
|
4221
4228
|
return options.isUsed ? Boolean(await options.isUsed(ref)) : false;
|
|
4222
4229
|
}
|
|
4223
4230
|
const key = ref.toLowerCase();
|
|
4231
|
+
const now = Date.now();
|
|
4232
|
+
pruneUsed(now);
|
|
4224
4233
|
if (localUsed.has(key)) return true;
|
|
4225
|
-
localUsed.
|
|
4234
|
+
localUsed.set(key, now + replayWindowMs);
|
|
4226
4235
|
return false;
|
|
4227
4236
|
}
|
|
4228
4237
|
async function settleTx(ref, ok) {
|
|
@@ -4382,7 +4391,13 @@ function createPaymentGate(options) {
|
|
|
4382
4391
|
}
|
|
4383
4392
|
const ref = sig.payload.txHash;
|
|
4384
4393
|
if (await claimTx(ref)) return rejection("tx_already_used", `Proof ${ref} was already redeemed.`);
|
|
4385
|
-
|
|
4394
|
+
let result;
|
|
4395
|
+
try {
|
|
4396
|
+
result = await spec.net.verify(ref, buildAccept(spec, sig.payload.nonce));
|
|
4397
|
+
} catch (err) {
|
|
4398
|
+
await settleTx(ref, false);
|
|
4399
|
+
throw err;
|
|
4400
|
+
}
|
|
4386
4401
|
if (!result.ok) {
|
|
4387
4402
|
await settleTx(ref, false);
|
|
4388
4403
|
return rejection(result.error, result.detail);
|
package/dist/index.js
CHANGED
|
@@ -4215,14 +4215,23 @@ function createPaymentGate(options) {
|
|
|
4215
4215
|
};
|
|
4216
4216
|
}
|
|
4217
4217
|
const hasCustomStore = Boolean(options.isUsed || options.markUsed);
|
|
4218
|
-
const localUsed = /* @__PURE__ */ new
|
|
4218
|
+
const localUsed = /* @__PURE__ */ new Map();
|
|
4219
|
+
const replayWindowMs = maxTimeoutSeconds * 1e3;
|
|
4220
|
+
function pruneUsed(now) {
|
|
4221
|
+
for (const [key, expiry] of localUsed) {
|
|
4222
|
+
if (expiry > now) break;
|
|
4223
|
+
localUsed.delete(key);
|
|
4224
|
+
}
|
|
4225
|
+
}
|
|
4219
4226
|
async function claimTx(ref) {
|
|
4220
4227
|
if (hasCustomStore) {
|
|
4221
4228
|
return options.isUsed ? Boolean(await options.isUsed(ref)) : false;
|
|
4222
4229
|
}
|
|
4223
4230
|
const key = ref.toLowerCase();
|
|
4231
|
+
const now = Date.now();
|
|
4232
|
+
pruneUsed(now);
|
|
4224
4233
|
if (localUsed.has(key)) return true;
|
|
4225
|
-
localUsed.
|
|
4234
|
+
localUsed.set(key, now + replayWindowMs);
|
|
4226
4235
|
return false;
|
|
4227
4236
|
}
|
|
4228
4237
|
async function settleTx(ref, ok) {
|
|
@@ -4382,7 +4391,13 @@ function createPaymentGate(options) {
|
|
|
4382
4391
|
}
|
|
4383
4392
|
const ref = sig.payload.txHash;
|
|
4384
4393
|
if (await claimTx(ref)) return rejection("tx_already_used", `Proof ${ref} was already redeemed.`);
|
|
4385
|
-
|
|
4394
|
+
let result;
|
|
4395
|
+
try {
|
|
4396
|
+
result = await spec.net.verify(ref, buildAccept(spec, sig.payload.nonce));
|
|
4397
|
+
} catch (err) {
|
|
4398
|
+
await settleTx(ref, false);
|
|
4399
|
+
throw err;
|
|
4400
|
+
}
|
|
4386
4401
|
if (!result.ok) {
|
|
4387
4402
|
await settleTx(ref, false);
|
|
4388
4403
|
return rejection(result.error, result.detail);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@piprail/sdk",
|
|
3
|
-
"version": "1.20.
|
|
3
|
+
"version": "1.20.1",
|
|
4
4
|
"description": "Accept x402 crypto payments across 29 chains — every major EVM chain plus Solana, TON, Tron, NEAR, Sui, Aptos, Algorand, Stellar & XRPL — in a couple of lines. No backend, no database, no fee; payments settle straight to your wallet.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|