@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.
- package/CHANGELOG.md +44 -0
- package/README.md +32 -15
- package/bin/siglume-sdrp.mjs +398 -0
- package/dist/index.cjs +25 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +26 -2
- package/dist/index.d.ts +26 -2
- package/dist/index.js +25 -1
- package/dist/index.js.map +1 -1
- package/docs/announcement-ja.md +17 -3
- package/docs/api-reference.md +57 -13
- package/docs/merchant-quickstart.md +11 -26
- package/docs/metered-statements.md +15 -13
- package/docs/payment-lifecycle.md +12 -9
- package/docs/pricing.md +7 -4
- package/docs/quickstart-10-minutes.md +126 -128
- package/docs/troubleshooting.md +20 -5
- package/examples/express-checkout.ts +37 -13
- package/examples/hosted-checkout-python/app.py +46 -31
- package/examples/hosted-checkout-python/order_store.py +13 -3
- package/examples/hosted-checkout-python/pyproject.toml +1 -1
- package/examples/hosted-checkout-typescript/src/order-store.ts +14 -3
- package/examples/hosted-checkout-typescript/src/server.ts +49 -37
- package/package.json +9 -1
- package/templates/express/README.md +42 -0
- package/templates/express/siglume-order-store.example.ts +69 -0
- package/templates/express/siglume-sdrp-routes.ts +231 -0
- package/templates/fastapi/README.md +26 -0
- package/templates/fastapi/siglume_order_store_example.py +77 -0
- package/templates/fastapi/siglume_sdrp_routes.py +170 -0
package/docs/api-reference.md
CHANGED
|
@@ -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
|
|
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
|
|
1016
|
-
`direct_payment.confirmed`
|
|
1017
|
-
|
|
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
|
|
1043
|
-
|
|
1044
|
-
|
|
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`.
|
|
1047
|
-
|
|
1048
|
-
|
|
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
|
-
- `
|
|
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
|
|
7
|
-
[10-Minute
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
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
|
-
|
|
464
|
-
|
|
465
|
-
|
|
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
|
-
|
|
536
|
-
|
|
537
|
-
|
|
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`:
|
|
199
|
-
|
|
200
|
-
|
|
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` |
|
|
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
|
|
402
|
-
the total unsettled exposure is at or above the fixed threshold,
|
|
403
|
-
failed or past-due block remains. The provider API is not called for
|
|
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 /
|
|
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
|
-
|
|
456
|
-
|
|
457
|
-
|
|
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
|
-
|
|
46
|
-
|
|
47
|
-
|
|
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
|
|
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,
|
|
69
|
-
|
|
70
|
-
|
|
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.
|
|
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
|
|
160
|
-
`
|
|
161
|
-
|
|
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
|
|
1
|
+
# 10-Minute Product Integration
|
|
2
2
|
|
|
3
|
-
This guide is the
|
|
4
|
-
|
|
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
|
-
|
|
7
|
+
- `POST /payments/checkout/siglume/start`
|
|
8
|
+
- `POST /payments/webhooks/siglume`
|
|
7
9
|
|
|
8
|
-
|
|
9
|
-
|
|
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
|
-
|
|
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
|
-
|
|
17
|
-
scheduled autopay, game entitlement recovery, or Micro / Nano accounting.
|
|
16
|
+
Install the SDK in your product.
|
|
18
17
|
|
|
19
|
-
|
|
18
|
+
Node / Express:
|
|
20
19
|
|
|
21
|
-
|
|
20
|
+
```bash
|
|
21
|
+
npm install @siglume/direct-request-payment
|
|
22
|
+
```
|
|
22
23
|
|
|
23
|
-
|
|
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
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
continuing with a human web checkout.
|
|
26
|
+
```bash
|
|
27
|
+
pip install siglume-direct-request-payment
|
|
28
|
+
```
|
|
37
29
|
|
|
38
|
-
|
|
30
|
+
Set these environment variables in your app or `.env`:
|
|
39
31
|
|
|
40
|
-
|
|
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
|
-
|
|
43
|
-
- [Python Flask starter](../examples/hosted-checkout-python)
|
|
40
|
+
Then run the matching CLI:
|
|
44
41
|
|
|
45
|
-
|
|
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
|
-
|
|
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
|
-
|
|
72
|
+
npx siglume-sdrp init express --target src/siglume
|
|
55
73
|
```
|
|
56
74
|
|
|
57
|
-
|
|
75
|
+
For FastAPI:
|
|
58
76
|
|
|
59
77
|
```bash
|
|
60
|
-
|
|
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
|
-
|
|
67
|
-
|
|
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
|
-
##
|
|
84
|
+
## 2. Mount the routes
|
|
70
85
|
|
|
71
|
-
|
|
86
|
+
Express:
|
|
72
87
|
|
|
73
88
|
```ts
|
|
74
|
-
import
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
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
|
|
97
|
+
const siglumeOptions: SiglumeSdrpRouterOptions = {
|
|
81
98
|
merchant: process.env.SIGLUME_DIRECT_PAYMENT_MERCHANT!,
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
|
|
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
|
-
|
|
93
|
-
|
|
94
|
-
|
|
112
|
+
app.use(express.json());
|
|
113
|
+
app.use("/payments", createSiglumeSdrpCheckoutRouter(siglumeOptions));
|
|
114
|
+
```
|
|
95
115
|
|
|
96
|
-
|
|
116
|
+
FastAPI:
|
|
97
117
|
|
|
98
|
-
|
|
99
|
-
|
|
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
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
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
|
-
|
|
122
|
-
session is single-use and expires.
|
|
128
|
+
## 3. Replace the order-store example
|
|
123
129
|
|
|
124
|
-
|
|
130
|
+
Replace the example store with your product's order database. The adapter must:
|
|
125
131
|
|
|
126
|
-
|
|
127
|
-
|
|
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
|
-
|
|
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
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
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
|
-
|
|
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
|
-
|
|
163
|
+
Your product is integrated when:
|
|
166
164
|
|
|
167
|
-
-
|
|
168
|
-
-
|
|
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
|
-
- `
|
|
171
|
-
-
|
|
169
|
+
- `standard_settled` marks the order paid once,
|
|
170
|
+
- duplicate webhook deliveries do not double-fulfill the order.
|
|
172
171
|
|
|
173
|
-
|
|
174
|
-
[
|
|
175
|
-
[
|
|
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).
|