@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/docs/security.md CHANGED
@@ -61,6 +61,11 @@ bare, lowercased origins and deduped. A return URL that is not on an allowed
61
61
  origin is rejected, so an attacker cannot point a session at an arbitrary
62
62
  redirect target.
63
63
 
64
+ Production allowlist entries must use `https`. Development `http` entries are
65
+ accepted only for `http://localhost`, `http://127.0.0.1`, or `http://[::1]`
66
+ (with optional ports). Userinfo such as `https://user@shop.example.com` is
67
+ rejected so an attacker cannot rely on origin-spoofing URL forms.
68
+
64
69
  For a Hosted Checkout session, Siglume authors the amount, currency, challenge,
65
70
  and return URLs server-side at session creation. The browser cannot tamper with
66
71
  the price or the redirect target, and the raw challenge is never exposed to the
@@ -114,6 +119,31 @@ Fulfill exactly once per order. Store at least:
114
119
  Duplicate webhook deliveries and manual redelivery can occur. A duplicate
115
120
  webhook with the same requirement id must not ship the order twice.
116
121
 
122
+ ## Micro / Nano Statement Privacy
123
+
124
+ Micro Payment and Nano Payment introduce operational statement APIs and CSV
125
+ exports because revenue is settled later in aggregated on-chain batches.
126
+
127
+ Provider-facing statement APIs intentionally do not expose raw `buyer_user_id`,
128
+ buyer email, buyer wallet address, relayer id, nonce, gas data, raw RPC errors,
129
+ or raw platform failure messages. Use `buyer_period_ref` for provider-side
130
+ reconciliation within a statement period, and show only the sanitized public
131
+ failure fields:
132
+
133
+ - `failure_reason_code`
134
+ - `failure_reason_label`
135
+ - `failure_reason_help`
136
+ - `support_reference`
137
+
138
+ Buyer-facing APIs may include past-due block reasons and balance / allowance /
139
+ BudgetVault sufficiency indicators for the buyer's own account. Do not forward
140
+ those buyer-account details to providers.
141
+
142
+ Webhooks remain required for fulfillment, but webhooks alone are not a complete
143
+ Micro / Nano revenue ledger. Use the statement APIs or CSV in
144
+ [Micro / Nano Statements and Notices](./metered-statements.md) to separate
145
+ settled, unsettled, and past-due provider amounts.
146
+
117
147
  ## What Direct Request Payment Is Not
118
148
 
119
149
  Direct Request Payment is not:
@@ -125,12 +155,18 @@ Direct Request Payment is not:
125
155
  - a card payment fallback
126
156
 
127
157
  Each payment is an individual wallet payment backed by an on-chain receipt. Small
128
- payments in the Micro and Nano amount bands are aggregated and settled on a
129
- weekly / monthly cadence instead of one transaction at a time (see the
130
- [pricing guide](./pricing.md#settlement-schedule)), but they are still wallet
131
- payments, not a stored balance. Before a small payment is fulfilled, Siglume
132
- checks the buyer's wallet budget and fails closed when it is invalid, so a
133
- rejected request is never charged. Provider revenue for Micro and Nano remains
134
- unsettled until the weekly or monthly on-chain settlement succeeds; Siglume does
135
- not advance or guarantee revenue when a buyer's balance, allowance, BudgetVault
158
+ payments in the Micro and Nano amount bands are aggregated and settled on
159
+ account-assigned weekly / monthly slots instead of one transaction at a time
160
+ (see the [pricing guide](./pricing.md#settlement-schedule)), but they are still
161
+ wallet payments, not a stored balance. Before a small payment is fulfilled,
162
+ Siglume checks the buyer's wallet budget and fails closed when it is invalid, so
163
+ a rejected request is never charged. Provider revenue for Micro and Nano remains
164
+ unsettled until the aggregated on-chain settlement succeeds; Siglume does not
165
+ advance or guarantee revenue when a buyer's balance, allowance, BudgetVault
136
166
  authorization, cap, or on-chain transaction fails.
167
+
168
+ A Micro / Nano budget reservation is not a token lock, escrow, or payment
169
+ guarantee. It reserves room against Siglume spending limits only. A later
170
+ settlement can still fail if the buyer no longer has sufficient balance,
171
+ allowance, BudgetVault authorization, or cap room; `past_due` records the issue
172
+ but does not guarantee eventual collection or provider payment.
@@ -1,13 +1,15 @@
1
1
  import express from "express";
2
2
  import {
3
- createDirectRequestPaymentChallenge,
4
- DirectRequestPaymentClient,
3
+ DirectRequestPaymentMerchantClient,
5
4
  verifyDirectRequestPaymentWebhook,
6
5
  } from "@siglume/direct-request-payment";
7
6
 
8
7
  const app = express();
9
8
  const port = Number(process.env.PORT || 3000);
10
9
  const merchantKey = process.env.SIGLUME_DIRECT_PAYMENT_MERCHANT || "example_merchant";
10
+ const siglumeMerchant = new DirectRequestPaymentMerchantClient({
11
+ auth_token: process.env.SIGLUME_MERCHANT_AUTH_TOKEN,
12
+ });
11
13
 
12
14
  // Use JSON for normal routes. Use raw body only on the webhook route.
13
15
  app.use((req, res, next) => {
@@ -35,49 +37,29 @@ app.post("/checkout/siglume/start", asyncRoute(async (req, res) => {
35
37
  }
36
38
 
37
39
  order.payment_attempt = Number(order.payment_attempt || 0) + 1;
38
- const challenge = await createDirectRequestPaymentChallenge({
40
+ const session = await siglumeMerchant.createCheckoutSession({
39
41
  merchant: merchantKey,
40
42
  amount_minor: order.amount_minor,
41
43
  currency: order.currency,
42
- secret: process.env.SIGLUME_DIRECT_PAYMENT_CHALLENGE_SECRET!,
43
44
  nonce: `${order.id}-attempt_${order.payment_attempt}`,
45
+ success_url: `${process.env.SHOP_PUBLIC_ORIGIN || "https://shop.example.com"}/thanks`,
46
+ cancel_url: `${process.env.SHOP_PUBLIC_ORIGIN || "https://shop.example.com"}/cart`,
47
+ metadata: { order_id: order.id },
44
48
  });
45
49
 
46
- order.siglume_challenge_hash = challenge.challenge_hash;
50
+ order.siglume_challenge_hash = session.challenge_hash;
51
+ order.siglume_checkout_session_id = session.session_id;
47
52
  order.siglume_payment_status = "pending";
48
53
 
49
54
  res.json({
50
55
  order_id: order.id,
51
56
  amount_minor: order.amount_minor,
52
57
  currency: order.currency,
53
- siglume_challenge: challenge.challenge,
58
+ checkout_url: session.checkout_url,
59
+ session_id: session.session_id,
54
60
  });
55
61
  }));
56
62
 
57
- app.post("/checkout/siglume/pay", asyncRoute(async (req, res) => {
58
- const order = orders.get(String(req.body.order_id || ""));
59
- if (!order) {
60
- res.status(404).json({ error: "order_not_found" });
61
- return;
62
- }
63
-
64
- // In production, obtain this from the authenticated buyer's Siglume session
65
- // or a hosted Siglume payment confirmation flow. Do not use a merchant secret
66
- // to charge a customer wallet.
67
- const siglume = new DirectRequestPaymentClient({
68
- auth_token: String(req.headers.authorization || "").replace(/^Bearer\s+/i, ""),
69
- });
70
-
71
- const requirement = await siglume.createPaymentRequirement({
72
- merchant: merchantKey,
73
- amount_minor: order.amount_minor,
74
- currency: order.currency,
75
- challenge: String(req.body.siglume_challenge || ""),
76
- });
77
-
78
- res.json({ requirement });
79
- }));
80
-
81
63
  app.post("/siglume/webhook", express.raw({ type: "application/json" }), asyncRoute(async (req, res) => {
82
64
  const header = String(req.headers["siglume-signature"] || "");
83
65
  const { event } = await verifyDirectRequestPaymentWebhook(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@siglume/direct-request-payment",
3
- "version": "0.4.2",
3
+ "version": "0.4.4",
4
4
  "description": "SDK for the Siglume Direct Request Payment SDRP payment protocol",
5
5
  "keywords": [
6
6
  "siglume",