openpay-x402-sdk 0.2.0 → 0.2.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 CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.2.1
4
+
5
+ - Compare `accept.resource` against the requested URL using decoded query
6
+ canonicalization (ordered `URLSearchParams` pairs) instead of byte equality.
7
+ Hosts such as Vercel/Next.js normalize `%20` to `+` before the app sees the
8
+ request, which made honest sellers fail `resource_mismatch`. Distinct decoded
9
+ values (`%2B`, double encoding, reordered or extra params) still mismatch.
10
+
3
11
  ## 0.2.0
4
12
 
5
13
  - Trust query-string variants of a query-free catalog URL after the live
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openpay-x402-sdk",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Guarded Node.js buyer SDK for OpenPay x402 JPYC resources",
5
5
  "type": "module",
6
6
  "main": "./src/index.mjs",
package/src/guards.mjs CHANGED
@@ -251,6 +251,19 @@ function addAssetReasons(reasons, rawAccept) {
251
251
  }
252
252
  }
253
253
 
254
+ // クエリの正準比較: URLSearchParams でデコードした (key, value) 列の順序付き一致。
255
+ // Vercel/Next 系ホストは request.url の時点でスペースを `+` に正規化するため (`%20` の原文は
256
+ // サーバー側で復元不可能)、accept.resource と要求 URL のバイト一致要求は正当な売り手を
257
+ // 恒常的に落とす (gateway.open-pay.jp で実害)。`%20` と `+` は form-urlencoding で同一の
258
+ // スペースにデコードされる一方、`%2B` (リテラル +) や二重エンコードは異なる値にデコード
259
+ // されるので、この比較は同義エンコーディングだけを同一視し resource 束縛は緩めない。
260
+ function sameQuery(a, b) {
261
+ const ap = [...a.searchParams];
262
+ const bp = [...b.searchParams];
263
+ if (ap.length !== bp.length) return false;
264
+ return ap.every(([k, v], i) => bp[i][0] === k && bp[i][1] === v);
265
+ }
266
+
254
267
  function addResourceReason(reasons, rawAccept, requestUrl) {
255
268
  const requested = parseHttpUrl(requestUrl, 'url');
256
269
  const resource =
@@ -261,7 +274,11 @@ function addResourceReason(reasons, rawAccept, requestUrl) {
261
274
  requested === null ||
262
275
  resource === null ||
263
276
  resource.hostname.toLowerCase() !== requested.hostname.toLowerCase() ||
264
- resource.toString() !== requested.toString()
277
+ resource.origin !== requested.origin ||
278
+ resource.pathname !== requested.pathname ||
279
+ resource.username !== requested.username ||
280
+ resource.password !== requested.password ||
281
+ !sameQuery(resource, requested)
265
282
  ) {
266
283
  reasons.push(REASONS.resourceMismatch);
267
284
  }