@siglume/direct-request-payment 0.1.0 → 0.3.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.
@@ -1,77 +1,112 @@
1
- # Merchant Quickstart
2
-
3
- This guide shows the minimum safe Siglume Direct Request Payment flow for an
4
- external merchant.
5
-
6
- ## Actors
7
-
8
- - Merchant server: owns the order, amount, currency, challenge secret, webhook
9
- endpoint, and order fulfillment.
10
- - Buyer: owns the Siglume wallet that pays the DirectPaymentHub transaction.
11
- - Siglume: creates the payment requirement, prepares the wallet transaction,
12
- verifies the receipt, and emits signed webhooks.
13
-
14
- The merchant server must not create charges with a customer wallet. It signs the
15
- order challenge; the buyer-facing Siglume payment flow pays it.
16
-
17
- ## 1. Configure the Merchant
18
-
19
- During onboarding, Siglume assigns:
20
-
21
- - `merchant` key, for example `example_merchant`
22
- - challenge secret
23
- - allowed currency and token, initially `JPY`/`JPYC`; `USD`/`USDC` requires
24
- separately agreed merchant billing terms
25
- - billing plan; see [Pricing](./pricing.md)
26
-
27
- Keep the challenge secret server-side. Create a marketplace webhook subscription
28
- to receive the `whsec_` signing secret.
29
-
30
- ```bash
31
- curl -X POST https://siglume.com/v1/market/webhooks/subscriptions \
32
- -H "Authorization: Bearer <merchant-siglume-bearer-token>" \
33
- -H "Content-Type: application/json" \
34
- -d '{
35
- "callback_url": "https://merchant.example/siglume/webhook",
36
- "event_types": ["direct_payment.confirmed"],
37
- "description": "Direct Request Payment production webhook"
38
- }'
39
- ```
40
-
41
- The `signing_secret` is returned only when the subscription is created or
42
- rotated. Store it as `SIGLUME_WEBHOOK_SECRET`.
43
-
44
- ## 2. Create an Order and Challenge
45
-
46
- The merchant server creates the order before asking Siglume for payment.
47
-
1
+ # Merchant Quickstart
2
+
3
+ This guide shows the minimum safe Siglume Direct Request Payment flow for an
4
+ external merchant.
5
+
6
+ ## Actors
7
+
8
+ - Merchant server: owns the order, amount, currency, challenge secret, webhook
9
+ endpoint, and order fulfillment.
10
+ - Buyer: owns the Siglume wallet that pays the DirectPaymentHub transaction.
11
+ - Siglume: creates the payment requirement, prepares the wallet transaction,
12
+ verifies the receipt, and emits signed webhooks.
13
+
14
+ The merchant server must not create charges with a customer wallet. It signs the
15
+ order challenge; the buyer-facing Siglume payment flow pays it.
16
+
17
+ ## 1. Run Merchant Setup
18
+
19
+ Run setup from the merchant server, CI, or an integration agent with the
20
+ merchant's Siglume JWT. Do not use a Developer Portal `cli_` key here.
21
+
22
+ TypeScript:
23
+
24
+ ```ts
25
+ import { DirectRequestPaymentMerchantClient } from "@siglume/direct-request-payment";
26
+
27
+ const merchantClient = new DirectRequestPaymentMerchantClient({
28
+ auth_token: process.env.SIGLUME_MERCHANT_AUTH_TOKEN!,
29
+ });
30
+
31
+ const setup = await merchantClient.setupCheckout({
32
+ merchant: "example_merchant",
33
+ display_name: "Example Merchant",
34
+ billing_plan: "launch",
35
+ billing_currency: "JPY",
36
+ webhook_callback_url: "https://merchant.example/siglume/webhook",
37
+ max_amount_minor: 100000,
38
+ });
39
+
40
+ console.log(setup.env);
41
+ ```
42
+
43
+ Python:
44
+
45
+ ```py
46
+ import os
47
+
48
+ from siglume_direct_request_payment import DirectRequestPaymentMerchantClient
49
+
50
+ merchant_client = DirectRequestPaymentMerchantClient(
51
+ auth_token=os.environ["SIGLUME_MERCHANT_AUTH_TOKEN"],
52
+ )
53
+
54
+ setup = merchant_client.setup_checkout(
55
+ merchant="example_merchant",
56
+ display_name="Example Merchant",
57
+ billing_plan="launch",
58
+ billing_currency="JPY",
59
+ webhook_callback_url="https://merchant.example/siglume/webhook",
60
+ max_amount_minor=100000,
61
+ )
62
+
63
+ print(setup["env"])
64
+ ```
65
+
66
+ `setupCheckout` / `setup_checkout` performs:
67
+
68
+ - merchant key claim
69
+ - challenge secret creation
70
+ - billing mandate preparation
71
+ - webhook subscription creation for `direct_payment.confirmed` and
72
+ `direct_payment.spent`
73
+
74
+ Store `SIGLUME_DIRECT_PAYMENT_CHALLENGE_SECRET` and `SIGLUME_WEBHOOK_SECRET`
75
+ server-side only. Secrets are returned only when they are created or rotated.
76
+ If the returned billing mandate requires wallet approval, complete that Siglume
77
+ wallet step before accepting production payments.
78
+
79
+ ## 2. Create an Order and Challenge
80
+
81
+ The merchant server creates the order before asking Siglume for payment.
82
+
48
83
  ```ts
49
84
  import { createDirectRequestPaymentChallenge } from "@siglume/direct-request-payment";
50
-
51
- const order = {
52
- id: "order_123",
53
- amount_minor: 1200,
54
- currency: "JPY",
55
- };
56
-
57
- const challenge = await createDirectRequestPaymentChallenge({
58
- merchant: "example_merchant",
59
- amount_minor: order.amount_minor,
60
- currency: order.currency,
61
- secret: process.env.SIGLUME_DIRECT_PAYMENT_CHALLENGE_SECRET!,
62
- nonce: `${order.id}-attempt_1`,
63
- });
64
-
65
- await orders.update(order.id, {
66
- siglume_challenge_hash: challenge.challenge_hash,
67
- siglume_payment_status: "pending",
68
- });
69
-
70
- return {
71
- order_id: order.id,
72
- amount_minor: order.amount_minor,
73
- currency: order.currency,
74
- siglume_challenge: challenge.challenge,
85
+
86
+ const order = {
87
+ id: "order_123",
88
+ amount_minor: 1200,
89
+ currency: "JPY",
90
+ };
91
+
92
+ const challenge = await createDirectRequestPaymentChallenge({
93
+ merchant: "example_merchant",
94
+ amount_minor: order.amount_minor,
95
+ currency: order.currency,
96
+ secret: process.env.SIGLUME_DIRECT_PAYMENT_CHALLENGE_SECRET!,
97
+ nonce: `${order.id}-attempt_1`,
98
+ });
99
+
100
+ await orders.update(order.id, {
101
+ siglume_challenge_hash: challenge.challenge_hash,
102
+ siglume_payment_status: "pending",
103
+ });
104
+
105
+ return {
106
+ order_id: order.id,
107
+ amount_minor: order.amount_minor,
108
+ currency: order.currency,
109
+ siglume_challenge: challenge.challenge,
75
110
  };
76
111
  ```
77
112
 
@@ -114,25 +149,25 @@ return {
114
149
 
115
150
  Never calculate `amount_minor` from browser input.
116
151
  The nonce must be unique per order payment attempt and must not contain `:`.
117
-
118
- ## 3. Buyer Creates and Pays the Requirement
119
-
120
- After the buyer authenticates with Siglume, create the payment requirement with
121
- the buyer's Siglume bearer token. Do not use a Developer Portal `cli_` API key
122
- or merchant API key here.
123
-
152
+
153
+ ## 3. Buyer Creates and Pays the Requirement
154
+
155
+ After the buyer authenticates with Siglume, create the payment requirement with
156
+ the buyer's Siglume bearer token. Do not use a Developer Portal `cli_` API key
157
+ or merchant API key here.
158
+
124
159
  ```ts
125
160
  import { DirectRequestPaymentClient } from "@siglume/direct-request-payment";
126
-
127
- const siglume = new DirectRequestPaymentClient({
128
- auth_token: buyerSiglumeBearerToken,
129
- });
130
-
131
- const requirement = await siglume.createPaymentRequirement({
132
- merchant: "example_merchant",
133
- amount_minor: order.amount_minor,
134
- currency: order.currency,
135
- challenge: order.siglume_challenge,
161
+
162
+ const siglume = new DirectRequestPaymentClient({
163
+ auth_token: buyerSiglumeBearerToken,
164
+ });
165
+
166
+ const requirement = await siglume.createPaymentRequirement({
167
+ merchant: "example_merchant",
168
+ amount_minor: order.amount_minor,
169
+ currency: order.currency,
170
+ challenge: order.siglume_challenge,
136
171
  });
137
172
  ```
138
173
 
@@ -153,16 +188,16 @@ requirement = siglume.create_payment_requirement(
153
188
 
154
189
  If Siglume returns `approve_transaction_request`, execute it first. Then execute
155
190
  the payment transaction and verify the receipt.
156
-
157
- ```ts
158
- if (requirement.approve_transaction_request) {
159
- await siglume.executeAllowanceTransaction(requirement, { await_finality: true });
160
- }
161
-
162
- const payment = await siglume.executePaymentTransaction(requirement, {
163
- await_finality: true,
164
- });
165
-
191
+
192
+ ```ts
193
+ if (requirement.approve_transaction_request) {
194
+ await siglume.executeAllowanceTransaction(requirement, { await_finality: true });
195
+ }
196
+
197
+ const payment = await siglume.executePaymentTransaction(requirement, {
198
+ await_finality: true,
199
+ });
200
+
166
201
  await siglume.verifyPaymentRequirement(requirement.requirement_id, {
167
202
  receipt_id: String(payment.receipt?.receipt_id ?? ""),
168
203
  });
@@ -183,27 +218,27 @@ siglume.verify_payment_requirement(
183
218
  ```
184
219
 
185
220
  ## 4. Fulfill from Webhook
186
-
187
- Use the webhook as the durable signal, not just the browser return path.
188
-
221
+
222
+ Use the webhook as the durable signal, not just the browser return path.
223
+
189
224
  ```ts
190
225
  import { verifyDirectRequestPaymentWebhook } from "@siglume/direct-request-payment";
191
-
192
- const { event } = await verifyDirectRequestPaymentWebhook(
193
- process.env.SIGLUME_WEBHOOK_SECRET!,
194
- rawRequestBody,
195
- siglumeSignatureHeader,
196
- );
197
-
198
- if (event.type === "direct_payment.confirmed") {
199
- const data = event.data;
200
- const order = await orders.findByChallengeHash(String(data.challenge_hash ?? ""));
201
- if (!order) {
202
- throw new Error("Unknown Siglume challenge hash");
203
- }
204
- await orders.markPaidOnce(order.id, {
205
- siglume_requirement_id: String(data.requirement_id ?? data.direct_payment_requirement_id ?? ""),
206
- });
226
+
227
+ const { event } = await verifyDirectRequestPaymentWebhook(
228
+ process.env.SIGLUME_WEBHOOK_SECRET!,
229
+ rawRequestBody,
230
+ siglumeSignatureHeader,
231
+ );
232
+
233
+ if (event.type === "direct_payment.confirmed") {
234
+ const data = event.data;
235
+ const order = await orders.findByChallengeHash(String(data.challenge_hash ?? ""));
236
+ if (!order) {
237
+ throw new Error("Unknown Siglume challenge hash");
238
+ }
239
+ await orders.markPaidOnce(order.id, {
240
+ siglume_requirement_id: String(data.requirement_id ?? data.direct_payment_requirement_id ?? ""),
241
+ });
207
242
  }
208
243
  ```
209
244
 
@@ -232,24 +267,30 @@ if verified["event"]["type"] == "direct_payment.confirmed":
232
267
  ```
233
268
 
234
269
  ## Failure Handling
235
-
236
- - `EXTERNAL_402_CHALLENGE_REQUIRED`: the merchant server did not provide a
237
- challenge.
238
- - `INVALID_EXTERNAL_402_CHALLENGE`: the amount, currency, merchant, nonce, or
239
- signature does not match.
240
- - `EXTERNAL_402_CHALLENGE_ALREADY_USED`: the challenge is already bound to a
241
- different buyer.
242
- - `EXTERNAL_402_MERCHANT_BILLING_SETUP_REQUIRED`: merchant onboarding is not
243
- complete.
244
- - `EXTERNAL_402_MERCHANT_BILLING_PAST_DUE` or
245
- `EXTERNAL_402_MERCHANT_BILLING_SUSPENDED`: merchant billing must be fixed
246
- before new payments can be accepted.
247
-
248
- ## Go-Live Checklist
249
-
250
- - Challenge secret is only in server-side environment variables.
251
- - Webhook endpoint receives raw body and verifies `Siglume-Signature`.
252
- - Orders store `challenge_hash`, `requirement_id`, and fulfillment status.
253
- - Fulfillment is idempotent.
254
- - Browser input cannot change the amount or currency.
255
- - Nonces cannot be reused for separate order attempts.
270
+
271
+ - `EXTERNAL_402_CHALLENGE_REQUIRED`: the merchant server did not provide a
272
+ challenge.
273
+ - `INVALID_EXTERNAL_402_CHALLENGE`: the amount, currency, merchant, nonce, or
274
+ signature does not match.
275
+ - `EXTERNAL_402_CHALLENGE_ALREADY_USED`: the challenge is already bound to a
276
+ different buyer.
277
+ - `EXTERNAL_402_MERCHANT_NOT_FOUND`: run merchant setup with the merchant's
278
+ Siglume JWT.
279
+ - `EXTERNAL_402_MERCHANT_BILLING_SETUP_REQUIRED`: the merchant billing mandate
280
+ is not active yet.
281
+ - `EXTERNAL_402_MERCHANT_BILLING_PAST_DUE` or
282
+ `EXTERNAL_402_MERCHANT_BILLING_SUSPENDED`: merchant billing must be fixed
283
+ before new payments can be accepted.
284
+
285
+ ## Go-Live Checklist
286
+
287
+ - `setupCheckout` / `setup_checkout` has claimed the merchant key.
288
+ - Merchant billing mandate is active.
289
+ - Challenge secret is only in server-side environment variables.
290
+ - Webhook endpoint receives raw body and verifies `Siglume-Signature`.
291
+ - Orders store `challenge_hash`, `requirement_id`, and fulfillment status.
292
+ - Fulfillment is idempotent.
293
+ - Browser input cannot change the amount or currency.
294
+ - Nonces cannot be reused for separate order attempts.
295
+ - The order is fulfilled only after a verified webhook maps back to the stored
296
+ `challenge_hash`.
package/docs/pricing.md CHANGED
@@ -1,56 +1,73 @@
1
- # Pricing
2
-
3
- This page documents the trial-phase merchant pricing for Siglume Direct Request
4
- Payment as of 2026-06-11. Pricing can change by agreement or future product
5
- release; the Siglume platform response is the source of truth for per-payment
6
- fee data returned at runtime.
7
-
8
- ## Trial Plans
9
-
10
- | Plan | Monthly fee | Payment fee | Intended starting point |
11
- | --- | ---: | ---: | --- |
12
- | Launch | JPY 0 | 0% through 100 payments/month, then 1.8% | Proofs of concept and low-volume trials |
13
- | Starter | JPY 980 | 1.0% | Early production checkout trials |
14
- | Growth | JPY 2,980 | 0.7% | Growing EC, booking, membership, and API services |
15
- | Pro | JPY 9,800 | 0.5% | Higher-volume merchant integrations |
16
-
17
- The minimum fee is JPY 3 for each fee-bearing payment, including Launch-plan
18
- payments after the included monthly allowance.
19
-
20
- If no paid plan is selected during onboarding, the merchant account uses the
21
- Launch plan. A merchant billing mandate is still required before accepting
22
- payments so Siglume can collect fees automatically after the 100-payment monthly
23
- allowance is exceeded.
24
-
25
- The current Siglume API and merchant registry may still expose the internal
26
- `billing_plan` value `free` for the Launch tier. Treat `free` as an internal
27
- compatibility key, not the public plan name.
28
-
29
- The 100-payment monthly allowance is not a hard processing cap. Payments after
30
- the allowance can continue when merchant billing is active, and those payments
31
- are fee-bearing at the Launch overage rate.
32
-
33
- Per-payment fees are collected during payment settlement through the
34
- DirectPaymentHub split. The merchant receives the net amount after that fee.
35
- Monthly base fees are collected separately through the merchant billing mandate.
36
-
37
- The public trial pricing above is JPY-denominated. If a merchant needs USD/USDC
38
- settlement, agree the USD merchant billing terms during onboarding; do not infer
39
- USD monthly or minimum fees from the JPY table.
40
-
41
- ## SDK Behavior
42
-
43
- The SDK does not calculate merchant invoices or enforce plan limits locally.
44
- Instead, it exposes billing-related values returned by Siglume, including
45
- `fee_bps` on a payment requirement. This keeps merchant billing centralized in
46
- the Siglume platform and avoids stale client-side pricing logic.
47
-
48
- ## Supported Use Cases
49
-
50
- The trial pricing is intended for:
51
-
52
- - Small EC checkout
53
- - Booking and reservation services
54
- - Membership services
55
- - Paid API access
56
- - Agent-to-agent payment experiments
1
+ # Pricing
2
+
3
+ This page documents the trial-phase merchant pricing for Siglume Direct Request
4
+ Payment as of 2026-06-12. Pricing can change by agreement or future product
5
+ release; the Siglume platform response is the source of truth for per-payment
6
+ fee data returned at runtime.
7
+
8
+ ## Settlement Currencies
9
+
10
+ Siglume Direct Request Payment launches in the US and Japan, and both settlement
11
+ currencies are first-class:
12
+
13
+ - **JPY**, settled on-chain in **JPYC**
14
+ - **USD**, settled on-chain in **USDC**
15
+
16
+ A merchant settles in a single currency, chosen at onboarding. The settlement fee
17
+ percentage (the payment fee column below) is identical in both currencies. Only
18
+ the flat amounts — the monthly base fee and the per-payment minimum fee — are
19
+ quoted per currency.
20
+
21
+ ## Trial Plans
22
+
23
+ | Plan | Monthly fee (JPY) | Monthly fee (USD) | Payment fee | Intended starting point |
24
+ | --- | ---: | ---: | ---: | --- |
25
+ | Launch | JPY 0 | USD 0 | 1.8% | Proofs of concept and low-volume trials |
26
+ | Starter | JPY 980 | USD 6.00 | 1.0% | Early production checkout trials |
27
+ | Growth | JPY 2,980 | USD 18.00 | 0.7% | Growing EC, booking, membership, and API services |
28
+ | Pro | JPY 9,800 | USD 60.00 | 0.5% | Higher-volume merchant integrations |
29
+
30
+ Every payment is fee-bearing at the plan rate. The minimum fee is JPY 30
31
+ (USD merchants: USD 0.20) per payment. The minimum covers the worst-case
32
+ per-payment settlement cost (an on-chain signature plus network gas), so small
33
+ payments are never processed at a loss; on larger payments the percentage rate
34
+ applies instead.
35
+
36
+ USD pricing is the JPY tier converted at roughly 160 JPY/USD and rounded to
37
+ clean price points that keep the same 1:3:10 tier ratio.
38
+
39
+ If no paid plan is selected during merchant setup, the merchant account uses the
40
+ Launch plan. A merchant billing mandate is still required before accepting
41
+ payments so Siglume can collect the monthly base fee automatically.
42
+
43
+ The current Siglume API and merchant registry may still expose the internal
44
+ `billing_plan` value `free` for the Launch tier. Treat `free` as an internal
45
+ compatibility key, not the public plan name. (Until 2026-06-12 the Launch plan
46
+ included a free monthly allowance of 100 payments; that allowance has been
47
+ retired — the platform `fee_bps` response is always the source of truth.)
48
+
49
+ Per-payment fees are collected during payment settlement through the
50
+ DirectPaymentHub split. The merchant receives the net amount after that fee.
51
+ Monthly base fees are collected separately through the merchant billing mandate.
52
+
53
+ The same fee schedule applies in JPY and USD. The Siglume platform returns
54
+ `fee_bps` in the merchant's settlement currency on every payment requirement, so
55
+ the SDK never has to know which currency table to read — it just trusts the
56
+ value Siglume returns.
57
+
58
+ ## SDK Behavior
59
+
60
+ The SDK does not calculate merchant invoices or enforce plan limits locally.
61
+ Instead, it exposes billing-related values returned by Siglume, including
62
+ `fee_bps` on a payment requirement. This keeps merchant billing centralized in
63
+ the Siglume platform and avoids stale client-side pricing logic.
64
+
65
+ ## Supported Use Cases
66
+
67
+ The trial pricing is intended for:
68
+
69
+ - Small EC checkout
70
+ - Booking and reservation services
71
+ - Membership services
72
+ - Paid API access
73
+ - Agent-to-agent payment experiments