@siglume/direct-request-payment 0.4.19 → 0.4.22
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 +50 -0
- package/README.md +18 -10
- package/bin/siglume-sdrp.mjs +550 -8
- package/dist/index.cjs +37 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +27 -2
- package/dist/index.d.ts +27 -2
- package/dist/index.js +37 -3
- package/dist/index.js.map +1 -1
- package/docs/announcement-ja.md +17 -3
- package/docs/api-reference.md +60 -13
- package/docs/merchant-quickstart.md +6 -20
- 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 +134 -24
- package/docs/sandbox.md +60 -0
- package/docs/troubleshooting.md +23 -8
- 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 +10 -2
- package/templates/express/README.md +40 -6
- package/templates/express/siglume-order-store.example.ts +22 -6
- package/templates/express/siglume-order-store.sql.ts +585 -0
- package/templates/express/siglume-sdrp-routes.ts +138 -64
- package/templates/fastapi/README.md +22 -3
- package/templates/fastapi/siglume_order_store_example.py +29 -6
- package/templates/fastapi/siglume_order_store_sqlalchemy.py +313 -0
- package/templates/fastapi/siglume_sdrp_routes.py +112 -49
package/docs/api-reference.md
CHANGED
|
@@ -48,6 +48,8 @@ Standard can be marked paid only after settled per-payment finality, while Micro
|
|
|
48
48
|
| `SIGLUME_MERCHANT_AUTH_TOKEN` | merchant setup helper | merchant Siglume bearer token for self-service setup |
|
|
49
49
|
| `SIGLUME_AUTH_TOKEN` | user-authenticated helper | buyer Siglume bearer token for payment / buyer statements, or provider Siglume bearer token for provider statements |
|
|
50
50
|
| `SIGLUME_API_BASE` | optional | API base URL override; defaults to `https://siglume.com/v1` |
|
|
51
|
+
| `SIGLUME_ENV` | optional | Set to `sandbox` to default clients and readiness to the local sandbox API |
|
|
52
|
+
| `SIGLUME_SANDBOX_API_BASE` | optional | Sandbox API override; defaults to `http://127.0.0.1:8787/v1` when `SIGLUME_ENV=sandbox` |
|
|
51
53
|
| `SIGLUME_WEBHOOK_SECRET` | merchant server | webhook signing secret returned as `whsec_...` |
|
|
52
54
|
|
|
53
55
|
Do not use a Developer Portal `cli_` API key as either auth token. Merchant
|
|
@@ -356,7 +358,8 @@ Returns:
|
|
|
356
358
|
`merchant.merchant_account.metadata_jsonb.metered_risk_acceptance` records the
|
|
357
359
|
merchant's Micro / Nano delayed-settlement risk acceptance receipt with
|
|
358
360
|
`terms_version`, `accepted_at`, `principal_user_id`, `receipt_id`, and fixed
|
|
359
|
-
market thresholds
|
|
361
|
+
market thresholds JPY 10,000 / USD 100.00 (`settlement_threshold_minor` is
|
|
362
|
+
`10000` for both JPY minor units and USD cents).
|
|
360
363
|
|
|
361
364
|
Secrets are returned only when created or rotated. Existing secrets are not
|
|
362
365
|
replayed by `getMerchant` / `get_merchant`.
|
|
@@ -372,7 +375,9 @@ POST /v1/sdrp/direct-payments/merchants
|
|
|
372
375
|
Creates or updates the merchant account for the authenticated merchant user.
|
|
373
376
|
Accepts the optional `checkout_allowed_origins: string[]` return-URL origin
|
|
374
377
|
allowlist described under `setupCheckout` above; the same normalization and
|
|
375
|
-
webhook-origin auto-allow apply.
|
|
378
|
+
webhook-origin auto-allow apply. Python annotates this direct response as
|
|
379
|
+
`DirectRequestPaymentMerchantResponse`; `setup_checkout(...)` returns
|
|
380
|
+
`DirectRequestPaymentCheckoutSetupResult`.
|
|
376
381
|
|
|
377
382
|
### `createCheckoutSession(input)` / `create_checkout_session(...)`
|
|
378
383
|
|
|
@@ -535,6 +540,41 @@ Defaults event types to `direct_payment.confirmed` and
|
|
|
535
540
|
`direct_payment.spent`. The returned `signing_secret` is shown only at creation
|
|
536
541
|
or rotation.
|
|
537
542
|
|
|
543
|
+
### `listWebhookSubscriptions()` / `list_webhook_subscriptions()`
|
|
544
|
+
|
|
545
|
+
Calls:
|
|
546
|
+
|
|
547
|
+
```text
|
|
548
|
+
GET /v1/market/webhooks/subscriptions
|
|
549
|
+
```
|
|
550
|
+
|
|
551
|
+
Returns the current user's webhook subscriptions without the full signing
|
|
552
|
+
secret. Use `signing_secret_hint` to confirm that the local
|
|
553
|
+
`SIGLUME_WEBHOOK_SECRET` is the expected secret.
|
|
554
|
+
|
|
555
|
+
### `queueWebhookTestDelivery(input)` / `queue_webhook_test_delivery(...)`
|
|
556
|
+
|
|
557
|
+
Calls:
|
|
558
|
+
|
|
559
|
+
```text
|
|
560
|
+
POST /v1/market/webhooks/test-deliveries
|
|
561
|
+
```
|
|
562
|
+
|
|
563
|
+
Queues a signed test event to one or more subscription ids. `siglume-check
|
|
564
|
+
readiness` uses this for a harmless `direct_payment.confirmed` readiness probe.
|
|
565
|
+
|
|
566
|
+
### `listWebhookDeliveries(input)` / `list_webhook_deliveries(...)`
|
|
567
|
+
|
|
568
|
+
Calls:
|
|
569
|
+
|
|
570
|
+
```text
|
|
571
|
+
GET /v1/market/webhooks/deliveries
|
|
572
|
+
```
|
|
573
|
+
|
|
574
|
+
Supports `subscription_id`, `event_type`, `status`, and `limit`. Readiness polls
|
|
575
|
+
this list after queueing the test delivery and only passes when the matching
|
|
576
|
+
delivery status becomes `delivered`.
|
|
577
|
+
|
|
538
578
|
## `DirectRequestPaymentClient`
|
|
539
579
|
|
|
540
580
|
Thin wrapper around the current Siglume Direct Request Payment HTTP contract.
|
|
@@ -1012,10 +1052,10 @@ the `Siglume-Signature` header; use
|
|
|
1012
1052
|
`verifyDirectRequestPaymentWebhook(...)` /
|
|
1013
1053
|
`verify_direct_request_payment_webhook(...)` for signature verification and
|
|
1014
1054
|
parsing together. Throws
|
|
1015
|
-
`SiglumeWebhookPayloadError` on a malformed event
|
|
1016
|
-
`direct_payment.confirmed`
|
|
1017
|
-
|
|
1018
|
-
argument is positional in both languages.
|
|
1055
|
+
`SiglumeWebhookPayloadError` on a malformed event. It does not reject an
|
|
1056
|
+
unsupported `direct_payment.confirmed` mode by itself; the classifier returns
|
|
1057
|
+
`kind: "unknown"` with `reason: "unsupported_confirmation_mode"` for that case.
|
|
1058
|
+
The `payload` argument is positional in both languages.
|
|
1019
1059
|
|
|
1020
1060
|
For `direct_payment.confirmed`, inspect `event.data.pricing_band`,
|
|
1021
1061
|
`event.data.settlement_cadence`, `event.data.finality`,
|
|
@@ -1039,13 +1079,15 @@ Recommended branch: call `classifyDirectPaymentConfirmation(event)` /
|
|
|
1039
1079
|
pricing, per-payment on-chain finality, settled status, non-empty
|
|
1040
1080
|
`requirement_id`, non-empty `challenge_hash`, and non-empty
|
|
1041
1081
|
`chain_receipt_id`.
|
|
1042
|
-
- `metered_usage_accepted`: treat the usage as accepted but unsettled
|
|
1043
|
-
|
|
1044
|
-
|
|
1082
|
+
- `metered_usage_accepted`: treat the usage as accepted but unsettled only if
|
|
1083
|
+
your integration has explicitly enabled Micro / Nano delayed-settlement
|
|
1084
|
+
handling. This requires Micro / Nano pricing,
|
|
1085
|
+
`finality === "aggregated_onchain_settlement"`, the matching settlement
|
|
1086
|
+
cadence (`micro` -> `weekly`, `nano` -> `monthly`),
|
|
1045
1087
|
`settlement_status === "pending_settlement"`, non-empty `requirement_id`, and
|
|
1046
|
-
non-empty `challenge_hash`.
|
|
1047
|
-
|
|
1048
|
-
|
|
1088
|
+
non-empty `challenge_hash`. Standard-only integrations should route this to
|
|
1089
|
+
review or return `METERED_INTEGRATION_REQUIRED`; Micro / Nano integrations
|
|
1090
|
+
must reconcile final revenue from statement APIs / settlement batches.
|
|
1049
1091
|
- `unknown`: do not mark paid or fulfilled from the event type alone; fetch the
|
|
1050
1092
|
requirement or route the event to manual review.
|
|
1051
1093
|
|
|
@@ -1153,7 +1195,11 @@ package exports `TypedDict` names for the high-risk response shapes:
|
|
|
1153
1195
|
- `DirectRequestPaymentPastDueBlock`
|
|
1154
1196
|
- `DirectRequestPaymentProviderMeteredTotals`
|
|
1155
1197
|
- `DirectRequestPaymentListResponse`
|
|
1156
|
-
- `
|
|
1198
|
+
- `DirectRequestPaymentMerchantResponse`
|
|
1199
|
+
- `DirectRequestPaymentCheckoutSetupResult`
|
|
1200
|
+
- `DirectRequestPaymentMerchantSetupResponse` (compatibility alias for checkout setup result)
|
|
1201
|
+
- `DirectRequestPaymentWebhookSubscription`
|
|
1202
|
+
- `DirectRequestPaymentWebhookDelivery`
|
|
1157
1203
|
- `DirectRequestPaymentWebhookVerification`
|
|
1158
1204
|
- `DirectRequestPaymentConfirmationClassification`
|
|
1159
1205
|
|
|
@@ -1170,6 +1216,7 @@ Both packages export these importable constants:
|
|
|
1170
1216
|
| Constant | Value |
|
|
1171
1217
|
| --- | --- |
|
|
1172
1218
|
| `DEFAULT_SIGLUME_API_BASE` | `https://siglume.com/v1` |
|
|
1219
|
+
| `DEFAULT_SIGLUME_SANDBOX_API_BASE` | `http://127.0.0.1:8787/v1` |
|
|
1173
1220
|
| `DIRECT_REQUEST_PAYMENT_CHALLENGE_SCHEME` | `siglume-external-402-v1` |
|
|
1174
1221
|
| `DIRECT_REQUEST_PAYMENT_RECURRING_CHALLENGE_SCHEME` | `siglume-external-402-recurring-v1` |
|
|
1175
1222
|
| `DIRECT_REQUEST_PAYMENT_MODE` | `external_402` |
|
|
@@ -459,16 +459,9 @@ if (confirmation.kind === "standard_settled") {
|
|
|
459
459
|
}
|
|
460
460
|
|
|
461
461
|
if (confirmation.kind === "metered_usage_accepted") {
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
reason: "unknown_metered_challenge_hash",
|
|
466
|
-
requirement_id: confirmation.requirement_id,
|
|
467
|
-
});
|
|
468
|
-
return new Response(null, { status: 204 });
|
|
469
|
-
}
|
|
470
|
-
await orders.markFulfilledButUnsettledOnce(order.id, {
|
|
471
|
-
siglume_requirement_id: confirmation.requirement_id,
|
|
462
|
+
await orders.flagForPaymentStateReview({
|
|
463
|
+
reason: "metered_integration_required",
|
|
464
|
+
requirement_id: confirmation.requirement_id,
|
|
472
465
|
pricing_band: confirmation.pricing_band,
|
|
473
466
|
});
|
|
474
467
|
return new Response(null, { status: 204 });
|
|
@@ -531,16 +524,9 @@ if confirmation["kind"] == "standard_settled":
|
|
|
531
524
|
return "", 204
|
|
532
525
|
|
|
533
526
|
if confirmation["kind"] == "metered_usage_accepted":
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
reason="unknown_metered_challenge_hash",
|
|
538
|
-
requirement_id=confirmation["requirement_id"],
|
|
539
|
-
)
|
|
540
|
-
return "", 204
|
|
541
|
-
orders.mark_fulfilled_but_unsettled_once(
|
|
542
|
-
order["id"],
|
|
543
|
-
siglume_requirement_id=confirmation["requirement_id"],
|
|
527
|
+
orders.flag_for_payment_state_review(
|
|
528
|
+
reason="metered_integration_required",
|
|
529
|
+
requirement_id=confirmation["requirement_id"],
|
|
544
530
|
pricing_band=confirmation["pricing_band"],
|
|
545
531
|
)
|
|
546
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.22. 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.
|
|
@@ -11,7 +11,43 @@ The SDK supplies the readiness check, route files, webhook verification, payment
|
|
|
11
11
|
classification, and the order-store adapter contract. Your app supplies the
|
|
12
12
|
real order lookup and fulfillment writes.
|
|
13
13
|
|
|
14
|
-
## 0.
|
|
14
|
+
## 0. Run the sandbox first
|
|
15
|
+
|
|
16
|
+
Do this before touching live Siglume credentials. The local sandbox is a tiny
|
|
17
|
+
Siglume-compatible API server bundled with the SDK. It creates fake checkout
|
|
18
|
+
sessions, signs webhooks with your sandbox secret, records delivery status, and
|
|
19
|
+
never charges a wallet.
|
|
20
|
+
|
|
21
|
+
In one terminal, point it at your product's local webhook route:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npx siglume-sdrp sandbox \
|
|
25
|
+
--origin http://localhost:3000 \
|
|
26
|
+
--webhook-url http://localhost:3000/payments/webhooks/siglume
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Use the values it prints in your product `.env`:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
SIGLUME_ENV=sandbox
|
|
33
|
+
SIGLUME_API_BASE=http://127.0.0.1:8787/v1
|
|
34
|
+
SIGLUME_MERCHANT_AUTH_TOKEN=sandbox_merchant_token
|
|
35
|
+
SIGLUME_DIRECT_PAYMENT_MERCHANT=sandbox_merchant
|
|
36
|
+
SHOP_PUBLIC_ORIGIN=http://localhost:3000
|
|
37
|
+
SHOP_WEBHOOK_URL=http://localhost:3000/payments/webhooks/siglume
|
|
38
|
+
SIGLUME_WEBHOOK_SECRET=whsec_sandbox_local
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Then run:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
npx siglume-check readiness --sandbox
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
The sandbox readiness check proves your local server can receive a signed
|
|
48
|
+
`direct_payment.confirmed` delivery before you use live credentials.
|
|
49
|
+
|
|
50
|
+
## 1. Live readiness
|
|
15
51
|
|
|
16
52
|
Install the SDK in your product.
|
|
17
53
|
|
|
@@ -34,29 +70,37 @@ SIGLUME_MERCHANT_AUTH_TOKEN=<merchant Siglume bearer token>
|
|
|
34
70
|
SIGLUME_DIRECT_PAYMENT_MERCHANT=<merchant key>
|
|
35
71
|
SHOP_PUBLIC_ORIGIN=https://www.your-product.example
|
|
36
72
|
SHOP_WEBHOOK_URL=https://api.your-product.example/payments/webhooks/siglume
|
|
73
|
+
SIGLUME_WEBHOOK_SECRET=<webhook signing secret from setupCheckout/setup_checkout>
|
|
37
74
|
```
|
|
38
75
|
|
|
39
|
-
Then run:
|
|
76
|
+
Then run the matching CLI:
|
|
40
77
|
|
|
41
78
|
```bash
|
|
79
|
+
# Node / Express
|
|
42
80
|
npx siglume-check readiness
|
|
81
|
+
|
|
82
|
+
# Python / FastAPI
|
|
83
|
+
siglume-check readiness
|
|
43
84
|
```
|
|
44
85
|
|
|
45
86
|
The readiness check fails before you write checkout code if any required item is
|
|
46
|
-
missing. It checks local config, reads the merchant account,
|
|
47
|
-
|
|
48
|
-
|
|
87
|
+
missing. It checks local config, reads the merchant account, requires active
|
|
88
|
+
billing, confirms the webhook subscription points to `SHOP_WEBHOOK_URL`, checks
|
|
89
|
+
that `direct_payment.confirmed` is subscribed, verifies the local webhook secret
|
|
90
|
+
against the subscription hint, creates one unpaid expiring Hosted Checkout probe
|
|
91
|
+
session, and queues a signed webhook test delivery. No buyer is charged.
|
|
49
92
|
|
|
50
|
-
For CI
|
|
93
|
+
For a CI local-config smoke test:
|
|
51
94
|
|
|
52
95
|
```bash
|
|
53
|
-
npx siglume-check readiness --json
|
|
96
|
+
npx siglume-check readiness --no-api --json
|
|
54
97
|
```
|
|
55
98
|
|
|
56
|
-
|
|
57
|
-
checkout path
|
|
99
|
+
`--no-api` does not prove Hosted Checkout or webhook delivery. Before opening a
|
|
100
|
+
human web checkout path, run readiness without `--no-api` and fix every FAIL
|
|
101
|
+
item.
|
|
58
102
|
|
|
59
|
-
##
|
|
103
|
+
## 2. Copy integration files into your product
|
|
60
104
|
|
|
61
105
|
For Express:
|
|
62
106
|
|
|
@@ -73,21 +117,36 @@ siglume-sdrp init fastapi --target app/siglume
|
|
|
73
117
|
These commands copy framework-specific route files into your codebase. The
|
|
74
118
|
generated files are intentionally small and are meant to be edited.
|
|
75
119
|
|
|
76
|
-
##
|
|
120
|
+
## 3. Mount the routes
|
|
77
121
|
|
|
78
122
|
Express:
|
|
79
123
|
|
|
80
124
|
```ts
|
|
81
|
-
import
|
|
125
|
+
import express from "express";
|
|
126
|
+
import {
|
|
127
|
+
createSiglumeSdrpCheckoutRouter,
|
|
128
|
+
createSiglumeSdrpWebhookHandler,
|
|
129
|
+
type SiglumeSdrpRouterOptions,
|
|
130
|
+
} from "./siglume/siglume-sdrp-routes.js";
|
|
82
131
|
import { siglumeOrderStore } from "./siglume/siglume-order-store.example.js";
|
|
83
132
|
|
|
84
|
-
|
|
133
|
+
const siglumeOptions: SiglumeSdrpRouterOptions = {
|
|
85
134
|
merchant: process.env.SIGLUME_DIRECT_PAYMENT_MERCHANT!,
|
|
86
135
|
merchant_auth_token: process.env.SIGLUME_MERCHANT_AUTH_TOKEN!,
|
|
87
136
|
webhook_secret: process.env.SIGLUME_WEBHOOK_SECRET!,
|
|
88
137
|
shop_public_origin: process.env.SHOP_PUBLIC_ORIGIN!,
|
|
89
138
|
order_store: siglumeOrderStore,
|
|
90
|
-
|
|
139
|
+
allow_metered_payments: false,
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
app.post(
|
|
143
|
+
"/payments/webhooks/siglume",
|
|
144
|
+
express.raw({ type: "application/json" }),
|
|
145
|
+
createSiglumeSdrpWebhookHandler(siglumeOptions),
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
app.use(express.json());
|
|
149
|
+
app.use("/payments", createSiglumeSdrpCheckoutRouter(siglumeOptions));
|
|
91
150
|
```
|
|
92
151
|
|
|
93
152
|
FastAPI:
|
|
@@ -97,27 +156,77 @@ from .siglume.siglume_order_store_example import ExampleSiglumeOrderStore
|
|
|
97
156
|
from .siglume.siglume_sdrp_routes import create_siglume_sdrp_router
|
|
98
157
|
|
|
99
158
|
app.include_router(
|
|
100
|
-
create_siglume_sdrp_router(ExampleSiglumeOrderStore()),
|
|
159
|
+
create_siglume_sdrp_router(ExampleSiglumeOrderStore(), allow_metered_payments=False),
|
|
101
160
|
prefix="/payments",
|
|
102
161
|
)
|
|
103
162
|
```
|
|
104
163
|
|
|
105
|
-
##
|
|
164
|
+
## 4. Adapter responsibilities
|
|
106
165
|
|
|
107
166
|
Replace the example store with your product's order database. The adapter must:
|
|
108
167
|
|
|
109
168
|
- load the order by your `order_id`,
|
|
110
169
|
- verify the current user is allowed to pay for that order,
|
|
111
170
|
- return the server-authored `amount_minor` and `currency`,
|
|
112
|
-
-
|
|
113
|
-
-
|
|
171
|
+
- create or reuse one active checkout attempt with a stable nonce,
|
|
172
|
+
- persist `challenge_hash`, `checkout_session_id`, and `checkout_url` before redirecting,
|
|
173
|
+
- process webhook event ids durably in the same transaction as the order update,
|
|
114
174
|
- mark Standard orders paid exactly once,
|
|
115
|
-
- mark Micro / Nano orders as fulfilled but unsettled exactly once,
|
|
116
175
|
- route unknown classifications to manual review.
|
|
117
176
|
|
|
118
177
|
Do not calculate the amount from browser input.
|
|
119
178
|
|
|
120
|
-
|
|
179
|
+
The generated route defaults to Standard-only. If an order amount falls into
|
|
180
|
+
Micro / Nano, checkout returns `METERED_INTEGRATION_REQUIRED` until you set
|
|
181
|
+
`allow_metered_payments: true` / `allow_metered_payments=True` and implement
|
|
182
|
+
fulfilled-but-unsettled state, settlement reconciliation, past-due handling, and
|
|
183
|
+
terminal write-off handling.
|
|
184
|
+
|
|
185
|
+
## 5. Use a real database adapter
|
|
186
|
+
|
|
187
|
+
The copied files include durable database adapters. Use these before opening
|
|
188
|
+
checkout to users; the `*.example.*` stores are only for reading the interface.
|
|
189
|
+
|
|
190
|
+
Express:
|
|
191
|
+
|
|
192
|
+
```ts
|
|
193
|
+
import {
|
|
194
|
+
createPrismaSiglumeOrderStore,
|
|
195
|
+
createTypeOrmSiglumeOrderStore,
|
|
196
|
+
createSequelizeSiglumeOrderStore,
|
|
197
|
+
createDrizzleSiglumeOrderStore,
|
|
198
|
+
} from "./siglume/siglume-order-store.sql.js";
|
|
199
|
+
|
|
200
|
+
const order_store = createPrismaSiglumeOrderStore(prisma, {
|
|
201
|
+
dialect: "postgres",
|
|
202
|
+
orders_table: "orders",
|
|
203
|
+
order_id_column: "id",
|
|
204
|
+
amount_minor_column: "amount_minor",
|
|
205
|
+
currency_column: "currency",
|
|
206
|
+
});
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
FastAPI:
|
|
210
|
+
|
|
211
|
+
```py
|
|
212
|
+
from sqlalchemy.orm import sessionmaker
|
|
213
|
+
from .siglume.siglume_order_store_sqlalchemy import (
|
|
214
|
+
SQLAlchemySiglumeOrderStore,
|
|
215
|
+
create_sqlalchemy_engine,
|
|
216
|
+
create_sqlalchemy_siglume_schema,
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
engine = create_sqlalchemy_engine(os.environ["DATABASE_URL"])
|
|
220
|
+
create_sqlalchemy_siglume_schema(engine)
|
|
221
|
+
SessionLocal = sessionmaker(engine, future=True)
|
|
222
|
+
order_store = SQLAlchemySiglumeOrderStore(SessionLocal)
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
The adapters persist one checkout attempt per order, reuse the checkout URL on
|
|
226
|
+
retries, record webhook event ids only after the order update/review write
|
|
227
|
+
succeeds, and keep duplicate deliveries from double-fulfilling an order.
|
|
228
|
+
|
|
229
|
+
## 6. Start checkout from your frontend
|
|
121
230
|
|
|
122
231
|
Call your own server route:
|
|
123
232
|
|
|
@@ -129,16 +238,17 @@ curl -X POST https://api.your-product.example/payments/checkout/siglume/start \
|
|
|
129
238
|
|
|
130
239
|
Redirect the shopper to the returned `checkout_url`.
|
|
131
240
|
|
|
132
|
-
##
|
|
241
|
+
## 7. Done means
|
|
133
242
|
|
|
134
243
|
Your product is integrated when:
|
|
135
244
|
|
|
136
|
-
- `npx siglume-check readiness` passes,
|
|
245
|
+
- `npx siglume-check readiness --sandbox` passes against your local product,
|
|
246
|
+
- `npx siglume-check readiness` passes against live Siglume credentials,
|
|
137
247
|
- your product has mounted checkout and webhook routes,
|
|
138
|
-
- your order database
|
|
248
|
+
- your order database uses the SQL/ORM adapter or an equivalent transactional store,
|
|
139
249
|
- the signed webhook verifies against the raw body,
|
|
140
250
|
- `standard_settled` marks the order paid once,
|
|
141
|
-
-
|
|
251
|
+
- a failed webhook handler is retried and duplicate webhook deliveries do not double-fulfill the order.
|
|
142
252
|
|
|
143
253
|
For Micro / Nano revenue reconciliation, read
|
|
144
254
|
[Payment lifecycle](./payment-lifecycle.md) and
|
package/docs/sandbox.md
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# SDRP Sandbox
|
|
2
|
+
|
|
3
|
+
Use the SDK sandbox before live Siglume credentials. It is a local
|
|
4
|
+
Siglume-compatible API server for product integration testing. It creates fake
|
|
5
|
+
checkout sessions, signs `direct_payment.confirmed` webhooks, records delivery
|
|
6
|
+
status, and never charges a wallet.
|
|
7
|
+
|
|
8
|
+
Start your product locally first, then run:
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npx siglume-sdrp sandbox \
|
|
12
|
+
--origin http://localhost:3000 \
|
|
13
|
+
--webhook-url http://localhost:3000/payments/webhooks/siglume
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Set the printed environment variables in your product:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
SIGLUME_ENV=sandbox
|
|
20
|
+
SIGLUME_API_BASE=http://127.0.0.1:8787/v1
|
|
21
|
+
SIGLUME_MERCHANT_AUTH_TOKEN=sandbox_merchant_token
|
|
22
|
+
SIGLUME_DIRECT_PAYMENT_MERCHANT=sandbox_merchant
|
|
23
|
+
SHOP_PUBLIC_ORIGIN=http://localhost:3000
|
|
24
|
+
SHOP_WEBHOOK_URL=http://localhost:3000/payments/webhooks/siglume
|
|
25
|
+
SIGLUME_WEBHOOK_SECRET=whsec_sandbox_local
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Then verify the integration:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
npx siglume-check readiness --sandbox
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Create a checkout through your own product route:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
curl -X POST http://localhost:3000/payments/checkout/siglume/start \
|
|
38
|
+
-H "content-type: application/json" \
|
|
39
|
+
-d "{\"order_id\":\"order_123\"}"
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Open the returned `checkout_url` and click the sandbox confirm button. Your
|
|
43
|
+
product should receive a signed webhook and mark the Standard order paid once.
|
|
44
|
+
|
|
45
|
+
Sandbox Micro / Nano behavior follows the same public classifications:
|
|
46
|
+
|
|
47
|
+
- JPY 501+ / USD 3.01+ returns `standard_settled`.
|
|
48
|
+
- JPY 50-500 / USD 0.31-3.00 returns `metered_usage_accepted` with weekly Micro settlement fields.
|
|
49
|
+
- JPY 1-49 / USD 0.01-0.30 returns `metered_usage_accepted` with monthly Nano settlement fields.
|
|
50
|
+
|
|
51
|
+
The generated route defaults to Standard-only, so Micro / Nano checkout returns
|
|
52
|
+
`METERED_INTEGRATION_REQUIRED` until you explicitly enable metered handling.
|
|
53
|
+
|
|
54
|
+
Before live launch:
|
|
55
|
+
|
|
56
|
+
- run `npx siglume-check readiness --sandbox` against the local product,
|
|
57
|
+
- run the same checkout path and confirm a sandbox webhook,
|
|
58
|
+
- switch to live `SIGLUME_MERCHANT_AUTH_TOKEN`, `SIGLUME_DIRECT_PAYMENT_MERCHANT`, `SHOP_PUBLIC_ORIGIN`, `SHOP_WEBHOOK_URL`, and `SIGLUME_WEBHOOK_SECRET`,
|
|
59
|
+
- run `npx siglume-check readiness` without `--sandbox`,
|
|
60
|
+
- confirm the live webhook subscription and signing secret are for the live product URL.
|