@routedock/routedock 0.1.0 → 0.1.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/dist/provider.cjs CHANGED
@@ -2,11 +2,13 @@
2
2
 
3
3
  var stellarSdk = require('@stellar/stellar-sdk');
4
4
  var facilitator = require('@x402/stellar/exact/facilitator');
5
+ var server$1 = require('@x402/stellar/exact/server');
6
+ var server = require('@x402/core/server');
5
7
  var stellar = require('@x402/stellar');
6
8
  var http = require('@x402/core/http');
7
- var server$1 = require('@stellar/mpp/charge/server');
8
- var server = require('mppx/server');
9
- var server$2 = require('@stellar/mpp/channel/server');
9
+ var server$3 = require('@stellar/mpp/charge/server');
10
+ var server$2 = require('mppx/server');
11
+ var server$4 = require('@stellar/mpp/channel/server');
10
12
 
11
13
  // src/types.ts
12
14
  var RouteDockError = class extends Error {
@@ -45,13 +47,29 @@ var CAIP2 = {
45
47
  testnet: "stellar:testnet",
46
48
  mainnet: "stellar:pubnet"
47
49
  };
50
+ var OZ_FACILITATOR_URL = "https://channels.openzeppelin.com/x402";
48
51
  function createX402Handler(opts) {
49
52
  const caip2 = CAIP2[opts.network];
50
53
  const payeeKeypair = stellarSdk.Keypair.fromSecret(opts.payeeSecretKey);
51
54
  const signer = stellar.createEd25519Signer(opts.payeeSecretKey, caip2);
52
- const facilitatorScheme = new facilitator.ExactStellarScheme([signer], {
55
+ const useOzFacilitator = opts.network === "mainnet" && opts.facilitatorApiKey;
56
+ const localFacilitator = new facilitator.ExactStellarScheme([signer], {
53
57
  areFeesSponsored: true
54
58
  });
59
+ let ozServer = null;
60
+ if (useOzFacilitator) {
61
+ const apiKey = opts.facilitatorApiKey;
62
+ const facilitator = new server.HTTPFacilitatorClient({
63
+ url: OZ_FACILITATOR_URL,
64
+ createAuthHeaders: async () => ({
65
+ verify: { Authorization: `Bearer ${apiKey}` },
66
+ settle: { Authorization: `Bearer ${apiKey}` },
67
+ supported: { Authorization: `Bearer ${apiKey}` }
68
+ })
69
+ });
70
+ ozServer = new server.x402ResourceServer(facilitator);
71
+ ozServer.register(caip2, new server$1.ExactStellarScheme());
72
+ }
55
73
  const amountInBaseUnits = String(Math.round(parseFloat(opts.amount) * 1e7));
56
74
  const requirements = {
57
75
  scheme: "exact",
@@ -62,52 +80,78 @@ function createX402Handler(opts) {
62
80
  maxTimeoutSeconds: 60,
63
81
  extra: {
64
82
  areFeesSponsored: true,
65
- facilitatorAddresses: [payeeKeypair.publicKey()]
83
+ ...useOzFacilitator ? {} : { facilitatorAddresses: [payeeKeypair.publicKey()] }
66
84
  }
67
85
  };
68
86
  return async (req, res, next) => {
69
87
  try {
70
88
  const paymentHeader = req.headers["payment-signature"] ?? req.headers["x-payment"];
71
89
  if (!paymentHeader) {
72
- const x402Response = {
73
- x402Version: 2,
74
- resource: {
90
+ if (ozServer) {
91
+ const resourceInfo = {
75
92
  url: `${req.protocol}://${req.get("host") ?? ""}${req.originalUrl}`,
76
93
  description: opts.manifest.name
77
- },
78
- accepts: [requirements]
79
- };
80
- res.status(402).setHeader("Content-Type", "application/json").setHeader("X-Payment-Requirements", http.encodePaymentRequiredHeader(x402Response)).json({ error: "Payment Required" });
94
+ };
95
+ const paymentRequired = await ozServer.createPaymentRequiredResponse(
96
+ [requirements],
97
+ resourceInfo
98
+ );
99
+ res.status(402).setHeader("Content-Type", "application/json").setHeader("X-Payment-Requirements", http.encodePaymentRequiredHeader(paymentRequired)).json({ error: "Payment Required" });
100
+ } else {
101
+ const x402Response = {
102
+ x402Version: 2,
103
+ resource: {
104
+ url: `${req.protocol}://${req.get("host") ?? ""}${req.originalUrl}`,
105
+ description: opts.manifest.name
106
+ },
107
+ accepts: [requirements]
108
+ };
109
+ res.status(402).setHeader("Content-Type", "application/json").setHeader("X-Payment-Requirements", http.encodePaymentRequiredHeader(x402Response)).json({ error: "Payment Required" });
110
+ }
81
111
  return;
82
112
  }
83
113
  const payload = http.decodePaymentSignatureHeader(paymentHeader);
84
- const verifyResult = await facilitatorScheme.verify(
85
- payload,
86
- requirements
87
- );
88
- if (!verifyResult.isValid) {
89
- res.status(401).json({
90
- error: "Payment verification failed",
91
- reason: verifyResult.invalidReason
92
- });
93
- return;
114
+ let txHash = null;
115
+ if (ozServer) {
116
+ const settleResult = await ozServer.settlePayment(payload, requirements);
117
+ txHash = settleResult.transaction ?? null;
118
+ if (settleResult) {
119
+ res.setHeader(
120
+ "X-Payment-Response",
121
+ http.encodePaymentResponseHeader(
122
+ settleResult
123
+ )
124
+ );
125
+ }
126
+ } else {
127
+ const verifyResult = await localFacilitator.verify(
128
+ payload,
129
+ requirements
130
+ );
131
+ if (!verifyResult.isValid) {
132
+ res.status(401).json({
133
+ error: "Payment verification failed",
134
+ reason: verifyResult.invalidReason
135
+ });
136
+ return;
137
+ }
138
+ const settleResult = await localFacilitator.settle(
139
+ payload,
140
+ requirements
141
+ );
142
+ txHash = settleResult.transaction ?? null;
143
+ if (settleResult) {
144
+ res.setHeader(
145
+ "X-Payment-Response",
146
+ http.encodePaymentResponseHeader(
147
+ settleResult
148
+ )
149
+ );
150
+ }
94
151
  }
95
- const settleResult = await facilitatorScheme.settle(
96
- payload,
97
- requirements
98
- );
99
- const txHash = settleResult.transaction ?? null;
100
152
  if (txHash && opts.onSettled) {
101
153
  await opts.onSettled(txHash, opts.amount, "x402");
102
154
  }
103
- if (settleResult) {
104
- res.setHeader(
105
- "X-Payment-Response",
106
- http.encodePaymentResponseHeader(
107
- settleResult
108
- )
109
- );
110
- }
111
155
  next();
112
156
  } catch (err) {
113
157
  console.error("[x402] Settlement error:", err);
@@ -122,10 +166,10 @@ var MPP_NETWORK = {
122
166
  function createMppChargeHandler(opts) {
123
167
  const networkId = MPP_NETWORK[opts.network];
124
168
  const amountHumanReadable = opts.amount;
125
- const mppx = server.Mppx.create({
169
+ const mppx = server$2.Mppx.create({
126
170
  secretKey: opts.payeeSecretKey,
127
171
  methods: [
128
- server$1.stellar.charge({
172
+ server$3.stellar.charge({
129
173
  recipient: opts.manifest.payee,
130
174
  currency: opts.assetContract,
131
175
  network: networkId,
@@ -135,7 +179,7 @@ function createMppChargeHandler(opts) {
135
179
  });
136
180
  return async (req, res, next) => {
137
181
  try {
138
- const fetchReq = server.Request.fromNodeListener(req, res);
182
+ const fetchReq = server$2.Request.fromNodeListener(req, res);
139
183
  const handler = mppx["stellar/charge"];
140
184
  const result = await handler({
141
185
  amount: amountHumanReadable,
@@ -179,8 +223,10 @@ function createMppSessionHandler(opts) {
179
223
  const rateHuman = opts.rate;
180
224
  const payeeKeypair = stellarSdk.Keypair.fromSecret(opts.payeeSecretKey);
181
225
  const cumulativeKey = `stellar:channel:cumulative:${opts.channelContract}`;
182
- const innerStore = server$2.Store.memory();
226
+ const innerStore = server$4.Store.memory();
183
227
  let lastCumulativeAmount = 0n;
228
+ let voucherCount = 0;
229
+ let sessionOpened = false;
184
230
  const wrappedStore = {
185
231
  async get(key) {
186
232
  return innerStore.get(key);
@@ -189,16 +235,27 @@ function createMppSessionHandler(opts) {
189
235
  await innerStore.put(key, value);
190
236
  if (key === cumulativeKey && value && typeof value === "object" && "amount" in value) {
191
237
  lastCumulativeAmount = BigInt(value.amount);
238
+ voucherCount++;
239
+ if (!sessionOpened) {
240
+ sessionOpened = true;
241
+ if (opts.onSessionOpen) {
242
+ await opts.onSessionOpen(opts.channelContract);
243
+ }
244
+ }
245
+ if (opts.onVoucher) {
246
+ const humanAmount = (Number(lastCumulativeAmount) / 1e7).toFixed(7);
247
+ await opts.onVoucher(voucherCount, humanAmount);
248
+ }
192
249
  }
193
250
  },
194
251
  async delete(key) {
195
252
  return innerStore.delete(key);
196
253
  }
197
254
  };
198
- const mppx = server.Mppx.create({
255
+ const mppx = server$2.Mppx.create({
199
256
  secretKey: opts.payeeSecretKey,
200
257
  methods: [
201
- server$2.stellar.channel({
258
+ server$4.stellar.channel({
202
259
  channel: opts.channelContract,
203
260
  commitmentKey: opts.commitmentPublicKey,
204
261
  network: networkId,
@@ -216,7 +273,7 @@ function createMppSessionHandler(opts) {
216
273
  const closeAmount = body?.amount ? BigInt(body.amount) : lastCumulativeAmount;
217
274
  const closeSig = body?.signature ?? lastSignatureHex;
218
275
  if (closeAmount > 0n && closeSig) {
219
- const closeTxHash = await server$2.close({
276
+ const closeTxHash = await server$4.close({
220
277
  channel: opts.channelContract,
221
278
  amount: closeAmount,
222
279
  signature: Buffer.from(closeSig, "hex"),
@@ -231,6 +288,9 @@ function createMppSessionHandler(opts) {
231
288
  } else {
232
289
  res.json({ closeTxHash: null, message: "no vouchers received" });
233
290
  }
291
+ sessionOpened = false;
292
+ voucherCount = 0;
293
+ lastCumulativeAmount = 0n;
234
294
  return;
235
295
  }
236
296
  const authHeader = req.headers["authorization"];
@@ -247,7 +307,7 @@ function createMppSessionHandler(opts) {
247
307
  } catch {
248
308
  }
249
309
  }
250
- const fetchReq = server.Request.fromNodeListener(req, res);
310
+ const fetchReq = server$2.Request.fromNodeListener(req, res);
251
311
  const result = await mppx.channel({
252
312
  amount: rateHuman,
253
313
  description: opts.manifest.name
@@ -316,7 +376,9 @@ function routedock(opts) {
316
376
  assetContract: opts.assetContract,
317
377
  manifest: opts.manifest,
318
378
  commitmentPublicKey: opts.commitmentPublicKey,
319
- ...opts.onSettled ? { onSettled: opts.onSettled } : {}
379
+ ...opts.onSettled ? { onSettled: opts.onSettled } : {},
380
+ ...opts.onSessionOpen ? { onSessionOpen: opts.onSessionOpen } : {},
381
+ ...opts.onVoucher ? { onVoucher: opts.onVoucher } : {}
320
382
  })
321
383
  );
322
384
  }
@@ -1,6 +1,6 @@
1
1
  import { c as RouteDockManifest, k as SessionStore } from './SessionStore-CCVO0PHM.cjs';
2
2
  export { P as PaymentMode, a as PaymentResult, b as PricingConfig, R as RouteDockError, d as RouteDockManifestError, e as RouteDockNoSupportedModeError, f as RouteDockPolicyRejectedError, g as RouteDockSessionError, S as SessionCloseResult, h as SessionHandle, i as SessionPricingConfig, j as SessionState, l as SupabaseSessionStore } from './SessionStore-CCVO0PHM.cjs';
3
- export { R as RouteDockMiddlewareOptions, r as routedock } from './routedockMiddleware-Beqan6QS.cjs';
3
+ export { R as RouteDockMiddlewareOptions, r as routedock } from './routedockMiddleware-B7WTB3LT.cjs';
4
4
  import { RequestHandler } from 'express';
5
5
  import '@supabase/supabase-js';
6
6
 
@@ -38,6 +38,8 @@ interface MppSessionHandlerOptions {
38
38
  manifest: RouteDockManifest;
39
39
  commitmentPublicKey: string;
40
40
  onSettled?: (txHash: string, totalPaid: string, mode: string) => Promise<void>;
41
+ onSessionOpen?: (channelId: string) => Promise<void>;
42
+ onVoucher?: (voucherIndex: number, cumulativeAmount: string) => Promise<void>;
41
43
  }
42
44
  declare function createMppSessionHandler(opts: MppSessionHandlerOptions): RequestHandler;
43
45
 
@@ -1,6 +1,6 @@
1
1
  import { c as RouteDockManifest, k as SessionStore } from './SessionStore-CCVO0PHM.js';
2
2
  export { P as PaymentMode, a as PaymentResult, b as PricingConfig, R as RouteDockError, d as RouteDockManifestError, e as RouteDockNoSupportedModeError, f as RouteDockPolicyRejectedError, g as RouteDockSessionError, S as SessionCloseResult, h as SessionHandle, i as SessionPricingConfig, j as SessionState, l as SupabaseSessionStore } from './SessionStore-CCVO0PHM.js';
3
- export { R as RouteDockMiddlewareOptions, r as routedock } from './routedockMiddleware-CqnyHjhO.js';
3
+ export { R as RouteDockMiddlewareOptions, r as routedock } from './routedockMiddleware-DRgdrycG.js';
4
4
  import { RequestHandler } from 'express';
5
5
  import '@supabase/supabase-js';
6
6
 
@@ -38,6 +38,8 @@ interface MppSessionHandlerOptions {
38
38
  manifest: RouteDockManifest;
39
39
  commitmentPublicKey: string;
40
40
  onSettled?: (txHash: string, totalPaid: string, mode: string) => Promise<void>;
41
+ onSessionOpen?: (channelId: string) => Promise<void>;
42
+ onVoucher?: (voucherIndex: number, cumulativeAmount: string) => Promise<void>;
41
43
  }
42
44
  declare function createMppSessionHandler(opts: MppSessionHandlerOptions): RequestHandler;
43
45
 
package/dist/provider.js CHANGED
@@ -1,5 +1,7 @@
1
1
  import { Keypair } from '@stellar/stellar-sdk';
2
2
  import { ExactStellarScheme } from '@x402/stellar/exact/facilitator';
3
+ import { ExactStellarScheme as ExactStellarScheme$1 } from '@x402/stellar/exact/server';
4
+ import { HTTPFacilitatorClient, x402ResourceServer } from '@x402/core/server';
3
5
  import { createEd25519Signer } from '@x402/stellar';
4
6
  import { encodePaymentRequiredHeader, decodePaymentSignatureHeader, encodePaymentResponseHeader } from '@x402/core/http';
5
7
  import { stellar } from '@stellar/mpp/charge/server';
@@ -43,13 +45,29 @@ var CAIP2 = {
43
45
  testnet: "stellar:testnet",
44
46
  mainnet: "stellar:pubnet"
45
47
  };
48
+ var OZ_FACILITATOR_URL = "https://channels.openzeppelin.com/x402";
46
49
  function createX402Handler(opts) {
47
50
  const caip2 = CAIP2[opts.network];
48
51
  const payeeKeypair = Keypair.fromSecret(opts.payeeSecretKey);
49
52
  const signer = createEd25519Signer(opts.payeeSecretKey, caip2);
50
- const facilitatorScheme = new ExactStellarScheme([signer], {
53
+ const useOzFacilitator = opts.network === "mainnet" && opts.facilitatorApiKey;
54
+ const localFacilitator = new ExactStellarScheme([signer], {
51
55
  areFeesSponsored: true
52
56
  });
57
+ let ozServer = null;
58
+ if (useOzFacilitator) {
59
+ const apiKey = opts.facilitatorApiKey;
60
+ const facilitator = new HTTPFacilitatorClient({
61
+ url: OZ_FACILITATOR_URL,
62
+ createAuthHeaders: async () => ({
63
+ verify: { Authorization: `Bearer ${apiKey}` },
64
+ settle: { Authorization: `Bearer ${apiKey}` },
65
+ supported: { Authorization: `Bearer ${apiKey}` }
66
+ })
67
+ });
68
+ ozServer = new x402ResourceServer(facilitator);
69
+ ozServer.register(caip2, new ExactStellarScheme$1());
70
+ }
53
71
  const amountInBaseUnits = String(Math.round(parseFloat(opts.amount) * 1e7));
54
72
  const requirements = {
55
73
  scheme: "exact",
@@ -60,52 +78,78 @@ function createX402Handler(opts) {
60
78
  maxTimeoutSeconds: 60,
61
79
  extra: {
62
80
  areFeesSponsored: true,
63
- facilitatorAddresses: [payeeKeypair.publicKey()]
81
+ ...useOzFacilitator ? {} : { facilitatorAddresses: [payeeKeypair.publicKey()] }
64
82
  }
65
83
  };
66
84
  return async (req, res, next) => {
67
85
  try {
68
86
  const paymentHeader = req.headers["payment-signature"] ?? req.headers["x-payment"];
69
87
  if (!paymentHeader) {
70
- const x402Response = {
71
- x402Version: 2,
72
- resource: {
88
+ if (ozServer) {
89
+ const resourceInfo = {
73
90
  url: `${req.protocol}://${req.get("host") ?? ""}${req.originalUrl}`,
74
91
  description: opts.manifest.name
75
- },
76
- accepts: [requirements]
77
- };
78
- res.status(402).setHeader("Content-Type", "application/json").setHeader("X-Payment-Requirements", encodePaymentRequiredHeader(x402Response)).json({ error: "Payment Required" });
92
+ };
93
+ const paymentRequired = await ozServer.createPaymentRequiredResponse(
94
+ [requirements],
95
+ resourceInfo
96
+ );
97
+ res.status(402).setHeader("Content-Type", "application/json").setHeader("X-Payment-Requirements", encodePaymentRequiredHeader(paymentRequired)).json({ error: "Payment Required" });
98
+ } else {
99
+ const x402Response = {
100
+ x402Version: 2,
101
+ resource: {
102
+ url: `${req.protocol}://${req.get("host") ?? ""}${req.originalUrl}`,
103
+ description: opts.manifest.name
104
+ },
105
+ accepts: [requirements]
106
+ };
107
+ res.status(402).setHeader("Content-Type", "application/json").setHeader("X-Payment-Requirements", encodePaymentRequiredHeader(x402Response)).json({ error: "Payment Required" });
108
+ }
79
109
  return;
80
110
  }
81
111
  const payload = decodePaymentSignatureHeader(paymentHeader);
82
- const verifyResult = await facilitatorScheme.verify(
83
- payload,
84
- requirements
85
- );
86
- if (!verifyResult.isValid) {
87
- res.status(401).json({
88
- error: "Payment verification failed",
89
- reason: verifyResult.invalidReason
90
- });
91
- return;
112
+ let txHash = null;
113
+ if (ozServer) {
114
+ const settleResult = await ozServer.settlePayment(payload, requirements);
115
+ txHash = settleResult.transaction ?? null;
116
+ if (settleResult) {
117
+ res.setHeader(
118
+ "X-Payment-Response",
119
+ encodePaymentResponseHeader(
120
+ settleResult
121
+ )
122
+ );
123
+ }
124
+ } else {
125
+ const verifyResult = await localFacilitator.verify(
126
+ payload,
127
+ requirements
128
+ );
129
+ if (!verifyResult.isValid) {
130
+ res.status(401).json({
131
+ error: "Payment verification failed",
132
+ reason: verifyResult.invalidReason
133
+ });
134
+ return;
135
+ }
136
+ const settleResult = await localFacilitator.settle(
137
+ payload,
138
+ requirements
139
+ );
140
+ txHash = settleResult.transaction ?? null;
141
+ if (settleResult) {
142
+ res.setHeader(
143
+ "X-Payment-Response",
144
+ encodePaymentResponseHeader(
145
+ settleResult
146
+ )
147
+ );
148
+ }
92
149
  }
93
- const settleResult = await facilitatorScheme.settle(
94
- payload,
95
- requirements
96
- );
97
- const txHash = settleResult.transaction ?? null;
98
150
  if (txHash && opts.onSettled) {
99
151
  await opts.onSettled(txHash, opts.amount, "x402");
100
152
  }
101
- if (settleResult) {
102
- res.setHeader(
103
- "X-Payment-Response",
104
- encodePaymentResponseHeader(
105
- settleResult
106
- )
107
- );
108
- }
109
153
  next();
110
154
  } catch (err) {
111
155
  console.error("[x402] Settlement error:", err);
@@ -179,6 +223,8 @@ function createMppSessionHandler(opts) {
179
223
  const cumulativeKey = `stellar:channel:cumulative:${opts.channelContract}`;
180
224
  const innerStore = Store.memory();
181
225
  let lastCumulativeAmount = 0n;
226
+ let voucherCount = 0;
227
+ let sessionOpened = false;
182
228
  const wrappedStore = {
183
229
  async get(key) {
184
230
  return innerStore.get(key);
@@ -187,6 +233,17 @@ function createMppSessionHandler(opts) {
187
233
  await innerStore.put(key, value);
188
234
  if (key === cumulativeKey && value && typeof value === "object" && "amount" in value) {
189
235
  lastCumulativeAmount = BigInt(value.amount);
236
+ voucherCount++;
237
+ if (!sessionOpened) {
238
+ sessionOpened = true;
239
+ if (opts.onSessionOpen) {
240
+ await opts.onSessionOpen(opts.channelContract);
241
+ }
242
+ }
243
+ if (opts.onVoucher) {
244
+ const humanAmount = (Number(lastCumulativeAmount) / 1e7).toFixed(7);
245
+ await opts.onVoucher(voucherCount, humanAmount);
246
+ }
190
247
  }
191
248
  },
192
249
  async delete(key) {
@@ -229,6 +286,9 @@ function createMppSessionHandler(opts) {
229
286
  } else {
230
287
  res.json({ closeTxHash: null, message: "no vouchers received" });
231
288
  }
289
+ sessionOpened = false;
290
+ voucherCount = 0;
291
+ lastCumulativeAmount = 0n;
232
292
  return;
233
293
  }
234
294
  const authHeader = req.headers["authorization"];
@@ -314,7 +374,9 @@ function routedock(opts) {
314
374
  assetContract: opts.assetContract,
315
375
  manifest: opts.manifest,
316
376
  commitmentPublicKey: opts.commitmentPublicKey,
317
- ...opts.onSettled ? { onSettled: opts.onSettled } : {}
377
+ ...opts.onSettled ? { onSettled: opts.onSettled } : {},
378
+ ...opts.onSessionOpen ? { onSessionOpen: opts.onSessionOpen } : {},
379
+ ...opts.onVoucher ? { onVoucher: opts.onVoucher } : {}
318
380
  })
319
381
  );
320
382
  }
@@ -26,6 +26,10 @@ interface RouteDockMiddlewareOptions {
26
26
  manifest: RouteDockManifest;
27
27
  /** Called after each successful on-chain settlement */
28
28
  onSettled?: (txHash: string, amount: string, mode: string) => Promise<void>;
29
+ /** Called when a new mpp-session is opened (first voucher received) */
30
+ onSessionOpen?: (channelId: string) => Promise<void>;
31
+ /** Called after each verified voucher in an mpp-session */
32
+ onVoucher?: (voucherIndex: number, cumulativeAmount: string) => Promise<void>;
29
33
  }
30
34
  /**
31
35
  * Express middleware factory for RouteDock provider endpoints.
@@ -26,6 +26,10 @@ interface RouteDockMiddlewareOptions {
26
26
  manifest: RouteDockManifest;
27
27
  /** Called after each successful on-chain settlement */
28
28
  onSettled?: (txHash: string, amount: string, mode: string) => Promise<void>;
29
+ /** Called when a new mpp-session is opened (first voucher received) */
30
+ onSessionOpen?: (channelId: string) => Promise<void>;
31
+ /** Called after each verified voucher in an mpp-session */
32
+ onVoucher?: (voucherIndex: number, cumulativeAmount: string) => Promise<void>;
29
33
  }
30
34
  /**
31
35
  * Express middleware factory for RouteDock provider endpoints.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@routedock/routedock",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Unified payment execution layer for autonomous agents on Stellar",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -20,6 +20,8 @@ export interface MppSessionHandlerOptions {
20
20
  manifest: RouteDockManifest
21
21
  commitmentPublicKey: string
22
22
  onSettled?: (txHash: string, totalPaid: string, mode: string) => Promise<void>
23
+ onSessionOpen?: (channelId: string) => Promise<void>
24
+ onVoucher?: (voucherIndex: number, cumulativeAmount: string) => Promise<void>
23
25
  }
24
26
 
25
27
  export function createMppSessionHandler(opts: MppSessionHandlerOptions): RequestHandler {
@@ -30,14 +32,29 @@ export function createMppSessionHandler(opts: MppSessionHandlerOptions): Request
30
32
 
31
33
  const innerStore = Store.memory()
32
34
 
33
- // Wrap the store to intercept cumulative amount writes
34
35
  let lastCumulativeAmount = 0n
36
+ let voucherCount = 0
37
+ let sessionOpened = false
38
+
35
39
  const wrappedStore: ReturnType<typeof Store.memory> = {
36
40
  async get(key: string) { return innerStore.get(key) },
37
41
  async put(key: string, value: unknown) {
38
42
  await innerStore.put(key, value)
39
43
  if (key === cumulativeKey && value && typeof value === 'object' && 'amount' in (value as Record<string, unknown>)) {
40
44
  lastCumulativeAmount = BigInt((value as { amount: string }).amount)
45
+ voucherCount++
46
+
47
+ if (!sessionOpened) {
48
+ sessionOpened = true
49
+ if (opts.onSessionOpen) {
50
+ await opts.onSessionOpen(opts.channelContract)
51
+ }
52
+ }
53
+
54
+ if (opts.onVoucher) {
55
+ const humanAmount = (Number(lastCumulativeAmount) / 1e7).toFixed(7)
56
+ await opts.onVoucher(voucherCount, humanAmount)
57
+ }
41
58
  }
42
59
  },
43
60
  async delete(key: string) { return innerStore.delete(key) },
@@ -85,6 +102,10 @@ export function createMppSessionHandler(opts: MppSessionHandlerOptions): Request
85
102
  } else {
86
103
  res.json({ closeTxHash: null, message: 'no vouchers received' })
87
104
  }
105
+
106
+ sessionOpened = false
107
+ voucherCount = 0
108
+ lastCumulativeAmount = 0n
88
109
  return
89
110
  }
90
111
 
@@ -26,6 +26,10 @@ export interface RouteDockMiddlewareOptions {
26
26
  manifest: RouteDockManifest
27
27
  /** Called after each successful on-chain settlement */
28
28
  onSettled?: (txHash: string, amount: string, mode: string) => Promise<void>
29
+ /** Called when a new mpp-session is opened (first voucher received) */
30
+ onSessionOpen?: (channelId: string) => Promise<void>
31
+ /** Called after each verified voucher in an mpp-session */
32
+ onVoucher?: (voucherIndex: number, cumulativeAmount: string) => Promise<void>
29
33
  }
30
34
 
31
35
  /**
@@ -97,6 +101,8 @@ export function routedock(opts: RouteDockMiddlewareOptions): RequestHandler {
97
101
  manifest: opts.manifest,
98
102
  commitmentPublicKey: opts.commitmentPublicKey,
99
103
  ...(opts.onSettled ? { onSettled: opts.onSettled } : {}),
104
+ ...(opts.onSessionOpen ? { onSessionOpen: opts.onSessionOpen } : {}),
105
+ ...(opts.onVoucher ? { onVoucher: opts.onVoucher } : {}),
100
106
  }),
101
107
  )
102
108
  }