@bitpal/checkout 0.7.0 → 0.9.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/README.md CHANGED
@@ -192,6 +192,88 @@ BitPal may still deliver the **same event more than once** within the window. De
192
192
  is not signed, so it is forgeable). Persist the dedup key in the **same transaction** as your side
193
193
  effect; handlers must be safe to run twice (UPSERT, not INSERT).
194
194
 
195
+ ## x402 — charge per API call
196
+
197
+ A different lane from checkout: no page, no redirect, no deposit address. The caller pays with a
198
+ signed authorization and retries the request. Settles to the **same payout wallet as checkout**
199
+ (register one in the console first).
200
+
201
+ ### Merchant side
202
+
203
+ ```ts
204
+ import { x402 } from '@bitpal/checkout';
205
+
206
+ // $1.00 USDC per call
207
+ app.get('/report', x402({
208
+ apiKey: process.env.BITPAL_API_KEY, // bp_test_… or bp_live_…
209
+ payTo: '0xYourPayoutWallet',
210
+ network: 'base', // test key → 'base-sepolia'
211
+ amount: '1.00', // human-readable — no decimals math
212
+ }), (req, res) => {
213
+ res.json({ report: '…' }); // req.x402Payment has the txHash
214
+ });
215
+ ```
216
+
217
+ Unpaid requests get a `402` with the payment terms. Paid ones run `verify` → `settle` and reach your
218
+ handler with `X-PAYMENT-RESPONSE` set. The middleware also does what the API cannot do for you:
219
+
220
+ - **One payment unlocks one response.** `/settle` is idempotent, so replaying the same `X-PAYMENT`
221
+ keeps returning success — which would let one payment buy unlimited calls. The middleware records
222
+ redeemed authorizations and rejects reuse with `409`. Redemption is recorded only once your
223
+ response actually ships: if the handler throws, the caller can retry with the same header instead
224
+ of paying for nothing.
225
+ - **Only re-prompts for payment when the money definitely did not move.** `reverted`, `expired` and
226
+ `failed` get a fresh `402`. Everything else — settlement still in flight, an uncertain broadcast, a
227
+ timeout — answers `503` and asks for a retry with the *same* header. Handing back a `402` there
228
+ would make the caller re-sign and pay twice.
229
+
230
+ Not on Express? `X402Gate` is the framework-neutral core — give it the header and the resource URL,
231
+ it tells you whether to bill or serve. On a `settled` result, call `commit()` once you have delivered
232
+ the response, or `release()` if you could not.
233
+
234
+ **Two things to set for production:**
235
+
236
+ - `replayStore` — the default keeps redeemed payments in process memory, so a second instance does
237
+ not know what the first one served. Pass a shared store (Redis/DB) whose `claim` is an atomic
238
+ compare-and-set. The memory store also refuses new payments rather than evicting live entries once
239
+ it fills, so a busy single instance needs a real store too.
240
+ - `resource` — by default it is derived from the request's `Host` header, which the caller controls.
241
+ The payment binds to that string, so pass an explicit `resource` (or a function) to pin it.
242
+
243
+ ### Payer side
244
+
245
+ The fee split needs two authorizations, which a stock x402 client does not know about.
246
+ `createX402Payment` reads the `402` and builds both. You supply the signer; the SDK has no crypto
247
+ dependency.
248
+
249
+ ```ts
250
+ import { createX402Payment } from '@bitpal/checkout';
251
+
252
+ const res = await fetch(url);
253
+ if (res.status === 402) {
254
+ const { header } = await createX402Payment({
255
+ requirements: await res.json(),
256
+ from: account.address,
257
+ signTypedData: (req) => walletClient.signTypedData({ account, ...req }), // viem
258
+ });
259
+ const paid = await fetch(url, { headers: { 'X-PAYMENT': header } });
260
+ }
261
+ ```
262
+
263
+ ### Supported
264
+
265
+ | Network | Token | `network` |
266
+ |---|---|---|
267
+ | Base | USDC | `base` |
268
+ | Base Sepolia | USDC | `base-sepolia` |
269
+
270
+ Amounts are human-readable (`'1.00'`), the same model as `line_items[].amount` — the server converts.
271
+ Pass `amountAtomic` instead if you already have raw units.
272
+
273
+ EOA signers only — smart-contract wallets cannot sign EIP-3009 authorizations. Amounts are exact:
274
+ over- and underpayment are both rejected, and this lane has no refund path. Authorizations are valid
275
+ for 90–3600 seconds (the server's floor is 60s; the extra 30s absorbs clock skew and round trips).
276
+
195
277
  ## Config options
196
278
 
197
279
  ```ts