@siglume/direct-request-payment 0.4.4 → 0.4.6

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,32 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.4.6 - 2026-06-19
4
+
5
+ Patch release for the external webhook-state re-review.
6
+
7
+ - Changed webhook examples to branch Standard settled payments, Micro / Nano
8
+ accepted-but-unsettled usage, and metered settlement batches separately.
9
+ - Added Hosted Checkout settlement machine fields and metered settlement batch
10
+ webhook fields to TypeScript types and API docs.
11
+ - Clarified public Nano one-time amount limits, Micro / Nano idempotency key
12
+ placement, and per-batch rounding adjustment behavior.
13
+ - Added `pytest python_tests` to the PyPI Trusted Publishing release workflow.
14
+
15
+ ## 0.4.5 - 2026-06-19
16
+
17
+ Patch release for the public beta re-review.
18
+
19
+ - Added `cursor` support to TypeScript and Python metered statement list
20
+ helpers, with tests that fetch a second page.
21
+ - Added missing TypeScript settlement batch retry fields
22
+ (`attempt_count`, `next_attempt_at`) and narrowed metered minor amount fields
23
+ to decimal strings.
24
+ - Clarified the public idempotency contract: one-time requirement creation uses
25
+ the challenge nonce / `challenge_hash` / `request_hash_v2`; the SDK does not
26
+ expose an unsupported requirement `idempotency_key`.
27
+ - Clarified Micro / Nano rounding, usage CSV `rounding_delta_minor`, provider
28
+ statement auth roles, and Standard vs aggregated on-chain receipt wording.
29
+
3
30
  ## 0.4.4 - 2026-06-19
4
31
 
5
32
  Correctness and security hardening release for the SDRP Direct Request Payment
package/README.md CHANGED
@@ -22,8 +22,9 @@ buyer-facing Siglume payment flow creates and pays the requirement.
22
22
 
23
23
  `DirectRequestPaymentMerchantClient` requires the merchant's Siglume bearer
24
24
  token for setup. `DirectRequestPaymentClient` requires the buyer's Siglume
25
- bearer token for payment requirements. Do not use a Developer Portal `cli_` API
26
- key with this package.
25
+ bearer token for payment requirements and buyer statements, or the provider /
26
+ merchant user's Siglume bearer token for provider statements. Do not use a
27
+ Developer Portal `cli_` API key with this package.
27
28
 
28
29
  ## Two Kinds of Buyer
29
30
 
@@ -66,12 +67,16 @@ card-style "instant" checkout for first-time buyers.
66
67
  Some merchant accounts may not have the server endpoint enabled yet. In that
67
68
  case `createCheckoutSession(...)` / `getCheckoutSession(...)` raises
68
69
  `HostedCheckoutNotAvailableError` instead of exposing the raw rollout 404/409.
69
- Keep fulfilling only from the signed `direct_payment.confirmed` webhook.
70
+ Keep the signed `direct_payment.confirmed` webhook as the durable signal, and
71
+ inspect its settlement machine fields before marking any order paid.
70
72
 
71
73
  Hosted Checkout is a Siglume-hosted page that turns a "Pay with Siglume" button
72
74
  into a completed wallet payment, then returns the shopper to your store. It
73
- orchestrates the same rails as the agent flow — there is no new money movement
74
- and the merchant fulfills on the same `direct_payment.confirmed` webhook.
75
+ orchestrates the same rails as the agent flow — there is no new money movement.
76
+ Fulfillment still starts from the signed `direct_payment.confirmed` webhook, but
77
+ you must inspect the settlement machine fields before deciding whether the event
78
+ means Standard settled payment, Micro / Nano accepted usage, or aggregated
79
+ Micro / Nano settlement.
75
80
 
76
81
  ```ts
77
82
  import { DirectRequestPaymentMerchantClient } from "@siglume/direct-request-payment";
@@ -98,9 +103,12 @@ const session = await merchant.createCheckoutSession({
98
103
  });
99
104
  redirect(session.checkout_url); // -> https://siglume.com/pay/<session_id>
100
105
 
101
- // 3. Fulfill when the signed direct_payment.confirmed webhook arrives (the
102
- // source of truth). Poll merchant.getCheckoutSession(session.session_id) if
103
- // you also want to show status in your own UI.
106
+ // 3. Handle the signed direct_payment.confirmed webhook. Fulfill Standard only
107
+ // when pricing_band=standard, finality=per_payment_onchain, and
108
+ // settlement_status=settled. Treat Micro / Nano as accepted but unsettled
109
+ // until the later metered settlement batch is settled.
110
+ // Poll merchant.getCheckoutSession(session.session_id) if you also want to
111
+ // show status in your own UI.
104
112
  ```
105
113
 
106
114
  ```py
@@ -130,9 +138,12 @@ session = merchant.create_checkout_session(
130
138
  )
131
139
  redirect(session["checkout_url"]) # -> https://siglume.com/pay/<session_id>
132
140
 
133
- # 3. Fulfill when the signed direct_payment.confirmed webhook arrives (the
134
- # source of truth). Poll merchant.get_checkout_session(session["session_id"])
135
- # if you also want to show status in your own UI.
141
+ # 3. Handle the signed direct_payment.confirmed webhook. Fulfill Standard only
142
+ # when pricing_band=standard, finality=per_payment_onchain, and
143
+ # settlement_status=settled. Treat Micro / Nano as accepted but unsettled
144
+ # until the later metered settlement batch is settled.
145
+ # Poll merchant.get_checkout_session(session["session_id"]) if you also want
146
+ # to show status in your own UI.
136
147
  ```
137
148
 
138
149
  Siglume fixes the amount, currency, challenge, and return URLs **server-side** at
@@ -183,11 +194,19 @@ Provider revenue in the Micro and Nano bands is not settled revenue until the
183
194
  weekly or monthly on-chain settlement succeeds. Siglume keeps outstanding failed
184
195
  settlements for retry under the published policy, but does not advance or
185
196
  guarantee provider revenue before settlement succeeds.
197
+ If your product cannot fulfill before provider revenue is settled, keep the
198
+ price in the Standard band or agree a merchant-specific contract with Siglume
199
+ before launch.
186
200
  Micro / Nano budget checks reserve spending capacity only; they do not lock,
187
201
  escrow, or guarantee the buyer's wallet balance, allowance, or settlement funds.
188
202
  Sub-minor-unit Nano fees are accumulated with decimal precision and rounded only
189
203
  when a settlement batch is created; see [Pricing](./docs/pricing.md) for the
190
204
  rounding formula and `rounding_delta_minor` semantics.
205
+ For low-count Nano batches, integer-token settlement can make the effective
206
+ buyer burden per usage higher than the headline USD 0.001 protocol fee; the
207
+ difference is reported as batch `rounding_delta_minor`. Treat Micro / Nano
208
+ minor amounts as decimal strings and use a decimal library or `Decimal` for
209
+ accounting.
191
210
  For operational reconciliation, expected revenue, settled revenue, retry state,
192
211
  and CSV exports, see
193
212
  [docs/metered-statements.md](./docs/metered-statements.md).
@@ -202,7 +221,8 @@ and CSV exports, see
202
221
  - buyer-authenticated payment requirement creation
203
222
  - prepared wallet transaction execution payloads
204
223
  - payment requirement verification
205
- - authenticated TypeScript JSON requests to Micro / Nano statement APIs
224
+ - authenticated TypeScript JSON requests and named Python helpers for Micro /
225
+ Nano statement APIs
206
226
  - signed webhook verification
207
227
 
208
228
  It does not custody funds or manage customer wallets. Merchant setup runs through
@@ -244,9 +264,11 @@ Launch plan. The current public API does not expose a flag that forces a
244
264
  JPY 500-and-under / USD 3-and-under payment into Standard immediate settlement.
245
265
  If immediate on-chain settlement is a hard requirement, price the item in the
246
266
  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`,
267
+ launch. Public Direct Payment / Hosted Checkout `amount_minor` is a positive
268
+ integer in minor currency units, so public one-time Nano amounts start at JPY 1
269
+ or USD 0.01. For Standard Payment, `fee_bps` returned on a payment requirement
270
+ is the authoritative fee rate for that payment in the merchant's settlement
271
+ currency. For Micro / Nano, the statement APIs expose `protocol_fee_minor`,
250
272
  `gross_buyer_debit_minor`, `buyer_debit_minor`, and `rounding_delta_minor`.
251
273
  The full fee table and the weekly / monthly settlement schedule live in
252
274
  [docs/pricing.md](./docs/pricing.md). Statement APIs for "how much was used,
@@ -358,7 +380,7 @@ The nonce must not contain `:` because the current platform challenge format is
358
380
 
359
381
  ## Buyer Payment Flow
360
382
 
361
- Use `DirectRequestPaymentClient` only with the authenticated buyer's Siglume
383
+ Use `DirectRequestPaymentClient` here with the authenticated buyer's Siglume
362
384
  bearer token. `SIGLUME_AUTH_TOKEN` may be used in server-side payment-confirmation
363
385
  helpers; `SIGLUME_API_KEY` and Developer Portal `cli_` keys are not accepted.
364
386
 
@@ -507,7 +529,19 @@ const { event } = await verifyDirectRequestPaymentWebhook(
507
529
  );
508
530
 
509
531
  if (event.type === "direct_payment.confirmed") {
510
- // Mark the order paid if event.data.challenge_hash/order mapping matches.
532
+ if (event.data.mode === "metered_settlement_batch") {
533
+ // Reconcile settled Micro / Nano batches by settlement_batch_id /
534
+ // usage_event_digest; these events do not carry an order challenge hash.
535
+ } else if (
536
+ event.data.pricing_band === "standard" &&
537
+ event.data.finality === "per_payment_onchain" &&
538
+ event.data.settlement_status === "settled"
539
+ ) {
540
+ // Mark the order paid once if event.data.challenge_hash/order mapping matches.
541
+ } else if (event.data.pricing_band === "micro" || event.data.pricing_band === "nano") {
542
+ // Mark fulfilled-but-unsettled only if your business allows fulfillment
543
+ // before the aggregated Micro / Nano settlement succeeds.
544
+ }
511
545
  }
512
546
  ```
513
547
 
@@ -523,14 +557,30 @@ verified = verify_direct_request_payment_webhook(
523
557
  )
524
558
 
525
559
  if verified["event"]["type"] == "direct_payment.confirmed":
526
- # Mark the order paid if event.data.challenge_hash/order mapping matches.
527
- pass
560
+ data = verified["event"]["data"]
561
+ if data.get("mode") == "metered_settlement_batch":
562
+ # Reconcile settled Micro / Nano batches by settlement_batch_id /
563
+ # usage_event_digest; these events do not carry an order challenge hash.
564
+ pass
565
+ elif (
566
+ data.get("pricing_band") == "standard"
567
+ and data.get("finality") == "per_payment_onchain"
568
+ and data.get("settlement_status") == "settled"
569
+ ):
570
+ # Mark the order paid once if event.data.challenge_hash/order mapping matches.
571
+ pass
572
+ elif data.get("pricing_band") in ("micro", "nano"):
573
+ # Mark fulfilled-but-unsettled only if your business allows fulfillment
574
+ # before the aggregated Micro / Nano settlement succeeds.
575
+ pass
528
576
  ```
529
577
 
530
578
  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.
579
+ `settlement_cadence`, `finality`, `protocol_fee_minor`, `settlement_status`,
580
+ `settlement_batch_id`, `chain_receipt_id`, `usage_event_digest`, `settled_at`,
581
+ and when available `request_hash_v2`. Use these machine fields instead of
582
+ inferring settlement semantics from the event name alone. Do not mark an order
583
+ paid from the event type alone.
534
584
 
535
585
  ## Security Rules
536
586
 
@@ -553,7 +603,8 @@ Read [docs/security.md](./docs/security.md) before going live.
553
603
  - Store `SIGLUME_DIRECT_PAYMENT_CHALLENGE_SECRET` only on the merchant server.
554
604
  - Store the returned `SIGLUME_WEBHOOK_SECRET` only on the merchant server.
555
605
  - Persist `challenge_hash`, `requirement_id`, and fulfillment state per order.
556
- - Fulfill orders only from verified webhook data, with idempotency.
606
+ - Fulfill orders only from verified webhook data, with idempotency, after
607
+ checking `pricing_band`, `finality`, and `settlement_status`.
557
608
  - Treat `fee_bps` returned by Siglume as the Standard Payment runtime fee source
558
609
  of truth; use statement API amount fields for Micro / Nano.
559
610
 
package/dist/index.cjs CHANGED
@@ -78,7 +78,7 @@ var DIRECT_REQUEST_PAYMENT_RECEIPT_KIND = "sdrp_direct_payment";
78
78
  var DIRECT_REQUEST_PAYMENT_ALLOWANCE_RECEIPT_KIND = "sdrp_direct_payment_allowance";
79
79
  var DIRECT_REQUEST_PAYMENT_REFERENCE_TYPE = "sdrp_direct_payment_requirement";
80
80
  var DEFAULT_WEBHOOK_TOLERANCE_SECONDS = 300;
81
- var DIRECT_REQUEST_PAYMENT_SDK_VERSION = "0.4.4";
81
+ var DIRECT_REQUEST_PAYMENT_SDK_VERSION = "0.4.6";
82
82
  var DIRECT_REQUEST_PAYMENT_CONFIRMED_WEBHOOK_MODES = /* @__PURE__ */ new Set([DIRECT_REQUEST_PAYMENT_MODE, "metered_settlement_batch"]);
83
83
  var SiglumeDirectRequestPaymentError = class extends Error {
84
84
  constructor(message) {
@@ -126,7 +126,7 @@ var DirectRequestPaymentClient = class {
126
126
  const authToken = options.auth_token ?? envValue("SIGLUME_AUTH_TOKEN");
127
127
  if (!authToken) {
128
128
  throw new SiglumeDirectRequestPaymentError(
129
- "A buyer Siglume bearer token is required for Direct Request Payment API calls. Developer Portal API keys are not accepted."
129
+ "A buyer or provider Siglume user bearer token is required for Direct Request Payment API calls. Developer Portal API keys are not accepted."
130
130
  );
131
131
  }
132
132
  const fetchImpl = options.fetch ?? globalThis.fetch;
@@ -811,6 +811,9 @@ function meteredQueryPath(path, input) {
811
811
  if ("limit" in input && input.limit !== void 0) {
812
812
  params.set("limit", String(positiveInteger(input.limit, "limit")));
813
813
  }
814
+ if ("cursor" in input && input.cursor !== void 0) {
815
+ params.set("cursor", requireNonEmpty(input.cursor, "cursor"));
816
+ }
814
817
  const query = params.toString();
815
818
  return query ? `${path}?${query}` : path;
816
819
  }