@agenzo/token-cli 0.3.0 → 0.3.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/README.md +37 -16
- package/dist/index.js +233 -421
- package/dist/index.js.map +1 -1
- package/package.json +8 -1
package/dist/index.js
CHANGED
|
@@ -270,10 +270,6 @@ var ApiClient = class {
|
|
|
270
270
|
const headers = {
|
|
271
271
|
"User-Agent": `agenzo-admin-cli/${getCurrentVersion()}`
|
|
272
272
|
};
|
|
273
|
-
const traceparent = process.env.TRACEPARENT;
|
|
274
|
-
if (traceparent) {
|
|
275
|
-
headers["traceparent"] = traceparent;
|
|
276
|
-
}
|
|
277
273
|
if (auth.type === "bearer") {
|
|
278
274
|
headers["Authorization"] = `Bearer ${auth.token}`;
|
|
279
275
|
} else if (auth.type === "api-key") {
|
|
@@ -696,17 +692,11 @@ async function renderWithContext(result, opts, configManager) {
|
|
|
696
692
|
render({ ...result, data: payload }, opts);
|
|
697
693
|
}
|
|
698
694
|
var PromptEngine = class {
|
|
699
|
-
/** Return flagValue if provided,
|
|
695
|
+
/** Return flagValue directly if provided, otherwise prompt interactively */
|
|
700
696
|
static async resolveInput(flagValue, config) {
|
|
701
697
|
if (flagValue !== void 0) {
|
|
702
698
|
return flagValue;
|
|
703
699
|
}
|
|
704
|
-
if (config.envVar) {
|
|
705
|
-
const envValue = process.env[config.envVar];
|
|
706
|
-
if (envValue !== void 0 && envValue !== "") {
|
|
707
|
-
return envValue;
|
|
708
|
-
}
|
|
709
|
-
}
|
|
710
700
|
if (config.type === "password") {
|
|
711
701
|
return password({ message: config.message, mask: "*" });
|
|
712
702
|
}
|
|
@@ -753,363 +743,232 @@ async function collectPaymentMethodParams(type, flags) {
|
|
|
753
743
|
return params;
|
|
754
744
|
}
|
|
755
745
|
|
|
756
|
-
// src/verb-schema.ts
|
|
757
|
-
var CLI_NAME = "agenzo-token-cli";
|
|
758
|
-
function wantsJsonSchema(argv = process.argv) {
|
|
759
|
-
for (let i = 0; i < argv.length; i++) {
|
|
760
|
-
const a = argv[i];
|
|
761
|
-
if (a === "--format=json") return true;
|
|
762
|
-
if (a === "--format" && argv[i + 1] === "json") return true;
|
|
763
|
-
}
|
|
764
|
-
return false;
|
|
765
|
-
}
|
|
766
|
-
function emitSchema(schema) {
|
|
767
|
-
console.log(JSON.stringify(schema, null, 2));
|
|
768
|
-
}
|
|
769
|
-
function attachSchemaHelp(cmd, schema) {
|
|
770
|
-
const baseHelp = cmd.helpInformation.bind(cmd);
|
|
771
|
-
cmd.helpInformation = (context) => {
|
|
772
|
-
if (!wantsJsonSchema()) return baseHelp(context);
|
|
773
|
-
emitSchema(schema);
|
|
774
|
-
return "";
|
|
775
|
-
};
|
|
776
|
-
return cmd;
|
|
777
|
-
}
|
|
778
|
-
var pmAddSchema = {
|
|
779
|
-
cli: CLI_NAME,
|
|
780
|
-
noun: "payment-methods",
|
|
781
|
-
verb: "add",
|
|
782
|
-
description: "Add a payment method (card binding + 3DS verification)",
|
|
783
|
-
flags: {
|
|
784
|
-
"card-number": { type: "string", required: true, description: "Card number (PAN)" },
|
|
785
|
-
"exp-month": { type: "string", required: true, description: "Expiration month (MM)" },
|
|
786
|
-
"exp-year": { type: "string", required: true, description: "Expiration year (YY or YYYY)" },
|
|
787
|
-
"cardholder-name": { type: "string", required: true, description: "Name on the card" },
|
|
788
|
-
"member-id": { type: "string", required: false, description: "Member ID to associate the card with" },
|
|
789
|
-
"api-key": { type: "string", required: true, description: "API Key for authentication", source: "config" }
|
|
790
|
-
},
|
|
791
|
-
response: {
|
|
792
|
-
id: { type: "string", description: "Payment method ID" },
|
|
793
|
-
status: { type: "string", description: "PENDING / ACTIVE / FAILED" },
|
|
794
|
-
card_brand: { type: "string", description: "visa / mastercard / ..." },
|
|
795
|
-
last_four: { type: "string", description: "Last 4 digits of card" },
|
|
796
|
-
verification_url: { type: "string|null", description: "3DS verification URL (when PENDING)" }
|
|
797
|
-
},
|
|
798
|
-
example: {
|
|
799
|
-
command: 'agenzo-token-cli payment-methods add --card-number 4111111111111111 --exp-month 12 --exp-year 2027 --cardholder-name "Alice Test"',
|
|
800
|
-
output_summary: "Returns payment method ID and 3DS verification URL. Complete 3DS to activate."
|
|
801
|
-
},
|
|
802
|
-
error_recovery: {
|
|
803
|
-
INVALID_CARD: "Card number failed Luhn check or is not supported. Verify and retry.",
|
|
804
|
-
EVO_ERROR: "Upstream payment processor error. Retry after a short delay."
|
|
805
|
-
}
|
|
806
|
-
};
|
|
807
|
-
var pmListSchema = {
|
|
808
|
-
cli: CLI_NAME,
|
|
809
|
-
noun: "payment-methods",
|
|
810
|
-
verb: "list",
|
|
811
|
-
description: "List payment methods for the authenticated developer",
|
|
812
|
-
flags: {
|
|
813
|
-
"member": { type: "string", required: false, description: "Filter by member ID" },
|
|
814
|
-
"api-key": { type: "string", required: true, description: "API Key for authentication", source: "config" }
|
|
815
|
-
},
|
|
816
|
-
response: {
|
|
817
|
-
payment_methods: {
|
|
818
|
-
type: "array",
|
|
819
|
-
description: "List of payment methods",
|
|
820
|
-
items: {
|
|
821
|
-
id: { type: "string", description: "Payment method ID" },
|
|
822
|
-
status: { type: "string", description: "PENDING / ACTIVE / DISABLED" },
|
|
823
|
-
card_brand: { type: "string", description: "Card brand" },
|
|
824
|
-
last_four: { type: "string", description: "Last 4 digits" },
|
|
825
|
-
created_at: { type: "string", description: "Creation time (ISO 8601)" }
|
|
826
|
-
}
|
|
827
|
-
}
|
|
828
|
-
},
|
|
829
|
-
example: {
|
|
830
|
-
command: "agenzo-token-cli payment-methods list",
|
|
831
|
-
output_summary: "Returns array of payment methods with status and card info."
|
|
832
|
-
}
|
|
833
|
-
};
|
|
834
|
-
var pmGetSchema = {
|
|
835
|
-
cli: CLI_NAME,
|
|
836
|
-
noun: "payment-methods",
|
|
837
|
-
verb: "get",
|
|
838
|
-
description: "Get a payment method by ID",
|
|
839
|
-
flags: {
|
|
840
|
-
"pm-id": { type: "string", required: true, description: "Payment method ID" },
|
|
841
|
-
"api-key": { type: "string", required: true, description: "API Key for authentication", source: "config" }
|
|
842
|
-
},
|
|
843
|
-
response: {
|
|
844
|
-
id: { type: "string", description: "Payment method ID" },
|
|
845
|
-
status: { type: "string", description: "PENDING / ACTIVE / DISABLED / FAILED / EXPIRED" },
|
|
846
|
-
card_brand: { type: "string", description: "Card brand" },
|
|
847
|
-
last_four: { type: "string", description: "Last 4 digits" },
|
|
848
|
-
member_id: { type: "string|null", description: "Associated member ID" },
|
|
849
|
-
created_at: { type: "string", description: "Creation time" }
|
|
850
|
-
},
|
|
851
|
-
example: {
|
|
852
|
-
command: "agenzo-token-cli payment-methods get pm_01HZXD...",
|
|
853
|
-
output_summary: "Returns full payment method details including 3DS verification status."
|
|
854
|
-
}
|
|
855
|
-
};
|
|
856
|
-
var pmDisableSchema = {
|
|
857
|
-
cli: CLI_NAME,
|
|
858
|
-
noun: "payment-methods",
|
|
859
|
-
verb: "disable",
|
|
860
|
-
description: "Disable a payment method (cascades revoke on active payment tokens)",
|
|
861
|
-
flags: {
|
|
862
|
-
"pm-id": { type: "string", required: true, description: "Payment method ID to disable" },
|
|
863
|
-
"api-key": { type: "string", required: true, description: "API Key for authentication", source: "config" }
|
|
864
|
-
},
|
|
865
|
-
response: {
|
|
866
|
-
id: { type: "string", description: "Disabled payment method ID" },
|
|
867
|
-
status: { type: "string", description: "DISABLED" },
|
|
868
|
-
revoked_tokens_count: { type: "int", description: "Number of payment tokens revoked by cascade" }
|
|
869
|
-
},
|
|
870
|
-
example: {
|
|
871
|
-
command: "agenzo-token-cli payment-methods disable pm_01HZXD...",
|
|
872
|
-
output_summary: "Disables the payment method and revokes any active tokens bound to it."
|
|
873
|
-
}
|
|
874
|
-
};
|
|
875
|
-
var ptCreateSchema = {
|
|
876
|
-
cli: CLI_NAME,
|
|
877
|
-
noun: "payment-tokens",
|
|
878
|
-
verb: "create",
|
|
879
|
-
description: "Create a payment token (VCN, Network Token cryptogram, or x402 USDC signature)",
|
|
880
|
-
flags: {
|
|
881
|
-
"type": { type: "string", required: true, description: "Token type: vcn / network_token / x402", constraints: "vcn | network_token | x402" },
|
|
882
|
-
"payment-method-id": { type: "string", required: true, description: "Source payment method ID (must be ACTIVE)" },
|
|
883
|
-
"amount": { type: "string", required: "conditional", description: "Amount in minor units (required for vcn and x402)" },
|
|
884
|
-
"currency": { type: "string", required: false, default: "USD", description: "ISO 4217 currency code" },
|
|
885
|
-
"member-id": { type: "string", required: false, description: "Member ID" },
|
|
886
|
-
"external-transaction-id": { type: "string", required: false, description: "External reference for reconciliation" },
|
|
887
|
-
"pay-to": { type: "string", required: "conditional", description: "Recipient address (required for x402)" },
|
|
888
|
-
"nonce": { type: "string", required: "conditional", description: "Nonce bytes32 hex (required for x402)" },
|
|
889
|
-
"network": { type: "string", required: "conditional", description: "Chain network (required for x402, e.g. base-sepolia)" },
|
|
890
|
-
"deadline": { type: "int", required: "conditional", description: "Epoch seconds deadline (required for x402)" },
|
|
891
|
-
"api-key": { type: "string", required: true, description: "API Key for authentication", source: "config" }
|
|
892
|
-
},
|
|
893
|
-
response: {
|
|
894
|
-
id: { type: "string", description: "Payment token ID" },
|
|
895
|
-
type: { type: "string", description: "vcn / network_token / x402" },
|
|
896
|
-
status: { type: "string", description: "ACTIVE / REVOKED / EXPIRED" },
|
|
897
|
-
token_data: { type: "object", description: "Type-specific token payload (cryptogram / VCN number / x402 signature)" },
|
|
898
|
-
expires_at: { type: "string|null", description: "Expiration time (ISO 8601)" }
|
|
899
|
-
},
|
|
900
|
-
example: {
|
|
901
|
-
command: "agenzo-token-cli payment-tokens create --type network_token --payment-method-id pm_01HZXD...",
|
|
902
|
-
output_summary: "Returns token ID and cryptogram/VCN/signature data for payment execution."
|
|
903
|
-
},
|
|
904
|
-
error_recovery: {
|
|
905
|
-
INVALID_PAYMENT_METHOD: "Payment method is not ACTIVE or not found. Verify status with payment-methods get.",
|
|
906
|
-
UNSUPPORTED_TOKEN_TYPE: "Use vcn, network_token, or x402."
|
|
907
|
-
}
|
|
908
|
-
};
|
|
909
|
-
var ptListSchema = {
|
|
910
|
-
cli: CLI_NAME,
|
|
911
|
-
noun: "payment-tokens",
|
|
912
|
-
verb: "list",
|
|
913
|
-
description: "List payment tokens for the authenticated developer",
|
|
914
|
-
flags: {
|
|
915
|
-
"type": { type: "string", required: false, description: "Filter by token type (vcn / network_token / x402)" },
|
|
916
|
-
"member-id": { type: "string", required: false, description: "Filter by member ID" },
|
|
917
|
-
"payment-method-id": { type: "string", required: false, description: "Filter by source payment method" },
|
|
918
|
-
"api-key": { type: "string", required: true, description: "API Key for authentication", source: "config" }
|
|
919
|
-
},
|
|
920
|
-
response: {
|
|
921
|
-
payment_tokens: {
|
|
922
|
-
type: "array",
|
|
923
|
-
description: "List of payment tokens",
|
|
924
|
-
items: {
|
|
925
|
-
id: { type: "string", description: "Token ID" },
|
|
926
|
-
type: { type: "string", description: "Token type" },
|
|
927
|
-
status: { type: "string", description: "ACTIVE / REVOKED / EXPIRED" },
|
|
928
|
-
payment_method_id: { type: "string", description: "Source payment method" },
|
|
929
|
-
created_at: { type: "string", description: "Creation time" }
|
|
930
|
-
}
|
|
931
|
-
}
|
|
932
|
-
},
|
|
933
|
-
example: {
|
|
934
|
-
command: "agenzo-token-cli payment-tokens list --type network_token",
|
|
935
|
-
output_summary: "Returns array of payment tokens filtered by type."
|
|
936
|
-
}
|
|
937
|
-
};
|
|
938
|
-
var ptGetSchema = {
|
|
939
|
-
cli: CLI_NAME,
|
|
940
|
-
noun: "payment-tokens",
|
|
941
|
-
verb: "get",
|
|
942
|
-
description: "Get a payment token by ID",
|
|
943
|
-
flags: {
|
|
944
|
-
"payment-token-id": { type: "string", required: true, description: "Payment token ID" },
|
|
945
|
-
"api-key": { type: "string", required: true, description: "API Key for authentication", source: "config" }
|
|
946
|
-
},
|
|
947
|
-
response: {
|
|
948
|
-
id: { type: "string", description: "Token ID" },
|
|
949
|
-
type: { type: "string", description: "Token type" },
|
|
950
|
-
status: { type: "string", description: "ACTIVE / REVOKED / EXPIRED" },
|
|
951
|
-
token_data: { type: "object", description: "Type-specific payload" },
|
|
952
|
-
payment_method_id: { type: "string", description: "Source payment method" },
|
|
953
|
-
created_at: { type: "string", description: "Creation time" },
|
|
954
|
-
expires_at: { type: "string|null", description: "Expiration time" }
|
|
955
|
-
},
|
|
956
|
-
example: {
|
|
957
|
-
command: "agenzo-token-cli payment-tokens get pt_01HZXD...",
|
|
958
|
-
output_summary: "Returns full token details including token_data payload."
|
|
959
|
-
}
|
|
960
|
-
};
|
|
961
|
-
var ptRevokeSchema = {
|
|
962
|
-
cli: CLI_NAME,
|
|
963
|
-
noun: "payment-tokens",
|
|
964
|
-
verb: "revoke",
|
|
965
|
-
description: "Revoke an active payment token",
|
|
966
|
-
flags: {
|
|
967
|
-
"payment-token-id": { type: "string", required: true, description: "Payment token ID to revoke" },
|
|
968
|
-
"api-key": { type: "string", required: true, description: "API Key for authentication", source: "config" }
|
|
969
|
-
},
|
|
970
|
-
response: {
|
|
971
|
-
id: { type: "string", description: "Revoked token ID" },
|
|
972
|
-
status: { type: "string", description: "REVOKED" }
|
|
973
|
-
},
|
|
974
|
-
example: {
|
|
975
|
-
command: "agenzo-token-cli payment-tokens revoke pt_01HZXD...",
|
|
976
|
-
output_summary: "Marks the token as REVOKED. Cannot be undone."
|
|
977
|
-
}
|
|
978
|
-
};
|
|
979
|
-
|
|
980
746
|
// src/payment-methods/add.ts
|
|
981
|
-
var
|
|
982
|
-
var
|
|
747
|
+
var MANUAL_POLL_INTERVAL_MS = 3e3;
|
|
748
|
+
var MANUAL_POLL_TIMEOUT_MS = 15 * 60 * 1e3;
|
|
749
|
+
var DROPIN_POLL_INTERVAL_MS = 5e3;
|
|
750
|
+
var DROPIN_POLL_TIMEOUT_MS = 30 * 60 * 1e3;
|
|
751
|
+
var TERMINAL_STATUSES = /* @__PURE__ */ new Set(["ACTIVE", "FAILED", "EXPIRED"]);
|
|
983
752
|
function registerAddCommand(parent, deps) {
|
|
984
|
-
const cmd = parent.command("add").description("Add a payment method (
|
|
753
|
+
const cmd = parent.command("add").description("Add a payment method (manual 3DS or Drop-in session)").option("--api-key <key>", "API Key for authentication").option("--type <type>", "Payment method type (default: card)", "card").option(
|
|
754
|
+
"--mode <mode>",
|
|
755
|
+
'Add mode: "manual" (default; CLI collects card details and polls 3DS) or "dropin" (mint a Drop-in session and poll until the user finishes adding the payment method in the browser)',
|
|
756
|
+
"manual"
|
|
757
|
+
).option(
|
|
758
|
+
"--email <email>",
|
|
759
|
+
"Manual mode: email for 3DS verification. Dropin mode: email used as the Drop-in session reference."
|
|
760
|
+
).option("--card-number <number>", "Card number (manual mode only)").option("--expiry <mmyy>", "Expiry date (MMYY format) (manual mode only)").option("--cvv <cvv>", "Card CVV (manual mode only)").option(
|
|
985
761
|
"--idempotency-key <key>",
|
|
986
|
-
"Idempotency key forwarded verbatim as the Idempotency-Key header"
|
|
762
|
+
"Idempotency key forwarded verbatim as the Idempotency-Key header (manual mode only)"
|
|
987
763
|
);
|
|
988
|
-
attachSchemaHelp(cmd, pmAddSchema);
|
|
989
764
|
cmd.action(async () => {
|
|
990
765
|
const opts = cmd.optsWithGlobals();
|
|
991
766
|
const format = resolveFormat(opts.format);
|
|
992
767
|
const isYes = Boolean(opts.yes);
|
|
993
|
-
const
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
const flags = {
|
|
1000
|
-
email: opts.email,
|
|
1001
|
-
cardNumber: opts.cardNumber,
|
|
1002
|
-
expiry: opts.expiry,
|
|
1003
|
-
cvv: opts.cvv
|
|
1004
|
-
};
|
|
1005
|
-
const params = await collectPaymentMethodParams(type, flags);
|
|
1006
|
-
let idempotencyKey = opts.idempotencyKey;
|
|
1007
|
-
if (!idempotencyKey) {
|
|
1008
|
-
if (isYes) {
|
|
1009
|
-
throw new IdempotencyKeyRequiredError("payment-methods add");
|
|
1010
|
-
}
|
|
1011
|
-
idempotencyKey = await PromptEngine.resolveInput(void 0, {
|
|
1012
|
-
message: "Idempotency key (unique per write, for safe retry):",
|
|
1013
|
-
validate: (v) => v.trim().length > 0 || "Idempotency key is required"
|
|
1014
|
-
});
|
|
768
|
+
const mode = String(opts.mode ?? "manual").toLowerCase();
|
|
769
|
+
if (mode !== "manual" && mode !== "dropin") {
|
|
770
|
+
throw new CliError(
|
|
771
|
+
"PARAM_INVALID",
|
|
772
|
+
`Unknown --mode "${opts.mode}". Expected "manual" or "dropin".`
|
|
773
|
+
);
|
|
1015
774
|
}
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
const result = await deps.apiClient.post(
|
|
1020
|
-
"/payment-methods/create",
|
|
1021
|
-
{ type: "api-key", key: apiKey },
|
|
1022
|
-
params,
|
|
1023
|
-
extraHeaders
|
|
1024
|
-
);
|
|
1025
|
-
if (!result.success) {
|
|
1026
|
-
throw CliError.fromApi(result, { auth: "api-key" });
|
|
775
|
+
if (mode === "dropin") {
|
|
776
|
+
await handleDropinMode(deps, opts, format);
|
|
777
|
+
return;
|
|
1027
778
|
}
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
779
|
+
await handleManualMode(deps, opts, format, isYes);
|
|
780
|
+
});
|
|
781
|
+
}
|
|
782
|
+
async function handleManualMode(deps, opts, format, isYes) {
|
|
783
|
+
const apiKey = await PromptEngine.resolveInput(opts.apiKey, {
|
|
784
|
+
message: "API Key:",
|
|
785
|
+
type: "password"
|
|
786
|
+
});
|
|
787
|
+
const type = opts.type || "card";
|
|
788
|
+
const flags = {
|
|
789
|
+
email: opts.email,
|
|
790
|
+
cardNumber: opts.cardNumber,
|
|
791
|
+
expiry: opts.expiry,
|
|
792
|
+
cvv: opts.cvv
|
|
793
|
+
};
|
|
794
|
+
const params = await collectPaymentMethodParams(type, flags);
|
|
795
|
+
let idempotencyKey = opts.idempotencyKey;
|
|
796
|
+
if (!idempotencyKey) {
|
|
797
|
+
if (isYes) {
|
|
798
|
+
throw new IdempotencyKeyRequiredError("payment-methods add");
|
|
799
|
+
}
|
|
800
|
+
idempotencyKey = await PromptEngine.resolveInput(void 0, {
|
|
801
|
+
message: "Idempotency key (unique per write, for safe retry):",
|
|
802
|
+
validate: (v) => v.trim().length > 0 || "Idempotency key is required"
|
|
803
|
+
});
|
|
804
|
+
}
|
|
805
|
+
const extraHeaders = {
|
|
806
|
+
"Idempotency-Key": idempotencyKey
|
|
807
|
+
};
|
|
808
|
+
const result = await deps.apiClient.post(
|
|
809
|
+
"/payment-methods/create",
|
|
810
|
+
{ type: "api-key", key: apiKey },
|
|
811
|
+
params,
|
|
812
|
+
extraHeaders
|
|
813
|
+
);
|
|
814
|
+
if (!result.success) {
|
|
815
|
+
throw CliError.fromApi(result, { auth: "api-key" });
|
|
816
|
+
}
|
|
817
|
+
const pm = result.data;
|
|
818
|
+
notify(format, "success", "Payment method created");
|
|
819
|
+
const createdResult = {
|
|
820
|
+
data: pm,
|
|
821
|
+
text: () => {
|
|
822
|
+
const lines = [
|
|
823
|
+
["ID", pm.id],
|
|
824
|
+
["Type", pm.type],
|
|
825
|
+
["Status", pm.status]
|
|
826
|
+
];
|
|
827
|
+
if (pm.brand) lines.push(["Brand", pm.brand]);
|
|
828
|
+
if (pm.first6) lines.push(["First 6", pm.first6]);
|
|
829
|
+
if (pm.last4) lines.push(["Last 4", pm.last4]);
|
|
830
|
+
return Formatter.keyValue(lines);
|
|
831
|
+
}
|
|
832
|
+
};
|
|
833
|
+
const configManager = new ConfigManager();
|
|
834
|
+
await renderWithContext(createdResult, { format }, configManager);
|
|
835
|
+
notify(format, "info", "Complete 3DS verification via email to activate");
|
|
836
|
+
if (type === "card" && pm.status === "PENDING") {
|
|
837
|
+
const finalStatus = await poll3dsVerification(deps.apiClient, apiKey, pm.id, format);
|
|
838
|
+
if (finalStatus === "ACTIVE") {
|
|
839
|
+
const getResult = await deps.apiClient.get(
|
|
840
|
+
`/payment-methods/${pm.id}`,
|
|
841
|
+
{ type: "api-key", key: apiKey }
|
|
1053
842
|
);
|
|
1054
|
-
if (
|
|
1055
|
-
const
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
if (degraded.last4) lines.push(["Last 4", degraded.last4]);
|
|
1091
|
-
return Formatter.keyValue(lines);
|
|
1092
|
-
}
|
|
1093
|
-
};
|
|
1094
|
-
await renderWithContext(degradedResult, { format }, configManager);
|
|
1095
|
-
}
|
|
1096
|
-
} else if (finalStatus === "FAILED") {
|
|
1097
|
-
notify(format, "error", "3DS verification failed");
|
|
1098
|
-
} else if (finalStatus === "TIMEOUT") {
|
|
1099
|
-
notify(
|
|
1100
|
-
format,
|
|
1101
|
-
"info",
|
|
1102
|
-
`Verification timed out (15 min). Check status with: agenzo-token-cli payment-methods get ${pm.id} --api-key <your_key>`
|
|
1103
|
-
);
|
|
843
|
+
if (getResult.success) {
|
|
844
|
+
const activatedPm = getResult.data;
|
|
845
|
+
notify(format, "success", "Payment method activated");
|
|
846
|
+
const activatedResult = {
|
|
847
|
+
data: activatedPm,
|
|
848
|
+
text: () => {
|
|
849
|
+
const lines = [
|
|
850
|
+
["ID", activatedPm.id],
|
|
851
|
+
["Type", activatedPm.type],
|
|
852
|
+
["Status", activatedPm.status]
|
|
853
|
+
];
|
|
854
|
+
if (activatedPm.brand) lines.push(["Brand", activatedPm.brand]);
|
|
855
|
+
if (activatedPm.first6) lines.push(["First 6", activatedPm.first6]);
|
|
856
|
+
if (activatedPm.last4) lines.push(["Last 4", activatedPm.last4]);
|
|
857
|
+
return Formatter.keyValue(lines);
|
|
858
|
+
}
|
|
859
|
+
};
|
|
860
|
+
await renderWithContext(activatedResult, { format }, configManager);
|
|
861
|
+
} else {
|
|
862
|
+
notify(format, "success", "Payment method activated");
|
|
863
|
+
const degraded = { ...pm, status: "ACTIVE" };
|
|
864
|
+
const degradedResult = {
|
|
865
|
+
data: degraded,
|
|
866
|
+
text: () => {
|
|
867
|
+
const lines = [
|
|
868
|
+
["ID", degraded.id],
|
|
869
|
+
["Type", degraded.type],
|
|
870
|
+
["Status", degraded.status]
|
|
871
|
+
];
|
|
872
|
+
if (degraded.brand) lines.push(["Brand", degraded.brand]);
|
|
873
|
+
if (degraded.first6) lines.push(["First 6", degraded.first6]);
|
|
874
|
+
if (degraded.last4) lines.push(["Last 4", degraded.last4]);
|
|
875
|
+
return Formatter.keyValue(lines);
|
|
876
|
+
}
|
|
877
|
+
};
|
|
878
|
+
await renderWithContext(degradedResult, { format }, configManager);
|
|
1104
879
|
}
|
|
880
|
+
} else if (finalStatus === "FAILED") {
|
|
881
|
+
notify(format, "error", "3DS verification failed");
|
|
882
|
+
} else if (finalStatus === "TIMEOUT") {
|
|
883
|
+
notify(
|
|
884
|
+
format,
|
|
885
|
+
"info",
|
|
886
|
+
`Verification timed out (15 min). Check status with: agenzo-token-cli payment-methods get ${pm.id} --api-key <your_key>`
|
|
887
|
+
);
|
|
1105
888
|
}
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
async function handleDropinMode(deps, opts, format) {
|
|
892
|
+
const apiKey = await PromptEngine.resolveInput(opts.apiKey, {
|
|
893
|
+
message: "API Key:",
|
|
894
|
+
type: "password"
|
|
1106
895
|
});
|
|
896
|
+
const email = await PromptEngine.resolveInput(opts.email, {
|
|
897
|
+
message: "Email:"
|
|
898
|
+
});
|
|
899
|
+
const configManager = new ConfigManager();
|
|
900
|
+
const sessionResult = await deps.apiClient.post(
|
|
901
|
+
"/payment-methods/dropin/create",
|
|
902
|
+
{ type: "api-key", key: apiKey },
|
|
903
|
+
{ email }
|
|
904
|
+
);
|
|
905
|
+
if (!sessionResult.success) {
|
|
906
|
+
throw CliError.fromApi(sessionResult, { auth: "api-key" });
|
|
907
|
+
}
|
|
908
|
+
const session = sessionResult.data;
|
|
909
|
+
const pmId = session.id;
|
|
910
|
+
notify(format, "success", "Drop-in session created");
|
|
911
|
+
const createdResult = {
|
|
912
|
+
data: session,
|
|
913
|
+
text: () => Formatter.keyValue([["Session ID", session.session_id || "-"]])
|
|
914
|
+
};
|
|
915
|
+
await renderWithContext(createdResult, { format }, configManager);
|
|
916
|
+
notify(
|
|
917
|
+
format,
|
|
918
|
+
"info",
|
|
919
|
+
"Use the Session ID to add the payment method in the browser via the Drop-in SDK"
|
|
920
|
+
);
|
|
921
|
+
const finalPm = await pollVerificationStatus(deps.apiClient, apiKey, pmId, {
|
|
922
|
+
intervalMs: DROPIN_POLL_INTERVAL_MS,
|
|
923
|
+
timeoutMs: DROPIN_POLL_TIMEOUT_MS
|
|
924
|
+
});
|
|
925
|
+
if (finalPm.status === "ACTIVE") {
|
|
926
|
+
notify(format, "success", "Payment method activated");
|
|
927
|
+
const activated = {
|
|
928
|
+
data: finalPm,
|
|
929
|
+
text: () => Formatter.keyValue([
|
|
930
|
+
["PM ID", finalPm.id],
|
|
931
|
+
["Brand", finalPm.brand ?? "-"],
|
|
932
|
+
["First 6", finalPm.first6 ?? "-"],
|
|
933
|
+
["Last 4", finalPm.last4 ?? "-"],
|
|
934
|
+
["Status", finalPm.status]
|
|
935
|
+
])
|
|
936
|
+
};
|
|
937
|
+
await renderWithContext(activated, { format }, configManager);
|
|
938
|
+
return;
|
|
939
|
+
}
|
|
940
|
+
if (finalPm.status === "FAILED") {
|
|
941
|
+
notify(format, "error", "Failed to add payment method");
|
|
942
|
+
await renderPmId(finalPm.id, format, configManager);
|
|
943
|
+
process.exitCode = 1;
|
|
944
|
+
return;
|
|
945
|
+
}
|
|
946
|
+
if (finalPm.status === "EXPIRED") {
|
|
947
|
+
notify(format, "error", "Session expired before the payment method was added");
|
|
948
|
+
await renderPmId(finalPm.id, format, configManager);
|
|
949
|
+
process.exitCode = 1;
|
|
950
|
+
return;
|
|
951
|
+
}
|
|
952
|
+
notify(
|
|
953
|
+
format,
|
|
954
|
+
"error",
|
|
955
|
+
"Adding payment method did not complete within 30 minutes. Re-run with the same email to resume."
|
|
956
|
+
);
|
|
957
|
+
await renderPmId(pmId, format, configManager);
|
|
958
|
+
process.exitCode = 1;
|
|
959
|
+
}
|
|
960
|
+
async function renderPmId(id, format, configManager) {
|
|
961
|
+
const result = {
|
|
962
|
+
data: { id },
|
|
963
|
+
text: () => Formatter.keyValue([["PM ID", id]])
|
|
964
|
+
};
|
|
965
|
+
await renderWithContext(result, { format }, configManager);
|
|
1107
966
|
}
|
|
1108
967
|
async function poll3dsVerification(apiClient, apiKey, paymentMethodId, format) {
|
|
1109
968
|
const startTime = Date.now();
|
|
1110
969
|
notify(format, "info", "Waiting for 3DS verification...");
|
|
1111
|
-
while (Date.now() - startTime <
|
|
1112
|
-
await sleep(
|
|
970
|
+
while (Date.now() - startTime < MANUAL_POLL_TIMEOUT_MS) {
|
|
971
|
+
await sleep(MANUAL_POLL_INTERVAL_MS);
|
|
1113
972
|
const result = await apiClient.get(
|
|
1114
973
|
"/payment-methods/verification/status",
|
|
1115
974
|
{ type: "api-key", key: apiKey },
|
|
@@ -1127,6 +986,21 @@ async function poll3dsVerification(apiClient, apiKey, paymentMethodId, format) {
|
|
|
1127
986
|
}
|
|
1128
987
|
return "TIMEOUT";
|
|
1129
988
|
}
|
|
989
|
+
async function pollVerificationStatus(apiClient, apiKey, pmId, options) {
|
|
990
|
+
const startTime = Date.now();
|
|
991
|
+
while (Date.now() - startTime < options.timeoutMs) {
|
|
992
|
+
const result = await apiClient.get(
|
|
993
|
+
"/payment-methods/verification/status",
|
|
994
|
+
{ type: "api-key", key: apiKey },
|
|
995
|
+
{ payment_method_id: pmId }
|
|
996
|
+
);
|
|
997
|
+
if (result.success && TERMINAL_STATUSES.has(result.data.status)) {
|
|
998
|
+
return result.data;
|
|
999
|
+
}
|
|
1000
|
+
await sleep(options.intervalMs);
|
|
1001
|
+
}
|
|
1002
|
+
return { id: pmId, status: "PENDING" };
|
|
1003
|
+
}
|
|
1130
1004
|
function sleep(ms) {
|
|
1131
1005
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
1132
1006
|
}
|
|
@@ -1134,14 +1008,12 @@ function sleep(ms) {
|
|
|
1134
1008
|
// src/payment-methods/list.ts
|
|
1135
1009
|
function registerListCommand(parent, deps) {
|
|
1136
1010
|
const cmd = parent.command("list").description("List payment methods").option("--api-key <key>", "API Key for authentication").option("--member <member_id>", "Filter by member ID");
|
|
1137
|
-
attachSchemaHelp(cmd, pmListSchema);
|
|
1138
1011
|
cmd.action(async () => {
|
|
1139
1012
|
const opts = cmd.optsWithGlobals();
|
|
1140
1013
|
const format = resolveFormat(opts.format);
|
|
1141
1014
|
const apiKey = await PromptEngine.resolveInput(opts.apiKey, {
|
|
1142
1015
|
message: "API Key:",
|
|
1143
|
-
type: "password"
|
|
1144
|
-
envVar: "AGENZO_API_KEY"
|
|
1016
|
+
type: "password"
|
|
1145
1017
|
});
|
|
1146
1018
|
const params = {};
|
|
1147
1019
|
if (opts.member) {
|
|
@@ -1182,14 +1054,12 @@ function registerListCommand(parent, deps) {
|
|
|
1182
1054
|
// src/payment-methods/get.ts
|
|
1183
1055
|
function registerGetCommand(parent, deps) {
|
|
1184
1056
|
const cmd = parent.command("get <pm_id>").description("Get a payment method by ID").option("--api-key <key>", "API key for authentication");
|
|
1185
|
-
attachSchemaHelp(cmd, pmGetSchema);
|
|
1186
1057
|
cmd.action(async (pmId) => {
|
|
1187
1058
|
const opts = cmd.optsWithGlobals();
|
|
1188
1059
|
const format = resolveFormat(opts.format);
|
|
1189
1060
|
const apiKey = await PromptEngine.resolveInput(opts.apiKey, {
|
|
1190
1061
|
message: "API Key:",
|
|
1191
|
-
type: "password"
|
|
1192
|
-
envVar: "AGENZO_API_KEY"
|
|
1062
|
+
type: "password"
|
|
1193
1063
|
});
|
|
1194
1064
|
const result = await deps.apiClient.get(
|
|
1195
1065
|
`/payment-methods/${pmId}`,
|
|
@@ -1229,14 +1099,12 @@ function registerDisableCommand(parent, deps) {
|
|
|
1229
1099
|
"--idempotency-key <key>",
|
|
1230
1100
|
"Idempotency key forwarded verbatim as the Idempotency-Key header"
|
|
1231
1101
|
);
|
|
1232
|
-
attachSchemaHelp(cmd, pmDisableSchema);
|
|
1233
1102
|
cmd.action(async (pmId) => {
|
|
1234
1103
|
const opts = cmd.optsWithGlobals();
|
|
1235
1104
|
const format = resolveFormat(opts.format);
|
|
1236
1105
|
const apiKey = await PromptEngine.resolveInput(opts.apiKey, {
|
|
1237
1106
|
message: "API Key:",
|
|
1238
|
-
type: "password"
|
|
1239
|
-
envVar: "AGENZO_API_KEY"
|
|
1107
|
+
type: "password"
|
|
1240
1108
|
});
|
|
1241
1109
|
let idempotencyKey = opts.idempotencyKey;
|
|
1242
1110
|
if (!idempotencyKey) {
|
|
@@ -1387,15 +1255,13 @@ function registerCreateCommand(parent, deps) {
|
|
|
1387
1255
|
"--idempotency-key <key>",
|
|
1388
1256
|
"Idempotency key forwarded verbatim as the Idempotency-Key header"
|
|
1389
1257
|
);
|
|
1390
|
-
attachSchemaHelp(cmd, ptCreateSchema);
|
|
1391
1258
|
cmd.action(async () => {
|
|
1392
1259
|
const opts = cmd.optsWithGlobals();
|
|
1393
1260
|
const format = resolveFormat(opts.format);
|
|
1394
1261
|
const isYes = Boolean(opts.yes);
|
|
1395
1262
|
const apiKey = await PromptEngine.resolveInput(opts.apiKey, {
|
|
1396
1263
|
message: "API Key:",
|
|
1397
|
-
type: "password"
|
|
1398
|
-
envVar: "AGENZO_API_KEY"
|
|
1264
|
+
type: "password"
|
|
1399
1265
|
});
|
|
1400
1266
|
const cliType = await PromptEngine.resolveInput(opts.type, {
|
|
1401
1267
|
message: "Token type:",
|
|
@@ -1534,7 +1400,7 @@ function registerCreateCommand(parent, deps) {
|
|
|
1534
1400
|
body.member_id = member;
|
|
1535
1401
|
}
|
|
1536
1402
|
if (opts.externalTxId) {
|
|
1537
|
-
body.
|
|
1403
|
+
body.external_tx_id = opts.externalTxId;
|
|
1538
1404
|
}
|
|
1539
1405
|
const extraHeaders = {
|
|
1540
1406
|
"Idempotency-Key": idempotencyKey
|
|
@@ -1620,14 +1486,12 @@ function getSummary(token) {
|
|
|
1620
1486
|
}
|
|
1621
1487
|
function registerListCommand2(parent, deps) {
|
|
1622
1488
|
const cmd = parent.command("list").description("List payment tokens").option("--api-key <key>", "API Key for authentication").option("--type <type>", "Filter by token type").option("--member <member_id>", "Filter by member ID");
|
|
1623
|
-
attachSchemaHelp(cmd, ptListSchema);
|
|
1624
1489
|
cmd.action(async () => {
|
|
1625
1490
|
const opts = cmd.optsWithGlobals();
|
|
1626
1491
|
const format = resolveFormat(opts.format);
|
|
1627
1492
|
const apiKey = await PromptEngine.resolveInput(opts.apiKey, {
|
|
1628
1493
|
message: "API Key:",
|
|
1629
|
-
type: "password"
|
|
1630
|
-
envVar: "AGENZO_API_KEY"
|
|
1494
|
+
type: "password"
|
|
1631
1495
|
});
|
|
1632
1496
|
const params = {};
|
|
1633
1497
|
if (opts.type) {
|
|
@@ -1758,14 +1622,12 @@ function formatCentsPlain(cents) {
|
|
|
1758
1622
|
}
|
|
1759
1623
|
function registerGetCommand2(parent, deps) {
|
|
1760
1624
|
const cmd = parent.command("get <payment_token_id>").description("Get a payment token by ID").option("--api-key <key>", "API key for authentication").option("--reveal", "Reveal full VCN card number and CVC in the output");
|
|
1761
|
-
attachSchemaHelp(cmd, ptGetSchema);
|
|
1762
1625
|
cmd.action(async (paymentTokenId) => {
|
|
1763
1626
|
const opts = cmd.optsWithGlobals();
|
|
1764
1627
|
const format = resolveFormat(opts.format);
|
|
1765
1628
|
const apiKey = await PromptEngine.resolveInput(opts.apiKey, {
|
|
1766
1629
|
message: "API Key:",
|
|
1767
|
-
type: "password"
|
|
1768
|
-
envVar: "AGENZO_API_KEY"
|
|
1630
|
+
type: "password"
|
|
1769
1631
|
});
|
|
1770
1632
|
const result = await deps.apiClient.get(
|
|
1771
1633
|
`/payment-tokens/${paymentTokenId}`,
|
|
@@ -1791,14 +1653,12 @@ function registerRevokeCommand(parent, deps) {
|
|
|
1791
1653
|
"--idempotency-key <key>",
|
|
1792
1654
|
"Idempotency key forwarded verbatim as the Idempotency-Key header"
|
|
1793
1655
|
);
|
|
1794
|
-
attachSchemaHelp(cmd, ptRevokeSchema);
|
|
1795
1656
|
cmd.action(async (paymentTokenId) => {
|
|
1796
1657
|
const opts = cmd.optsWithGlobals();
|
|
1797
1658
|
const format = resolveFormat(opts.format);
|
|
1798
1659
|
const apiKey = await PromptEngine.resolveInput(opts.apiKey, {
|
|
1799
1660
|
message: "API Key:",
|
|
1800
|
-
type: "password"
|
|
1801
|
-
envVar: "AGENZO_API_KEY"
|
|
1661
|
+
type: "password"
|
|
1802
1662
|
});
|
|
1803
1663
|
let idempotencyKey = opts.idempotencyKey;
|
|
1804
1664
|
if (!idempotencyKey) {
|
|
@@ -1867,7 +1727,7 @@ async function main() {
|
|
|
1867
1727
|
const program = new Command();
|
|
1868
1728
|
programRef = program;
|
|
1869
1729
|
program.name("agenzo-token-cli").version(getCurrentVersion()).description(
|
|
1870
|
-
"Agenzo token plane: payment methods (
|
|
1730
|
+
"Agenzo token plane: payment methods (add payment method + 3DS) and payment tokens (VCN / Network Token / X402)"
|
|
1871
1731
|
).option("--verbose", "Show verbose logs").option("--yes", "Skip confirmation prompts (for automation/AI Agents)").option(
|
|
1872
1732
|
"--format <format>",
|
|
1873
1733
|
"Output format: json | table (default: table; or set AGENZO_FORMAT)"
|
|
@@ -1886,54 +1746,6 @@ async function main() {
|
|
|
1886
1746
|
registerListCommand2(ptCmd, deps);
|
|
1887
1747
|
registerGetCommand2(ptCmd, deps);
|
|
1888
1748
|
registerRevokeCommand(ptCmd, deps);
|
|
1889
|
-
const svcCmd = program.command("services").description("Token service discovery");
|
|
1890
|
-
svcCmd.command("list").description("List available token services from platform catalog").option("--api-key <key>", "API Key for authentication").action(async () => {
|
|
1891
|
-
const opts = svcCmd.parent.optsWithGlobals();
|
|
1892
|
-
const format = resolveFormat(opts.format);
|
|
1893
|
-
const apiKey = await PromptEngine.resolveInput(opts.apiKey, {
|
|
1894
|
-
message: "API Key:",
|
|
1895
|
-
type: "password",
|
|
1896
|
-
envVar: "AGENZO_API_KEY"
|
|
1897
|
-
});
|
|
1898
|
-
const host = (await configManager.load()).api_host || "http://localhost:8001";
|
|
1899
|
-
const discoveryClient = new ApiClient({ baseUrl: `${host}/api/discovery/v1` });
|
|
1900
|
-
const result = await discoveryClient.get(
|
|
1901
|
-
"/catalog",
|
|
1902
|
-
{ type: "api-key", key: apiKey },
|
|
1903
|
-
{ category: "payment" }
|
|
1904
|
-
);
|
|
1905
|
-
if (!result.success) {
|
|
1906
|
-
throw CliError.fromApi(result, { auth: "api-key" });
|
|
1907
|
-
}
|
|
1908
|
-
const cmdResult = {
|
|
1909
|
-
text: () => JSON.stringify(result.data, null, 2),
|
|
1910
|
-
data: result.data
|
|
1911
|
-
};
|
|
1912
|
-
await renderWithContext(cmdResult, { format }, configManager);
|
|
1913
|
-
});
|
|
1914
|
-
svcCmd.command("get <service_id>").description("Get a token service capability by ID").option("--api-key <key>", "API Key for authentication").action(async (serviceId) => {
|
|
1915
|
-
const opts = svcCmd.parent.optsWithGlobals();
|
|
1916
|
-
const format = resolveFormat(opts.format);
|
|
1917
|
-
const apiKey = await PromptEngine.resolveInput(opts.apiKey, {
|
|
1918
|
-
message: "API Key:",
|
|
1919
|
-
type: "password",
|
|
1920
|
-
envVar: "AGENZO_API_KEY"
|
|
1921
|
-
});
|
|
1922
|
-
const host2 = (await configManager.load()).api_host || "http://localhost:8001";
|
|
1923
|
-
const discoveryClient2 = new ApiClient({ baseUrl: `${host2}/api/discovery/v1` });
|
|
1924
|
-
const result = await discoveryClient2.get(
|
|
1925
|
-
`/catalog/${serviceId}`,
|
|
1926
|
-
{ type: "api-key", key: apiKey }
|
|
1927
|
-
);
|
|
1928
|
-
if (!result.success) {
|
|
1929
|
-
throw CliError.fromApi(result, { auth: "api-key" });
|
|
1930
|
-
}
|
|
1931
|
-
const cmdResult = {
|
|
1932
|
-
text: () => JSON.stringify(result.data, null, 2),
|
|
1933
|
-
data: result.data
|
|
1934
|
-
};
|
|
1935
|
-
await renderWithContext(cmdResult, { format }, configManager);
|
|
1936
|
-
});
|
|
1937
1749
|
await program.parseAsync(process.argv);
|
|
1938
1750
|
}
|
|
1939
1751
|
function resolveActiveFormat() {
|