@siglume/direct-request-payment 0.4.0 → 0.4.2
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 +42 -0
- package/README.md +51 -1
- package/dist/index.cjs +32 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.js +32 -5
- package/dist/index.js.map +1 -1
- package/docs/announcement-ja.md +8 -6
- package/docs/api-reference.md +344 -18
- package/docs/merchant-quickstart.md +32 -1
- package/docs/pricing.md +18 -14
- package/package.json +1 -1
package/docs/api-reference.md
CHANGED
|
@@ -10,7 +10,7 @@ SDRP serves two kinds of buyer, and you integrate each differently. In both
|
|
|
10
10
|
cases the buyer pays from a Siglume wallet (JPYC for JPY, USDC for USD) — not a
|
|
11
11
|
card — and the merchant SDK never authenticates the buyer.
|
|
12
12
|
|
|
13
|
-
- **Human web shopper → Hosted Checkout.** Call
|
|
13
|
+
- **Human web shopper → Hosted Checkout (Beta; server rollout in progress).** Call
|
|
14
14
|
[`createCheckoutSession`](#createcheckoutsessioninput--create_checkout_session)
|
|
15
15
|
on `DirectRequestPaymentMerchantClient` and redirect the shopper to the
|
|
16
16
|
returned `checkout_url`. The shopper signs into Siglume on the hosted page,
|
|
@@ -80,10 +80,79 @@ Returns:
|
|
|
80
80
|
`nonce` must not contain `:` because the platform challenge string is delimited
|
|
81
81
|
as `scheme:nonce:signature`.
|
|
82
82
|
|
|
83
|
+
## `createDirectRequestPaymentChallengeSignature(secret, input)` / `create_direct_request_payment_challenge_signature(...)`
|
|
84
|
+
|
|
85
|
+
Returns just the HMAC-SHA256 hex digest (no `scheme:nonce:signature` wrapper),
|
|
86
|
+
for callers who assemble the challenge string themselves. This is the primitive
|
|
87
|
+
that `createDirectRequestPaymentChallenge` calls internally. The HMAC material is
|
|
88
|
+
`merchant:amount_minor:currency:nonce`.
|
|
89
|
+
|
|
90
|
+
```ts
|
|
91
|
+
const signature = await createDirectRequestPaymentChallengeSignature(secret, {
|
|
92
|
+
merchant: "example_merchant",
|
|
93
|
+
amount_minor: 1200,
|
|
94
|
+
currency: "JPY",
|
|
95
|
+
nonce: "order_123-attempt_1",
|
|
96
|
+
});
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
```py
|
|
100
|
+
signature = create_direct_request_payment_challenge_signature(
|
|
101
|
+
secret=secret,
|
|
102
|
+
merchant="example_merchant",
|
|
103
|
+
amount_minor=1200,
|
|
104
|
+
currency="JPY",
|
|
105
|
+
nonce="order_123-attempt_1",
|
|
106
|
+
)
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
In TypeScript the secret is the first positional argument and the rest are an
|
|
110
|
+
object. In Python every argument is keyword-only:
|
|
111
|
+
`create_direct_request_payment_challenge_signature(*, secret, merchant, amount_minor, currency, nonce)`.
|
|
112
|
+
Returns a `string` (TS) / `str` (Py).
|
|
113
|
+
|
|
83
114
|
## `verifyDirectRequestPaymentChallenge(secret, input)` / `verify_direct_request_payment_challenge(...)`
|
|
84
115
|
|
|
85
116
|
Verifies a challenge against merchant, amount, currency, and secret. This is
|
|
86
|
-
useful in tests and internal checkout assertions.
|
|
117
|
+
useful in tests and internal checkout assertions. Returns `boolean` (TS) /
|
|
118
|
+
`bool` (Py) — `true` only when the challenge scheme matches and the recomputed
|
|
119
|
+
signature is a timing-safe match.
|
|
120
|
+
|
|
121
|
+
```ts
|
|
122
|
+
const ok = await verifyDirectRequestPaymentChallenge(secret, {
|
|
123
|
+
merchant: "example_merchant",
|
|
124
|
+
amount_minor: 1200,
|
|
125
|
+
currency: "JPY",
|
|
126
|
+
challenge: challengeString,
|
|
127
|
+
});
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
In TypeScript the secret is positional and the rest is an object
|
|
131
|
+
(`verifyDirectRequestPaymentChallenge(secret, { merchant, amount_minor, currency, challenge })`).
|
|
132
|
+
In Python every argument is keyword-only:
|
|
133
|
+
|
|
134
|
+
```py
|
|
135
|
+
ok = verify_direct_request_payment_challenge(
|
|
136
|
+
secret=secret,
|
|
137
|
+
merchant="example_merchant",
|
|
138
|
+
amount_minor=1200,
|
|
139
|
+
currency="JPY",
|
|
140
|
+
challenge=challenge_string,
|
|
141
|
+
)
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## `parseDirectRequestPaymentChallenge(challenge)` / `parse_direct_request_payment_challenge(challenge)`
|
|
145
|
+
|
|
146
|
+
Splits a `scheme:nonce:signature` challenge string into its parts. Throws
|
|
147
|
+
`SiglumeDirectRequestPaymentError` (TS) / `DirectRequestPaymentError` (Py) when
|
|
148
|
+
the string is not exactly three non-empty colon-delimited parts. The `challenge`
|
|
149
|
+
argument is positional in both languages.
|
|
150
|
+
|
|
151
|
+
Returns:
|
|
152
|
+
|
|
153
|
+
- `scheme`
|
|
154
|
+
- `nonce`
|
|
155
|
+
- `signature`
|
|
87
156
|
|
|
88
157
|
## `createDirectRequestPaymentRecurringChallenge(input)` / `create_direct_request_payment_recurring_challenge(...)`
|
|
89
158
|
|
|
@@ -137,10 +206,59 @@ monthly auto-pay budget.
|
|
|
137
206
|
|
|
138
207
|
Returns the same fields as the one-time challenge helper, plus `cadence`.
|
|
139
208
|
|
|
209
|
+
## `createDirectRequestPaymentRecurringChallengeSignature(secret, input)` / `create_direct_request_payment_recurring_challenge_signature(...)`
|
|
210
|
+
|
|
211
|
+
Returns just the HMAC-SHA256 hex digest for a recurring approval, for callers who
|
|
212
|
+
assemble the challenge string themselves. This is the primitive that
|
|
213
|
+
`createDirectRequestPaymentRecurringChallenge` calls internally. The HMAC
|
|
214
|
+
material is `merchant:amount_minor:currency:cadence:nonce` and must stay
|
|
215
|
+
byte-identical to the server's recurring-challenge signer.
|
|
216
|
+
|
|
217
|
+
```ts
|
|
218
|
+
const signature = await createDirectRequestPaymentRecurringChallengeSignature(secret, {
|
|
219
|
+
merchant: "example_merchant",
|
|
220
|
+
amount_minor: 980,
|
|
221
|
+
currency: "JPY",
|
|
222
|
+
cadence: "monthly",
|
|
223
|
+
nonce: "subscription_setup_4711",
|
|
224
|
+
});
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
```py
|
|
228
|
+
signature = create_direct_request_payment_recurring_challenge_signature(
|
|
229
|
+
secret=secret,
|
|
230
|
+
merchant="example_merchant",
|
|
231
|
+
amount_minor=980,
|
|
232
|
+
currency="JPY",
|
|
233
|
+
cadence="monthly",
|
|
234
|
+
nonce="subscription_setup_4711",
|
|
235
|
+
)
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
In TypeScript the secret is positional and the rest is an object. In Python every
|
|
239
|
+
argument is keyword-only:
|
|
240
|
+
`create_direct_request_payment_recurring_challenge_signature(*, secret, merchant, amount_minor, currency, cadence, nonce)`.
|
|
241
|
+
Returns a `string` (TS) / `str` (Py).
|
|
242
|
+
|
|
140
243
|
## `verifyDirectRequestPaymentRecurringChallenge(secret, input)` / `verify_direct_request_payment_recurring_challenge(...)`
|
|
141
244
|
|
|
142
245
|
Verifies a recurring approval challenge against merchant, amount, currency,
|
|
143
|
-
cadence, and secret.
|
|
246
|
+
cadence, and secret. Returns `boolean` (TS) / `bool` (Py).
|
|
247
|
+
|
|
248
|
+
In TypeScript the secret is positional and the rest is an object
|
|
249
|
+
(`verifyDirectRequestPaymentRecurringChallenge(secret, { merchant, amount_minor, currency, cadence, challenge })`).
|
|
250
|
+
In Python every argument is keyword-only:
|
|
251
|
+
|
|
252
|
+
```py
|
|
253
|
+
ok = verify_direct_request_payment_recurring_challenge(
|
|
254
|
+
secret=secret,
|
|
255
|
+
merchant="example_merchant",
|
|
256
|
+
amount_minor=980,
|
|
257
|
+
currency="JPY",
|
|
258
|
+
cadence="monthly",
|
|
259
|
+
challenge=challenge_string,
|
|
260
|
+
)
|
|
261
|
+
```
|
|
144
262
|
|
|
145
263
|
## `directRequestPaymentChallengeHash(challenge)` / `direct_request_payment_challenge_hash(...)`
|
|
146
264
|
|
|
@@ -180,7 +298,9 @@ Input:
|
|
|
180
298
|
- `merchant`: self-service merchant key, 3-64 chars using lowercase letters,
|
|
181
299
|
numbers, `_`, or `-`
|
|
182
300
|
- `display_name`: optional public merchant name
|
|
183
|
-
- `billing_plan`: `launch`, `starter`, `growth`, or `pro`
|
|
301
|
+
- `billing_plan`: `launch`, `starter`, `growth`, or `pro` (default `launch`). The
|
|
302
|
+
legacy key `free` is also accepted as a compatibility input and maps to the
|
|
303
|
+
Launch tier; prefer `launch` in new code.
|
|
184
304
|
- `billing_currency`: `JPY`; `USD` requires agreed USD/USDC billing terms
|
|
185
305
|
- `webhook_callback_url`: HTTPS callback URL for signed payment events
|
|
186
306
|
- `max_amount_minor`: optional billing mandate cap
|
|
@@ -191,6 +311,21 @@ Input:
|
|
|
191
311
|
absolute origin such as `https://shop.example.com`; entries are normalized to
|
|
192
312
|
bare, lowercased origins and deduped.
|
|
193
313
|
|
|
314
|
+
In addition to the `setupMerchant` inputs above, `setupCheckout` accepts these
|
|
315
|
+
orchestration toggles:
|
|
316
|
+
|
|
317
|
+
- `prepare_billing_mandate`: default `true`. When `false`, the billing mandate
|
|
318
|
+
step is skipped and `billing_mandate` in the result is `null`.
|
|
319
|
+
- `create_webhook_subscription`: optional. When omitted, a webhook subscription
|
|
320
|
+
is created only if `webhook_callback_url` is set. Set `false` to skip webhook
|
|
321
|
+
creation even when a callback URL is present (TS uses `?? Boolean(webhook_callback_url)`;
|
|
322
|
+
Py defaults to `bool(webhook_callback_url)`).
|
|
323
|
+
- `webhook_event_types`: optional `string[]` of event types for the created
|
|
324
|
+
subscription. When omitted the subscription defaults to
|
|
325
|
+
`direct_payment.confirmed` and `direct_payment.spent`.
|
|
326
|
+
- `webhook_description`: optional description for the created subscription;
|
|
327
|
+
defaults to `"<merchant> Direct Request Payment"`.
|
|
328
|
+
|
|
194
329
|
Returns:
|
|
195
330
|
|
|
196
331
|
- `merchant`: merchant account setup response
|
|
@@ -216,6 +351,12 @@ webhook-origin auto-allow apply.
|
|
|
216
351
|
|
|
217
352
|
### `createCheckoutSession(input)` / `create_checkout_session(...)`
|
|
218
353
|
|
|
354
|
+
Beta / server rollout: Hosted Checkout is rolling out account by account. If the
|
|
355
|
+
server endpoint is not enabled for the merchant yet, the SDK raises
|
|
356
|
+
`HostedCheckoutNotAvailableError` (TS + Py) rather than leaking a raw rollout
|
|
357
|
+
404/409. Fulfillment must still key off the signed `direct_payment.confirmed`
|
|
358
|
+
webhook.
|
|
359
|
+
|
|
219
360
|
Creates a single-use, expiring Hosted Checkout session for a human web shopper
|
|
220
361
|
and returns the URL to redirect them to. Requires the merchant's Siglume bearer
|
|
221
362
|
token. The server authors the challenge from the merchant's challenge secret —
|
|
@@ -285,7 +426,7 @@ Calls:
|
|
|
285
426
|
GET /v1/sdrp/direct-payments/checkout-sessions/{session_id}
|
|
286
427
|
```
|
|
287
428
|
|
|
288
|
-
Returns a
|
|
429
|
+
Returns a `HostedCheckoutSession` status object with:
|
|
289
430
|
|
|
290
431
|
- `session_id`
|
|
291
432
|
- `merchant`
|
|
@@ -295,13 +436,19 @@ Returns a session status object with:
|
|
|
295
436
|
- `status`: one of `open`, `authenticated`, `paid`, `expired`, `cancelled`,
|
|
296
437
|
`failed`
|
|
297
438
|
- `challenge_hash`
|
|
298
|
-
- `requirement_id`
|
|
439
|
+
- `requirement_id` (nullable until a requirement is created)
|
|
299
440
|
- `success_url`
|
|
300
441
|
- `cancel_url`
|
|
301
|
-
- `expires_at`
|
|
302
|
-
- `
|
|
442
|
+
- `expires_at` (nullable)
|
|
443
|
+
- `authenticated_at` (nullable; set when the shopper signs into Siglume)
|
|
444
|
+
- `paid_at` (nullable; set when the payment confirms)
|
|
445
|
+
- `cancelled_at` (nullable; set when the shopper cancels)
|
|
446
|
+
- `created_at` (nullable)
|
|
303
447
|
- `metadata_jsonb`
|
|
304
448
|
|
|
449
|
+
The TS `HostedCheckoutSession` interface also carries an index signature, so the
|
|
450
|
+
server may include additional pass-through fields.
|
|
451
|
+
|
|
305
452
|
### `getMerchant(merchant)` / `get_merchant(merchant)`
|
|
306
453
|
|
|
307
454
|
Calls:
|
|
@@ -395,6 +542,61 @@ Input:
|
|
|
395
542
|
- `allowance_amount_minor`: optional positive integer
|
|
396
543
|
- `metadata`: optional JSON object
|
|
397
544
|
|
|
545
|
+
### `getPaymentRequirement(requirement_id)` / `get_payment_requirement(requirement_id)`
|
|
546
|
+
|
|
547
|
+
Calls:
|
|
548
|
+
|
|
549
|
+
```text
|
|
550
|
+
GET /v1/sdrp/direct-payments/requirements/{requirement_id}
|
|
551
|
+
```
|
|
552
|
+
|
|
553
|
+
Fetches the current state of a payment requirement (status, `transaction_request`,
|
|
554
|
+
`approve_transaction_request`, `chain_receipt_id`, etc.) by id. The
|
|
555
|
+
`requirement_id` argument is positional in both languages. Returns the same
|
|
556
|
+
requirement object shape as `createPaymentRequirement`.
|
|
557
|
+
|
|
558
|
+
```ts
|
|
559
|
+
const requirement = await siglume.getPaymentRequirement(requirementId);
|
|
560
|
+
```
|
|
561
|
+
|
|
562
|
+
```py
|
|
563
|
+
requirement = siglume.get_payment_requirement(requirement_id)
|
|
564
|
+
```
|
|
565
|
+
|
|
566
|
+
### `executePreparedTransaction(payload)` / `execute_prepared_transaction(payload)`
|
|
567
|
+
|
|
568
|
+
Calls:
|
|
569
|
+
|
|
570
|
+
```text
|
|
571
|
+
POST /v1/market/web3/transactions/execute-prepared
|
|
572
|
+
```
|
|
573
|
+
|
|
574
|
+
The raw prepared-transaction executor. It posts a prepared-transaction payload
|
|
575
|
+
(`transaction_request`, `receipt_kind`, `reference_type`, `reference_id`,
|
|
576
|
+
`metadata`, `await_finality`) to the marketplace web3 route and returns the
|
|
577
|
+
execution result (`{ receipt?, finalization?, ... }`). The `payload` argument is
|
|
578
|
+
positional in both languages.
|
|
579
|
+
|
|
580
|
+
`executePaymentTransaction` / `execute_payment_transaction` and
|
|
581
|
+
`executeAllowanceTransaction` / `execute_allowance_transaction` are convenience
|
|
582
|
+
wrappers over this method: they build the payload from the requirement (via
|
|
583
|
+
[`buildPaymentExecutionPayload`](#payload-builders) /
|
|
584
|
+
[`buildAllowanceExecutionPayload`](#payload-builders)) and call
|
|
585
|
+
`executePreparedTransaction` for you. Call `executePreparedTransaction` directly
|
|
586
|
+
only when you build the payload yourself.
|
|
587
|
+
|
|
588
|
+
```ts
|
|
589
|
+
const result = await siglume.executePreparedTransaction(
|
|
590
|
+
buildPaymentExecutionPayload(requirement, { await_finality: true }),
|
|
591
|
+
);
|
|
592
|
+
```
|
|
593
|
+
|
|
594
|
+
```py
|
|
595
|
+
result = siglume.execute_prepared_transaction(
|
|
596
|
+
build_payment_execution_payload(requirement, await_finality=True),
|
|
597
|
+
)
|
|
598
|
+
```
|
|
599
|
+
|
|
398
600
|
### `executeAllowanceTransaction(requirement)` / `execute_allowance_transaction(...)`
|
|
399
601
|
|
|
400
602
|
Executes `requirement.approve_transaction_request` through:
|
|
@@ -427,19 +629,139 @@ Input may include:
|
|
|
427
629
|
- `await_timeout_seconds`
|
|
428
630
|
- `await_poll_seconds`
|
|
429
631
|
|
|
632
|
+
## Payload Builders
|
|
633
|
+
|
|
634
|
+
These pure functions build the `execute-prepared` payload from a payment
|
|
635
|
+
requirement, for callers who execute the transaction themselves (rather than via
|
|
636
|
+
the `executePaymentTransaction` / `executeAllowanceTransaction` wrappers). They
|
|
637
|
+
make no network calls.
|
|
638
|
+
|
|
639
|
+
### `buildPaymentExecutionPayload(requirement, options)` / `build_payment_execution_payload(...)`
|
|
640
|
+
|
|
641
|
+
Builds the payment-transaction payload from `requirement.transaction_request`
|
|
642
|
+
with `receipt_kind = "sdrp_direct_payment"`.
|
|
643
|
+
|
|
644
|
+
In TypeScript `options` is an optional object `{ await_finality?, metadata? }`.
|
|
645
|
+
In Python the options are keyword-only:
|
|
646
|
+
`build_payment_execution_payload(requirement, *, await_finality=False, metadata=None)`.
|
|
647
|
+
Returns the prepared-transaction payload object.
|
|
648
|
+
|
|
649
|
+
### `buildAllowanceExecutionPayload(requirement, options)` / `build_allowance_execution_payload(...)`
|
|
650
|
+
|
|
651
|
+
Builds the allowance/approval-transaction payload from
|
|
652
|
+
`requirement.approve_transaction_request` with
|
|
653
|
+
`receipt_kind = "sdrp_direct_payment_allowance"`. Throws
|
|
654
|
+
`SiglumeDirectRequestPaymentError` (TS) / `DirectRequestPaymentError` (Py) when
|
|
655
|
+
the requirement carries no allowance approval transaction. Same options shape as
|
|
656
|
+
`buildPaymentExecutionPayload`.
|
|
657
|
+
|
|
658
|
+
### `buildPreparedTransactionExecutionPayload(requirement, transaction_request, options)` / `build_prepared_transaction_execution_payload(...)`
|
|
659
|
+
|
|
660
|
+
The lower-level builder both of the above call. It merges
|
|
661
|
+
`transaction_request.metadata_jsonb` with any `options.metadata`, and sets
|
|
662
|
+
`reference_type = "sdrp_direct_payment_requirement"` and `reference_id =
|
|
663
|
+
requirement.requirement_id`.
|
|
664
|
+
|
|
665
|
+
In TypeScript `options` is required and must include `receipt_kind`:
|
|
666
|
+
`{ receipt_kind, await_finality?, metadata? }`. In Python the third argument is
|
|
667
|
+
the `transaction_request` and the rest are keyword-only:
|
|
668
|
+
`build_prepared_transaction_execution_payload(requirement, transaction_request, *, receipt_kind, await_finality=False, metadata=None)`.
|
|
669
|
+
Returns the prepared-transaction payload object.
|
|
670
|
+
|
|
430
671
|
## Webhook Helpers
|
|
431
672
|
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
- `
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
673
|
+
### `computeWebhookSignature(secret, body, options)` / `compute_webhook_signature(secret, body, *, timestamp)`
|
|
674
|
+
|
|
675
|
+
Returns the bare HMAC-SHA256 hex digest over `"<timestamp>.<body>"`. This is the
|
|
676
|
+
primitive `buildWebhookSignatureHeader` / `verifyWebhookSignature` use. In
|
|
677
|
+
TypeScript `options` is `{ timestamp: number }`; in Python `timestamp` is a
|
|
678
|
+
keyword-only `int`. `body` may be raw bytes, a string, or a JSON object.
|
|
679
|
+
|
|
680
|
+
### `buildWebhookSignatureHeader(secret, body, options)` / `build_webhook_signature_header(secret, body, *, timestamp=None)`
|
|
681
|
+
|
|
682
|
+
Returns a `t=<timestamp>,v1=<signature>` header string. Mainly for tests /
|
|
683
|
+
mocking inbound webhooks. In TypeScript `options` is an optional
|
|
684
|
+
`{ timestamp?: number }` (defaults to now); in Python `timestamp` is a
|
|
685
|
+
keyword-only optional `int`.
|
|
686
|
+
|
|
687
|
+
### `verifyWebhookSignature(secret, body, signature_header, options)` / `verify_webhook_signature(secret, body, signature_header, *, tolerance_seconds=300, now=None)`
|
|
688
|
+
|
|
689
|
+
Verifies the `Siglume-Signature` header against the raw `body`. Throws
|
|
690
|
+
`SiglumeWebhookSignatureError` (TS) / `SiglumeWebhookSignatureError` (Py) when
|
|
691
|
+
the timestamp is outside tolerance or the signature does not match. In TypeScript
|
|
692
|
+
`options` is `{ tolerance_seconds?, now? }`; in Python those are keyword-only
|
|
693
|
+
(`tolerance_seconds` defaults to `DEFAULT_WEBHOOK_TOLERANCE_SECONDS` = 300).
|
|
694
|
+
Returns `{ timestamp, signature }`.
|
|
695
|
+
|
|
696
|
+
### `parseDirectRequestPaymentWebhookEvent(payload)` / `parse_direct_request_payment_webhook_event(payload)`
|
|
697
|
+
|
|
698
|
+
Validates and normalizes a parsed webhook event object (requires `id`, `type`,
|
|
699
|
+
`api_version`, `occurred_at`, and an object `data`). Throws
|
|
700
|
+
`SiglumeWebhookPayloadError` on a malformed event, or when a
|
|
701
|
+
`direct_payment.confirmed` event does not carry `data.mode = "external_402"`. The
|
|
702
|
+
`payload` argument is positional in both languages.
|
|
703
|
+
|
|
704
|
+
### `verifyDirectRequestPaymentWebhook(secret, body, signature_header, options)` / `verify_direct_request_payment_webhook(secret, body, signature_header, *, tolerance_seconds=300, now=None)`
|
|
705
|
+
|
|
706
|
+
Verifies the signature and parses the event in one call. Returns
|
|
707
|
+
`{ event, verification }` (TS) / `{"event": ..., "verification": ...}` (Py). Same
|
|
708
|
+
options shape as `verifyWebhookSignature` (keyword-only in Python).
|
|
440
709
|
|
|
441
|
-
|
|
442
|
-
|
|
710
|
+
Webhook-verification trio (typical inbound webhook handler):
|
|
711
|
+
|
|
712
|
+
```ts
|
|
713
|
+
import { verifyDirectRequestPaymentWebhook } from "@siglume/direct-request-payment";
|
|
714
|
+
|
|
715
|
+
const { event, verification } = await verifyDirectRequestPaymentWebhook(
|
|
716
|
+
process.env.SIGLUME_WEBHOOK_SECRET!,
|
|
717
|
+
rawRequestBody, // the RAW body bytes/string, not re-stringified JSON
|
|
718
|
+
request.headers["siglume-signature"],
|
|
719
|
+
);
|
|
720
|
+
// event.type === "direct_payment.confirmed" -> fulfill once; verification.timestamp is the signed time
|
|
721
|
+
```
|
|
722
|
+
|
|
723
|
+
```py
|
|
724
|
+
from siglume_direct_request_payment import verify_direct_request_payment_webhook
|
|
725
|
+
|
|
726
|
+
verified = verify_direct_request_payment_webhook(
|
|
727
|
+
os.environ["SIGLUME_WEBHOOK_SECRET"],
|
|
728
|
+
raw_request_body, # the RAW body bytes/string
|
|
729
|
+
siglume_signature_header,
|
|
730
|
+
)
|
|
731
|
+
event = verified["event"]
|
|
732
|
+
# event["type"] == "direct_payment.confirmed" -> fulfill once
|
|
733
|
+
```
|
|
734
|
+
|
|
735
|
+
## Exported Constants
|
|
736
|
+
|
|
737
|
+
Both packages export these importable constants:
|
|
738
|
+
|
|
739
|
+
| Constant | Value |
|
|
740
|
+
| --- | --- |
|
|
741
|
+
| `DEFAULT_SIGLUME_API_BASE` | `https://siglume.com/v1` |
|
|
742
|
+
| `DIRECT_REQUEST_PAYMENT_CHALLENGE_SCHEME` | `siglume-external-402-v1` |
|
|
743
|
+
| `DIRECT_REQUEST_PAYMENT_RECURRING_CHALLENGE_SCHEME` | `siglume-external-402-recurring-v1` |
|
|
744
|
+
| `DIRECT_REQUEST_PAYMENT_MODE` | `external_402` |
|
|
745
|
+
| `DIRECT_REQUEST_PAYMENT_RECEIPT_KIND` | `sdrp_direct_payment` |
|
|
746
|
+
| `DIRECT_REQUEST_PAYMENT_ALLOWANCE_RECEIPT_KIND` | `sdrp_direct_payment_allowance` |
|
|
747
|
+
| `DIRECT_REQUEST_PAYMENT_REFERENCE_TYPE` | `sdrp_direct_payment_requirement` |
|
|
748
|
+
| `DEFAULT_WEBHOOK_TOLERANCE_SECONDS` | `300` |
|
|
749
|
+
|
|
750
|
+
The `external_402` / `siglume-external-402-*` values are legacy wire-compat
|
|
751
|
+
identifiers, not public product names (see the README "Compatibility Notes").
|
|
752
|
+
|
|
753
|
+
## Aliases
|
|
754
|
+
|
|
755
|
+
For legacy wire-compat naming, the following exported names are aliases of the
|
|
756
|
+
preferred `DirectRequestPayment*` functions. They are identical functions; new
|
|
757
|
+
code should prefer the `DirectRequestPayment*` names.
|
|
758
|
+
|
|
759
|
+
| Alias (TS) | Alias (Py) | Preferred function |
|
|
760
|
+
| --- | --- | --- |
|
|
761
|
+
| `createExternal402Challenge` | `create_external_402_challenge` | `createDirectRequestPaymentChallenge` / `create_direct_request_payment_challenge` |
|
|
762
|
+
| `verifyExternal402Challenge` | `verify_external_402_challenge` | `verifyDirectRequestPaymentChallenge` / `verify_direct_request_payment_challenge` |
|
|
763
|
+
| `createExternal402RecurringChallenge` | `create_external_402_recurring_challenge` | `createDirectRequestPaymentRecurringChallenge` / `create_direct_request_payment_recurring_challenge` |
|
|
764
|
+
| `verifyExternal402RecurringChallenge` | `verify_external_402_recurring_challenge` | `verifyDirectRequestPaymentRecurringChallenge` / `verify_direct_request_payment_recurring_challenge` |
|
|
443
765
|
|
|
444
766
|
## Errors
|
|
445
767
|
|
|
@@ -447,6 +769,7 @@ TypeScript exports:
|
|
|
447
769
|
|
|
448
770
|
- `SiglumeDirectRequestPaymentError`
|
|
449
771
|
- `SiglumeApiError`
|
|
772
|
+
- `HostedCheckoutNotAvailableError`
|
|
450
773
|
- `SiglumeWebhookSignatureError`
|
|
451
774
|
- `SiglumeWebhookPayloadError`
|
|
452
775
|
|
|
@@ -454,8 +777,11 @@ Python exports:
|
|
|
454
777
|
|
|
455
778
|
- `DirectRequestPaymentError`
|
|
456
779
|
- `SiglumeApiError`
|
|
780
|
+
- `HostedCheckoutNotAvailableError`
|
|
457
781
|
- `SiglumeWebhookSignatureError`
|
|
458
782
|
- `SiglumeWebhookPayloadError`
|
|
459
783
|
|
|
460
784
|
`SiglumeApiError` includes the HTTP status, platform error code, and parsed
|
|
461
785
|
response data where available.
|
|
786
|
+
`HostedCheckoutNotAvailableError` is raised when the Hosted Checkout server
|
|
787
|
+
surface is not enabled for the account yet during the rollout.
|
|
@@ -24,7 +24,7 @@ revenue remains unsettled until the later on-chain settlement succeeds.
|
|
|
24
24
|
|
|
25
25
|
There are two ways a buyer reaches you, and you integrate each differently:
|
|
26
26
|
|
|
27
|
-
- **Human web shopper → Hosted Checkout.** Create a checkout session and
|
|
27
|
+
- **Human web shopper → Hosted Checkout (Beta; server rollout in progress).** Create a checkout session and
|
|
28
28
|
redirect the shopper to the Siglume-hosted page (the
|
|
29
29
|
[section below](#hosted-checkout-human-web-shoppers)). This is the path that
|
|
30
30
|
resembles a Stripe-style hosted checkout.
|
|
@@ -38,6 +38,10 @@ the merchant SDK never authenticates the buyer, and you fulfill on the same
|
|
|
38
38
|
|
|
39
39
|
## Hosted Checkout (Human Web Shoppers)
|
|
40
40
|
|
|
41
|
+
**Beta / server rollout:** Hosted Checkout is rolling out account by account.
|
|
42
|
+
Some merchant accounts may not have the server endpoint enabled yet. The SDK
|
|
43
|
+
raises `HostedCheckoutNotAvailableError` for rollout 404/409 responses.
|
|
44
|
+
|
|
41
45
|
When a person clicks "Pay with Siglume" on your site, create a session and
|
|
42
46
|
redirect them to the returned `checkout_url`. They sign into Siglume on the
|
|
43
47
|
hosted page, approve, and pay from their own wallet, then return to your
|
|
@@ -357,6 +361,26 @@ siglume.verify_payment_requirement(
|
|
|
357
361
|
)
|
|
358
362
|
```
|
|
359
363
|
|
|
364
|
+
### Subscriptions and scheduled autopay (no SDK method)
|
|
365
|
+
|
|
366
|
+
The SDK signs the merchant-side recurring approval challenge
|
|
367
|
+
(`createDirectRequestPaymentRecurringChallenge` /
|
|
368
|
+
`create_direct_request_payment_recurring_challenge`), but there is **no SDK
|
|
369
|
+
method for subscription creation**. After you hand the buyer the recurring
|
|
370
|
+
challenge, the subscription itself is created over **raw HTTP** with the buyer's
|
|
371
|
+
Siglume bearer token:
|
|
372
|
+
|
|
373
|
+
```text
|
|
374
|
+
POST /v1/sdrp/direct-payments/subscriptions
|
|
375
|
+
{ merchant, amount_minor, currency, cadence: "monthly", challenge }
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
For scheduled autopay (`cadence: "daily"`), the buyer instead creates a scheduled
|
|
379
|
+
auto-pay authorization and hands you a `schedule_token`; your scheduler triggers
|
|
380
|
+
each occurrence with that token. Neither of these calls is wrapped by
|
|
381
|
+
`DirectRequestPaymentClient` today — the SDK's recurring surface is the challenge
|
|
382
|
+
signer and verifier only.
|
|
383
|
+
|
|
360
384
|
## 4. Fulfill from Webhook
|
|
361
385
|
|
|
362
386
|
Use the webhook as the durable signal, not just the browser return path.
|
|
@@ -421,6 +445,13 @@ if verified["event"]["type"] == "direct_payment.confirmed":
|
|
|
421
445
|
- `EXTERNAL_402_MERCHANT_BILLING_PAST_DUE` or
|
|
422
446
|
`EXTERNAL_402_MERCHANT_BILLING_SUSPENDED`: merchant billing must be fixed
|
|
423
447
|
before new payments can be accepted.
|
|
448
|
+
- `METERED_SETTLEMENT_PAST_DUE` (Micro / Nano only): a previous Micro / Nano
|
|
449
|
+
metered settlement for this buyer is unresolved, so new Micro / Nano usage in
|
|
450
|
+
the same fee band is paused until it settles. Siglume retries settlement
|
|
451
|
+
automatically every 6 hours, up to 28 attempts, before it requires manual
|
|
452
|
+
resolution. The provider's Micro / Nano revenue stays unsettled until the
|
|
453
|
+
settlement succeeds. This is a settlement-side state, not a per-request
|
|
454
|
+
challenge error.
|
|
424
455
|
|
|
425
456
|
## Go-Live Checklist
|
|
426
457
|
|
package/docs/pricing.md
CHANGED
|
@@ -59,34 +59,38 @@ confirmed payment turns into money in your settlement wallet.
|
|
|
59
59
|
| Band | Cadence | Period | You are paid |
|
|
60
60
|
| --- | --- | --- | --- |
|
|
61
61
|
| Standard Payment | Per payment | n/a | On-chain, immediately after each payment confirms |
|
|
62
|
-
| Micro Payment | Weekly |
|
|
63
|
-
| Nano Payment | Monthly |
|
|
62
|
+
| Micro Payment | Weekly | Fixed weekly slot assigned per account | After the period closes, after the final notice and an approximately 3-day pre-debit notice site, in aggregated on-chain settlement(s) grouped per buyer, payee, token, and period |
|
|
63
|
+
| Nano Payment | Monthly | Fixed monthly slot assigned per account | After the period closes, after the final notice and an approximately 3-day pre-debit notice site, in aggregated on-chain settlement(s) grouped per buyer, payee, token, and period |
|
|
64
64
|
|
|
65
65
|
### Micro weekly settlement
|
|
66
66
|
|
|
67
|
-
- **Closing period.** Micro-band payments accrue across one
|
|
68
|
-
|
|
67
|
+
- **Closing period.** Micro-band payments accrue across one weekly period. The
|
|
68
|
+
specific closing weekday and time are assigned as a fixed slot per account to
|
|
69
|
+
spread settlement load.
|
|
69
70
|
- **Timezone.** Period boundaries are evaluated in the buyer's configured
|
|
70
|
-
settlement timezone, defaulting to UTC
|
|
71
|
-
|
|
71
|
+
settlement timezone, defaulting to UTC. Assigned slots are persisted and are
|
|
72
|
+
not recalculated on the fly.
|
|
72
73
|
- **Settlement.** After the week closes, Siglume aggregates that week's Micro
|
|
73
74
|
payments — grouped per buyer, payee, token, and period — into on-chain
|
|
74
|
-
settlement(s).
|
|
75
|
-
|
|
76
|
-
|
|
75
|
+
settlement(s). Siglume sends the final debit notice first; the on-chain debit
|
|
76
|
+
is not attempted until the scheduled attempt time after an approximately
|
|
77
|
+
3-day pre-debit notice site (`not_before_attempt_at`).
|
|
77
78
|
- **Revenue recognition.** A Micro payment is final only once its weekly
|
|
78
79
|
settlement confirms on-chain. Until then it is accrued, not settled.
|
|
79
80
|
|
|
80
81
|
### Nano monthly settlement
|
|
81
82
|
|
|
82
|
-
- **Closing period.** Nano-band payments accrue across one
|
|
83
|
-
|
|
84
|
-
settlement
|
|
83
|
+
- **Closing period.** Nano-band payments accrue across one monthly period. The
|
|
84
|
+
specific closing day and time are assigned as a fixed slot per account to
|
|
85
|
+
spread settlement load.
|
|
85
86
|
- **Timezone.** As with Micro, period boundaries use the buyer's configured
|
|
86
|
-
settlement timezone, defaulting to UTC.
|
|
87
|
+
settlement timezone, defaulting to UTC. Assigned slots are persisted and are
|
|
88
|
+
not recalculated on the fly.
|
|
87
89
|
- **Settlement.** After the month closes, Siglume aggregates that month's Nano
|
|
88
90
|
payments — grouped per buyer, payee, token, and period — into on-chain
|
|
89
|
-
settlement(s)
|
|
91
|
+
settlement(s). Siglume sends the final debit notice first; the on-chain debit
|
|
92
|
+
is not attempted until the scheduled attempt time after an approximately
|
|
93
|
+
3-day pre-debit notice site (`not_before_attempt_at`).
|
|
90
94
|
- **Revenue recognition.** A Nano payment is final only once its monthly
|
|
91
95
|
settlement confirms on-chain.
|
|
92
96
|
|