@siglume/direct-request-payment 0.4.2 → 0.4.4

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,38 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.4.4 - 2026-06-19
4
+
5
+ Correctness and security hardening release for the SDRP Direct Request Payment
6
+ SDK manual and public helpers.
7
+
8
+ - Clarified that Standard / Micro / Nano are selected by amount, removed the
9
+ unsupported "force Standard for immediate finality" implication, and changed
10
+ Hosted Checkout Standard examples to Standard-band amounts.
11
+ - Removed the Express sample route that accepted a buyer `Authorization` header
12
+ on the merchant server; human web checkout now redirects through Hosted
13
+ Checkout, while agent payment remains buyer-side direct API/tool work.
14
+ - Documented Micro / Nano decimal fee rounding, `rounding_delta_minor`, budget
15
+ reservation versus token locking, no guaranteed `past_due` collection, HTTP
16
+ result accounting, and operational status handling.
17
+ - Added typed TypeScript and named Python helpers for Micro / Nano statement
18
+ APIs.
19
+ - Added `request_hash_v2` helpers/docs and documented the new
20
+ requirement/webhook machine fields for pricing band, settlement cadence,
21
+ finality, protocol fee, and settlement status.
22
+ - Hardened TS/Python integer and `checkout_allowed_origins` validation.
23
+
24
+ ## 0.4.3 - 2026-06-19
25
+
26
+ Documentation-only release for SDRP Micro / Nano operations.
27
+
28
+ - Added the Micro / Nano Statements and Notices manual, covering buyer and
29
+ provider statement APIs, CSV export, final debit notices, the close-plus-3-day
30
+ debit site, past-due blocks, sanitized failure fields, and support references.
31
+ - Expanded API reference, merchant quickstart, security, pricing, and Japanese
32
+ announcement docs so integrators can reconcile settled, unsettled, retrying,
33
+ and past-due Micro / Nano revenue without relying on private platform fields.
34
+ - No wire-format or runtime behavior changes.
35
+
3
36
  ## 0.4.2 - 2026-06-19
4
37
 
5
38
  Documentation completeness release. No wire-format or API changes; 0.4.x clients
package/README.md CHANGED
@@ -89,7 +89,7 @@ await merchant.setupMerchant({
89
89
  // 2. Per order: create a session and redirect the shopper to checkout_url.
90
90
  const session = await merchant.createCheckoutSession({
91
91
  merchant: "your_merchant_key",
92
- amount_minor: 500, // server-fixed; the browser cannot change it
92
+ amount_minor: 1200, // server-fixed; the browser cannot change it
93
93
  currency: "JPY",
94
94
  nonce: order.id, // unique per order
95
95
  success_url: "https://www.your-shop.com/thanks",
@@ -121,7 +121,7 @@ merchant.setup_merchant(
121
121
  # 2. Per order: create a session and redirect the shopper to checkout_url.
122
122
  session = merchant.create_checkout_session(
123
123
  merchant="your_merchant_key",
124
- amount_minor=500, # server-fixed; the browser cannot change it
124
+ amount_minor=1200, # server-fixed; the browser cannot change it
125
125
  currency="JPY",
126
126
  nonce=order["id"], # unique per order
127
127
  success_url="https://www.your-shop.com/thanks",
@@ -183,6 +183,14 @@ Provider revenue in the Micro and Nano bands is not settled revenue until the
183
183
  weekly or monthly on-chain settlement succeeds. Siglume keeps outstanding failed
184
184
  settlements for retry under the published policy, but does not advance or
185
185
  guarantee provider revenue before settlement succeeds.
186
+ Micro / Nano budget checks reserve spending capacity only; they do not lock,
187
+ escrow, or guarantee the buyer's wallet balance, allowance, or settlement funds.
188
+ Sub-minor-unit Nano fees are accumulated with decimal precision and rounded only
189
+ when a settlement batch is created; see [Pricing](./docs/pricing.md) for the
190
+ rounding formula and `rounding_delta_minor` semantics.
191
+ For operational reconciliation, expected revenue, settled revenue, retry state,
192
+ and CSV exports, see
193
+ [docs/metered-statements.md](./docs/metered-statements.md).
186
194
 
187
195
  ## What This SDK Covers
188
196
 
@@ -194,6 +202,7 @@ guarantee provider revenue before settlement succeeds.
194
202
  - buyer-authenticated payment requirement creation
195
203
  - prepared wallet transaction execution payloads
196
204
  - payment requirement verification
205
+ - authenticated TypeScript JSON requests to Micro / Nano statement APIs
197
206
  - signed webhook verification
198
207
 
199
208
  It does not custody funds or manage customer wallets. Merchant setup runs through
@@ -226,15 +235,23 @@ amounts differ.
226
235
 
227
236
  | Payment amount | Applied automatically | What you select | Fee | Settlement |
228
237
  | --- | --- | --- | --- | --- |
229
- | Over JPY 500 / over USD 3.00, or whenever immediate finality is required | Standard Payment | Select one Standard plan: Launch, Starter, Growth, or Pro | Launch: JPY 0 / USD 0 monthly, 1.8%; Starter: JPY 980 / USD 6 monthly, 1.0%; Growth: JPY 2,980 / USD 18 monthly, 0.7%; Pro: JPY 9,800 / USD 60 monthly, 0.5%. Minimum JPY 30 / USD 0.20 per payment. | Settled on-chain immediately after the payment confirms |
230
- | JPY 50-500 / about USD 0.30-3.00 | Micro Payment | Applied automatically by amount | USD 0.01 / Tx, about JPY 2 | Weekly settlement see [Settlement schedule](./docs/pricing.md#settlement-schedule) |
231
- | Under JPY 1 to JPY 49 / under USD 0.01 to about USD 0.30 | Nano Payment | Applied automatically by amount | USD 0.001 / usage, about JPY 0.2 | Monthly settlement see [Settlement schedule](./docs/pricing.md#settlement-schedule) |
238
+ | Over JPY 500 / over USD 3.00 | Standard Payment | Select one Standard plan: Launch, Starter, Growth, or Pro | Launch: JPY 0 / USD 0 monthly, 1.8%; Starter: JPY 980 / USD 6 monthly, 1.0%; Growth: JPY 2,980 / USD 18 monthly, 0.7%; Pro: JPY 9,800 / USD 60 monthly, 0.5%. Minimum JPY 30 / USD 0.20 per payment. | Settled on-chain immediately after the payment confirms |
239
+ | JPY 50-500 / over USD 0.30 and up to USD 3.00 | Micro Payment | Applied automatically by amount | USD 0.01 / Tx, about JPY 2 | Weekly settlement - see [Settlement schedule](./docs/pricing.md#settlement-schedule) |
240
+ | Under JPY 50 / up to USD 0.30 | Nano Payment | Applied automatically by amount | USD 0.001 / usage, about JPY 0.2 | Monthly settlement - see [Settlement schedule](./docs/pricing.md#settlement-schedule) |
232
241
 
233
242
  A merchant billing mandate is required before accepting payments, even on the
234
- Launch plan. `fee_bps` returned on a payment requirement is the authoritative
235
- fee rate for that payment in the merchant's settlement currency. The full fee
236
- table and the weekly / monthly settlement schedule live in
237
- [docs/pricing.md](./docs/pricing.md).
243
+ Launch plan. The current public API does not expose a flag that forces a
244
+ JPY 500-and-under / USD 3-and-under payment into Standard immediate settlement.
245
+ If immediate on-chain settlement is a hard requirement, price the item in the
246
+ Standard band or confirm a merchant-specific contract with Siglume before
247
+ launch. For Standard Payment, `fee_bps` returned on a payment requirement is the
248
+ authoritative fee rate for that payment in the merchant's settlement currency.
249
+ For Micro / Nano, the statement APIs expose `protocol_fee_minor`,
250
+ `gross_buyer_debit_minor`, `buyer_debit_minor`, and `rounding_delta_minor`.
251
+ The full fee table and the weekly / monthly settlement schedule live in
252
+ [docs/pricing.md](./docs/pricing.md). Statement APIs for "how much was used,
253
+ when will it close, when can it debit, and what is settled" are documented in
254
+ [docs/metered-statements.md](./docs/metered-statements.md).
238
255
 
239
256
  ## Merchant Setup: One SDK Call
240
257
 
@@ -510,6 +527,11 @@ if verified["event"]["type"] == "direct_payment.confirmed":
510
527
  pass
511
528
  ```
512
529
 
530
+ New `direct_payment.confirmed` payloads include `pricing_band`,
531
+ `settlement_cadence`, `finality`, `protocol_fee_minor`, `settlement_status`, and
532
+ when available `request_hash_v2`. Use these machine fields instead of inferring
533
+ settlement semantics from the event name alone.
534
+
513
535
  ## Security Rules
514
536
 
515
537
  - Keep the challenge secret on the merchant server only.
@@ -532,7 +554,8 @@ Read [docs/security.md](./docs/security.md) before going live.
532
554
  - Store the returned `SIGLUME_WEBHOOK_SECRET` only on the merchant server.
533
555
  - Persist `challenge_hash`, `requirement_id`, and fulfillment state per order.
534
556
  - Fulfill orders only from verified webhook data, with idempotency.
535
- - Treat `fee_bps` returned by Siglume as the runtime fee source of truth.
557
+ - Treat `fee_bps` returned by Siglume as the Standard Payment runtime fee source
558
+ of truth; use statement API amount fields for Micro / Nano.
536
559
 
537
560
  ## Compatibility Notes
538
561
 
@@ -549,6 +572,7 @@ Read [docs/security.md](./docs/security.md) before going live.
549
572
  - [Merchant quickstart](./docs/merchant-quickstart.md)
550
573
  - [API reference](./docs/api-reference.md)
551
574
  - [Pricing](./docs/pricing.md)
575
+ - [Micro / Nano statements and notices](./docs/metered-statements.md)
552
576
  - [Security guide](./docs/security.md)
553
577
  - [Merchant setup example](./examples/setup-merchant.ts)
554
578
  - [Express checkout example](./examples/express-checkout.ts)
package/dist/index.cjs CHANGED
@@ -38,6 +38,7 @@ __export(src_exports, {
38
38
  DIRECT_REQUEST_PAYMENT_RECEIPT_KIND: () => DIRECT_REQUEST_PAYMENT_RECEIPT_KIND,
39
39
  DIRECT_REQUEST_PAYMENT_RECURRING_CHALLENGE_SCHEME: () => DIRECT_REQUEST_PAYMENT_RECURRING_CHALLENGE_SCHEME,
40
40
  DIRECT_REQUEST_PAYMENT_REFERENCE_TYPE: () => DIRECT_REQUEST_PAYMENT_REFERENCE_TYPE,
41
+ DIRECT_REQUEST_PAYMENT_SDK_VERSION: () => DIRECT_REQUEST_PAYMENT_SDK_VERSION,
41
42
  DirectRequestPaymentClient: () => DirectRequestPaymentClient,
42
43
  DirectRequestPaymentMerchantClient: () => DirectRequestPaymentMerchantClient,
43
44
  HostedCheckoutNotAvailableError: () => HostedCheckoutNotAvailableError,
@@ -58,6 +59,7 @@ __export(src_exports, {
58
59
  createExternal402RecurringChallenge: () => createExternal402RecurringChallenge,
59
60
  directRequestPaymentChallengeHash: () => directRequestPaymentChallengeHash,
60
61
  directRequestPaymentRequestHash: () => directRequestPaymentRequestHash,
62
+ directRequestPaymentRequestHashV2: () => directRequestPaymentRequestHashV2,
61
63
  parseDirectRequestPaymentChallenge: () => parseDirectRequestPaymentChallenge,
62
64
  parseDirectRequestPaymentWebhookEvent: () => parseDirectRequestPaymentWebhookEvent,
63
65
  verifyDirectRequestPaymentChallenge: () => verifyDirectRequestPaymentChallenge,
@@ -76,6 +78,8 @@ var DIRECT_REQUEST_PAYMENT_RECEIPT_KIND = "sdrp_direct_payment";
76
78
  var DIRECT_REQUEST_PAYMENT_ALLOWANCE_RECEIPT_KIND = "sdrp_direct_payment_allowance";
77
79
  var DIRECT_REQUEST_PAYMENT_REFERENCE_TYPE = "sdrp_direct_payment_requirement";
78
80
  var DEFAULT_WEBHOOK_TOLERANCE_SECONDS = 300;
81
+ var DIRECT_REQUEST_PAYMENT_SDK_VERSION = "0.4.4";
82
+ var DIRECT_REQUEST_PAYMENT_CONFIRMED_WEBHOOK_MODES = /* @__PURE__ */ new Set([DIRECT_REQUEST_PAYMENT_MODE, "metered_settlement_batch"]);
79
83
  var SiglumeDirectRequestPaymentError = class extends Error {
80
84
  constructor(message) {
81
85
  super(message);
@@ -132,7 +136,7 @@ var DirectRequestPaymentClient = class {
132
136
  this.auth_token = authToken;
133
137
  this.base_url = (options.base_url ?? envValue("SIGLUME_API_BASE") ?? DEFAULT_SIGLUME_API_BASE).replace(/\/+$/, "");
134
138
  this.timeout_ms = Math.max(1, Math.trunc(options.timeout_ms ?? 15e3));
135
- this.user_agent = options.user_agent ?? "@siglume/direct-request-payment/0.4.2";
139
+ this.user_agent = options.user_agent ?? `@siglume/direct-request-payment/${DIRECT_REQUEST_PAYMENT_SDK_VERSION}`;
136
140
  this.fetch_impl = fetchImpl;
137
141
  }
138
142
  async createPaymentRequirement(input) {
@@ -180,6 +184,53 @@ var DirectRequestPaymentClient = class {
180
184
  async executeAllowanceTransaction(requirement, options = {}) {
181
185
  return this.executePreparedTransaction(buildAllowanceExecutionPayload(requirement, options));
182
186
  }
187
+ async getBuyerMeteredSummary(input = {}) {
188
+ return this.request(
189
+ "GET",
190
+ meteredQueryPath("/sdrp/metered/my-summary", input)
191
+ );
192
+ }
193
+ async listBuyerUsageEvents(input = {}) {
194
+ return this.request(
195
+ "GET",
196
+ meteredQueryPath("/sdrp/metered/my-usage-events", input)
197
+ );
198
+ }
199
+ async listBuyerSettlementBatches(input = {}) {
200
+ return this.request(
201
+ "GET",
202
+ meteredQueryPath("/sdrp/metered/my-settlement-batches", input)
203
+ );
204
+ }
205
+ async getProviderMeteredSummary(input = {}) {
206
+ return this.request(
207
+ "GET",
208
+ meteredQueryPath("/sdrp/metered/provider/summary", input)
209
+ );
210
+ }
211
+ async listProviderUsageEvents(input = {}) {
212
+ return this.request(
213
+ "GET",
214
+ meteredQueryPath("/sdrp/metered/provider/usage-events", input)
215
+ );
216
+ }
217
+ async listProviderSettlementBatches(input = {}) {
218
+ return this.request(
219
+ "GET",
220
+ meteredQueryPath("/sdrp/metered/provider/settlement-batches", input)
221
+ );
222
+ }
223
+ async getProviderSettlementBatch(settlement_batch_id, input = {}) {
224
+ return this.request(
225
+ "GET",
226
+ meteredQueryPath(
227
+ `/sdrp/metered/provider/settlement-batches/${encodeURIComponent(
228
+ requireNonEmpty(settlement_batch_id, "settlement_batch_id")
229
+ )}`,
230
+ input
231
+ )
232
+ );
233
+ }
183
234
  async request(method, path, json_body) {
184
235
  const controller = new AbortController();
185
236
  const timeout = setTimeout(() => controller.abort(), this.timeout_ms);
@@ -237,7 +288,7 @@ var DirectRequestPaymentMerchantClient = class {
237
288
  this.auth_token = authToken;
238
289
  this.base_url = (options.base_url ?? envValue("SIGLUME_API_BASE") ?? DEFAULT_SIGLUME_API_BASE).replace(/\/+$/, "");
239
290
  this.timeout_ms = Math.max(1, Math.trunc(options.timeout_ms ?? 15e3));
240
- this.user_agent = options.user_agent ?? "@siglume/direct-request-payment/0.4.2";
291
+ this.user_agent = options.user_agent ?? `@siglume/direct-request-payment/${DIRECT_REQUEST_PAYMENT_SDK_VERSION}`;
241
292
  this.fetch_impl = fetchImpl;
242
293
  }
243
294
  async setupMerchant(input) {
@@ -530,6 +581,16 @@ async function directRequestPaymentRequestHash(input) {
530
581
  const material = `${normalizeMerchant(input.merchant)}${positiveInteger(input.amount_minor, "amount_minor")}${normalizeCurrency(input.currency)}${requireNonEmpty(input.challenge, "challenge")}`;
531
582
  return sha256Prefixed(material);
532
583
  }
584
+ async function directRequestPaymentRequestHashV2(input) {
585
+ const material = JSON.stringify({
586
+ amount_minor: positiveInteger(input.amount_minor, "amount_minor"),
587
+ challenge: requireNonEmpty(input.challenge, "challenge"),
588
+ currency: normalizeCurrency(input.currency),
589
+ merchant: normalizeMerchant(input.merchant),
590
+ version: 2
591
+ });
592
+ return sha256Prefixed(material);
593
+ }
533
594
  function buildPaymentExecutionPayload(requirement, options = {}) {
534
595
  return buildPreparedTransactionExecutionPayload(requirement, requirement.transaction_request, {
535
596
  receipt_kind: DIRECT_REQUEST_PAYMENT_RECEIPT_KIND,
@@ -603,8 +664,10 @@ function parseDirectRequestPaymentWebhookEvent(payload) {
603
664
  occurred_at: requireNonEmpty(stringOrNull(event.occurred_at) ?? "", "webhook occurred_at"),
604
665
  data: { ...data }
605
666
  };
606
- if (parsed.type === "direct_payment.confirmed" && parsed.data.mode !== DIRECT_REQUEST_PAYMENT_MODE) {
607
- throw new SiglumeWebhookPayloadError("direct_payment.confirmed webhook must carry data.mode='external_402'.");
667
+ if (parsed.type === "direct_payment.confirmed" && !DIRECT_REQUEST_PAYMENT_CONFIRMED_WEBHOOK_MODES.has(String(parsed.data.mode ?? ""))) {
668
+ throw new SiglumeWebhookPayloadError(
669
+ "direct_payment.confirmed webhook must carry a supported Direct Request Payment mode."
670
+ );
608
671
  }
609
672
  return parsed;
610
673
  }
@@ -658,6 +721,13 @@ function normalizeToken(value) {
658
721
  }
659
722
  return token;
660
723
  }
724
+ function normalizeMeteredPlanType(value) {
725
+ const planType = requireNonEmpty(value, "plan_type").toLowerCase();
726
+ if (planType === "micro" || planType === "nano") {
727
+ return planType;
728
+ }
729
+ throw new SiglumeDirectRequestPaymentError("plan_type must be micro or nano.");
730
+ }
661
731
  function normalizeAllowedCurrencies(value) {
662
732
  const normalized = {};
663
733
  if (Array.isArray(value)) {
@@ -695,7 +765,15 @@ function normalizeOriginList(value) {
695
765
  "each checkout_allowed_origins entry must be an absolute origin such as https://shop.example.com."
696
766
  );
697
767
  }
698
- const origin = `${url.protocol.toLowerCase()}//${url.host.toLowerCase()}`;
768
+ if (url.username || url.password) {
769
+ throw new SiglumeDirectRequestPaymentError("checkout_allowed_origins entries must not include userinfo.");
770
+ }
771
+ if (!isAllowedCheckoutOriginScheme(url)) {
772
+ throw new SiglumeDirectRequestPaymentError(
773
+ "checkout_allowed_origins entries must use https, except http is allowed for localhost, 127.0.0.1, or [::1]."
774
+ );
775
+ }
776
+ const origin = url.origin.toLowerCase();
699
777
  if (!seen.has(origin)) {
700
778
  seen.add(origin);
701
779
  origins.push(origin);
@@ -703,8 +781,44 @@ function normalizeOriginList(value) {
703
781
  }
704
782
  return origins;
705
783
  }
784
+ function isAllowedCheckoutOriginScheme(url) {
785
+ if (url.protocol === "https:") {
786
+ return Boolean(url.hostname);
787
+ }
788
+ if (url.protocol !== "http:") {
789
+ return false;
790
+ }
791
+ const hostname = url.hostname.toLowerCase();
792
+ return hostname === "localhost" || hostname === "127.0.0.1" || hostname === "[::1]" || hostname === "::1";
793
+ }
794
+ function meteredQueryPath(path, input) {
795
+ const params = new URLSearchParams();
796
+ if (input.plan_type !== void 0) {
797
+ params.set("plan_type", normalizeMeteredPlanType(input.plan_type));
798
+ }
799
+ if (input.token_symbol !== void 0) {
800
+ params.set("token_symbol", normalizeToken(input.token_symbol));
801
+ }
802
+ if ("status" in input && input.status !== void 0) {
803
+ params.set("status", requireNonEmpty(input.status, "status"));
804
+ }
805
+ if ("listing_id" in input && input.listing_id !== void 0) {
806
+ params.set("listing_id", requireNonEmpty(input.listing_id, "listing_id"));
807
+ }
808
+ if ("capability_key" in input && input.capability_key !== void 0) {
809
+ params.set("capability_key", requireNonEmpty(input.capability_key, "capability_key"));
810
+ }
811
+ if ("limit" in input && input.limit !== void 0) {
812
+ params.set("limit", String(positiveInteger(input.limit, "limit")));
813
+ }
814
+ const query = params.toString();
815
+ return query ? `${path}?${query}` : path;
816
+ }
706
817
  function positiveInteger(value, name) {
707
- const parsed = Number(value);
818
+ if (typeof value !== "number") {
819
+ throw new SiglumeDirectRequestPaymentError(`${name} must be a positive safe integer.`);
820
+ }
821
+ const parsed = value;
708
822
  if (!Number.isSafeInteger(parsed) || parsed <= 0) {
709
823
  throw new SiglumeDirectRequestPaymentError(`${name} must be a positive safe integer.`);
710
824
  }