@routedock/routedock 0.1.0 → 0.1.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/dist/index.cjs +106 -44
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +95 -33
- package/dist/provider.cjs +106 -44
- package/dist/provider.d.cts +3 -1
- package/dist/provider.d.ts +3 -1
- package/dist/provider.js +95 -33
- package/dist/{routedockMiddleware-Beqan6QS.d.cts → routedockMiddleware-B7WTB3LT.d.cts} +4 -0
- package/dist/{routedockMiddleware-CqnyHjhO.d.ts → routedockMiddleware-DRgdrycG.d.ts} +4 -0
- package/package.json +1 -1
- package/src/provider/MppSessionHandler.ts +22 -1
- package/src/provider/routedockMiddleware.ts +6 -0
- package/src/provider/x402Handler.ts +88 -36
package/dist/index.cjs
CHANGED
|
@@ -10,9 +10,11 @@ var client$3 = require('@stellar/mpp/charge/client');
|
|
|
10
10
|
var client$2 = require('mppx/client');
|
|
11
11
|
var client$4 = require('@stellar/mpp/channel/client');
|
|
12
12
|
var facilitator = require('@x402/stellar/exact/facilitator');
|
|
13
|
-
var server$1 = require('@stellar/
|
|
14
|
-
var server = require('
|
|
15
|
-
var server$
|
|
13
|
+
var server$1 = require('@x402/stellar/exact/server');
|
|
14
|
+
var server = require('@x402/core/server');
|
|
15
|
+
var server$3 = require('@stellar/mpp/charge/server');
|
|
16
|
+
var server$2 = require('mppx/server');
|
|
17
|
+
var server$4 = require('@stellar/mpp/channel/server');
|
|
16
18
|
|
|
17
19
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
18
20
|
|
|
@@ -611,13 +613,29 @@ var CAIP22 = {
|
|
|
611
613
|
testnet: "stellar:testnet",
|
|
612
614
|
mainnet: "stellar:pubnet"
|
|
613
615
|
};
|
|
616
|
+
var OZ_FACILITATOR_URL = "https://channels.openzeppelin.com/x402";
|
|
614
617
|
function createX402Handler(opts) {
|
|
615
618
|
const caip2 = CAIP22[opts.network];
|
|
616
619
|
const payeeKeypair = stellarSdk.Keypair.fromSecret(opts.payeeSecretKey);
|
|
617
620
|
const signer = stellar.createEd25519Signer(opts.payeeSecretKey, caip2);
|
|
618
|
-
const
|
|
621
|
+
const useOzFacilitator = opts.network === "mainnet" && opts.facilitatorApiKey;
|
|
622
|
+
const localFacilitator = new facilitator.ExactStellarScheme([signer], {
|
|
619
623
|
areFeesSponsored: true
|
|
620
624
|
});
|
|
625
|
+
let ozServer = null;
|
|
626
|
+
if (useOzFacilitator) {
|
|
627
|
+
const apiKey = opts.facilitatorApiKey;
|
|
628
|
+
const facilitator = new server.HTTPFacilitatorClient({
|
|
629
|
+
url: OZ_FACILITATOR_URL,
|
|
630
|
+
createAuthHeaders: async () => ({
|
|
631
|
+
verify: { Authorization: `Bearer ${apiKey}` },
|
|
632
|
+
settle: { Authorization: `Bearer ${apiKey}` },
|
|
633
|
+
supported: { Authorization: `Bearer ${apiKey}` }
|
|
634
|
+
})
|
|
635
|
+
});
|
|
636
|
+
ozServer = new server.x402ResourceServer(facilitator);
|
|
637
|
+
ozServer.register(caip2, new server$1.ExactStellarScheme());
|
|
638
|
+
}
|
|
621
639
|
const amountInBaseUnits = String(Math.round(parseFloat(opts.amount) * 1e7));
|
|
622
640
|
const requirements = {
|
|
623
641
|
scheme: "exact",
|
|
@@ -628,52 +646,78 @@ function createX402Handler(opts) {
|
|
|
628
646
|
maxTimeoutSeconds: 60,
|
|
629
647
|
extra: {
|
|
630
648
|
areFeesSponsored: true,
|
|
631
|
-
facilitatorAddresses: [payeeKeypair.publicKey()]
|
|
649
|
+
...useOzFacilitator ? {} : { facilitatorAddresses: [payeeKeypair.publicKey()] }
|
|
632
650
|
}
|
|
633
651
|
};
|
|
634
652
|
return async (req, res, next) => {
|
|
635
653
|
try {
|
|
636
654
|
const paymentHeader = req.headers["payment-signature"] ?? req.headers["x-payment"];
|
|
637
655
|
if (!paymentHeader) {
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
resource: {
|
|
656
|
+
if (ozServer) {
|
|
657
|
+
const resourceInfo = {
|
|
641
658
|
url: `${req.protocol}://${req.get("host") ?? ""}${req.originalUrl}`,
|
|
642
659
|
description: opts.manifest.name
|
|
643
|
-
}
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
660
|
+
};
|
|
661
|
+
const paymentRequired = await ozServer.createPaymentRequiredResponse(
|
|
662
|
+
[requirements],
|
|
663
|
+
resourceInfo
|
|
664
|
+
);
|
|
665
|
+
res.status(402).setHeader("Content-Type", "application/json").setHeader("X-Payment-Requirements", http.encodePaymentRequiredHeader(paymentRequired)).json({ error: "Payment Required" });
|
|
666
|
+
} else {
|
|
667
|
+
const x402Response = {
|
|
668
|
+
x402Version: 2,
|
|
669
|
+
resource: {
|
|
670
|
+
url: `${req.protocol}://${req.get("host") ?? ""}${req.originalUrl}`,
|
|
671
|
+
description: opts.manifest.name
|
|
672
|
+
},
|
|
673
|
+
accepts: [requirements]
|
|
674
|
+
};
|
|
675
|
+
res.status(402).setHeader("Content-Type", "application/json").setHeader("X-Payment-Requirements", http.encodePaymentRequiredHeader(x402Response)).json({ error: "Payment Required" });
|
|
676
|
+
}
|
|
647
677
|
return;
|
|
648
678
|
}
|
|
649
679
|
const payload = http.decodePaymentSignatureHeader(paymentHeader);
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
requirements
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
680
|
+
let txHash = null;
|
|
681
|
+
if (ozServer) {
|
|
682
|
+
const settleResult = await ozServer.settlePayment(payload, requirements);
|
|
683
|
+
txHash = settleResult.transaction ?? null;
|
|
684
|
+
if (settleResult) {
|
|
685
|
+
res.setHeader(
|
|
686
|
+
"X-Payment-Response",
|
|
687
|
+
http.encodePaymentResponseHeader(
|
|
688
|
+
settleResult
|
|
689
|
+
)
|
|
690
|
+
);
|
|
691
|
+
}
|
|
692
|
+
} else {
|
|
693
|
+
const verifyResult = await localFacilitator.verify(
|
|
694
|
+
payload,
|
|
695
|
+
requirements
|
|
696
|
+
);
|
|
697
|
+
if (!verifyResult.isValid) {
|
|
698
|
+
res.status(401).json({
|
|
699
|
+
error: "Payment verification failed",
|
|
700
|
+
reason: verifyResult.invalidReason
|
|
701
|
+
});
|
|
702
|
+
return;
|
|
703
|
+
}
|
|
704
|
+
const settleResult = await localFacilitator.settle(
|
|
705
|
+
payload,
|
|
706
|
+
requirements
|
|
707
|
+
);
|
|
708
|
+
txHash = settleResult.transaction ?? null;
|
|
709
|
+
if (settleResult) {
|
|
710
|
+
res.setHeader(
|
|
711
|
+
"X-Payment-Response",
|
|
712
|
+
http.encodePaymentResponseHeader(
|
|
713
|
+
settleResult
|
|
714
|
+
)
|
|
715
|
+
);
|
|
716
|
+
}
|
|
660
717
|
}
|
|
661
|
-
const settleResult = await facilitatorScheme.settle(
|
|
662
|
-
payload,
|
|
663
|
-
requirements
|
|
664
|
-
);
|
|
665
|
-
const txHash = settleResult.transaction ?? null;
|
|
666
718
|
if (txHash && opts.onSettled) {
|
|
667
719
|
await opts.onSettled(txHash, opts.amount, "x402");
|
|
668
720
|
}
|
|
669
|
-
if (settleResult) {
|
|
670
|
-
res.setHeader(
|
|
671
|
-
"X-Payment-Response",
|
|
672
|
-
http.encodePaymentResponseHeader(
|
|
673
|
-
settleResult
|
|
674
|
-
)
|
|
675
|
-
);
|
|
676
|
-
}
|
|
677
721
|
next();
|
|
678
722
|
} catch (err) {
|
|
679
723
|
console.error("[x402] Settlement error:", err);
|
|
@@ -688,10 +732,10 @@ var MPP_NETWORK = {
|
|
|
688
732
|
function createMppChargeHandler(opts) {
|
|
689
733
|
const networkId = MPP_NETWORK[opts.network];
|
|
690
734
|
const amountHumanReadable = opts.amount;
|
|
691
|
-
const mppx = server.Mppx.create({
|
|
735
|
+
const mppx = server$2.Mppx.create({
|
|
692
736
|
secretKey: opts.payeeSecretKey,
|
|
693
737
|
methods: [
|
|
694
|
-
server$
|
|
738
|
+
server$3.stellar.charge({
|
|
695
739
|
recipient: opts.manifest.payee,
|
|
696
740
|
currency: opts.assetContract,
|
|
697
741
|
network: networkId,
|
|
@@ -701,7 +745,7 @@ function createMppChargeHandler(opts) {
|
|
|
701
745
|
});
|
|
702
746
|
return async (req, res, next) => {
|
|
703
747
|
try {
|
|
704
|
-
const fetchReq = server.Request.fromNodeListener(req, res);
|
|
748
|
+
const fetchReq = server$2.Request.fromNodeListener(req, res);
|
|
705
749
|
const handler = mppx["stellar/charge"];
|
|
706
750
|
const result = await handler({
|
|
707
751
|
amount: amountHumanReadable,
|
|
@@ -745,8 +789,10 @@ function createMppSessionHandler(opts) {
|
|
|
745
789
|
const rateHuman = opts.rate;
|
|
746
790
|
const payeeKeypair = stellarSdk.Keypair.fromSecret(opts.payeeSecretKey);
|
|
747
791
|
const cumulativeKey = `stellar:channel:cumulative:${opts.channelContract}`;
|
|
748
|
-
const innerStore = server$
|
|
792
|
+
const innerStore = server$4.Store.memory();
|
|
749
793
|
let lastCumulativeAmount = 0n;
|
|
794
|
+
let voucherCount = 0;
|
|
795
|
+
let sessionOpened = false;
|
|
750
796
|
const wrappedStore = {
|
|
751
797
|
async get(key) {
|
|
752
798
|
return innerStore.get(key);
|
|
@@ -755,16 +801,27 @@ function createMppSessionHandler(opts) {
|
|
|
755
801
|
await innerStore.put(key, value);
|
|
756
802
|
if (key === cumulativeKey && value && typeof value === "object" && "amount" in value) {
|
|
757
803
|
lastCumulativeAmount = BigInt(value.amount);
|
|
804
|
+
voucherCount++;
|
|
805
|
+
if (!sessionOpened) {
|
|
806
|
+
sessionOpened = true;
|
|
807
|
+
if (opts.onSessionOpen) {
|
|
808
|
+
await opts.onSessionOpen(opts.channelContract);
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
if (opts.onVoucher) {
|
|
812
|
+
const humanAmount = (Number(lastCumulativeAmount) / 1e7).toFixed(7);
|
|
813
|
+
await opts.onVoucher(voucherCount, humanAmount);
|
|
814
|
+
}
|
|
758
815
|
}
|
|
759
816
|
},
|
|
760
817
|
async delete(key) {
|
|
761
818
|
return innerStore.delete(key);
|
|
762
819
|
}
|
|
763
820
|
};
|
|
764
|
-
const mppx = server.Mppx.create({
|
|
821
|
+
const mppx = server$2.Mppx.create({
|
|
765
822
|
secretKey: opts.payeeSecretKey,
|
|
766
823
|
methods: [
|
|
767
|
-
server$
|
|
824
|
+
server$4.stellar.channel({
|
|
768
825
|
channel: opts.channelContract,
|
|
769
826
|
commitmentKey: opts.commitmentPublicKey,
|
|
770
827
|
network: networkId,
|
|
@@ -782,7 +839,7 @@ function createMppSessionHandler(opts) {
|
|
|
782
839
|
const closeAmount = body?.amount ? BigInt(body.amount) : lastCumulativeAmount;
|
|
783
840
|
const closeSig = body?.signature ?? lastSignatureHex;
|
|
784
841
|
if (closeAmount > 0n && closeSig) {
|
|
785
|
-
const closeTxHash = await server$
|
|
842
|
+
const closeTxHash = await server$4.close({
|
|
786
843
|
channel: opts.channelContract,
|
|
787
844
|
amount: closeAmount,
|
|
788
845
|
signature: Buffer.from(closeSig, "hex"),
|
|
@@ -797,6 +854,9 @@ function createMppSessionHandler(opts) {
|
|
|
797
854
|
} else {
|
|
798
855
|
res.json({ closeTxHash: null, message: "no vouchers received" });
|
|
799
856
|
}
|
|
857
|
+
sessionOpened = false;
|
|
858
|
+
voucherCount = 0;
|
|
859
|
+
lastCumulativeAmount = 0n;
|
|
800
860
|
return;
|
|
801
861
|
}
|
|
802
862
|
const authHeader = req.headers["authorization"];
|
|
@@ -813,7 +873,7 @@ function createMppSessionHandler(opts) {
|
|
|
813
873
|
} catch {
|
|
814
874
|
}
|
|
815
875
|
}
|
|
816
|
-
const fetchReq = server.Request.fromNodeListener(req, res);
|
|
876
|
+
const fetchReq = server$2.Request.fromNodeListener(req, res);
|
|
817
877
|
const result = await mppx.channel({
|
|
818
878
|
amount: rateHuman,
|
|
819
879
|
description: opts.manifest.name
|
|
@@ -882,7 +942,9 @@ function routedock(opts) {
|
|
|
882
942
|
assetContract: opts.assetContract,
|
|
883
943
|
manifest: opts.manifest,
|
|
884
944
|
commitmentPublicKey: opts.commitmentPublicKey,
|
|
885
|
-
...opts.onSettled ? { onSettled: opts.onSettled } : {}
|
|
945
|
+
...opts.onSettled ? { onSettled: opts.onSettled } : {},
|
|
946
|
+
...opts.onSessionOpen ? { onSessionOpen: opts.onSessionOpen } : {},
|
|
947
|
+
...opts.onVoucher ? { onVoucher: opts.onVoucher } : {}
|
|
886
948
|
})
|
|
887
949
|
);
|
|
888
950
|
}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { P as PaymentMode, a as PaymentResult, b as PricingConfig, R as RouteDockError, c as RouteDockManifest, d as RouteDockManifestError, e as RouteDockNoSupportedModeError, f as RouteDockPolicyRejectedError, g as RouteDockSessionError, S as SessionCloseResult, h as SessionHandle, i as SessionPricingConfig, j as SessionState, k as SessionStore, l as SupabaseSessionStore } from './SessionStore-CCVO0PHM.cjs';
|
|
2
2
|
export { ModeSelectOptions, MppChargeClient, MppSessionClient, RouteDockClient, RouteDockClientConfig, SpendCap, X402Client, fetchManifest, selectMode } from './client.cjs';
|
|
3
|
-
export { R as RouteDockMiddlewareOptions, r as routedock } from './routedockMiddleware-
|
|
3
|
+
export { R as RouteDockMiddlewareOptions, r as routedock } from './routedockMiddleware-B7WTB3LT.cjs';
|
|
4
4
|
import '@supabase/supabase-js';
|
|
5
5
|
import '@stellar/stellar-sdk';
|
|
6
6
|
import 'express';
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { P as PaymentMode, a as PaymentResult, b as PricingConfig, R as RouteDockError, c as RouteDockManifest, d as RouteDockManifestError, e as RouteDockNoSupportedModeError, f as RouteDockPolicyRejectedError, g as RouteDockSessionError, S as SessionCloseResult, h as SessionHandle, i as SessionPricingConfig, j as SessionState, k as SessionStore, l as SupabaseSessionStore } from './SessionStore-CCVO0PHM.js';
|
|
2
2
|
export { ModeSelectOptions, MppChargeClient, MppSessionClient, RouteDockClient, RouteDockClientConfig, SpendCap, X402Client, fetchManifest, selectMode } from './client.js';
|
|
3
|
-
export { R as RouteDockMiddlewareOptions, r as routedock } from './routedockMiddleware-
|
|
3
|
+
export { R as RouteDockMiddlewareOptions, r as routedock } from './routedockMiddleware-DRgdrycG.js';
|
|
4
4
|
import '@supabase/supabase-js';
|
|
5
5
|
import '@stellar/stellar-sdk';
|
|
6
6
|
import 'express';
|
package/dist/index.js
CHANGED
|
@@ -8,6 +8,8 @@ import { stellar } from '@stellar/mpp/charge/client';
|
|
|
8
8
|
import { Mppx } from 'mppx/client';
|
|
9
9
|
import { stellar as stellar$1 } from '@stellar/mpp/channel/client';
|
|
10
10
|
import { ExactStellarScheme as ExactStellarScheme$1 } from '@x402/stellar/exact/facilitator';
|
|
11
|
+
import { ExactStellarScheme as ExactStellarScheme$2 } from '@x402/stellar/exact/server';
|
|
12
|
+
import { HTTPFacilitatorClient, x402ResourceServer } from '@x402/core/server';
|
|
11
13
|
import { stellar as stellar$2 } from '@stellar/mpp/charge/server';
|
|
12
14
|
import { Mppx as Mppx$1, Request } from 'mppx/server';
|
|
13
15
|
import { Store, stellar as stellar$3, close } from '@stellar/mpp/channel/server';
|
|
@@ -605,13 +607,29 @@ var CAIP22 = {
|
|
|
605
607
|
testnet: "stellar:testnet",
|
|
606
608
|
mainnet: "stellar:pubnet"
|
|
607
609
|
};
|
|
610
|
+
var OZ_FACILITATOR_URL = "https://channels.openzeppelin.com/x402";
|
|
608
611
|
function createX402Handler(opts) {
|
|
609
612
|
const caip2 = CAIP22[opts.network];
|
|
610
613
|
const payeeKeypair = Keypair.fromSecret(opts.payeeSecretKey);
|
|
611
614
|
const signer = createEd25519Signer(opts.payeeSecretKey, caip2);
|
|
612
|
-
const
|
|
615
|
+
const useOzFacilitator = opts.network === "mainnet" && opts.facilitatorApiKey;
|
|
616
|
+
const localFacilitator = new ExactStellarScheme$1([signer], {
|
|
613
617
|
areFeesSponsored: true
|
|
614
618
|
});
|
|
619
|
+
let ozServer = null;
|
|
620
|
+
if (useOzFacilitator) {
|
|
621
|
+
const apiKey = opts.facilitatorApiKey;
|
|
622
|
+
const facilitator = new HTTPFacilitatorClient({
|
|
623
|
+
url: OZ_FACILITATOR_URL,
|
|
624
|
+
createAuthHeaders: async () => ({
|
|
625
|
+
verify: { Authorization: `Bearer ${apiKey}` },
|
|
626
|
+
settle: { Authorization: `Bearer ${apiKey}` },
|
|
627
|
+
supported: { Authorization: `Bearer ${apiKey}` }
|
|
628
|
+
})
|
|
629
|
+
});
|
|
630
|
+
ozServer = new x402ResourceServer(facilitator);
|
|
631
|
+
ozServer.register(caip2, new ExactStellarScheme$2());
|
|
632
|
+
}
|
|
615
633
|
const amountInBaseUnits = String(Math.round(parseFloat(opts.amount) * 1e7));
|
|
616
634
|
const requirements = {
|
|
617
635
|
scheme: "exact",
|
|
@@ -622,52 +640,78 @@ function createX402Handler(opts) {
|
|
|
622
640
|
maxTimeoutSeconds: 60,
|
|
623
641
|
extra: {
|
|
624
642
|
areFeesSponsored: true,
|
|
625
|
-
facilitatorAddresses: [payeeKeypair.publicKey()]
|
|
643
|
+
...useOzFacilitator ? {} : { facilitatorAddresses: [payeeKeypair.publicKey()] }
|
|
626
644
|
}
|
|
627
645
|
};
|
|
628
646
|
return async (req, res, next) => {
|
|
629
647
|
try {
|
|
630
648
|
const paymentHeader = req.headers["payment-signature"] ?? req.headers["x-payment"];
|
|
631
649
|
if (!paymentHeader) {
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
resource: {
|
|
650
|
+
if (ozServer) {
|
|
651
|
+
const resourceInfo = {
|
|
635
652
|
url: `${req.protocol}://${req.get("host") ?? ""}${req.originalUrl}`,
|
|
636
653
|
description: opts.manifest.name
|
|
637
|
-
}
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
654
|
+
};
|
|
655
|
+
const paymentRequired = await ozServer.createPaymentRequiredResponse(
|
|
656
|
+
[requirements],
|
|
657
|
+
resourceInfo
|
|
658
|
+
);
|
|
659
|
+
res.status(402).setHeader("Content-Type", "application/json").setHeader("X-Payment-Requirements", encodePaymentRequiredHeader(paymentRequired)).json({ error: "Payment Required" });
|
|
660
|
+
} else {
|
|
661
|
+
const x402Response = {
|
|
662
|
+
x402Version: 2,
|
|
663
|
+
resource: {
|
|
664
|
+
url: `${req.protocol}://${req.get("host") ?? ""}${req.originalUrl}`,
|
|
665
|
+
description: opts.manifest.name
|
|
666
|
+
},
|
|
667
|
+
accepts: [requirements]
|
|
668
|
+
};
|
|
669
|
+
res.status(402).setHeader("Content-Type", "application/json").setHeader("X-Payment-Requirements", encodePaymentRequiredHeader(x402Response)).json({ error: "Payment Required" });
|
|
670
|
+
}
|
|
641
671
|
return;
|
|
642
672
|
}
|
|
643
673
|
const payload = decodePaymentSignatureHeader(paymentHeader);
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
requirements
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
674
|
+
let txHash = null;
|
|
675
|
+
if (ozServer) {
|
|
676
|
+
const settleResult = await ozServer.settlePayment(payload, requirements);
|
|
677
|
+
txHash = settleResult.transaction ?? null;
|
|
678
|
+
if (settleResult) {
|
|
679
|
+
res.setHeader(
|
|
680
|
+
"X-Payment-Response",
|
|
681
|
+
encodePaymentResponseHeader(
|
|
682
|
+
settleResult
|
|
683
|
+
)
|
|
684
|
+
);
|
|
685
|
+
}
|
|
686
|
+
} else {
|
|
687
|
+
const verifyResult = await localFacilitator.verify(
|
|
688
|
+
payload,
|
|
689
|
+
requirements
|
|
690
|
+
);
|
|
691
|
+
if (!verifyResult.isValid) {
|
|
692
|
+
res.status(401).json({
|
|
693
|
+
error: "Payment verification failed",
|
|
694
|
+
reason: verifyResult.invalidReason
|
|
695
|
+
});
|
|
696
|
+
return;
|
|
697
|
+
}
|
|
698
|
+
const settleResult = await localFacilitator.settle(
|
|
699
|
+
payload,
|
|
700
|
+
requirements
|
|
701
|
+
);
|
|
702
|
+
txHash = settleResult.transaction ?? null;
|
|
703
|
+
if (settleResult) {
|
|
704
|
+
res.setHeader(
|
|
705
|
+
"X-Payment-Response",
|
|
706
|
+
encodePaymentResponseHeader(
|
|
707
|
+
settleResult
|
|
708
|
+
)
|
|
709
|
+
);
|
|
710
|
+
}
|
|
654
711
|
}
|
|
655
|
-
const settleResult = await facilitatorScheme.settle(
|
|
656
|
-
payload,
|
|
657
|
-
requirements
|
|
658
|
-
);
|
|
659
|
-
const txHash = settleResult.transaction ?? null;
|
|
660
712
|
if (txHash && opts.onSettled) {
|
|
661
713
|
await opts.onSettled(txHash, opts.amount, "x402");
|
|
662
714
|
}
|
|
663
|
-
if (settleResult) {
|
|
664
|
-
res.setHeader(
|
|
665
|
-
"X-Payment-Response",
|
|
666
|
-
encodePaymentResponseHeader(
|
|
667
|
-
settleResult
|
|
668
|
-
)
|
|
669
|
-
);
|
|
670
|
-
}
|
|
671
715
|
next();
|
|
672
716
|
} catch (err) {
|
|
673
717
|
console.error("[x402] Settlement error:", err);
|
|
@@ -741,6 +785,8 @@ function createMppSessionHandler(opts) {
|
|
|
741
785
|
const cumulativeKey = `stellar:channel:cumulative:${opts.channelContract}`;
|
|
742
786
|
const innerStore = Store.memory();
|
|
743
787
|
let lastCumulativeAmount = 0n;
|
|
788
|
+
let voucherCount = 0;
|
|
789
|
+
let sessionOpened = false;
|
|
744
790
|
const wrappedStore = {
|
|
745
791
|
async get(key) {
|
|
746
792
|
return innerStore.get(key);
|
|
@@ -749,6 +795,17 @@ function createMppSessionHandler(opts) {
|
|
|
749
795
|
await innerStore.put(key, value);
|
|
750
796
|
if (key === cumulativeKey && value && typeof value === "object" && "amount" in value) {
|
|
751
797
|
lastCumulativeAmount = BigInt(value.amount);
|
|
798
|
+
voucherCount++;
|
|
799
|
+
if (!sessionOpened) {
|
|
800
|
+
sessionOpened = true;
|
|
801
|
+
if (opts.onSessionOpen) {
|
|
802
|
+
await opts.onSessionOpen(opts.channelContract);
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
if (opts.onVoucher) {
|
|
806
|
+
const humanAmount = (Number(lastCumulativeAmount) / 1e7).toFixed(7);
|
|
807
|
+
await opts.onVoucher(voucherCount, humanAmount);
|
|
808
|
+
}
|
|
752
809
|
}
|
|
753
810
|
},
|
|
754
811
|
async delete(key) {
|
|
@@ -791,6 +848,9 @@ function createMppSessionHandler(opts) {
|
|
|
791
848
|
} else {
|
|
792
849
|
res.json({ closeTxHash: null, message: "no vouchers received" });
|
|
793
850
|
}
|
|
851
|
+
sessionOpened = false;
|
|
852
|
+
voucherCount = 0;
|
|
853
|
+
lastCumulativeAmount = 0n;
|
|
794
854
|
return;
|
|
795
855
|
}
|
|
796
856
|
const authHeader = req.headers["authorization"];
|
|
@@ -876,7 +936,9 @@ function routedock(opts) {
|
|
|
876
936
|
assetContract: opts.assetContract,
|
|
877
937
|
manifest: opts.manifest,
|
|
878
938
|
commitmentPublicKey: opts.commitmentPublicKey,
|
|
879
|
-
...opts.onSettled ? { onSettled: opts.onSettled } : {}
|
|
939
|
+
...opts.onSettled ? { onSettled: opts.onSettled } : {},
|
|
940
|
+
...opts.onSessionOpen ? { onSessionOpen: opts.onSessionOpen } : {},
|
|
941
|
+
...opts.onVoucher ? { onVoucher: opts.onVoucher } : {}
|
|
880
942
|
})
|
|
881
943
|
);
|
|
882
944
|
}
|
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$
|
|
8
|
-
var server = require('mppx/server');
|
|
9
|
-
var 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
|
|
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
|
-
|
|
73
|
-
|
|
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
|
-
|
|
79
|
-
|
|
80
|
-
|
|
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
|
-
|
|
85
|
-
|
|
86
|
-
requirements
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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
|
}
|
package/dist/provider.d.cts
CHANGED
|
@@ -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-
|
|
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
|
|
package/dist/provider.d.ts
CHANGED
|
@@ -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-
|
|
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
|
|
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
|
-
|
|
71
|
-
|
|
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
|
-
|
|
77
|
-
|
|
78
|
-
|
|
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
|
-
|
|
83
|
-
|
|
84
|
-
requirements
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
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
|
@@ -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
|
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { Request, Response, NextFunction, RequestHandler } from 'express'
|
|
2
2
|
import { Keypair } from '@stellar/stellar-sdk'
|
|
3
3
|
import { ExactStellarScheme as ExactStellarFacilitatorScheme } from '@x402/stellar/exact/facilitator'
|
|
4
|
+
import { ExactStellarScheme as ExactStellarServerScheme } from '@x402/stellar/exact/server'
|
|
5
|
+
import { HTTPFacilitatorClient, x402ResourceServer } from '@x402/core/server'
|
|
4
6
|
import { createEd25519Signer } from '@x402/stellar'
|
|
5
7
|
import {
|
|
6
8
|
decodePaymentSignatureHeader,
|
|
@@ -17,6 +19,8 @@ const CAIP2: Record<Network, X402Network> = {
|
|
|
17
19
|
mainnet: 'stellar:pubnet',
|
|
18
20
|
}
|
|
19
21
|
|
|
22
|
+
const OZ_FACILITATOR_URL = 'https://channels.openzeppelin.com/x402'
|
|
23
|
+
|
|
20
24
|
export interface X402HandlerOptions {
|
|
21
25
|
payeeSecretKey: string
|
|
22
26
|
network: Network
|
|
@@ -32,10 +36,29 @@ export function createX402Handler(opts: X402HandlerOptions): RequestHandler {
|
|
|
32
36
|
const payeeKeypair = Keypair.fromSecret(opts.payeeSecretKey)
|
|
33
37
|
const signer = createEd25519Signer(opts.payeeSecretKey, caip2)
|
|
34
38
|
|
|
35
|
-
const
|
|
39
|
+
const useOzFacilitator = opts.network === 'mainnet' && opts.facilitatorApiKey
|
|
40
|
+
|
|
41
|
+
// Mainnet: OZ hosted facilitator via x402ResourceServer
|
|
42
|
+
// Testnet: local ExactStellarFacilitatorScheme (OZ facilitator does not serve testnet)
|
|
43
|
+
const localFacilitator = new ExactStellarFacilitatorScheme([signer], {
|
|
36
44
|
areFeesSponsored: true,
|
|
37
45
|
})
|
|
38
46
|
|
|
47
|
+
let ozServer: x402ResourceServer | null = null
|
|
48
|
+
if (useOzFacilitator) {
|
|
49
|
+
const apiKey = opts.facilitatorApiKey!
|
|
50
|
+
const facilitator = new HTTPFacilitatorClient({
|
|
51
|
+
url: OZ_FACILITATOR_URL,
|
|
52
|
+
createAuthHeaders: async () => ({
|
|
53
|
+
verify: { Authorization: `Bearer ${apiKey}` },
|
|
54
|
+
settle: { Authorization: `Bearer ${apiKey}` },
|
|
55
|
+
supported: { Authorization: `Bearer ${apiKey}` },
|
|
56
|
+
}),
|
|
57
|
+
})
|
|
58
|
+
ozServer = new x402ResourceServer(facilitator)
|
|
59
|
+
ozServer.register(caip2, new ExactStellarServerScheme())
|
|
60
|
+
}
|
|
61
|
+
|
|
39
62
|
const amountInBaseUnits = String(Math.round(parseFloat(opts.amount) * 1e7))
|
|
40
63
|
const requirements = {
|
|
41
64
|
scheme: 'exact' as const,
|
|
@@ -46,7 +69,7 @@ export function createX402Handler(opts: X402HandlerOptions): RequestHandler {
|
|
|
46
69
|
maxTimeoutSeconds: 60,
|
|
47
70
|
extra: {
|
|
48
71
|
areFeesSponsored: true,
|
|
49
|
-
facilitatorAddresses: [payeeKeypair.publicKey()],
|
|
72
|
+
...(useOzFacilitator ? {} : { facilitatorAddresses: [payeeKeypair.publicKey()] }),
|
|
50
73
|
},
|
|
51
74
|
}
|
|
52
75
|
|
|
@@ -55,54 +78,83 @@ export function createX402Handler(opts: X402HandlerOptions): RequestHandler {
|
|
|
55
78
|
const paymentHeader = (req.headers['payment-signature'] ?? req.headers['x-payment']) as string | undefined
|
|
56
79
|
|
|
57
80
|
if (!paymentHeader) {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
resource: {
|
|
81
|
+
if (ozServer) {
|
|
82
|
+
const resourceInfo = {
|
|
61
83
|
url: `${req.protocol}://${req.get('host') ?? ''}${req.originalUrl}`,
|
|
62
84
|
description: opts.manifest.name,
|
|
63
|
-
}
|
|
64
|
-
|
|
85
|
+
}
|
|
86
|
+
const paymentRequired = await ozServer.createPaymentRequiredResponse(
|
|
87
|
+
[requirements],
|
|
88
|
+
resourceInfo,
|
|
89
|
+
)
|
|
90
|
+
res
|
|
91
|
+
.status(402)
|
|
92
|
+
.setHeader('Content-Type', 'application/json')
|
|
93
|
+
.setHeader('X-Payment-Requirements', encodePaymentRequiredHeader(paymentRequired))
|
|
94
|
+
.json({ error: 'Payment Required' })
|
|
95
|
+
} else {
|
|
96
|
+
const x402Response = {
|
|
97
|
+
x402Version: 2,
|
|
98
|
+
resource: {
|
|
99
|
+
url: `${req.protocol}://${req.get('host') ?? ''}${req.originalUrl}`,
|
|
100
|
+
description: opts.manifest.name,
|
|
101
|
+
},
|
|
102
|
+
accepts: [requirements],
|
|
103
|
+
}
|
|
104
|
+
res
|
|
105
|
+
.status(402)
|
|
106
|
+
.setHeader('Content-Type', 'application/json')
|
|
107
|
+
.setHeader('X-Payment-Requirements', encodePaymentRequiredHeader(x402Response))
|
|
108
|
+
.json({ error: 'Payment Required' })
|
|
65
109
|
}
|
|
66
|
-
res
|
|
67
|
-
.status(402)
|
|
68
|
-
.setHeader('Content-Type', 'application/json')
|
|
69
|
-
.setHeader('X-Payment-Requirements', encodePaymentRequiredHeader(x402Response))
|
|
70
|
-
.json({ error: 'Payment Required' })
|
|
71
110
|
return
|
|
72
111
|
}
|
|
73
112
|
|
|
74
113
|
const payload = decodePaymentSignatureHeader(paymentHeader)
|
|
75
|
-
|
|
76
|
-
payload as Parameters<typeof facilitatorScheme.verify>[0],
|
|
77
|
-
requirements,
|
|
78
|
-
)
|
|
114
|
+
let txHash: string | null = null
|
|
79
115
|
|
|
80
|
-
if (
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
116
|
+
if (ozServer) {
|
|
117
|
+
const settleResult = await ozServer.settlePayment(payload, requirements)
|
|
118
|
+
txHash = (settleResult as { transaction?: string }).transaction ?? null
|
|
119
|
+
if (settleResult) {
|
|
120
|
+
res.setHeader(
|
|
121
|
+
'X-Payment-Response',
|
|
122
|
+
encodePaymentResponseHeader(
|
|
123
|
+
settleResult as Parameters<typeof encodePaymentResponseHeader>[0],
|
|
124
|
+
),
|
|
125
|
+
)
|
|
126
|
+
}
|
|
127
|
+
} else {
|
|
128
|
+
const verifyResult = await localFacilitator.verify(
|
|
129
|
+
payload as Parameters<typeof localFacilitator.verify>[0],
|
|
130
|
+
requirements,
|
|
131
|
+
)
|
|
132
|
+
if (!verifyResult.isValid) {
|
|
133
|
+
res.status(401).json({
|
|
134
|
+
error: 'Payment verification failed',
|
|
135
|
+
reason: (verifyResult as { invalidReason?: string }).invalidReason,
|
|
136
|
+
})
|
|
137
|
+
return
|
|
138
|
+
}
|
|
139
|
+
const settleResult = await localFacilitator.settle(
|
|
140
|
+
payload as Parameters<typeof localFacilitator.settle>[0],
|
|
141
|
+
requirements,
|
|
142
|
+
)
|
|
143
|
+
txHash = (settleResult as { transaction?: string }).transaction ?? null
|
|
144
|
+
if (settleResult) {
|
|
145
|
+
res.setHeader(
|
|
146
|
+
'X-Payment-Response',
|
|
147
|
+
encodePaymentResponseHeader(
|
|
148
|
+
settleResult as Parameters<typeof encodePaymentResponseHeader>[0],
|
|
149
|
+
),
|
|
150
|
+
)
|
|
151
|
+
}
|
|
86
152
|
}
|
|
87
153
|
|
|
88
|
-
const settleResult = await facilitatorScheme.settle(
|
|
89
|
-
payload as Parameters<typeof facilitatorScheme.settle>[0],
|
|
90
|
-
requirements,
|
|
91
|
-
)
|
|
92
|
-
|
|
93
|
-
const txHash = (settleResult as { transaction?: string }).transaction ?? null
|
|
94
154
|
if (txHash && opts.onSettled) {
|
|
95
155
|
await opts.onSettled(txHash, opts.amount, 'x402')
|
|
96
156
|
}
|
|
97
157
|
|
|
98
|
-
if (settleResult) {
|
|
99
|
-
res.setHeader(
|
|
100
|
-
'X-Payment-Response',
|
|
101
|
-
encodePaymentResponseHeader(
|
|
102
|
-
settleResult as Parameters<typeof encodePaymentResponseHeader>[0],
|
|
103
|
-
),
|
|
104
|
-
)
|
|
105
|
-
}
|
|
106
158
|
next()
|
|
107
159
|
} catch (err) {
|
|
108
160
|
console.error('[x402] Settlement error:', err)
|