@siglume/direct-request-payment 0.4.5 → 0.4.6
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 +12 -0
- package/README.md +65 -20
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +11 -1
- package/dist/index.d.ts +11 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/docs/announcement-ja.md +3 -1
- package/docs/api-reference.md +53 -13
- package/docs/merchant-quickstart.md +102 -10
- package/docs/metered-statements.md +21 -5
- package/docs/pricing.md +10 -2
- package/docs/security.md +7 -0
- package/examples/express-checkout.ts +43 -6
- package/package.json +1 -1
package/docs/api-reference.md
CHANGED
|
@@ -26,9 +26,11 @@ card — and the merchant SDK never authenticates the buyer.
|
|
|
26
26
|
runs are bounded by Siglume's approval gates / spending budgets (per-run /
|
|
27
27
|
daily / monthly auto-pay budgets, or Works approval).
|
|
28
28
|
|
|
29
|
-
In both systems the merchant
|
|
30
|
-
|
|
31
|
-
and
|
|
29
|
+
In both systems the merchant handles the same signed `direct_payment.confirmed`
|
|
30
|
+
webhook. Hosted Checkout adds no new money movement and no new webhook. Inspect
|
|
31
|
+
`pricing_band`, `finality`, and `settlement_status`: Standard can be marked paid
|
|
32
|
+
only after settled per-payment finality, while Micro / Nano usage is accepted
|
|
33
|
+
before the later aggregated settlement.
|
|
32
34
|
|
|
33
35
|
## Environment Variables
|
|
34
36
|
|
|
@@ -364,8 +366,9 @@ webhook-origin auto-allow apply.
|
|
|
364
366
|
Beta / server rollout: Hosted Checkout is rolling out account by account. If the
|
|
365
367
|
server endpoint is not enabled for the merchant yet, the SDK raises
|
|
366
368
|
`HostedCheckoutNotAvailableError` (TS + Py) rather than leaking a raw rollout
|
|
367
|
-
404/409.
|
|
368
|
-
webhook
|
|
369
|
+
404/409. Payment handling must still key off the signed
|
|
370
|
+
`direct_payment.confirmed` webhook and its settlement machine fields, not the
|
|
371
|
+
event name alone.
|
|
369
372
|
|
|
370
373
|
Creates a single-use, expiring Hosted Checkout session for a human web shopper
|
|
371
374
|
and returns the URL to redirect them to. Requires the merchant's Siglume bearer
|
|
@@ -447,11 +450,24 @@ Returns a `HostedCheckoutSession` status object with:
|
|
|
447
450
|
`failed`
|
|
448
451
|
- `challenge_hash`
|
|
449
452
|
- `requirement_id` (nullable until a requirement is created)
|
|
453
|
+
- `pricing_band` (nullable until a requirement is created): `standard`,
|
|
454
|
+
`micro`, or `nano`
|
|
455
|
+
- `settlement_cadence` (nullable until a requirement is created):
|
|
456
|
+
`per_payment`, `weekly`, or `monthly`
|
|
457
|
+
- `finality` (nullable until a requirement is created), for example
|
|
458
|
+
`per_payment_onchain` or `aggregated_onchain_settlement`
|
|
459
|
+
- `protocol_fee_minor` (nullable; decimal string for Micro / Nano)
|
|
460
|
+
- `settlement_status` (nullable until a requirement is created), for example
|
|
461
|
+
`pending_payment`, `provisional`, `settled`, or `pending_settlement`
|
|
462
|
+
- `chain_receipt_id` (nullable)
|
|
450
463
|
- `success_url`
|
|
451
464
|
- `cancel_url`
|
|
452
465
|
- `expires_at` (nullable)
|
|
453
466
|
- `authenticated_at` (nullable; set when the shopper signs into Siglume)
|
|
454
|
-
- `paid_at` (nullable; set when the
|
|
467
|
+
- `paid_at` (nullable; set when Hosted Checkout has accepted the wallet
|
|
468
|
+
payment flow. For Micro / Nano, this is not the same as final provider
|
|
469
|
+
settlement; use `pricing_band`, `finality`, `settlement_status`, and the
|
|
470
|
+
statement APIs.)
|
|
455
471
|
- `cancelled_at` (nullable; set when the shopper cancels)
|
|
456
472
|
- `created_at` (nullable)
|
|
457
473
|
- `metadata_jsonb`
|
|
@@ -565,6 +581,13 @@ nonce: derive `nonce` from a durable order payment attempt, store the returned
|
|
|
565
581
|
hash for the same payment request. Do not retry the same order by minting a new
|
|
566
582
|
nonce unless you intentionally want a new payment attempt.
|
|
567
583
|
|
|
584
|
+
For Siglume Marketplace paid capability execution / MCP tools, `idempotency_key`
|
|
585
|
+
is a separate top-level JSON field on the execution payload or tool arguments.
|
|
586
|
+
Use one stable key per logical paid operation, up to 128 characters, and do not
|
|
587
|
+
reuse it for a different payload. A retry with the same key returns or reconciles
|
|
588
|
+
the first recorded outcome instead of creating another chargeable usage event.
|
|
589
|
+
The HTTP `Idempotency-Key` header is not the public requirement-create contract.
|
|
590
|
+
|
|
568
591
|
The returned requirement includes both compatibility and machine-readable
|
|
569
592
|
settlement fields:
|
|
570
593
|
|
|
@@ -937,11 +960,26 @@ argument is positional in both languages.
|
|
|
937
960
|
|
|
938
961
|
For `direct_payment.confirmed`, inspect `event.data.pricing_band`,
|
|
939
962
|
`event.data.settlement_cadence`, `event.data.finality`,
|
|
940
|
-
`event.data.protocol_fee_minor`,
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
963
|
+
`event.data.protocol_fee_minor`, `event.data.settlement_status`,
|
|
964
|
+
`event.data.settlement_batch_id`, `event.data.chain_receipt_id`,
|
|
965
|
+
`event.data.usage_event_digest`, and `event.data.settled_at` instead of
|
|
966
|
+
inferring whether the event means per-payment on-chain confirmation, Micro /
|
|
967
|
+
Nano accepted-but-unsettled usage, or an aggregated Micro / Nano settlement
|
|
968
|
+
confirmation. `event.data.request_hash_v2` is present on new challenge-backed
|
|
969
|
+
requirements; keep accepting `event.data.request_hash` for historical payloads.
|
|
970
|
+
|
|
971
|
+
Recommended branch:
|
|
972
|
+
|
|
973
|
+
- `mode === "metered_settlement_batch"`: no order `challenge_hash` is expected.
|
|
974
|
+
Reconcile the batch only when `settlement_status === "settled"`.
|
|
975
|
+
- `pricing_band === "standard"`, `finality === "per_payment_onchain"`, and
|
|
976
|
+
`settlement_status === "settled"`: mark the mapped order paid once.
|
|
977
|
+
- `pricing_band === "micro" || pricing_band === "nano"`: treat the usage as
|
|
978
|
+
accepted but unsettled. Fulfill only if your business accepts delayed
|
|
979
|
+
settlement risk, and reconcile final revenue from statement APIs / settlement
|
|
980
|
+
batches.
|
|
981
|
+
- Missing machine fields: do not mark paid from the event type alone; fetch the
|
|
982
|
+
requirement or route the event to manual review.
|
|
945
983
|
|
|
946
984
|
### `verifyDirectRequestPaymentWebhook(secret, body, signature_header, options)` / `verify_direct_request_payment_webhook(secret, body, signature_header, *, tolerance_seconds=300, now=None)`
|
|
947
985
|
|
|
@@ -959,7 +997,8 @@ const { event, verification } = await verifyDirectRequestPaymentWebhook(
|
|
|
959
997
|
rawRequestBody, // the RAW body bytes/string, not re-stringified JSON
|
|
960
998
|
request.headers["siglume-signature"],
|
|
961
999
|
);
|
|
962
|
-
// event.type === "direct_payment.confirmed"
|
|
1000
|
+
// event.type === "direct_payment.confirmed"; inspect pricing_band/finality/
|
|
1001
|
+
// settlement_status before marking an order paid.
|
|
963
1002
|
```
|
|
964
1003
|
|
|
965
1004
|
```py
|
|
@@ -971,7 +1010,8 @@ verified = verify_direct_request_payment_webhook(
|
|
|
971
1010
|
siglume_signature_header,
|
|
972
1011
|
)
|
|
973
1012
|
event = verified["event"]
|
|
974
|
-
# event["type"] == "direct_payment.confirmed"
|
|
1013
|
+
# event["type"] == "direct_payment.confirmed"; inspect pricing_band/finality/
|
|
1014
|
+
# settlement_status before marking an order paid.
|
|
975
1015
|
```
|
|
976
1016
|
|
|
977
1017
|
## Exported Constants
|
|
@@ -396,16 +396,63 @@ const { event } = await verifyDirectRequestPaymentWebhook(
|
|
|
396
396
|
siglumeSignatureHeader,
|
|
397
397
|
);
|
|
398
398
|
|
|
399
|
-
if (event.type
|
|
400
|
-
|
|
399
|
+
if (event.type !== "direct_payment.confirmed") {
|
|
400
|
+
return new Response(null, { status: 204 });
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
const data = event.data;
|
|
404
|
+
|
|
405
|
+
if (data.mode === "metered_settlement_batch") {
|
|
406
|
+
// Aggregated Micro/Nano settlement events do not carry an order challenge.
|
|
407
|
+
if (data.settlement_status === "settled") {
|
|
408
|
+
await orders.reconcileMeteredSettlementOnce({
|
|
409
|
+
settlement_batch_id: String(data.settlement_batch_id ?? ""),
|
|
410
|
+
chain_receipt_id: String(data.chain_receipt_id ?? ""),
|
|
411
|
+
usage_event_digest: String(data.usage_event_digest ?? ""),
|
|
412
|
+
settled_at: String(data.settled_at ?? ""),
|
|
413
|
+
});
|
|
414
|
+
}
|
|
415
|
+
return new Response(null, { status: 204 });
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
if (
|
|
419
|
+
data.pricing_band === "standard" &&
|
|
420
|
+
data.finality === "per_payment_onchain" &&
|
|
421
|
+
data.settlement_status === "settled"
|
|
422
|
+
) {
|
|
401
423
|
const order = await orders.findByChallengeHash(String(data.challenge_hash ?? ""));
|
|
402
424
|
if (!order) {
|
|
403
|
-
|
|
425
|
+
await orders.flagForPaymentStateReview({
|
|
426
|
+
reason: "unknown_challenge_hash",
|
|
427
|
+
requirement_id: String(data.requirement_id ?? data.direct_payment_requirement_id ?? ""),
|
|
428
|
+
});
|
|
429
|
+
return new Response(null, { status: 204 });
|
|
404
430
|
}
|
|
405
431
|
await orders.markPaidOnce(order.id, {
|
|
406
432
|
siglume_requirement_id: String(data.requirement_id ?? data.direct_payment_requirement_id ?? ""),
|
|
433
|
+
chain_receipt_id: String(data.chain_receipt_id ?? ""),
|
|
407
434
|
});
|
|
435
|
+
return new Response(null, { status: 204 });
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
if (data.pricing_band === "micro" || data.pricing_band === "nano") {
|
|
439
|
+
const order = await orders.findByChallengeHash(String(data.challenge_hash ?? ""));
|
|
440
|
+
if (order) {
|
|
441
|
+
await orders.markFulfilledButUnsettledOnce(order.id, {
|
|
442
|
+
siglume_requirement_id: String(data.requirement_id ?? data.direct_payment_requirement_id ?? ""),
|
|
443
|
+
pricing_band: String(data.pricing_band),
|
|
444
|
+
});
|
|
445
|
+
}
|
|
446
|
+
return new Response(null, { status: 204 });
|
|
408
447
|
}
|
|
448
|
+
|
|
449
|
+
// Missing or unknown machine fields: do not mark the order paid from the event
|
|
450
|
+
// name alone. Fetch the requirement or route it to manual review.
|
|
451
|
+
await orders.flagForPaymentStateReview({
|
|
452
|
+
reason: "missing_settlement_machine_fields",
|
|
453
|
+
requirement_id: String(data.requirement_id ?? data.direct_payment_requirement_id ?? ""),
|
|
454
|
+
});
|
|
455
|
+
return new Response(null, { status: 204 });
|
|
409
456
|
```
|
|
410
457
|
|
|
411
458
|
Python:
|
|
@@ -421,23 +468,68 @@ verified = verify_direct_request_payment_webhook(
|
|
|
421
468
|
siglume_signature_header,
|
|
422
469
|
)
|
|
423
470
|
|
|
424
|
-
if verified["event"]["type"]
|
|
425
|
-
|
|
471
|
+
if verified["event"]["type"] != "direct_payment.confirmed":
|
|
472
|
+
return "", 204
|
|
473
|
+
|
|
474
|
+
data = verified["event"]["data"]
|
|
475
|
+
|
|
476
|
+
if data.get("mode") == "metered_settlement_batch":
|
|
477
|
+
# Aggregated Micro/Nano settlement events do not carry an order challenge.
|
|
478
|
+
if data.get("settlement_status") == "settled":
|
|
479
|
+
orders.reconcile_metered_settlement_once(
|
|
480
|
+
settlement_batch_id=str(data.get("settlement_batch_id") or ""),
|
|
481
|
+
chain_receipt_id=str(data.get("chain_receipt_id") or ""),
|
|
482
|
+
usage_event_digest=str(data.get("usage_event_digest") or ""),
|
|
483
|
+
settled_at=str(data.get("settled_at") or ""),
|
|
484
|
+
)
|
|
485
|
+
return "", 204
|
|
486
|
+
|
|
487
|
+
if (
|
|
488
|
+
data.get("pricing_band") == "standard"
|
|
489
|
+
and data.get("finality") == "per_payment_onchain"
|
|
490
|
+
and data.get("settlement_status") == "settled"
|
|
491
|
+
):
|
|
426
492
|
order = orders.find_by_challenge_hash(str(data.get("challenge_hash") or ""))
|
|
427
493
|
if not order:
|
|
428
|
-
|
|
494
|
+
orders.flag_for_payment_state_review(
|
|
495
|
+
reason="unknown_challenge_hash",
|
|
496
|
+
requirement_id=str(data.get("requirement_id") or data.get("direct_payment_requirement_id") or ""),
|
|
497
|
+
)
|
|
498
|
+
return "", 204
|
|
429
499
|
orders.mark_paid_once(
|
|
430
500
|
order["id"],
|
|
431
501
|
siglume_requirement_id=str(data.get("requirement_id") or data.get("direct_payment_requirement_id") or ""),
|
|
502
|
+
chain_receipt_id=str(data.get("chain_receipt_id") or ""),
|
|
432
503
|
)
|
|
504
|
+
return "", 204
|
|
505
|
+
|
|
506
|
+
if data.get("pricing_band") in ("micro", "nano"):
|
|
507
|
+
order = orders.find_by_challenge_hash(str(data.get("challenge_hash") or ""))
|
|
508
|
+
if order:
|
|
509
|
+
orders.mark_fulfilled_but_unsettled_once(
|
|
510
|
+
order["id"],
|
|
511
|
+
siglume_requirement_id=str(data.get("requirement_id") or data.get("direct_payment_requirement_id") or ""),
|
|
512
|
+
pricing_band=str(data.get("pricing_band") or ""),
|
|
513
|
+
)
|
|
514
|
+
return "", 204
|
|
515
|
+
|
|
516
|
+
# Missing or unknown machine fields: do not mark the order paid from the event
|
|
517
|
+
# name alone. Fetch the requirement or route it to manual review.
|
|
518
|
+
orders.flag_for_payment_state_review(
|
|
519
|
+
reason="missing_settlement_machine_fields",
|
|
520
|
+
requirement_id=str(data.get("requirement_id") or data.get("direct_payment_requirement_id") or ""),
|
|
521
|
+
)
|
|
522
|
+
return "", 204
|
|
433
523
|
```
|
|
434
524
|
|
|
435
525
|
## Reconcile Micro / Nano Statements
|
|
436
526
|
|
|
437
|
-
Standard Payment can be
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
527
|
+
Standard Payment can be marked paid from the verified `direct_payment.confirmed`
|
|
528
|
+
webhook only when `pricing_band === "standard"`,
|
|
529
|
+
`finality === "per_payment_onchain"`, and `settlement_status === "settled"`.
|
|
530
|
+
Micro Payment and Nano Payment are different: they are automatic amount bands
|
|
531
|
+
and are settled later in aggregated on-chain batches. Use the statement APIs to
|
|
532
|
+
answer:
|
|
441
533
|
|
|
442
534
|
- how much Micro / Nano usage is open this week or month,
|
|
443
535
|
- when the buyer's assigned period closes,
|
|
@@ -109,7 +109,10 @@ rounding_delta_minor = buyer_debit_minor - gross_buyer_debit_minor
|
|
|
109
109
|
For low-count Nano batches, the ceiling can make the effective buyer burden per
|
|
110
110
|
usage higher than the headline "USD 0.001 / usage" protocol fee. The protocol
|
|
111
111
|
fee remains the decimal statement amount; the extra integer-minor-unit
|
|
112
|
-
adjustment is recorded as `rounding_delta_minor` on the settlement batch.
|
|
112
|
+
adjustment is recorded as `rounding_delta_minor` on the settlement batch. Each
|
|
113
|
+
settlement batch can add a positive rounding adjustment of less than 1 token
|
|
114
|
+
minor unit; if a buyer uses many providers / payees in one period, that
|
|
115
|
+
adjustment can occur once per settlement batch.
|
|
113
116
|
|
|
114
117
|
`rounding_delta_minor` belongs to the buyer debit and Siglume's rounding
|
|
115
118
|
adjustment accounting for that batch. It is not provider revenue. Provider
|
|
@@ -377,9 +380,20 @@ or support staff. Do not depend on raw platform failure messages.
|
|
|
377
380
|
|
|
378
381
|
## Usage Accounting by Result
|
|
379
382
|
|
|
380
|
-
Use idempotency keys for every paid operation. Siglume
|
|
381
|
-
|
|
382
|
-
|
|
383
|
+
Use idempotency keys for every paid operation. For Siglume Marketplace paid
|
|
384
|
+
capability execution and MCP tools, pass `idempotency_key` as a top-level JSON
|
|
385
|
+
field on the execution payload / tool arguments. Do not rely on an HTTP
|
|
386
|
+
`Idempotency-Key` header for this public paid-operation contract; Siglume may
|
|
387
|
+
also use that header internally when it calls providers.
|
|
388
|
+
|
|
389
|
+
The key should be a stable retry key for one logical paid operation, such as
|
|
390
|
+
`order:<order_id>:attempt:<n>` or `<provider_event_id>`, and should not be reused
|
|
391
|
+
for a different payload. The current public contract stores up to 128
|
|
392
|
+
characters. Siglume records one chargeable usage event per idempotency key within
|
|
393
|
+
the same buyer / listing / capability scope; a retry with the same key returns or
|
|
394
|
+
reconciles the first recorded outcome rather than creating another chargeable
|
|
395
|
+
event. If a provider times out after doing work, retry or reconcile with the
|
|
396
|
+
same key before repeating side effects.
|
|
383
397
|
|
|
384
398
|
| Case | Provider API executed? | Usage counted? | Integration rule |
|
|
385
399
|
| --- | --- | --- | --- |
|
|
@@ -446,7 +460,9 @@ separate from settled revenue.
|
|
|
446
460
|
## Go-Live Checklist
|
|
447
461
|
|
|
448
462
|
- Your order fulfillment is idempotent by order id and requirement id.
|
|
449
|
-
- Standard Payment fulfillment still uses verified `direct_payment.confirmed
|
|
463
|
+
- Standard Payment fulfillment still uses verified `direct_payment.confirmed`
|
|
464
|
+
only when `pricing_band`, `finality`, and `settlement_status` show settled
|
|
465
|
+
per-payment finality.
|
|
450
466
|
- Micro / Nano accounting uses statement APIs or CSV, not only webhooks.
|
|
451
467
|
- Your dashboard separates settled, unsettled, and past-due provider amounts.
|
|
452
468
|
- Your support UI shows sanitized failure fields and `support_reference`.
|
package/docs/pricing.md
CHANGED
|
@@ -40,6 +40,11 @@ The current public API chooses the band from `amount_minor`; it does not expose
|
|
|
40
40
|
`settlement_mode: "immediate"` or `require_immediate_finality: true`. If a
|
|
41
41
|
merchant needs immediate on-chain finality, the payment amount must be in the
|
|
42
42
|
Standard band or the merchant must have a separately agreed platform contract.
|
|
43
|
+
For public one-time Direct Payment / Hosted Checkout, `amount_minor` is a
|
|
44
|
+
positive integer in minor currency units. That means the smallest public
|
|
45
|
+
one-time checkout amount is JPY 1 or USD 0.01. Nano Payment on this public path
|
|
46
|
+
therefore means JPY 1-49 or USD 0.01-0.30. Sub-minor Nano protocol fees are
|
|
47
|
+
settlement-accounting amounts, not externally submitted one-time item prices.
|
|
43
48
|
|
|
44
49
|
For the operational statement APIs, CSV export, buyer past-due blocks, and the
|
|
45
50
|
field-by-field meaning of `scheduled_debit_at`, `not_before_attempt_at`,
|
|
@@ -183,8 +188,11 @@ For low-count Nano batches, the integer ceiling can make the effective buyer
|
|
|
183
188
|
burden per usage higher than the headline USD 0.001 / usage protocol fee. The
|
|
184
189
|
decimal protocol fee remains visible as `protocol_fee_minor`; the difference
|
|
185
190
|
created by integer-token settlement is visible as `rounding_delta_minor` on the
|
|
186
|
-
batch.
|
|
187
|
-
|
|
191
|
+
batch. Each settlement batch can add a positive rounding adjustment of less than
|
|
192
|
+
1 token minor unit. If a buyer uses many providers / payees in one period, that
|
|
193
|
+
adjustment can occur once per settlement batch. JavaScript integrations should
|
|
194
|
+
not sum Micro / Nano minor amounts with `number`; use a decimal library. Python
|
|
195
|
+
integrations should use `Decimal`.
|
|
188
196
|
|
|
189
197
|
## Statement APIs and Notices
|
|
190
198
|
|
package/docs/security.md
CHANGED
|
@@ -125,6 +125,13 @@ merchant-authored challenge nonce plus the returned `challenge_hash` /
|
|
|
125
125
|
`request_hash_v2`. Reuse the same order-attempt nonce when reconciling a retry;
|
|
126
126
|
mint a new nonce only for a new payment attempt.
|
|
127
127
|
|
|
128
|
+
For Micro / Nano paid capability execution through Siglume Marketplace or MCP
|
|
129
|
+
tools, use the top-level JSON field `idempotency_key` on the execution payload /
|
|
130
|
+
tool arguments. Treat it as a stable retry key for one logical paid operation
|
|
131
|
+
and do not reuse it for a different payload. The HTTP `Idempotency-Key` header is
|
|
132
|
+
not the public contract for this requirement-create API; Siglume may use headers
|
|
133
|
+
internally when calling providers.
|
|
134
|
+
|
|
128
135
|
## Micro / Nano Statement Privacy
|
|
129
136
|
|
|
130
137
|
Micro Payment and Nano Payment introduce operational statement APIs and CSV
|
|
@@ -22,6 +22,48 @@ app.use((req, res, next) => {
|
|
|
22
22
|
|
|
23
23
|
const orders = new Map<string, any>();
|
|
24
24
|
|
|
25
|
+
async function handleDirectPaymentConfirmed(data: Record<string, any>): Promise<void> {
|
|
26
|
+
if (data.mode === "metered_settlement_batch") {
|
|
27
|
+
// Aggregated Micro/Nano settlement events do not carry an order challenge.
|
|
28
|
+
// Reconcile them against statement / settlement batch data instead.
|
|
29
|
+
if (data.settlement_status === "settled") {
|
|
30
|
+
console.log("settled metered batch", data.settlement_batch_id || data.usage_event_digest);
|
|
31
|
+
}
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (
|
|
36
|
+
data.pricing_band === "standard" &&
|
|
37
|
+
data.finality === "per_payment_onchain" &&
|
|
38
|
+
data.settlement_status === "settled"
|
|
39
|
+
) {
|
|
40
|
+
const challengeHash = String(data.challenge_hash || "");
|
|
41
|
+
const order = [...orders.values()].find((item) => item.siglume_challenge_hash === challengeHash);
|
|
42
|
+
if (order) {
|
|
43
|
+
order.siglume_payment_status = "paid";
|
|
44
|
+
order.siglume_requirement_id = data.requirement_id || data.direct_payment_requirement_id;
|
|
45
|
+
order.siglume_chain_receipt_id = data.chain_receipt_id || null;
|
|
46
|
+
}
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (data.pricing_band === "micro" || data.pricing_band === "nano") {
|
|
51
|
+
const challengeHash = String(data.challenge_hash || "");
|
|
52
|
+
const order = [...orders.values()].find((item) => item.siglume_challenge_hash === challengeHash);
|
|
53
|
+
if (order) {
|
|
54
|
+
order.siglume_payment_status = "fulfilled_unsettled";
|
|
55
|
+
order.siglume_requirement_id = data.requirement_id || data.direct_payment_requirement_id;
|
|
56
|
+
}
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Unknown or older payload shape: do not mark paid from the event name alone.
|
|
61
|
+
console.warn("direct_payment.confirmed missing settlement machine fields", {
|
|
62
|
+
id: data.id,
|
|
63
|
+
requirement_id: data.requirement_id || data.direct_payment_requirement_id,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
|
|
25
67
|
const asyncRoute =
|
|
26
68
|
(handler: express.RequestHandler): express.RequestHandler =>
|
|
27
69
|
(req, res, next) => {
|
|
@@ -69,12 +111,7 @@ app.post("/siglume/webhook", express.raw({ type: "application/json" }), asyncRou
|
|
|
69
111
|
);
|
|
70
112
|
|
|
71
113
|
if (event.type === "direct_payment.confirmed") {
|
|
72
|
-
|
|
73
|
-
const order = [...orders.values()].find((item) => item.siglume_challenge_hash === challengeHash);
|
|
74
|
-
if (order) {
|
|
75
|
-
order.siglume_payment_status = "paid";
|
|
76
|
-
order.siglume_requirement_id = event.data.requirement_id || event.data.direct_payment_requirement_id;
|
|
77
|
-
}
|
|
114
|
+
await handleDirectPaymentConfirmed(event.data);
|
|
78
115
|
}
|
|
79
116
|
|
|
80
117
|
res.status(204).send();
|