@siglume/direct-request-payment 0.3.6 → 0.4.0
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 +30 -0
- package/README.md +87 -0
- package/dist/index.cjs +64 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +51 -1
- package/dist/index.d.ts +51 -1
- package/dist/index.js +64 -2
- package/dist/index.js.map +1 -1
- package/docs/announcement-ja.md +16 -0
- package/docs/api-reference.md +123 -0
- package/docs/merchant-quickstart.md +130 -0
- package/docs/security.md +17 -0
- package/package.json +1 -1
package/docs/api-reference.md
CHANGED
|
@@ -4,6 +4,32 @@ The TypeScript package is `@siglume/direct-request-payment`. The Python package
|
|
|
4
4
|
is `siglume-direct-request-payment` and imports as
|
|
5
5
|
`siglume_direct_request_payment`.
|
|
6
6
|
|
|
7
|
+
## Two Buyer Systems
|
|
8
|
+
|
|
9
|
+
SDRP serves two kinds of buyer, and you integrate each differently. In both
|
|
10
|
+
cases the buyer pays from a Siglume wallet (JPYC for JPY, USDC for USD) — not a
|
|
11
|
+
card — and the merchant SDK never authenticates the buyer.
|
|
12
|
+
|
|
13
|
+
- **Human web shopper → Hosted Checkout.** Call
|
|
14
|
+
[`createCheckoutSession`](#createcheckoutsessioninput--create_checkout_session)
|
|
15
|
+
on `DirectRequestPaymentMerchantClient` and redirect the shopper to the
|
|
16
|
+
returned `checkout_url`. The shopper signs into Siglume on the hosted page,
|
|
17
|
+
approves, and pays from their own wallet.
|
|
18
|
+
- **AI agent / agent-to-agent (AtoA) → direct API / tools.** An autonomous
|
|
19
|
+
buyer agent pays through `DirectRequestPaymentClient` (your app holds the
|
|
20
|
+
buyer's Siglume JWT) or through the Siglume marketplace tool
|
|
21
|
+
`market_confirm_direct_payment_and_execute` (MCP). Agent payment assumes the
|
|
22
|
+
buyer agent is **already connected to Siglume before the payment**: an AI
|
|
23
|
+
client (Claude / ChatGPT / Cursor) connects through the Siglume MCP server
|
|
24
|
+
(OAuth authorization with a consent screen), or a custom app holds the buyer's
|
|
25
|
+
Siglume bearer token. The merchant SDK does not log the buyer in. Unattended
|
|
26
|
+
runs are bounded by Siglume's approval gates / spending budgets (per-run /
|
|
27
|
+
daily / monthly auto-pay budgets, or Works approval).
|
|
28
|
+
|
|
29
|
+
In both systems the merchant fulfills on the same signed
|
|
30
|
+
`direct_payment.confirmed` webhook. Hosted Checkout adds no new money movement
|
|
31
|
+
and no new webhook.
|
|
32
|
+
|
|
7
33
|
## Environment Variables
|
|
8
34
|
|
|
9
35
|
| Name | Used by | Purpose |
|
|
@@ -158,6 +184,12 @@ Input:
|
|
|
158
184
|
- `billing_currency`: `JPY`; `USD` requires agreed USD/USDC billing terms
|
|
159
185
|
- `webhook_callback_url`: HTTPS callback URL for signed payment events
|
|
160
186
|
- `max_amount_minor`: optional billing mandate cap
|
|
187
|
+
- `checkout_allowed_origins`: optional `string[]` return-URL origin allowlist for
|
|
188
|
+
Hosted Checkout (open-redirect defense). A Hosted Checkout `success_url` /
|
|
189
|
+
`cancel_url` must be on a registered origin; the origin of
|
|
190
|
+
`webhook_callback_url` is auto-allowed in addition. Each entry must be an
|
|
191
|
+
absolute origin such as `https://shop.example.com`; entries are normalized to
|
|
192
|
+
bare, lowercased origins and deduped.
|
|
161
193
|
|
|
162
194
|
Returns:
|
|
163
195
|
|
|
@@ -178,6 +210,97 @@ POST /v1/sdrp/direct-payments/merchants
|
|
|
178
210
|
```
|
|
179
211
|
|
|
180
212
|
Creates or updates the merchant account for the authenticated merchant user.
|
|
213
|
+
Accepts the optional `checkout_allowed_origins: string[]` return-URL origin
|
|
214
|
+
allowlist described under `setupCheckout` above; the same normalization and
|
|
215
|
+
webhook-origin auto-allow apply.
|
|
216
|
+
|
|
217
|
+
### `createCheckoutSession(input)` / `create_checkout_session(...)`
|
|
218
|
+
|
|
219
|
+
Creates a single-use, expiring Hosted Checkout session for a human web shopper
|
|
220
|
+
and returns the URL to redirect them to. Requires the merchant's Siglume bearer
|
|
221
|
+
token. The server authors the challenge from the merchant's challenge secret —
|
|
222
|
+
the browser never sees or supplies it.
|
|
223
|
+
|
|
224
|
+
Calls:
|
|
225
|
+
|
|
226
|
+
```text
|
|
227
|
+
POST /v1/sdrp/direct-payments/checkout-sessions
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
```ts
|
|
231
|
+
const session = await merchant.createCheckoutSession({
|
|
232
|
+
merchant: "example_merchant",
|
|
233
|
+
amount_minor: 500,
|
|
234
|
+
currency: "JPY",
|
|
235
|
+
nonce: order.id,
|
|
236
|
+
success_url: "https://www.your-shop.com/thanks",
|
|
237
|
+
cancel_url: "https://www.your-shop.com/cart",
|
|
238
|
+
metadata: { order_id: order.id },
|
|
239
|
+
});
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
```py
|
|
243
|
+
session = merchant.create_checkout_session(
|
|
244
|
+
merchant="example_merchant",
|
|
245
|
+
amount_minor=500,
|
|
246
|
+
currency="JPY",
|
|
247
|
+
nonce=order["id"],
|
|
248
|
+
success_url="https://www.your-shop.com/thanks",
|
|
249
|
+
cancel_url="https://www.your-shop.com/cart",
|
|
250
|
+
metadata={"order_id": order["id"]},
|
|
251
|
+
)
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
Input:
|
|
255
|
+
|
|
256
|
+
- `merchant`: Siglume merchant key
|
|
257
|
+
- `amount_minor`: positive integer in minor currency units (server-fixed; the
|
|
258
|
+
browser cannot change it)
|
|
259
|
+
- `currency`: `JPY`, or `USD` when enabled for the merchant account
|
|
260
|
+
- `nonce`: unique per order; must not contain `:`
|
|
261
|
+
- `success_url`: return URL after a completed payment; must be on a registered
|
|
262
|
+
`checkout_allowed_origins` origin (or the webhook origin)
|
|
263
|
+
- `cancel_url`: return URL after the shopper cancels; same origin rule
|
|
264
|
+
- `metadata`: optional JSON object
|
|
265
|
+
|
|
266
|
+
Returns:
|
|
267
|
+
|
|
268
|
+
- `checkout_url`: hosted page to redirect the shopper to
|
|
269
|
+
(`https://siglume.com/pay/<session_id>`)
|
|
270
|
+
- `session_id`
|
|
271
|
+
- `challenge_hash`: store this on the order to map the later webhook back
|
|
272
|
+
- `status`
|
|
273
|
+
- `expires_at`: the session is single-use and expires (~30 minutes)
|
|
274
|
+
|
|
275
|
+
### `getCheckoutSession(session_id)` / `get_checkout_session(session_id)`
|
|
276
|
+
|
|
277
|
+
Returns the current status of a Hosted Checkout session. Useful if you want to
|
|
278
|
+
show progress in your own UI; the signed `direct_payment.confirmed` webhook
|
|
279
|
+
remains the source of truth for fulfillment. Never exposes the raw challenge or
|
|
280
|
+
buyer PII.
|
|
281
|
+
|
|
282
|
+
Calls:
|
|
283
|
+
|
|
284
|
+
```text
|
|
285
|
+
GET /v1/sdrp/direct-payments/checkout-sessions/{session_id}
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
Returns a session status object with:
|
|
289
|
+
|
|
290
|
+
- `session_id`
|
|
291
|
+
- `merchant`
|
|
292
|
+
- `currency`
|
|
293
|
+
- `token_symbol`
|
|
294
|
+
- `amount_minor`
|
|
295
|
+
- `status`: one of `open`, `authenticated`, `paid`, `expired`, `cancelled`,
|
|
296
|
+
`failed`
|
|
297
|
+
- `challenge_hash`
|
|
298
|
+
- `requirement_id`
|
|
299
|
+
- `success_url`
|
|
300
|
+
- `cancel_url`
|
|
301
|
+
- `expires_at`
|
|
302
|
+
- `paid_at`
|
|
303
|
+
- `metadata_jsonb`
|
|
181
304
|
|
|
182
305
|
### `getMerchant(merchant)` / `get_merchant(merchant)`
|
|
183
306
|
|
|
@@ -20,6 +20,124 @@ a weekly / monthly cadence (see [Pricing](./pricing.md#settlement-schedule)); th
|
|
|
20
20
|
are not browser checkout requirements you create with this SDK. Their provider
|
|
21
21
|
revenue remains unsettled until the later on-chain settlement succeeds.
|
|
22
22
|
|
|
23
|
+
## Two Buyer Systems
|
|
24
|
+
|
|
25
|
+
There are two ways a buyer reaches you, and you integrate each differently:
|
|
26
|
+
|
|
27
|
+
- **Human web shopper → Hosted Checkout.** Create a checkout session and
|
|
28
|
+
redirect the shopper to the Siglume-hosted page (the
|
|
29
|
+
[section below](#hosted-checkout-human-web-shoppers)). This is the path that
|
|
30
|
+
resembles a Stripe-style hosted checkout.
|
|
31
|
+
- **AI agent / agent-to-agent (AtoA) → direct API / tools.** An autonomous
|
|
32
|
+
buyer pays through `DirectRequestPaymentClient` or the marketplace tool
|
|
33
|
+
`market_confirm_direct_payment_and_execute`, as in sections 2-4 below.
|
|
34
|
+
|
|
35
|
+
In both cases the buyer pays from a Siglume wallet (JPYC / USDC, not a card),
|
|
36
|
+
the merchant SDK never authenticates the buyer, and you fulfill on the same
|
|
37
|
+
`direct_payment.confirmed` webhook.
|
|
38
|
+
|
|
39
|
+
## Hosted Checkout (Human Web Shoppers)
|
|
40
|
+
|
|
41
|
+
When a person clicks "Pay with Siglume" on your site, create a session and
|
|
42
|
+
redirect them to the returned `checkout_url`. They sign into Siglume on the
|
|
43
|
+
hosted page, approve, and pay from their own wallet, then return to your
|
|
44
|
+
`success_url`. Siglume fixes the amount, currency, challenge, and return URLs
|
|
45
|
+
server-side, so the browser cannot tamper with the price or the redirect target.
|
|
46
|
+
|
|
47
|
+
Register your return-URL origins once (open-redirect defense). The origin of
|
|
48
|
+
your `webhook_callback_url` is auto-allowed in addition to these.
|
|
49
|
+
|
|
50
|
+
TypeScript:
|
|
51
|
+
|
|
52
|
+
```ts
|
|
53
|
+
import { DirectRequestPaymentMerchantClient } from "@siglume/direct-request-payment";
|
|
54
|
+
|
|
55
|
+
const merchant = new DirectRequestPaymentMerchantClient({
|
|
56
|
+
auth_token: process.env.SIGLUME_MERCHANT_AUTH_TOKEN!,
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// Once, at setup: register the return-URL origin allowlist.
|
|
60
|
+
await merchant.setupCheckout({
|
|
61
|
+
merchant: "example_merchant",
|
|
62
|
+
display_name: "Example Merchant",
|
|
63
|
+
billing_plan: "launch",
|
|
64
|
+
billing_currency: "JPY",
|
|
65
|
+
webhook_callback_url: "https://merchant.example/siglume/webhook",
|
|
66
|
+
checkout_allowed_origins: ["https://www.example.com"],
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
// Per order: create a session and redirect the shopper to checkout_url.
|
|
70
|
+
const session = await merchant.createCheckoutSession({
|
|
71
|
+
merchant: "example_merchant",
|
|
72
|
+
amount_minor: 500, // server-fixed; the browser cannot change it
|
|
73
|
+
currency: "JPY",
|
|
74
|
+
nonce: order.id, // unique per order
|
|
75
|
+
success_url: "https://www.example.com/thanks",
|
|
76
|
+
cancel_url: "https://www.example.com/cart",
|
|
77
|
+
metadata: { order_id: order.id },
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
await orders.update(order.id, {
|
|
81
|
+
siglume_challenge_hash: session.challenge_hash,
|
|
82
|
+
siglume_payment_status: "pending",
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
redirect(session.checkout_url); // -> https://siglume.com/pay/<session_id>
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Python:
|
|
89
|
+
|
|
90
|
+
```py
|
|
91
|
+
import os
|
|
92
|
+
|
|
93
|
+
from siglume_direct_request_payment import DirectRequestPaymentMerchantClient
|
|
94
|
+
|
|
95
|
+
merchant = DirectRequestPaymentMerchantClient(
|
|
96
|
+
auth_token=os.environ["SIGLUME_MERCHANT_AUTH_TOKEN"],
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
# Once, at setup: register the return-URL origin allowlist.
|
|
100
|
+
merchant.setup_checkout(
|
|
101
|
+
merchant="example_merchant",
|
|
102
|
+
display_name="Example Merchant",
|
|
103
|
+
billing_plan="launch",
|
|
104
|
+
billing_currency="JPY",
|
|
105
|
+
webhook_callback_url="https://merchant.example/siglume/webhook",
|
|
106
|
+
checkout_allowed_origins=["https://www.example.com"],
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
# Per order: create a session and redirect the shopper to checkout_url.
|
|
110
|
+
session = merchant.create_checkout_session(
|
|
111
|
+
merchant="example_merchant",
|
|
112
|
+
amount_minor=500, # server-fixed; the browser cannot change it
|
|
113
|
+
currency="JPY",
|
|
114
|
+
nonce=order["id"], # unique per order
|
|
115
|
+
success_url="https://www.example.com/thanks",
|
|
116
|
+
cancel_url="https://www.example.com/cart",
|
|
117
|
+
metadata={"order_id": order["id"]},
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
orders.update(
|
|
121
|
+
order["id"],
|
|
122
|
+
{
|
|
123
|
+
"siglume_challenge_hash": session["challenge_hash"],
|
|
124
|
+
"siglume_payment_status": "pending",
|
|
125
|
+
},
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
# Redirect the shopper to session["checkout_url"]
|
|
129
|
+
# -> https://siglume.com/pay/<session_id>
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Fulfill exactly as in [section 4](#4-fulfill-from-webhook): on the signed
|
|
133
|
+
`direct_payment.confirmed` webhook, look up the order by `challenge_hash` and
|
|
134
|
+
mark it paid once. The session is single-use and expires (~30 minutes); you can
|
|
135
|
+
poll `getCheckoutSession` / `get_checkout_session` if you also want to show
|
|
136
|
+
status in your own UI, but the webhook is the source of truth. Honest framing:
|
|
137
|
+
the merchant plumbing integrates quickly, but human web payment still requires
|
|
138
|
+
the shopper to have — or create — a Siglume wallet and pay from it; it is not a
|
|
139
|
+
card-style "instant" checkout for first-time buyers.
|
|
140
|
+
|
|
23
141
|
## 1. Run Merchant Setup
|
|
24
142
|
|
|
25
143
|
Run setup from the merchant server, CI, or an integration agent with the
|
|
@@ -162,6 +280,18 @@ The nonce must be unique per order payment attempt and must not contain `:`.
|
|
|
162
280
|
|
|
163
281
|
## 3. Buyer Creates and Pays the Requirement
|
|
164
282
|
|
|
283
|
+
This is the AI agent / AtoA path: the buyer pays directly through
|
|
284
|
+
`DirectRequestPaymentClient` (or the marketplace tool
|
|
285
|
+
`market_confirm_direct_payment_and_execute`), rather than through Hosted
|
|
286
|
+
Checkout. It assumes the buyer agent is **already connected to Siglume before
|
|
287
|
+
the payment**: an AI client (Claude / ChatGPT / Cursor) connects through the
|
|
288
|
+
Siglume MCP server (OAuth authorization with a consent screen), or a custom app
|
|
289
|
+
holds the buyer's Siglume bearer token (JWT). Either way a Siglume
|
|
290
|
+
authentication context is established first — the merchant SDK does not log the
|
|
291
|
+
buyer in. Unattended runs are bounded by Siglume's approval gates / spending
|
|
292
|
+
budgets (per-run / daily / monthly auto-pay budgets, or Works approval), not by
|
|
293
|
+
the merchant.
|
|
294
|
+
|
|
165
295
|
After the buyer authenticates with Siglume, create the payment requirement with
|
|
166
296
|
the buyer's Siglume bearer token. Do not use a Developer Portal `cli_` API key
|
|
167
297
|
or merchant API key here.
|
package/docs/security.md
CHANGED
|
@@ -49,6 +49,23 @@ autopay approval tag; it does not itself limit occurrences to once per day.
|
|
|
49
49
|
Scheduled autopay execution is bounded by the buyer-approved per-run, daily, and
|
|
50
50
|
monthly auto-pay budget.
|
|
51
51
|
|
|
52
|
+
## Hosted Checkout Return URLs
|
|
53
|
+
|
|
54
|
+
Hosted Checkout adds a return-URL origin allowlist as open-redirect defense.
|
|
55
|
+
Register your allowed origins once via `checkout_allowed_origins` on
|
|
56
|
+
`setupCheckout` / `setupMerchant`. A checkout session's `success_url` and
|
|
57
|
+
`cancel_url` must be on a registered origin; the origin of your
|
|
58
|
+
`webhook_callback_url` is auto-allowed in addition. Each entry must be an
|
|
59
|
+
absolute origin such as `https://shop.example.com`; entries are normalized to
|
|
60
|
+
bare, lowercased origins and deduped. A return URL that is not on an allowed
|
|
61
|
+
origin is rejected, so an attacker cannot point a session at an arbitrary
|
|
62
|
+
redirect target.
|
|
63
|
+
|
|
64
|
+
For a Hosted Checkout session, Siglume authors the amount, currency, challenge,
|
|
65
|
+
and return URLs server-side at session creation. The browser cannot tamper with
|
|
66
|
+
the price or the redirect target, and the raw challenge is never exposed to the
|
|
67
|
+
browser or returned by `getCheckoutSession`.
|
|
68
|
+
|
|
52
69
|
## Do Not Trust Browser Amounts
|
|
53
70
|
|
|
54
71
|
The merchant server owns:
|