@siglume/direct-request-payment 0.1.0 → 0.3.1
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 +61 -0
- package/LICENSE +21 -21
- package/README.md +317 -148
- package/dist/index.cjs +257 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +134 -1
- package/dist/index.d.ts +134 -1
- package/dist/index.js +257 -1
- package/dist/index.js.map +1 -1
- package/docs/announcement-ja.md +69 -0
- package/docs/api-reference.md +280 -65
- package/docs/merchant-quickstart.md +180 -139
- package/docs/pricing.md +73 -56
- package/docs/security.md +110 -85
- package/examples/express-checkout.ts +106 -105
- package/examples/setup-merchant.ts +17 -0
- package/package.json +71 -57
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.3.1 - 2026-06-12
|
|
4
|
+
|
|
5
|
+
- Docs: scheduled autopay (`cadence: "daily"`) is documented as an approval
|
|
6
|
+
tag, not a once-per-day run limit. Siglume no longer caps scheduled autopay
|
|
7
|
+
at one charge per day; occurrences are bounded by the buyer's per-run,
|
|
8
|
+
daily, and monthly auto-pay budget plus the authorization's `max_runs` /
|
|
9
|
+
expiry. No code or wire-format changes — challenges signed by 0.3.0 verify
|
|
10
|
+
unchanged.
|
|
11
|
+
- Release automation now uses npm and PyPI Trusted Publishing from GitHub
|
|
12
|
+
Actions, so normal releases do not require local npm OTP or PyPI credentials.
|
|
13
|
+
|
|
14
|
+
## 0.3.0 - 2026-06-12
|
|
15
|
+
|
|
16
|
+
Recurring payment approval release.
|
|
17
|
+
|
|
18
|
+
- Added recurring (subscription / scheduled autopay) merchant approval helpers
|
|
19
|
+
in TypeScript (`createDirectRequestPaymentRecurringChallenge`,
|
|
20
|
+
`createDirectRequestPaymentRecurringChallengeSignature`,
|
|
21
|
+
`verifyDirectRequestPaymentRecurringChallenge`) and Python
|
|
22
|
+
(`create_direct_request_payment_recurring_challenge`,
|
|
23
|
+
`create_direct_request_payment_recurring_challenge_signature`,
|
|
24
|
+
`verify_direct_request_payment_recurring_challenge`).
|
|
25
|
+
- Recurring approvals use a new single-use challenge scheme
|
|
26
|
+
(`siglume-external-402-recurring-v1`) with the cadence (`monthly` for
|
|
27
|
+
subscriptions, `daily` for scheduled autopay) bound into the HMAC, so
|
|
28
|
+
one-time checkout challenges and recurring approvals can never be replayed
|
|
29
|
+
as each other.
|
|
30
|
+
- Documented the recurring payment flow (subscription and scheduled autopay)
|
|
31
|
+
in the README.
|
|
32
|
+
- Updated pricing docs: the Launch plan's free monthly allowance of 100
|
|
33
|
+
payments is retired — Launch is now a flat 1.8% payment fee; the per-payment
|
|
34
|
+
minimum fee is JPY 30 (USD merchants: USD 0.20); JPY/JPYC and USD/USDC
|
|
35
|
+
settlement are both documented as first-class.
|
|
36
|
+
|
|
37
|
+
## 0.2.0 - 2026-06-12
|
|
38
|
+
|
|
39
|
+
Merchant self-service setup release.
|
|
40
|
+
|
|
41
|
+
- Added TypeScript `DirectRequestPaymentMerchantClient` and Python
|
|
42
|
+
`DirectRequestPaymentMerchantClient`.
|
|
43
|
+
- Added `setupCheckout` / `setup_checkout` to claim a merchant key, prepare a
|
|
44
|
+
billing mandate, and create a webhook subscription from the SDK.
|
|
45
|
+
- Added challenge secret rotation, merchant status lookup, billing mandate
|
|
46
|
+
preparation, and webhook subscription helpers.
|
|
47
|
+
- Updated docs to remove manual onboarding assumptions and clarify merchant JWT
|
|
48
|
+
vs buyer JWT responsibilities.
|
|
49
|
+
|
|
50
|
+
## 0.1.0 - 2026-06-11
|
|
51
|
+
|
|
52
|
+
Initial public release.
|
|
53
|
+
|
|
54
|
+
- Published TypeScript/JavaScript SDK as `@siglume/direct-request-payment`.
|
|
55
|
+
- Published Python SDK as `siglume-direct-request-payment`.
|
|
56
|
+
- Added merchant challenge helpers, buyer-authenticated Direct Request Payment
|
|
57
|
+
client methods, prepared transaction payload helpers, and webhook signature
|
|
58
|
+
verification helpers.
|
|
59
|
+
- Documented Launch, Starter, Growth, and Pro trial pricing.
|
|
60
|
+
- Documented the distinction from `@siglume/api-sdk` and Developer Portal
|
|
61
|
+
`cli_` API keys.
|
package/LICENSE
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2026 Siglume Contributors
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Siglume Contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,17 +1,36 @@
|
|
|
1
|
-
# @siglume/direct-request-payment
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
1
|
+
# @siglume/direct-request-payment
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@siglume/direct-request-payment)
|
|
4
|
+
[](https://pypi.org/project/siglume-direct-request-payment/)
|
|
5
|
+
|
|
6
|
+
Merchant SDK for Siglume Direct Request Payment checkout integrations.
|
|
7
|
+
|
|
8
|
+
Use this package when an external EC site, booking service, membership service,
|
|
9
|
+
or paid API wants to accept Siglume wallet payments without taking custody of
|
|
10
|
+
customer funds.
|
|
11
|
+
|
|
12
|
+
This SDK is intentionally separate from `@siglume/api-sdk`:
|
|
13
|
+
|
|
14
|
+
- `@siglume/api-sdk` is for publishing agent-facing APIs to the Siglume API Store.
|
|
15
|
+
- `@siglume/direct-request-payment` is for external merchants integrating
|
|
16
|
+
Siglume Direct Request Payment into their own checkout.
|
|
17
|
+
|
|
18
|
+
## What This SDK Covers
|
|
19
|
+
|
|
20
|
+
- merchant self-service setup with a Siglume merchant JWT
|
|
21
|
+
- challenge secret creation and rotation
|
|
22
|
+
- merchant billing mandate preparation
|
|
23
|
+
- webhook subscription creation
|
|
24
|
+
- merchant-signed payment challenges
|
|
25
|
+
- buyer-authenticated payment requirement creation
|
|
26
|
+
- prepared wallet transaction execution payloads
|
|
27
|
+
- payment requirement verification
|
|
28
|
+
- signed webhook verification
|
|
29
|
+
|
|
30
|
+
It does not custody funds or manage customer wallets. Merchant setup runs through
|
|
31
|
+
Siglume APIs with the merchant's Siglume JWT; buyer payment creation runs with
|
|
32
|
+
the buyer's Siglume JWT.
|
|
33
|
+
|
|
15
34
|
## Install
|
|
16
35
|
|
|
17
36
|
```bash
|
|
@@ -24,60 +43,129 @@ pip install siglume-direct-request-payment
|
|
|
24
43
|
|
|
25
44
|
Node.js 18 or later is required for the TypeScript SDK. Python 3.11 or later is
|
|
26
45
|
required for the Python SDK.
|
|
27
|
-
|
|
28
|
-
## Current Platform Contract
|
|
29
|
-
|
|
30
|
-
The public product name is **Siglume Direct Request Payment**. The current
|
|
31
|
-
platform payload still uses the internal mode name `external_402`; this SDK sets
|
|
32
|
-
that value for you when creating a payment requirement.
|
|
33
|
-
|
|
34
|
-
Payment requirement creation must run in the authenticated buyer's Siglume
|
|
35
|
-
context. Your merchant server must not use a merchant secret or API key to
|
|
36
|
-
charge a customer wallet. The merchant server creates the signed challenge; the
|
|
37
|
-
buyer-facing Siglume payment flow creates and pays the requirement.
|
|
38
|
-
|
|
39
|
-
`
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
46
|
+
|
|
47
|
+
## Current Platform Contract
|
|
48
|
+
|
|
49
|
+
The public product name is **Siglume Direct Request Payment**. The current
|
|
50
|
+
platform payload still uses the internal mode name `external_402`; this SDK sets
|
|
51
|
+
that value for you when creating a payment requirement.
|
|
52
|
+
|
|
53
|
+
Payment requirement creation must run in the authenticated buyer's Siglume
|
|
54
|
+
context. Your merchant server must not use a merchant secret or API key to
|
|
55
|
+
charge a customer wallet. The merchant server creates the signed challenge; the
|
|
56
|
+
buyer-facing Siglume payment flow creates and pays the requirement.
|
|
57
|
+
|
|
58
|
+
`DirectRequestPaymentMerchantClient` requires the merchant's Siglume bearer
|
|
59
|
+
token for setup. `DirectRequestPaymentClient` requires the buyer's Siglume
|
|
60
|
+
bearer token for payment requirements. Do not use a Developer Portal `cli_` API
|
|
61
|
+
key with this package.
|
|
62
|
+
|
|
63
|
+
Current HTTP endpoints live under Siglume's market/API Store route namespace for
|
|
64
|
+
compatibility with the existing platform contract. That does not make this SDK an
|
|
65
|
+
API Store publishing SDK.
|
|
66
|
+
|
|
67
|
+
## Trial Pricing
|
|
68
|
+
|
|
69
|
+
Siglume Direct Request Payment is currently offered with trial-phase merchant
|
|
70
|
+
pricing designed for small EC sites, booking services, membership services, paid
|
|
71
|
+
APIs, and agent-to-agent payment experiments.
|
|
72
|
+
|
|
73
|
+
Both launch settlement currencies are first-class: JPY settled in JPYC, and USD
|
|
74
|
+
settled in USDC. A merchant settles in one currency, chosen at onboarding. The
|
|
75
|
+
settlement fee percentage is identical in both currencies; only the flat
|
|
76
|
+
amounts differ.
|
|
77
|
+
|
|
78
|
+
| Plan | Monthly fee (JPY / USD) | Payment fee |
|
|
79
|
+
| --- | ---: | ---: |
|
|
80
|
+
| Launch | JPY 0 / USD 0 | 1.8% |
|
|
81
|
+
| Starter | JPY 980 / USD 6.00 | 1.0% |
|
|
82
|
+
| Growth | JPY 2,980 / USD 18.00 | 0.7% |
|
|
83
|
+
| Pro | JPY 9,800 / USD 60.00 | 0.5% |
|
|
84
|
+
|
|
85
|
+
Every payment is fee-bearing at the plan rate. The minimum fee is JPY 30
|
|
86
|
+
(USD merchants: USD 0.20) per payment — it recovers the per-payment settlement
|
|
87
|
+
cost (an on-chain signature plus network gas) on small payments; the percentage
|
|
88
|
+
rate applies on larger payments. A merchant billing
|
|
89
|
+
mandate is required before accepting payments, even on the Launch plan. The API
|
|
90
|
+
and merchant registry may still expose the internal plan key `free` for this
|
|
91
|
+
tier. See [docs/pricing.md](./docs/pricing.md) for details.
|
|
92
|
+
|
|
93
|
+
Per-payment fees are deducted at payment settlement time, so the merchant
|
|
94
|
+
receives the net amount. Monthly base fees are collected through the merchant
|
|
95
|
+
billing mandate. `fee_bps` returned on a payment requirement is the authoritative
|
|
96
|
+
per-payment rate for that payment in the merchant's settlement currency.
|
|
97
|
+
|
|
98
|
+
## Merchant Setup: One SDK Call
|
|
99
|
+
|
|
100
|
+
Run this once from the merchant server or an integration agent with the
|
|
101
|
+
merchant's Siglume JWT. It reserves the merchant key, creates the challenge
|
|
102
|
+
secret, prepares the billing mandate, and creates the webhook subscription.
|
|
103
|
+
|
|
104
|
+
```ts
|
|
105
|
+
import { DirectRequestPaymentMerchantClient } from "@siglume/direct-request-payment";
|
|
106
|
+
|
|
107
|
+
const merchant = new DirectRequestPaymentMerchantClient({
|
|
108
|
+
auth_token: process.env.SIGLUME_MERCHANT_AUTH_TOKEN!,
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
const setup = await merchant.setupCheckout({
|
|
112
|
+
merchant: "example_merchant",
|
|
113
|
+
display_name: "Example Merchant",
|
|
114
|
+
billing_plan: "launch",
|
|
115
|
+
billing_currency: "JPY",
|
|
116
|
+
webhook_callback_url: "https://merchant.example/siglume/webhook",
|
|
117
|
+
max_amount_minor: 100000,
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
console.log(setup.env);
|
|
121
|
+
// {
|
|
122
|
+
// SIGLUME_DIRECT_PAYMENT_MERCHANT: "example_merchant",
|
|
123
|
+
// SIGLUME_DIRECT_PAYMENT_CHALLENGE_SECRET: "edrp_...",
|
|
124
|
+
// SIGLUME_WEBHOOK_SECRET: "whsec_..."
|
|
125
|
+
// }
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
```py
|
|
129
|
+
import os
|
|
130
|
+
|
|
131
|
+
from siglume_direct_request_payment import DirectRequestPaymentMerchantClient
|
|
132
|
+
|
|
133
|
+
merchant = DirectRequestPaymentMerchantClient(
|
|
134
|
+
auth_token=os.environ["SIGLUME_MERCHANT_AUTH_TOKEN"],
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
setup = merchant.setup_checkout(
|
|
138
|
+
merchant="example_merchant",
|
|
139
|
+
display_name="Example Merchant",
|
|
140
|
+
billing_plan="launch",
|
|
141
|
+
billing_currency="JPY",
|
|
142
|
+
webhook_callback_url="https://merchant.example/siglume/webhook",
|
|
143
|
+
max_amount_minor=100000,
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
print(setup["env"])
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Store returned secrets on the merchant server. `challenge_secret` and
|
|
150
|
+
`signing_secret` are returned only when they are created or rotated. If a billing
|
|
151
|
+
mandate response requires wallet approval, complete that Siglume wallet step
|
|
152
|
+
before accepting production payments.
|
|
153
|
+
|
|
154
|
+
## Merchant Server: Create a Challenge
|
|
155
|
+
|
|
68
156
|
```ts
|
|
69
157
|
import { createDirectRequestPaymentChallenge } from "@siglume/direct-request-payment";
|
|
70
|
-
|
|
71
|
-
const challenge = await createDirectRequestPaymentChallenge({
|
|
72
|
-
merchant: "example_merchant",
|
|
73
|
-
amount_minor: 1200,
|
|
74
|
-
currency: "JPY",
|
|
75
|
-
secret: process.env.SIGLUME_DIRECT_PAYMENT_CHALLENGE_SECRET!,
|
|
76
|
-
nonce: "order_123-attempt_1",
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
// Return only challenge.challenge to the buyer-facing checkout.
|
|
80
|
-
// Never return the challenge secret to the browser.
|
|
158
|
+
|
|
159
|
+
const challenge = await createDirectRequestPaymentChallenge({
|
|
160
|
+
merchant: "example_merchant",
|
|
161
|
+
amount_minor: 1200,
|
|
162
|
+
currency: "JPY",
|
|
163
|
+
secret: process.env.SIGLUME_DIRECT_PAYMENT_CHALLENGE_SECRET!,
|
|
164
|
+
nonce: "order_123-attempt_1",
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
// Return only challenge.challenge to the buyer-facing checkout.
|
|
168
|
+
// Never return the challenge secret to the browser.
|
|
81
169
|
console.log(challenge.challenge);
|
|
82
170
|
```
|
|
83
171
|
|
|
@@ -98,50 +186,50 @@ print(challenge["challenge"])
|
|
|
98
186
|
```
|
|
99
187
|
|
|
100
188
|
The signed challenge binds:
|
|
101
|
-
|
|
102
|
-
- merchant key
|
|
103
|
-
- amount in minor units
|
|
104
|
-
- currency
|
|
105
|
-
- nonce
|
|
106
|
-
|
|
107
|
-
Changing any of those values invalidates the challenge.
|
|
108
|
-
The nonce must not contain `:` because the current platform challenge format is
|
|
109
|
-
`scheme:nonce:signature`.
|
|
110
|
-
|
|
111
|
-
## Buyer Payment Flow
|
|
112
|
-
|
|
113
|
-
Use `DirectRequestPaymentClient` only with the authenticated buyer's Siglume
|
|
114
|
-
bearer token. `SIGLUME_AUTH_TOKEN` may be used in server-side payment-confirmation
|
|
115
|
-
helpers; `SIGLUME_API_KEY` and Developer Portal `cli_` keys are not accepted.
|
|
116
|
-
|
|
189
|
+
|
|
190
|
+
- merchant key
|
|
191
|
+
- amount in minor units
|
|
192
|
+
- currency
|
|
193
|
+
- nonce
|
|
194
|
+
|
|
195
|
+
Changing any of those values invalidates the challenge.
|
|
196
|
+
The nonce must not contain `:` because the current platform challenge format is
|
|
197
|
+
`scheme:nonce:signature`.
|
|
198
|
+
|
|
199
|
+
## Buyer Payment Flow
|
|
200
|
+
|
|
201
|
+
Use `DirectRequestPaymentClient` only with the authenticated buyer's Siglume
|
|
202
|
+
bearer token. `SIGLUME_AUTH_TOKEN` may be used in server-side payment-confirmation
|
|
203
|
+
helpers; `SIGLUME_API_KEY` and Developer Portal `cli_` keys are not accepted.
|
|
204
|
+
|
|
117
205
|
```ts
|
|
118
206
|
import { DirectRequestPaymentClient } from "@siglume/direct-request-payment";
|
|
119
|
-
|
|
120
|
-
const siglume = new DirectRequestPaymentClient({
|
|
121
|
-
auth_token: buyerSiglumeBearerToken,
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
const requirement = await siglume.createPaymentRequirement({
|
|
125
|
-
merchant: "example_merchant",
|
|
126
|
-
amount_minor: 1200,
|
|
127
|
-
currency: "JPY",
|
|
128
|
-
challenge: challengeFromMerchantServer,
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
if (requirement.approve_transaction_request) {
|
|
132
|
-
await siglume.executeAllowanceTransaction(requirement, { await_finality: true });
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
const payment = await siglume.executePaymentTransaction(requirement, {
|
|
136
|
-
await_finality: true,
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
const receiptId = String(payment.receipt?.receipt_id ?? "");
|
|
140
|
-
const verified = await siglume.verifyPaymentRequirement(requirement.requirement_id, {
|
|
141
|
-
receipt_id: receiptId,
|
|
142
|
-
await_finality: false,
|
|
143
|
-
});
|
|
144
|
-
|
|
207
|
+
|
|
208
|
+
const siglume = new DirectRequestPaymentClient({
|
|
209
|
+
auth_token: buyerSiglumeBearerToken,
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
const requirement = await siglume.createPaymentRequirement({
|
|
213
|
+
merchant: "example_merchant",
|
|
214
|
+
amount_minor: 1200,
|
|
215
|
+
currency: "JPY",
|
|
216
|
+
challenge: challengeFromMerchantServer,
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
if (requirement.approve_transaction_request) {
|
|
220
|
+
await siglume.executeAllowanceTransaction(requirement, { await_finality: true });
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const payment = await siglume.executePaymentTransaction(requirement, {
|
|
224
|
+
await_finality: true,
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
const receiptId = String(payment.receipt?.receipt_id ?? "");
|
|
228
|
+
const verified = await siglume.verifyPaymentRequirement(requirement.requirement_id, {
|
|
229
|
+
receipt_id: receiptId,
|
|
230
|
+
await_finality: false,
|
|
231
|
+
});
|
|
232
|
+
|
|
145
233
|
console.log(verified.status);
|
|
146
234
|
```
|
|
147
235
|
|
|
@@ -172,25 +260,93 @@ verified = siglume.verify_payment_requirement(
|
|
|
172
260
|
print(verified["status"])
|
|
173
261
|
```
|
|
174
262
|
|
|
263
|
+
## Recurring Payments: Subscription and Scheduled Autopay
|
|
264
|
+
|
|
265
|
+
Beyond one-time checkout, a buyer can authorize recurring payments. The merchant
|
|
266
|
+
approves the price and recurring product tag ONCE by signing a recurring
|
|
267
|
+
challenge (a distinct scheme, so one-time challenges and recurring approvals can
|
|
268
|
+
never be replayed as each other); after that, recurring charges are
|
|
269
|
+
challenge-free by design. Subscriptions are bounded by the buyer's mandate;
|
|
270
|
+
scheduled autopay is bounded by the buyer's per-run, daily, and monthly
|
|
271
|
+
auto-pay budget.
|
|
272
|
+
|
|
273
|
+
- **Subscription** (`cadence: "monthly"`): Siglume charges the buyer's wallet
|
|
274
|
+
monthly and pays your merchant wallet automatically. First month is charged at
|
|
275
|
+
setup. The buyer can cancel from their Siglume wallet at any time.
|
|
276
|
+
- **Scheduled autopay** (`cadence: "daily"`): `daily` is the approval tag for
|
|
277
|
+
merchant-triggered scheduled autopay, not a run-count limiter. The
|
|
278
|
+
buyer authorizes the per-run amount and budget envelope, then hands you a
|
|
279
|
+
`schedule_token`; YOUR scheduler triggers each occurrence with that token.
|
|
280
|
+
|
|
281
|
+
```ts
|
|
282
|
+
import { createDirectRequestPaymentRecurringChallenge } from "@siglume/direct-request-payment";
|
|
283
|
+
|
|
284
|
+
// Merchant server: approve a JPY 980 monthly subscription once.
|
|
285
|
+
const recurring = await createDirectRequestPaymentRecurringChallenge({
|
|
286
|
+
merchant: "example_merchant",
|
|
287
|
+
amount_minor: 980,
|
|
288
|
+
currency: "JPY",
|
|
289
|
+
cadence: "monthly",
|
|
290
|
+
secret: process.env.SIGLUME_DIRECT_PAYMENT_CHALLENGE_SECRET!,
|
|
291
|
+
nonce: "subscription_setup_4711",
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
// Hand recurring.challenge to the buyer-facing page. The buyer creates the
|
|
295
|
+
// subscription with their Siglume token:
|
|
296
|
+
// POST /v1/market/api-store/direct-payments/subscriptions
|
|
297
|
+
// { merchant, amount_minor, currency, cadence: "monthly", challenge }
|
|
298
|
+
// For scheduled autopay, the buyer instead creates a scheduled auto-pay
|
|
299
|
+
// authorization (mode: "external_402") and gives you the schedule_token.
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
```py
|
|
303
|
+
import os
|
|
304
|
+
|
|
305
|
+
from siglume_direct_request_payment import create_direct_request_payment_recurring_challenge
|
|
306
|
+
|
|
307
|
+
# Merchant server: approve a JPY 980 monthly subscription once.
|
|
308
|
+
recurring = create_direct_request_payment_recurring_challenge(
|
|
309
|
+
merchant="example_merchant",
|
|
310
|
+
amount_minor=980,
|
|
311
|
+
currency="JPY",
|
|
312
|
+
cadence="monthly",
|
|
313
|
+
secret=os.environ["SIGLUME_DIRECT_PAYMENT_CHALLENGE_SECRET"],
|
|
314
|
+
nonce="subscription_setup_4711",
|
|
315
|
+
)
|
|
316
|
+
|
|
317
|
+
# Hand recurring["challenge"] to the buyer-facing page, as in the TS example.
|
|
318
|
+
print(recurring["challenge"])
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
Each recurring challenge is single-use: it authorizes exactly one subscription
|
|
322
|
+
or schedule, bound to the first buyer who redeems it. Issue a fresh challenge
|
|
323
|
+
per setup. The platform fee on recurring charges is your plan's payment fee
|
|
324
|
+
(with the per-payment minimum), frozen at setup.
|
|
325
|
+
|
|
326
|
+
Merchant-facing webhook events: `subscription.created`, `subscription.renewed`
|
|
327
|
+
(each monthly charge), `payment.failed` (renewal failure, with `will_retry` /
|
|
328
|
+
`final_failure` flags), `subscription.cancelled`, and — for each scheduled
|
|
329
|
+
autopay occurrence — the usual `direct_payment.confirmed`.
|
|
330
|
+
|
|
175
331
|
## Webhooks
|
|
176
|
-
|
|
177
|
-
Your merchant system should treat Siglume webhooks as the durable delivery
|
|
178
|
-
signal. Always verify the signature against the raw request body before trusting
|
|
179
|
-
the payload. Create a marketplace webhook subscription with
|
|
180
|
-
`POST /v1/market/webhooks/subscriptions`; the response returns the `whsec_`
|
|
181
|
-
signing secret once.
|
|
182
|
-
|
|
332
|
+
|
|
333
|
+
Your merchant system should treat Siglume webhooks as the durable delivery
|
|
334
|
+
signal. Always verify the signature against the raw request body before trusting
|
|
335
|
+
the payload. Create a marketplace webhook subscription with
|
|
336
|
+
`POST /v1/market/webhooks/subscriptions`; the response returns the `whsec_`
|
|
337
|
+
signing secret once.
|
|
338
|
+
|
|
183
339
|
```ts
|
|
184
340
|
import { verifyDirectRequestPaymentWebhook } from "@siglume/direct-request-payment";
|
|
185
|
-
|
|
186
|
-
const { event } = await verifyDirectRequestPaymentWebhook(
|
|
187
|
-
process.env.SIGLUME_WEBHOOK_SECRET!,
|
|
188
|
-
rawRequestBody,
|
|
189
|
-
request.headers["siglume-signature"],
|
|
190
|
-
);
|
|
191
|
-
|
|
192
|
-
if (event.type === "direct_payment.confirmed") {
|
|
193
|
-
// Mark the order paid if event.data.challenge_hash/order mapping matches.
|
|
341
|
+
|
|
342
|
+
const { event } = await verifyDirectRequestPaymentWebhook(
|
|
343
|
+
process.env.SIGLUME_WEBHOOK_SECRET!,
|
|
344
|
+
rawRequestBody,
|
|
345
|
+
request.headers["siglume-signature"],
|
|
346
|
+
);
|
|
347
|
+
|
|
348
|
+
if (event.type === "direct_payment.confirmed") {
|
|
349
|
+
// Mark the order paid if event.data.challenge_hash/order mapping matches.
|
|
194
350
|
}
|
|
195
351
|
```
|
|
196
352
|
|
|
@@ -211,27 +367,40 @@ if verified["event"]["type"] == "direct_payment.confirmed":
|
|
|
211
367
|
```
|
|
212
368
|
|
|
213
369
|
## Security Rules
|
|
214
|
-
|
|
215
|
-
- Keep the challenge secret on the merchant server only.
|
|
216
|
-
- Keep merchant order amount and currency server-authored.
|
|
217
|
-
- Use one nonce per order payment attempt.
|
|
218
|
-
- Store `challenge_hash` with the order and reject mismatches.
|
|
219
|
-
- Make order fulfillment idempotent by `requirement_id` and order id.
|
|
220
|
-
- Verify webhook signatures against the raw body.
|
|
221
|
-
- Do not use a merchant token to charge a customer wallet.
|
|
222
|
-
- Do not treat Direct Request Payment as stored value, prepaid points, escrow, or
|
|
223
|
-
a platform balance.
|
|
224
|
-
|
|
225
|
-
Read [docs/security.md](./docs/security.md) before going live.
|
|
226
|
-
|
|
227
|
-
##
|
|
228
|
-
|
|
229
|
-
-
|
|
230
|
-
-
|
|
231
|
-
-
|
|
232
|
-
-
|
|
233
|
-
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
370
|
+
|
|
371
|
+
- Keep the challenge secret on the merchant server only.
|
|
372
|
+
- Keep merchant order amount and currency server-authored.
|
|
373
|
+
- Use one nonce per order payment attempt.
|
|
374
|
+
- Store `challenge_hash` with the order and reject mismatches.
|
|
375
|
+
- Make order fulfillment idempotent by `requirement_id` and order id.
|
|
376
|
+
- Verify webhook signatures against the raw body.
|
|
377
|
+
- Do not use a merchant token to charge a customer wallet.
|
|
378
|
+
- Do not treat Direct Request Payment as stored value, prepaid points, escrow, or
|
|
379
|
+
a platform balance.
|
|
380
|
+
|
|
381
|
+
Read [docs/security.md](./docs/security.md) before going live.
|
|
382
|
+
|
|
383
|
+
## Go-Live Checklist
|
|
384
|
+
|
|
385
|
+
- Run `setupCheckout` with the merchant Siglume JWT.
|
|
386
|
+
- Complete the merchant billing mandate wallet approval if required.
|
|
387
|
+
- Store `SIGLUME_DIRECT_PAYMENT_CHALLENGE_SECRET` only on the merchant server.
|
|
388
|
+
- Store the returned `SIGLUME_WEBHOOK_SECRET` only on the merchant server.
|
|
389
|
+
- Persist `challenge_hash`, `requirement_id`, and fulfillment state per order.
|
|
390
|
+
- Fulfill orders only from verified webhook data, with idempotency.
|
|
391
|
+
- Treat `fee_bps` returned by Siglume as the runtime fee source of truth.
|
|
392
|
+
|
|
393
|
+
## Documentation
|
|
394
|
+
|
|
395
|
+
- [Merchant quickstart](./docs/merchant-quickstart.md)
|
|
396
|
+
- [API reference](./docs/api-reference.md)
|
|
397
|
+
- [Pricing](./docs/pricing.md)
|
|
398
|
+
- [Security guide](./docs/security.md)
|
|
399
|
+
- [Merchant setup example](./examples/setup-merchant.ts)
|
|
400
|
+
- [Express checkout example](./examples/express-checkout.ts)
|
|
401
|
+
- [Japanese launch announcement draft](./docs/announcement-ja.md)
|
|
402
|
+
- [Changelog](./CHANGELOG.md)
|
|
403
|
+
|
|
404
|
+
## License
|
|
405
|
+
|
|
406
|
+
MIT
|