@siglume/direct-request-payment 0.4.18 → 0.4.20

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.
@@ -356,7 +356,8 @@ Returns:
356
356
  `merchant.merchant_account.metadata_jsonb.metered_risk_acceptance` records the
357
357
  merchant's Micro / Nano delayed-settlement risk acceptance receipt with
358
358
  `terms_version`, `accepted_at`, `principal_user_id`, `receipt_id`, and fixed
359
- market thresholds `JPY: 10000` / `USD: 10000`.
359
+ market thresholds JPY 10,000 / USD 100.00 (`settlement_threshold_minor` is
360
+ `10000` for both JPY minor units and USD cents).
360
361
 
361
362
  Secrets are returned only when created or rotated. Existing secrets are not
362
363
  replayed by `getMerchant` / `get_merchant`.
@@ -372,7 +373,9 @@ POST /v1/sdrp/direct-payments/merchants
372
373
  Creates or updates the merchant account for the authenticated merchant user.
373
374
  Accepts the optional `checkout_allowed_origins: string[]` return-URL origin
374
375
  allowlist described under `setupCheckout` above; the same normalization and
375
- webhook-origin auto-allow apply.
376
+ webhook-origin auto-allow apply. Python annotates this direct response as
377
+ `DirectRequestPaymentMerchantResponse`; `setup_checkout(...)` returns
378
+ `DirectRequestPaymentCheckoutSetupResult`.
376
379
 
377
380
  ### `createCheckoutSession(input)` / `create_checkout_session(...)`
378
381
 
@@ -535,6 +538,41 @@ Defaults event types to `direct_payment.confirmed` and
535
538
  `direct_payment.spent`. The returned `signing_secret` is shown only at creation
536
539
  or rotation.
537
540
 
541
+ ### `listWebhookSubscriptions()` / `list_webhook_subscriptions()`
542
+
543
+ Calls:
544
+
545
+ ```text
546
+ GET /v1/market/webhooks/subscriptions
547
+ ```
548
+
549
+ Returns the current user's webhook subscriptions without the full signing
550
+ secret. Use `signing_secret_hint` to confirm that the local
551
+ `SIGLUME_WEBHOOK_SECRET` is the expected secret.
552
+
553
+ ### `queueWebhookTestDelivery(input)` / `queue_webhook_test_delivery(...)`
554
+
555
+ Calls:
556
+
557
+ ```text
558
+ POST /v1/market/webhooks/test-deliveries
559
+ ```
560
+
561
+ Queues a signed test event to one or more subscription ids. `siglume-check
562
+ readiness` uses this for a harmless `direct_payment.confirmed` readiness probe.
563
+
564
+ ### `listWebhookDeliveries(input)` / `list_webhook_deliveries(...)`
565
+
566
+ Calls:
567
+
568
+ ```text
569
+ GET /v1/market/webhooks/deliveries
570
+ ```
571
+
572
+ Supports `subscription_id`, `event_type`, `status`, and `limit`. Readiness polls
573
+ this list after queueing the test delivery and only passes when the matching
574
+ delivery status becomes `delivered`.
575
+
538
576
  ## `DirectRequestPaymentClient`
539
577
 
540
578
  Thin wrapper around the current Siglume Direct Request Payment HTTP contract.
@@ -1012,10 +1050,10 @@ the `Siglume-Signature` header; use
1012
1050
  `verifyDirectRequestPaymentWebhook(...)` /
1013
1051
  `verify_direct_request_payment_webhook(...)` for signature verification and
1014
1052
  parsing together. Throws
1015
- `SiglumeWebhookPayloadError` on a malformed event, or when a
1016
- `direct_payment.confirmed` event does not carry a supported Direct Request
1017
- Payment mode (`external_402` or `metered_settlement_batch`). The `payload`
1018
- argument is positional in both languages.
1053
+ `SiglumeWebhookPayloadError` on a malformed event. It does not reject an
1054
+ unsupported `direct_payment.confirmed` mode by itself; the classifier returns
1055
+ `kind: "unknown"` with `reason: "unsupported_confirmation_mode"` for that case.
1056
+ The `payload` argument is positional in both languages.
1019
1057
 
1020
1058
  For `direct_payment.confirmed`, inspect `event.data.pricing_band`,
1021
1059
  `event.data.settlement_cadence`, `event.data.finality`,
@@ -1039,13 +1077,15 @@ Recommended branch: call `classifyDirectPaymentConfirmation(event)` /
1039
1077
  pricing, per-payment on-chain finality, settled status, non-empty
1040
1078
  `requirement_id`, non-empty `challenge_hash`, and non-empty
1041
1079
  `chain_receipt_id`.
1042
- - `metered_usage_accepted`: treat the usage as accepted but unsettled. This
1043
- requires Micro / Nano pricing, `finality === "aggregated_onchain_settlement"`,
1044
- the matching settlement cadence (`micro` -> `weekly`, `nano` -> `monthly`),
1080
+ - `metered_usage_accepted`: treat the usage as accepted but unsettled only if
1081
+ your integration has explicitly enabled Micro / Nano delayed-settlement
1082
+ handling. This requires Micro / Nano pricing,
1083
+ `finality === "aggregated_onchain_settlement"`, the matching settlement
1084
+ cadence (`micro` -> `weekly`, `nano` -> `monthly`),
1045
1085
  `settlement_status === "pending_settlement"`, non-empty `requirement_id`, and
1046
- non-empty `challenge_hash`. SDRP merchant setup and terms assume the merchant
1047
- accepts this delayed aggregated settlement model for Micro / Nano amount
1048
- bands; reconcile final revenue from statement APIs / settlement batches.
1086
+ non-empty `challenge_hash`. Standard-only integrations should route this to
1087
+ review or return `METERED_INTEGRATION_REQUIRED`; Micro / Nano integrations
1088
+ must reconcile final revenue from statement APIs / settlement batches.
1049
1089
  - `unknown`: do not mark paid or fulfilled from the event type alone; fetch the
1050
1090
  requirement or route the event to manual review.
1051
1091
 
@@ -1153,7 +1193,11 @@ package exports `TypedDict` names for the high-risk response shapes:
1153
1193
  - `DirectRequestPaymentPastDueBlock`
1154
1194
  - `DirectRequestPaymentProviderMeteredTotals`
1155
1195
  - `DirectRequestPaymentListResponse`
1156
- - `DirectRequestPaymentMerchantSetupResponse`
1196
+ - `DirectRequestPaymentMerchantResponse`
1197
+ - `DirectRequestPaymentCheckoutSetupResult`
1198
+ - `DirectRequestPaymentMerchantSetupResponse` (compatibility alias for checkout setup result)
1199
+ - `DirectRequestPaymentWebhookSubscription`
1200
+ - `DirectRequestPaymentWebhookDelivery`
1157
1201
  - `DirectRequestPaymentWebhookVerification`
1158
1202
  - `DirectRequestPaymentConfirmationClassification`
1159
1203
 
@@ -3,12 +3,11 @@
3
3
  This guide shows the minimum safe Siglume Direct Request Payment flow for an
4
4
  external merchant.
5
5
 
6
- For the shortest first-test path, use
7
- [10-Minute First Test Payment](./quickstart-10-minutes.md). That guide covers
8
- only one Standard Payment test after account, Hosted Checkout, billing mandate,
9
- HTTPS webhook, and buyer wallet prerequisites are ready. This merchant
10
- quickstart is broader and includes the agent/API path plus Micro / Nano
11
- reconciliation notes.
6
+ For the shortest existing-product integration path, use
7
+ [10-Minute Product Integration](./quickstart-10-minutes.md). That guide copies
8
+ checkout and webhook routes into an Express or FastAPI product and verifies
9
+ Hosted Checkout readiness before coding. This merchant quickstart is broader
10
+ and includes the agent/API path plus Micro / Nano reconciliation notes.
12
11
 
13
12
  ## Actors
14
13
 
@@ -460,16 +459,9 @@ if (confirmation.kind === "standard_settled") {
460
459
  }
461
460
 
462
461
  if (confirmation.kind === "metered_usage_accepted") {
463
- const order = await orders.findByChallengeHash(confirmation.challenge_hash);
464
- if (!order) {
465
- await orders.flagForPaymentStateReview({
466
- reason: "unknown_metered_challenge_hash",
467
- requirement_id: confirmation.requirement_id,
468
- });
469
- return new Response(null, { status: 204 });
470
- }
471
- await orders.markFulfilledButUnsettledOnce(order.id, {
472
- siglume_requirement_id: confirmation.requirement_id,
462
+ await orders.flagForPaymentStateReview({
463
+ reason: "metered_integration_required",
464
+ requirement_id: confirmation.requirement_id,
473
465
  pricing_band: confirmation.pricing_band,
474
466
  });
475
467
  return new Response(null, { status: 204 });
@@ -532,16 +524,9 @@ if confirmation["kind"] == "standard_settled":
532
524
  return "", 204
533
525
 
534
526
  if confirmation["kind"] == "metered_usage_accepted":
535
- order = orders.find_by_challenge_hash(confirmation["challenge_hash"])
536
- if not order:
537
- orders.flag_for_payment_state_review(
538
- reason="unknown_metered_challenge_hash",
539
- requirement_id=confirmation["requirement_id"],
540
- )
541
- return "", 204
542
- orders.mark_fulfilled_but_unsettled_once(
543
- order["id"],
544
- siglume_requirement_id=confirmation["requirement_id"],
527
+ orders.flag_for_payment_state_review(
528
+ reason="metered_integration_required",
529
+ requirement_id=confirmation["requirement_id"],
545
530
  pricing_band=confirmation["pricing_band"],
546
531
  )
547
532
  return "", 204
@@ -195,9 +195,12 @@ Threshold-control fields:
195
195
  - `settlement_trigger`: `amount_threshold` or `scheduled_close`
196
196
  - `settlement_threshold_minor`: JPY `10000` or USD `10000` minor units
197
197
  - `threshold_reached_at`: set when the fixed amount threshold closed the batch
198
- - `total_unsettled_exposure_minor`: open plus `notice_pending`, `ready`,
199
- `submitted`, retrying, and `past_due` provider gross exposure for the same
200
- buyer / provider / token / pricing band
198
+ - `total_unsettled_exposure_minor`: chargeable provider gross exposure for the
199
+ same buyer / provider / token / pricing band where the batch is not
200
+ `settled`, `uncollectible`, or `written_off`. This includes open usage,
201
+ `notice_pending`, `notice_delivery_failed`, `ready`, `submitted`,
202
+ `submitted_reconcile_required`, `failed_retryable`, `retrying`, and
203
+ `past_due`.
201
204
 
202
205
  JPY 10,000 and USD 100.00 are market-specific fixed thresholds, not FX
203
206
  conversions of one another.
@@ -318,7 +321,7 @@ Important batch fields:
318
321
  | `settlement_trigger` | `amount_threshold` for early threshold close, or `scheduled_close` for weekly/monthly close |
319
322
  | `settlement_threshold_minor` | Fixed market threshold for early settlement: JPY `10000` or USD `10000` minor units |
320
323
  | `threshold_reached_at` | Timestamp when the fixed threshold closed the batch, otherwise null |
321
- | `total_unsettled_exposure_minor` | Current open plus notice/ready/submitted/retrying/past-due provider gross exposure for the same buyer / provider / token / pricing band |
324
+ | `total_unsettled_exposure_minor` | Chargeable provider gross exposure for the same buyer / provider / token / pricing band where status is not `settled`, `uncollectible`, or `written_off`; includes open, notice, ready, submitted, reconcile-required, retryable, retrying, and past-due states |
322
325
  | `expected_scheduled_debit_at` | Expected debit time for an open period before a batch exists |
323
326
  | `scheduled_debit_at` | Scheduled debit time after batch creation |
324
327
  | `not_before_attempt_at` | Earliest allowed debit attempt; this is the close-plus-3-day gate |
@@ -398,10 +401,10 @@ Siglume retries failed Micro / Nano settlement every 6 hours for up to 28
398
401
  automatic attempts. After that the batch remains `past_due` until operator
399
402
  requeue.
400
403
 
401
- New Micro / Nano usage for the same buyer / provider / token is paused while
402
- the total unsettled exposure is at or above the fixed threshold, and while a
403
- failed or past-due block remains. The provider API is not called for the
404
- rejected request, and the request is not charged.
404
+ New Micro / Nano usage for the same buyer / provider / token / pricing band is
405
+ paused while the total unsettled exposure is at or above the fixed threshold,
406
+ and while a failed or past-due block remains. The provider API is not called for
407
+ the rejected request, and the request is not charged.
405
408
 
406
409
  Public failure fields are sanitized. Show `failure_reason_code`,
407
410
  `failure_reason_label`, `failure_reason_help`, and `support_reference` to users
@@ -449,13 +452,12 @@ using stable idempotency keys and provider-side completion records.
449
452
  | --- | --- | --- | --- | --- |
450
453
  | `notice_delivery_failed` | Buyer debit is not yet allowed; provider revenue remains unsettled | Notice delivery can be retried or reviewed | Required if delivery keeps failing | Do not attempt your own debit notice or mark revenue settled. Show support context only. |
451
454
  | `submitted_reconcile_required` | A settlement submission exists but final on-chain outcome is not yet reconciled | Reconciliation may complete if a receipt is found | Required if reconciliation stalls | Do not retry payment yourself. Wait for `settled`, `failed_retryable`, or `past_due`. |
452
- | `past_due` | Buyer has an unresolved settlement block; provider sees past-due revenue | New Micro / Nano usage for the same buyer / plan / token is paused | Operator requeue or manual resolution only | Do not promise collection or provider payment. Ask the buyer to repair balance / allowance / BudgetVault / caps and reference `support_reference`. |
455
+ | `past_due` | Buyer has an unresolved settlement block; provider sees past-due revenue | New Micro / Nano usage for the same buyer / provider / token / pricing band is paused | Operator requeue or manual resolution only | Do not promise collection or provider payment. Ask the buyer to repair balance / allowance / BudgetVault / caps and reference `support_reference`. |
453
456
  | `failed_chargeable` | Usage is still chargeable because provider work was accepted or completed | Included in later settlement attempts | Review if the provider disputes completion | Keep fulfillment idempotent and preserve evidence keyed by idempotency key. |
454
457
 
455
- Future platform versions may add explicit terminal states such as
456
- `closed_unpaid`, `uncollectible`, or `written_off`. Treat unknown terminal
457
- settlement states as not settled unless `status === "settled"` and
458
- `chain_receipt_id` is present.
458
+ Terminal public states include `uncollectible` and `written_off` after operator
459
+ review. Treat unknown terminal settlement states as not settled unless
460
+ `status === "settled"` and `chain_receipt_id` is present.
459
461
 
460
462
  ## Operational Recipes
461
463
 
@@ -32,7 +32,7 @@ checkout open or agent/API payment starts
32
32
  -> usage accepted
33
33
  -> direct_payment.confirmed webhook
34
34
  -> classifier kind: metered_usage_accepted
35
- -> merchant may fulfill as fulfilled_unsettled
35
+ -> merchant may fulfill as fulfilled_unsettled only after enabling Micro/Nano handling
36
36
  -> open period closes by amount threshold or schedule
37
37
  -> final notice window
38
38
  -> submitted / retrying / past_due if needed
@@ -41,10 +41,12 @@ checkout open or agent/API payment starts
41
41
  -> provider revenue is settled
42
42
  ```
43
43
 
44
- For Micro / Nano, `metered_usage_accepted` means the usage can be fulfilled
45
- under the SDRP delayed settlement model, but provider revenue is not settled
46
- yet. Provider revenue becomes settled only when the settlement batch is settled
47
- on-chain and has a `chain_receipt_id`.
44
+ For Micro / Nano, `metered_usage_accepted` means the usage can be fulfilled only
45
+ by an integration that has explicitly accepted SDRP delayed settlement and
46
+ implemented fulfilled-but-unsettled state, settlement reconciliation, past-due
47
+ handling, and terminal accounting. Provider revenue is not settled yet. Provider
48
+ revenue becomes settled only when the settlement batch is settled on-chain and
49
+ has a `chain_receipt_id`.
48
50
 
49
51
  ## Field meanings
50
52
 
@@ -52,7 +54,7 @@ on-chain and has a `chain_receipt_id`.
52
54
  | --- | --- | --- |
53
55
  | Hosted Checkout `status: "paid"` | The checkout session accepted the wallet payment flow. | For Micro / Nano, it does not mean provider revenue is settled. |
54
56
  | `standard_settled` | Standard payment is on-chain settled and can mark an order paid. | It is not used for Micro / Nano accepted usage. |
55
- | `metered_usage_accepted` | Micro / Nano usage is accepted and may be fulfilled as unsettled. | It is not settled provider revenue. |
57
+ | `metered_usage_accepted` | Micro / Nano usage is accepted for integrations that have enabled delayed settlement handling. | It is not settled provider revenue, and Standard-only integrations should not fulfill it. |
56
58
  | `fulfilled_unsettled` | Your merchant system delivered the item before Micro / Nano settlement. | It is not a Siglume settlement status. |
57
59
  | `metered_batch_settled` | Aggregated Micro / Nano batch settled on-chain. | It does not identify one order by challenge hash. |
58
60
  | `pending_settlement` | Micro / Nano usage is waiting for aggregated settlement. | It is not a failure by itself. |
@@ -65,9 +67,10 @@ on-chain and has a `chain_receipt_id`.
65
67
  re-stringified JSON object.
66
68
  - Store `challenge_hash` on the order before redirecting the buyer.
67
69
  - For Standard, mark paid only from `standard_settled`.
68
- - For Micro / Nano, use a separate local state such as
69
- `fulfilled_unsettled`; reconcile final revenue from statement APIs and batch
70
- settlement events.
70
+ - For Micro / Nano, fulfill only after you have explicitly enabled delayed
71
+ settlement handling. Use a separate local state such as
72
+ `fulfilled_unsettled`, then reconcile final revenue from statement APIs and
73
+ batch settlement events.
71
74
  - Treat `unknown` classifications as manual review. Do not mark paid or
72
75
  fulfilled from the event name alone.
73
76
 
package/docs/pricing.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # Pricing
2
2
 
3
3
  This page documents the trial-phase merchant pricing for Siglume Direct Request
4
- Payment as of SDK v0.4.18. Pricing can change by agreement or future product
4
+ Payment as of SDK v0.4.20. Pricing can change by agreement or future product
5
5
  release; the Siglume platform response is the source of truth for per-payment
6
6
  fee data returned at runtime.
7
7
 
@@ -156,9 +156,12 @@ confirmed payment turns into money in your settlement wallet.
156
156
  - While the same buyer / provider / token / pricing band has total unsettled
157
157
  exposure at or above the fixed threshold, new Micro/Nano usage is paused with the
158
158
  machine-readable error `METERED_SETTLEMENT_PAST_DUE`; the provider API is not
159
- called. Exposure includes open usage plus `notice_pending`, `ready`,
160
- `submitted`, retrying, and `past_due` batches, and remains paused while
161
- settlement failure or `past_due` is unresolved.
159
+ called. Exposure is chargeable provider gross where status is not `settled`,
160
+ `uncollectible`, or `written_off`; it includes open usage,
161
+ `notice_pending`, `notice_delivery_failed`, `ready`, `submitted`,
162
+ `submitted_reconcile_required`, `failed_retryable`, `retrying`, and
163
+ `past_due`. Usage remains paused while settlement failure or `past_due` is
164
+ unresolved.
162
165
  - Outstanding amounts remain attached to the failed settlement and are retried
163
166
  under this policy. They are not settled revenue, and Siglume does not advance,
164
167
  guarantee, or insure provider revenue before on-chain settlement succeeds.
@@ -1,176 +1,174 @@
1
- # 10-Minute First Test Payment
1
+ # 10-Minute Product Integration
2
2
 
3
- This guide is the shortest supported path to one **Standard Payment** test
4
- through Siglume wallet Hosted Checkout. It is not a full production launch.
3
+ This guide is the supported 10-minute path for adding SDRP Hosted Checkout to
4
+ an existing product. The goal is to
5
+ add two routes to your own server:
5
6
 
6
- ## What the 10 minutes cover
7
+ - `POST /payments/checkout/siglume/start`
8
+ - `POST /payments/webhooks/siglume`
7
9
 
8
- You can count the 10 minutes only after the prerequisites below are already
9
- ready. The target outcome is:
10
+ The SDK supplies the readiness check, route files, webhook verification, payment
11
+ classification, and the order-store adapter contract. Your app supplies the
12
+ real order lookup and fulfillment writes.
10
13
 
11
- - one Standard-band order,
12
- - one Hosted Checkout session,
13
- - one signed `direct_payment.confirmed` webhook,
14
- - one idempotent local fulfillment decision.
14
+ ## 0. Readiness first
15
15
 
16
- This guide does **not** cover production monitoring, refunds, subscriptions,
17
- scheduled autopay, game entitlement recovery, or Micro / Nano accounting.
16
+ Install the SDK in your product.
18
17
 
19
- ## Prerequisites
18
+ Node / Express:
20
19
 
21
- Before starting, confirm:
20
+ ```bash
21
+ npm install @siglume/direct-request-payment
22
+ ```
22
23
 
23
- - You have a Siglume merchant account and merchant Siglume bearer token.
24
- - Hosted Checkout is enabled for that merchant account.
25
- - The merchant billing mandate is active, including any required wallet
26
- approval.
27
- - You have a public HTTPS webhook URL that can receive the raw request body.
28
- - Your checkout return URL origin is known and can be registered.
29
- - The buyer has a Siglume wallet funded in the settlement token for the test
30
- market: JPYC for JPY, USDC for USD.
31
- - Your order amount is in the Standard band: JPY 501+ or USD 3.01+.
24
+ Python / FastAPI:
32
25
 
33
- If Hosted Checkout is not enabled, stop here. The SDK raises
34
- `HostedCheckoutNotAvailableError` for rollout 404/409 responses; contact
35
- Siglume support or your Siglume account contact to enable the account before
36
- continuing with a human web checkout.
26
+ ```bash
27
+ pip install siglume-direct-request-payment
28
+ ```
37
29
 
38
- ## 1. Install
30
+ Set these environment variables in your app or `.env`:
39
31
 
40
- Runnable starter directories are available if you want a small server to edit:
32
+ ```bash
33
+ SIGLUME_MERCHANT_AUTH_TOKEN=<merchant Siglume bearer token>
34
+ SIGLUME_DIRECT_PAYMENT_MERCHANT=<merchant key>
35
+ SHOP_PUBLIC_ORIGIN=https://www.your-product.example
36
+ SHOP_WEBHOOK_URL=https://api.your-product.example/payments/webhooks/siglume
37
+ SIGLUME_WEBHOOK_SECRET=<webhook signing secret from setupCheckout/setup_checkout>
38
+ ```
41
39
 
42
- - [TypeScript Express starter](../examples/hosted-checkout-typescript)
43
- - [Python Flask starter](../examples/hosted-checkout-python)
40
+ Then run the matching CLI:
44
41
 
45
- For an existing app, install the SDK directly:
42
+ ```bash
43
+ # Node / Express
44
+ npx siglume-check readiness
45
+
46
+ # Python / FastAPI
47
+ siglume-check readiness
48
+ ```
49
+
50
+ The readiness check fails before you write checkout code if any required item is
51
+ missing. It checks local config, reads the merchant account, requires active
52
+ billing, confirms the webhook subscription points to `SHOP_WEBHOOK_URL`, checks
53
+ that `direct_payment.confirmed` is subscribed, verifies the local webhook secret
54
+ against the subscription hint, creates one unpaid expiring Hosted Checkout probe
55
+ session, and queues a signed webhook test delivery. No buyer is charged.
56
+
57
+ For a CI local-config smoke test:
46
58
 
47
59
  ```bash
48
- npm install @siglume/direct-request-payment
60
+ npx siglume-check readiness --no-api --json
49
61
  ```
50
62
 
51
- or:
63
+ `--no-api` does not prove Hosted Checkout or webhook delivery. Before opening a
64
+ human web checkout path, run readiness without `--no-api` and fix every FAIL
65
+ item.
66
+
67
+ ## 1. Copy integration files into your product
68
+
69
+ For Express:
52
70
 
53
71
  ```bash
54
- pip install siglume-direct-request-payment
72
+ npx siglume-sdrp init express --target src/siglume
55
73
  ```
56
74
 
57
- ## 2. Set environment variables
75
+ For FastAPI:
58
76
 
59
77
  ```bash
60
- SIGLUME_MERCHANT_AUTH_TOKEN=<merchant Siglume bearer token>
61
- SIGLUME_DIRECT_PAYMENT_MERCHANT=example_merchant
62
- SHOP_PUBLIC_ORIGIN=https://www.example.com
63
- SHOP_WEBHOOK_URL=https://api.example.com/siglume/webhook
78
+ siglume-sdrp init fastapi --target app/siglume
64
79
  ```
65
80
 
66
- Do not use a Developer Portal `cli_` API key. Merchant setup requires the
67
- merchant's Siglume bearer token.
81
+ These commands copy framework-specific route files into your codebase. The
82
+ generated files are intentionally small and are meant to be edited.
68
83
 
69
- ## 3. Register merchant settings
84
+ ## 2. Mount the routes
70
85
 
71
- Run setup once from your server, CI, or integration machine:
86
+ Express:
72
87
 
73
88
  ```ts
74
- import { DirectRequestPaymentMerchantClient } from "@siglume/direct-request-payment";
75
-
76
- const merchant = new DirectRequestPaymentMerchantClient({
77
- auth_token: process.env.SIGLUME_MERCHANT_AUTH_TOKEN!,
78
- });
89
+ import express from "express";
90
+ import {
91
+ createSiglumeSdrpCheckoutRouter,
92
+ createSiglumeSdrpWebhookHandler,
93
+ type SiglumeSdrpRouterOptions,
94
+ } from "./siglume/siglume-sdrp-routes.js";
95
+ import { siglumeOrderStore } from "./siglume/siglume-order-store.example.js";
79
96
 
80
- const setup = await merchant.setupCheckout({
97
+ const siglumeOptions: SiglumeSdrpRouterOptions = {
81
98
  merchant: process.env.SIGLUME_DIRECT_PAYMENT_MERCHANT!,
82
- display_name: "Example Merchant",
83
- billing_plan: "launch",
84
- billing_currency: "JPY",
85
- webhook_callback_url: process.env.SHOP_WEBHOOK_URL!,
86
- checkout_allowed_origins: [process.env.SHOP_PUBLIC_ORIGIN!],
87
- });
88
-
89
- console.log(setup.env.SIGLUME_DIRECT_PAYMENT_MERCHANT);
90
- ```
99
+ merchant_auth_token: process.env.SIGLUME_MERCHANT_AUTH_TOKEN!,
100
+ webhook_secret: process.env.SIGLUME_WEBHOOK_SECRET!,
101
+ shop_public_origin: process.env.SHOP_PUBLIC_ORIGIN!,
102
+ order_store: siglumeOrderStore,
103
+ allow_metered_payments: false,
104
+ };
105
+
106
+ app.post(
107
+ "/payments/webhooks/siglume",
108
+ express.raw({ type: "application/json" }),
109
+ createSiglumeSdrpWebhookHandler(siglumeOptions),
110
+ );
91
111
 
92
- Store the returned `SIGLUME_DIRECT_PAYMENT_CHALLENGE_SECRET` and
93
- `SIGLUME_WEBHOOK_SECRET` in a server-side secret store. Secret values are
94
- returned only when created or rotated.
112
+ app.use(express.json());
113
+ app.use("/payments", createSiglumeSdrpCheckoutRouter(siglumeOptions));
114
+ ```
95
115
 
96
- ## 4. Create a Standard checkout session
116
+ FastAPI:
97
117
 
98
- For each order, create the order on your server first. Then create a Hosted
99
- Checkout session:
118
+ ```py
119
+ from .siglume.siglume_order_store_example import ExampleSiglumeOrderStore
120
+ from .siglume.siglume_sdrp_routes import create_siglume_sdrp_router
100
121
 
101
- ```ts
102
- const session = await merchant.createCheckoutSession({
103
- merchant: process.env.SIGLUME_DIRECT_PAYMENT_MERCHANT!,
104
- amount_minor: 1200,
105
- currency: "JPY",
106
- nonce: "order_123-attempt_1",
107
- success_url: `${process.env.SHOP_PUBLIC_ORIGIN}/thanks`,
108
- cancel_url: `${process.env.SHOP_PUBLIC_ORIGIN}/cart`,
109
- metadata: { order_id: "order_123" },
110
- });
111
-
112
- await orders.update("order_123", {
113
- siglume_challenge_hash: session.challenge_hash,
114
- siglume_checkout_session_id: session.session_id,
115
- siglume_payment_status: "pending",
116
- });
117
-
118
- redirect(session.checkout_url);
122
+ app.include_router(
123
+ create_siglume_sdrp_router(ExampleSiglumeOrderStore(), allow_metered_payments=False),
124
+ prefix="/payments",
125
+ )
119
126
  ```
120
127
 
121
- The browser must never choose the amount, currency, nonce, or return URL. The
122
- session is single-use and expires.
128
+ ## 3. Replace the order-store example
123
129
 
124
- ## 5. Fulfill from the signed webhook
130
+ Replace the example store with your product's order database. The adapter must:
125
131
 
126
- The browser return path is not the source of truth. Use the signed webhook and
127
- classify the confirmation:
132
+ - load the order by your `order_id`,
133
+ - verify the current user is allowed to pay for that order,
134
+ - return the server-authored `amount_minor` and `currency`,
135
+ - create or reuse one active checkout attempt with a stable nonce,
136
+ - persist `challenge_hash`, `checkout_session_id`, and `checkout_url` before redirecting,
137
+ - process webhook event ids durably in the same transaction as the order update,
138
+ - mark Standard orders paid exactly once,
139
+ - route unknown classifications to manual review.
128
140
 
129
- ```ts
130
- import {
131
- classifyDirectPaymentConfirmation,
132
- verifyDirectRequestPaymentWebhook,
133
- } from "@siglume/direct-request-payment";
134
-
135
- const { event } = await verifyDirectRequestPaymentWebhook(
136
- process.env.SIGLUME_WEBHOOK_SECRET!,
137
- rawRequestBody,
138
- siglumeSignatureHeader,
139
- );
141
+ Do not calculate the amount from browser input.
140
142
 
141
- if (event.type === "direct_payment.confirmed") {
142
- const confirmation = classifyDirectPaymentConfirmation(event);
143
-
144
- if (confirmation.kind === "standard_settled") {
145
- await orders.markPaidOnceByChallengeHash(confirmation.challenge_hash, {
146
- requirement_id: confirmation.requirement_id,
147
- chain_receipt_id: confirmation.chain_receipt_id,
148
- });
149
- } else if (confirmation.kind === "metered_usage_accepted") {
150
- await orders.markFulfilledButUnsettledOnceByChallengeHash(
151
- confirmation.challenge_hash,
152
- { requirement_id: confirmation.requirement_id },
153
- );
154
- } else {
155
- await orders.flagForPaymentStateReview(confirmation);
156
- }
157
- }
143
+ The generated route defaults to Standard-only. If an order amount falls into
144
+ Micro / Nano, checkout returns `METERED_INTEGRATION_REQUIRED` until you set
145
+ `allow_metered_payments: true` / `allow_metered_payments=True` and implement
146
+ fulfilled-but-unsettled state, settlement reconciliation, past-due handling, and
147
+ terminal write-off handling.
148
+
149
+ ## 4. Start checkout from your frontend
150
+
151
+ Call your own server route:
152
+
153
+ ```bash
154
+ curl -X POST https://api.your-product.example/payments/checkout/siglume/start \
155
+ -H "content-type: application/json" \
156
+ -d "{\"order_id\":\"order_123\"}"
158
157
  ```
159
158
 
160
- For this 10-minute guide, keep the test order in the Standard band so the
161
- expected successful branch is `standard_settled`.
159
+ Redirect the shopper to the returned `checkout_url`.
162
160
 
163
- ## Done means
161
+ ## 5. Done means
164
162
 
165
- You are done with the quickstart when:
163
+ Your product is integrated when:
166
164
 
167
- - the checkout session is created,
168
- - the buyer reaches the Siglume wallet hosted checkout page,
165
+ - `npx siglume-check readiness` passes,
166
+ - your product has mounted checkout and webhook routes,
167
+ - your order database stores one active checkout attempt and `challenge_hash` for the order,
169
168
  - the signed webhook verifies against the raw body,
170
- - `classifyDirectPaymentConfirmation(event)` returns `standard_settled`,
171
- - your order is marked paid once, keyed by the stored `challenge_hash`.
169
+ - `standard_settled` marks the order paid once,
170
+ - duplicate webhook deliveries do not double-fulfill the order.
172
171
 
173
- Before production, complete the full checklist in
174
- [Merchant Quickstart](./merchant-quickstart.md#go-live-checklist), read
175
- [Payment lifecycle](./payment-lifecycle.md), and prepare the failure handling in
176
- [Troubleshooting](./troubleshooting.md).
172
+ For Micro / Nano revenue reconciliation, read
173
+ [Payment lifecycle](./payment-lifecycle.md) and
174
+ [Micro / Nano Statements and Notices](./metered-statements.md).