@bosonprotocol/x402-server 0.2.0 → 0.3.0-alpha-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/dist/cjs/challenge/index.d.ts +2 -1
- package/dist/cjs/client-B2yejopi.d.ts +88 -0
- package/dist/cjs/{config-CBr9qMps.d.ts → config-hnCiZiXF.d.ts} +241 -22
- package/dist/cjs/facilitator/index.d.ts +1 -1
- package/dist/cjs/facilitator/index.js +120 -8
- package/dist/cjs/facilitator/index.js.map +1 -1
- package/dist/cjs/handlers/index.d.ts +8 -264
- package/dist/cjs/handlers/index.js +147 -18
- package/dist/cjs/handlers/index.js.map +1 -1
- package/dist/cjs/index-COmLTH-U.d.ts +292 -0
- package/dist/cjs/index.d.ts +91 -7
- package/dist/cjs/index.js +559 -73
- package/dist/cjs/index.js.map +1 -1
- package/dist/esm/chunk-OMJEQEOF.js +223 -0
- package/dist/esm/chunk-OMJEQEOF.js.map +1 -0
- package/dist/esm/{chunk-PU5Y7FZG.js → chunk-RAESVNQG.js} +15 -3
- package/dist/esm/chunk-RAESVNQG.js.map +1 -0
- package/dist/esm/{chunk-Y5HFLCAT.js → chunk-SHYOIKYU.js} +138 -21
- package/dist/esm/chunk-SHYOIKYU.js.map +1 -0
- package/dist/esm/facilitator/index.js +2 -2
- package/dist/esm/handlers/index.js +2 -2
- package/dist/esm/index.js +306 -53
- package/dist/esm/index.js.map +1 -1
- package/package.json +3 -3
- package/dist/cjs/client-tnrpqVMW.d.ts +0 -36
- package/dist/esm/chunk-EAKQ4EFZ.js +0 -124
- package/dist/esm/chunk-EAKQ4EFZ.js.map +0 -1
- package/dist/esm/chunk-PU5Y7FZG.js.map +0 -1
- package/dist/esm/chunk-Y5HFLCAT.js.map +0 -1
|
@@ -60,6 +60,21 @@ function emitNextActions(input, registry, facilitatorUrl) {
|
|
|
60
60
|
};
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
+
// src/handlers/fulfillment-result.ts
|
|
64
|
+
function serializeFulfillmentResult(result) {
|
|
65
|
+
if (result.kind === "inline") {
|
|
66
|
+
return {
|
|
67
|
+
kind: "inline",
|
|
68
|
+
body: Buffer.from(result.body).toString("base64"),
|
|
69
|
+
contentType: result.contentType
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
return {
|
|
73
|
+
kind: "async",
|
|
74
|
+
...result.pointer !== void 0 ? { pointer: result.pointer } : {}
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
63
78
|
// src/handlers/types.ts
|
|
64
79
|
function handlerOk(body) {
|
|
65
80
|
return { ok: true, status: 200, body };
|
|
@@ -578,6 +593,18 @@ var FacilitatorHttpError = class extends Error {
|
|
|
578
593
|
}
|
|
579
594
|
};
|
|
580
595
|
|
|
596
|
+
// src/logger.ts
|
|
597
|
+
var noopLogger = {
|
|
598
|
+
debug: () => {
|
|
599
|
+
},
|
|
600
|
+
info: () => {
|
|
601
|
+
},
|
|
602
|
+
warn: () => {
|
|
603
|
+
},
|
|
604
|
+
error: () => {
|
|
605
|
+
}
|
|
606
|
+
};
|
|
607
|
+
|
|
581
608
|
// src/handlers/commit-and-redeem.ts
|
|
582
609
|
async function handleCommit(input, ctx) {
|
|
583
610
|
return await handleCommitImpl(input, ctx, {
|
|
@@ -592,6 +619,7 @@ async function handleCommitAndRedeem(input, ctx) {
|
|
|
592
619
|
});
|
|
593
620
|
}
|
|
594
621
|
async function handleCommitImpl(input, ctx, expected) {
|
|
622
|
+
const logger = ctx.logger ?? noopLogger;
|
|
595
623
|
const decoded = decodeXPaymentHeader(input.paymentHeader);
|
|
596
624
|
if (!decoded.ok) {
|
|
597
625
|
const status = decoded.code === "MISSING_HEADER" ? 402 : 400;
|
|
@@ -680,28 +708,38 @@ async function handleCommitImpl(input, ctx, expected) {
|
|
|
680
708
|
);
|
|
681
709
|
}
|
|
682
710
|
if (expected.expectedState === x402Actions.ExchangeState.COMMITTED) {
|
|
683
|
-
ctx.exchangeFulfillmentOptionStore.set(
|
|
711
|
+
await ctx.exchangeFulfillmentOptionStore.set(
|
|
684
712
|
settleResult.exchangeId,
|
|
685
713
|
input.requirements.fulfillment?.options.map((option) => option.id) ?? []
|
|
686
714
|
);
|
|
687
715
|
}
|
|
688
716
|
const warnings = [];
|
|
717
|
+
let delivery;
|
|
689
718
|
if (expected.expectedState === x402Actions.ExchangeState.REDEEMED && decoded.payload.fulfillment !== void 0 && decoded.payload.fulfillment.data !== void 0) {
|
|
690
719
|
const pending = {
|
|
691
720
|
exchangeId: settleResult.exchangeId,
|
|
692
721
|
option: decoded.payload.fulfillment.option,
|
|
693
722
|
data: decoded.payload.fulfillment.data,
|
|
694
723
|
redeemer: decoded.payload.payload.buyer,
|
|
695
|
-
recordedAt: Date.now()
|
|
724
|
+
recordedAt: Date.now(),
|
|
725
|
+
phase: "commit"
|
|
696
726
|
};
|
|
697
|
-
ctx.fulfillmentRecoveryStore.set(settleResult.exchangeId, pending);
|
|
727
|
+
await ctx.fulfillmentRecoveryStore.set(settleResult.exchangeId, pending);
|
|
728
|
+
logger.debug("x402-server: fulfillment recovery entry recorded (Flow B)", {
|
|
729
|
+
exchangeId: settleResult.exchangeId,
|
|
730
|
+
option: decoded.payload.fulfillment.option
|
|
731
|
+
});
|
|
698
732
|
const channel = channelById.get(decoded.payload.fulfillment.option);
|
|
699
733
|
if (channel === void 0) {
|
|
700
734
|
const reason = "no channel adapter is registered";
|
|
701
|
-
ctx.fulfillmentRecoveryStore.set(settleResult.exchangeId, {
|
|
735
|
+
await ctx.fulfillmentRecoveryStore.set(settleResult.exchangeId, {
|
|
702
736
|
...pending,
|
|
703
737
|
error: reason
|
|
704
738
|
});
|
|
739
|
+
logger.error("x402-server: Flow B channel adapter missing post-settle", {
|
|
740
|
+
exchangeId: settleResult.exchangeId,
|
|
741
|
+
option: decoded.payload.fulfillment.option
|
|
742
|
+
});
|
|
705
743
|
warnings.push({
|
|
706
744
|
code: "FULFILLMENT_COMMIT_DEFERRED",
|
|
707
745
|
reason: "atomic redeem succeeded on-chain, but no channel adapter is registered",
|
|
@@ -714,13 +752,54 @@ async function handleCommitImpl(input, ctx, expected) {
|
|
|
714
752
|
} else {
|
|
715
753
|
try {
|
|
716
754
|
await channel.onCommit(settleResult.exchangeId, decoded.payload.fulfillment.data);
|
|
717
|
-
|
|
755
|
+
if (channel.onFulfill !== void 0) {
|
|
756
|
+
const deliveryPending = {
|
|
757
|
+
...pending,
|
|
758
|
+
phase: "delivery",
|
|
759
|
+
recordedAt: Date.now()
|
|
760
|
+
};
|
|
761
|
+
await ctx.fulfillmentRecoveryStore.set(settleResult.exchangeId, deliveryPending);
|
|
762
|
+
try {
|
|
763
|
+
delivery = serializeFulfillmentResult(await channel.onFulfill(settleResult.exchangeId));
|
|
764
|
+
await ctx.fulfillmentRecoveryStore.delete(settleResult.exchangeId);
|
|
765
|
+
logger.debug("x402-server: Flow B channel onCommit succeeded", {
|
|
766
|
+
exchangeId: settleResult.exchangeId,
|
|
767
|
+
option: decoded.payload.fulfillment.option
|
|
768
|
+
});
|
|
769
|
+
} catch (e) {
|
|
770
|
+
const reason = e instanceof Error ? e.message : String(e);
|
|
771
|
+
await ctx.fulfillmentRecoveryStore.set(settleResult.exchangeId, {
|
|
772
|
+
...deliveryPending,
|
|
773
|
+
error: reason
|
|
774
|
+
});
|
|
775
|
+
warnings.push({
|
|
776
|
+
code: "FULFILLMENT_DELIVERY_DEFERRED",
|
|
777
|
+
reason: "atomic redeem succeeded on-chain, but the channel's delivery dispatch failed",
|
|
778
|
+
details: {
|
|
779
|
+
exchangeId: settleResult.exchangeId,
|
|
780
|
+
option: decoded.payload.fulfillment.option,
|
|
781
|
+
error: reason
|
|
782
|
+
}
|
|
783
|
+
});
|
|
784
|
+
}
|
|
785
|
+
} else {
|
|
786
|
+
await ctx.fulfillmentRecoveryStore.delete(settleResult.exchangeId);
|
|
787
|
+
logger.debug("x402-server: Flow B channel onCommit succeeded", {
|
|
788
|
+
exchangeId: settleResult.exchangeId,
|
|
789
|
+
option: decoded.payload.fulfillment.option
|
|
790
|
+
});
|
|
791
|
+
}
|
|
718
792
|
} catch (e) {
|
|
719
793
|
const reason = e instanceof Error ? e.message : String(e);
|
|
720
|
-
ctx.fulfillmentRecoveryStore.set(settleResult.exchangeId, {
|
|
794
|
+
await ctx.fulfillmentRecoveryStore.set(settleResult.exchangeId, {
|
|
721
795
|
...pending,
|
|
722
796
|
error: reason
|
|
723
797
|
});
|
|
798
|
+
logger.warn("x402-server: Flow B channel onCommit failed; recovery entry retained", {
|
|
799
|
+
exchangeId: settleResult.exchangeId,
|
|
800
|
+
option: decoded.payload.fulfillment.option,
|
|
801
|
+
error: reason
|
|
802
|
+
});
|
|
724
803
|
warnings.push({
|
|
725
804
|
code: "FULFILLMENT_COMMIT_DEFERRED",
|
|
726
805
|
reason: "atomic redeem succeeded on-chain, but the channel adapter rejected the data",
|
|
@@ -745,7 +824,8 @@ async function handleCommitImpl(input, ctx, expected) {
|
|
|
745
824
|
exchangeId: settleResult.exchangeId,
|
|
746
825
|
txHash: settleResult.txHash,
|
|
747
826
|
nextActions,
|
|
748
|
-
...warnings.length > 0 ? { warnings } : {}
|
|
827
|
+
...warnings.length > 0 ? { warnings } : {},
|
|
828
|
+
...delivery !== void 0 ? { fulfillment: delivery } : {}
|
|
749
829
|
});
|
|
750
830
|
}
|
|
751
831
|
function buildExpectedFromRequirements(requirements, state) {
|
|
@@ -845,7 +925,7 @@ async function handleRedeem(input, ctx) {
|
|
|
845
925
|
}
|
|
846
926
|
let resolvedChannel;
|
|
847
927
|
if (input.fulfillment !== void 0) {
|
|
848
|
-
const advertisedOptions = ctx.exchangeFulfillmentOptionStore.get(input.exchangeId);
|
|
928
|
+
const advertisedOptions = await ctx.exchangeFulfillmentOptionStore.get(input.exchangeId);
|
|
849
929
|
if (advertisedOptions !== void 0 && !advertisedOptions.includes(input.fulfillment.option)) {
|
|
850
930
|
return handlerErr(
|
|
851
931
|
400,
|
|
@@ -888,23 +968,40 @@ async function handleRedeem(input, ctx) {
|
|
|
888
968
|
}
|
|
889
969
|
const result = await handlePerformAction("boson-redeem", input, ctx);
|
|
890
970
|
if (!result.ok) return result;
|
|
891
|
-
|
|
971
|
+
const warnings = [];
|
|
972
|
+
let delivery;
|
|
892
973
|
if (resolvedChannel !== void 0 && input.fulfillment !== void 0) {
|
|
893
974
|
const pending = {
|
|
894
975
|
exchangeId: input.exchangeId,
|
|
895
976
|
option: input.fulfillment.option,
|
|
896
977
|
data: input.fulfillment.data,
|
|
897
978
|
redeemer,
|
|
898
|
-
recordedAt: Date.now()
|
|
979
|
+
recordedAt: Date.now(),
|
|
980
|
+
phase: "commit"
|
|
899
981
|
};
|
|
900
|
-
ctx.
|
|
982
|
+
const logger = ctx.logger ?? noopLogger;
|
|
983
|
+
await ctx.fulfillmentRecoveryStore.set(input.exchangeId, pending);
|
|
984
|
+
logger.debug("x402-server: fulfillment recovery entry recorded (Flow A redeem)", {
|
|
985
|
+
exchangeId: input.exchangeId,
|
|
986
|
+
option: input.fulfillment.option
|
|
987
|
+
});
|
|
988
|
+
let onCommitOk = false;
|
|
901
989
|
try {
|
|
902
990
|
await resolvedChannel.onCommit(input.exchangeId, input.fulfillment.data);
|
|
903
|
-
|
|
991
|
+
onCommitOk = true;
|
|
992
|
+
logger.debug("x402-server: Flow A channel onCommit succeeded", {
|
|
993
|
+
exchangeId: input.exchangeId,
|
|
994
|
+
option: input.fulfillment.option
|
|
995
|
+
});
|
|
904
996
|
} catch (e) {
|
|
905
997
|
const reason = errorMessage(e);
|
|
906
|
-
ctx.fulfillmentRecoveryStore.set(input.exchangeId, { ...pending, error: reason });
|
|
907
|
-
|
|
998
|
+
await ctx.fulfillmentRecoveryStore.set(input.exchangeId, { ...pending, error: reason });
|
|
999
|
+
logger.warn("x402-server: Flow A channel onCommit failed; recovery entry retained", {
|
|
1000
|
+
exchangeId: input.exchangeId,
|
|
1001
|
+
option: input.fulfillment.option,
|
|
1002
|
+
error: reason
|
|
1003
|
+
});
|
|
1004
|
+
warnings.push({
|
|
908
1005
|
code: "FULFILLMENT_UPDATE_DEFERRED",
|
|
909
1006
|
reason: "redeem succeeded on-chain, but the server could not persist the fulfillment update",
|
|
910
1007
|
details: {
|
|
@@ -912,16 +1009,48 @@ async function handleRedeem(input, ctx) {
|
|
|
912
1009
|
option: input.fulfillment.option,
|
|
913
1010
|
error: reason
|
|
914
1011
|
}
|
|
915
|
-
};
|
|
1012
|
+
});
|
|
1013
|
+
}
|
|
1014
|
+
if (onCommitOk) {
|
|
1015
|
+
if (resolvedChannel.onFulfill !== void 0) {
|
|
1016
|
+
const deliveryPending = {
|
|
1017
|
+
...pending,
|
|
1018
|
+
phase: "delivery",
|
|
1019
|
+
recordedAt: Date.now()
|
|
1020
|
+
};
|
|
1021
|
+
await ctx.fulfillmentRecoveryStore.set(input.exchangeId, deliveryPending);
|
|
1022
|
+
try {
|
|
1023
|
+
delivery = serializeFulfillmentResult(await resolvedChannel.onFulfill(input.exchangeId));
|
|
1024
|
+
await ctx.fulfillmentRecoveryStore.delete(input.exchangeId);
|
|
1025
|
+
} catch (e) {
|
|
1026
|
+
const reason = errorMessage(e);
|
|
1027
|
+
await ctx.fulfillmentRecoveryStore.set(input.exchangeId, {
|
|
1028
|
+
...deliveryPending,
|
|
1029
|
+
error: reason
|
|
1030
|
+
});
|
|
1031
|
+
warnings.push({
|
|
1032
|
+
code: "FULFILLMENT_DELIVERY_DEFERRED",
|
|
1033
|
+
reason: "redeem succeeded on-chain, but the fulfillment channel's delivery dispatch failed",
|
|
1034
|
+
details: {
|
|
1035
|
+
exchangeId: input.exchangeId,
|
|
1036
|
+
option: input.fulfillment.option,
|
|
1037
|
+
error: reason
|
|
1038
|
+
}
|
|
1039
|
+
});
|
|
1040
|
+
}
|
|
1041
|
+
} else {
|
|
1042
|
+
await ctx.fulfillmentRecoveryStore.delete(input.exchangeId);
|
|
1043
|
+
}
|
|
916
1044
|
}
|
|
917
1045
|
}
|
|
918
|
-
ctx.exchangeFulfillmentOptionStore.delete(input.exchangeId);
|
|
919
|
-
if (
|
|
1046
|
+
await ctx.exchangeFulfillmentOptionStore.delete(input.exchangeId);
|
|
1047
|
+
if (warnings.length > 0 || delivery !== void 0) {
|
|
920
1048
|
return {
|
|
921
1049
|
...result,
|
|
922
1050
|
body: {
|
|
923
1051
|
...result.body,
|
|
924
|
-
|
|
1052
|
+
...delivery !== void 0 ? { fulfillment: delivery } : {},
|
|
1053
|
+
...warnings.length > 0 ? { warnings: [...result.body.warnings ?? [], ...warnings] } : {}
|
|
925
1054
|
}
|
|
926
1055
|
};
|
|
927
1056
|
}
|