@agenzo/token-cli 0.3.1 → 0.3.3
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 +304 -113
- package/dist/index.js.map +1 -1
- package/package.json +5 -1
package/README.md
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
# @agenzo/token-cli
|
|
2
2
|
|
|
3
|
+
[](https://www.npmjs.com/package/@agenzo/token-cli) [](../../LICENSE) 
|
|
4
|
+
|
|
3
5
|
> The **payment credential CLI** (runtime plane) for the Agenzo platform. Agents use it to manage payment methods (card details + 3DS verification) and to issue payment tokens (VCN / Network Token / X402) before a transaction.
|
|
4
6
|
|
|
7
|
+
**[Installation](#installation)** · **[Authentication](#authentication)** · **[Commands](#command-matrix)** · **[payment-methods](#payment-methods)** · **[payment-tokens](#payment-tokens)** · **[Errors](#output-and-errors)**
|
|
8
|
+
|
|
5
9
|
Binary: `agenzo-token-cli` | Auth: API Key (`X-Api-Key`)
|
|
6
10
|
|
|
7
11
|
## Installation
|
|
@@ -10,7 +14,7 @@ Binary: `agenzo-token-cli` | Auth: API Key (`X-Api-Key`)
|
|
|
10
14
|
npm install -g @agenzo/token-cli
|
|
11
15
|
```
|
|
12
16
|
|
|
13
|
-
The `agenzo-token-cli` command is available after installation. Requires Node.js ≥
|
|
17
|
+
The `agenzo-token-cli` command is available after installation. Requires Node.js ≥ 22. Upgrade later with `npm install -g @agenzo/token-cli@latest`.
|
|
14
18
|
|
|
15
19
|
## Authentication
|
|
16
20
|
|
|
@@ -30,7 +34,7 @@ agenzo-admin-cli keys create --developer-id <dev_id> --key-name "Prod Key" --sco
|
|
|
30
34
|
token-cli reuses the environment configuration (API host / path) written by admin-cli; it has no environment-management commands of its own. The default target is production `https://agent.everonet.com`. Switch environments via admin-cli:
|
|
31
35
|
|
|
32
36
|
```bash
|
|
33
|
-
agenzo-admin-cli config set-host https://agent-
|
|
37
|
+
agenzo-admin-cli config set-host https://agent-dev.agenzo.com # switch to the test environment
|
|
34
38
|
agenzo-admin-cli config show # show current host / path
|
|
35
39
|
```
|
|
36
40
|
|
|
@@ -38,8 +42,8 @@ agenzo-admin-cli config show # show curren
|
|
|
38
42
|
|
|
39
43
|
| Option | Description |
|
|
40
44
|
|---|---|
|
|
41
|
-
| `--format <json\|table>` | Output format.
|
|
42
|
-
| `--yes` | Skip all confirmation prompts (
|
|
45
|
+
| `--format <json\|table>` | Output format. Defaults to `table`; pass `json` (or set `AGENZO_FORMAT=json`) for machine-readable output |
|
|
46
|
+
| `--yes` | Skip all confirmation prompts (non-interactive automation) |
|
|
43
47
|
| `--verbose` | Print verbose logs to stderr |
|
|
44
48
|
| `--version` | Print the CLI version |
|
|
45
49
|
|
|
@@ -49,7 +53,7 @@ agenzo-admin-cli config show # show curren
|
|
|
49
53
|
|
|
50
54
|
| Noun | Verb | Purpose | Write/Read |
|
|
51
55
|
|---|---|---|---|
|
|
52
|
-
| `payment-methods` | `add` | Add a payment method
|
|
56
|
+
| `payment-methods` | `add` | Add a payment method via `--mode manual` (collect card + 3DS, default) or `--mode dropin` (Drop-in session), then auto-poll verification | W |
|
|
53
57
|
| `payment-methods` | `list` | List payment methods under the current API Key | R |
|
|
54
58
|
| `payment-methods` | `get <pm_id>` | Show details of a single payment method | R |
|
|
55
59
|
| `payment-methods` | `disable <pm_id>` | Disable a payment method (cascades to revoke its issued tokens) | W |
|
|
@@ -60,7 +64,14 @@ agenzo-admin-cli config show # show curren
|
|
|
60
64
|
|
|
61
65
|
## payment-methods
|
|
62
66
|
|
|
63
|
-
### add — add a payment method
|
|
67
|
+
### add — add a payment method
|
|
68
|
+
|
|
69
|
+
Two modes, selected with `--mode`:
|
|
70
|
+
|
|
71
|
+
- **`manual`** (default): the CLI collects card details and polls 3DS verification.
|
|
72
|
+
- **`dropin`**: the CLI mints a Drop-in session and polls until the user finishes adding the payment method in their browser — no card details are entered at the terminal.
|
|
73
|
+
|
|
74
|
+
#### Manual mode (default)
|
|
64
75
|
|
|
65
76
|
```bash
|
|
66
77
|
agenzo-token-cli payment-methods add \
|
|
@@ -75,14 +86,33 @@ agenzo-token-cli payment-methods add \
|
|
|
75
86
|
| Flag | Required | Description |
|
|
76
87
|
|---|---|---|
|
|
77
88
|
| `--api-key` | Yes | Prompted interactively when omitted |
|
|
89
|
+
| `--mode` | No | `manual` (default) or `dropin` |
|
|
78
90
|
| `--type` | No | Payment method type, defaults to `card` |
|
|
79
91
|
| `--email` | Yes | Used to deliver the 3DS email challenge |
|
|
80
92
|
| `--card-number` | Yes | Card number |
|
|
81
93
|
| `--expiry` | Yes | Expiry date in `MMYY` format (note: not `MM/YY`) |
|
|
82
94
|
| `--cvv` | Yes | CVV; piping via stdin is recommended to keep it out of shell history |
|
|
95
|
+
| `--idempotency-key` | Yes (in `--yes`) | Forwarded verbatim as the `Idempotency-Key` header |
|
|
83
96
|
|
|
84
97
|
Returns a `PM ID` with `PENDING` status immediately, then auto-polls 3DS verification (3s interval, 15 min timeout). On success it prints `ACTIVE` status plus card brand, first six and last four. On timeout it suggests continuing with `payment-methods get`.
|
|
85
98
|
|
|
99
|
+
#### Drop-in mode
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
agenzo-token-cli payment-methods add \
|
|
103
|
+
--api-key <api_key> \
|
|
104
|
+
--mode dropin \
|
|
105
|
+
--email user@example.com
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
| Flag | Required | Description |
|
|
109
|
+
|---|---|---|
|
|
110
|
+
| `--api-key` | Yes | Prompted interactively when omitted |
|
|
111
|
+
| `--mode` | Yes | Set to `dropin` |
|
|
112
|
+
| `--email` | Yes | Reference for the Drop-in session |
|
|
113
|
+
|
|
114
|
+
Mints a Drop-in session and prints a `Session ID`. Initialise the add-payment UI in your own front-end with that `Session ID` (the user enters card details and completes verification in the browser). The CLI then polls the same verification endpoint (5s interval, 30 min timeout) and prints `ACTIVE` with brand / first six / last four on success. If the payment method is not added it reports `FAILED` / `EXPIRED` (or a 30-minute timeout) with the `PM ID` and exits non-zero — re-run with the same email to resume. Card flags (`--card-number` / `--expiry` / `--cvv`) and `--idempotency-key` are not used in this mode.
|
|
115
|
+
|
|
86
116
|
### list
|
|
87
117
|
|
|
88
118
|
```bash
|
|
@@ -170,7 +200,7 @@ Revokes immediately and prints `REVOKED` plus the revocation time; X402 tokens u
|
|
|
170
200
|
|
|
171
201
|
## Output and errors
|
|
172
202
|
|
|
173
|
-
- **Success**: `table` mode prints
|
|
203
|
+
- **Success**: `table` mode prints formatted text; `json` mode emits the structured payload to stdout.
|
|
174
204
|
- **Failure**: an error envelope is written to stderr. In `json` mode it is `{ "error": { "code", "code_num", "message", "request_id?" } }`; in `table` mode it is `✗ [<code_num>] <message>`.
|
|
175
205
|
- **Exit codes**: `0` on success, `1`–`5` for different error categories (e.g. user cancellation = `5`).
|
|
176
206
|
|
|
@@ -178,13 +208,4 @@ Common error codes: `KEY_INVALID` (invalid API Key), `KEY_SCOPE_DENIED` (scope l
|
|
|
178
208
|
|
|
179
209
|
## Related
|
|
180
210
|
|
|
181
|
-
- Full field-level specification: internal design doc `architecture-upgrade/v1/cli-design.md` §3.
|
|
182
211
|
- Control plane (login / organizations / developers / API Key management): [`@agenzo/admin-cli`](https://www.npmjs.com/package/@agenzo/admin-cli).
|
|
183
|
-
|
|
184
|
-
## Development
|
|
185
|
-
|
|
186
|
-
```bash
|
|
187
|
-
npm install # install dependencies from the monorepo root
|
|
188
|
-
npm run build # build (tsup, output at dist/index.js)
|
|
189
|
-
npm test # run tests (vitest)
|
|
190
|
-
```
|
package/dist/index.js
CHANGED
|
@@ -697,6 +697,10 @@ var PromptEngine = class {
|
|
|
697
697
|
if (flagValue !== void 0) {
|
|
698
698
|
return flagValue;
|
|
699
699
|
}
|
|
700
|
+
const envKey = process.env.AGENZO_API_KEY;
|
|
701
|
+
if (envKey) {
|
|
702
|
+
return envKey;
|
|
703
|
+
}
|
|
700
704
|
if (config.type === "password") {
|
|
701
705
|
return password({ message: config.message, mask: "*" });
|
|
702
706
|
}
|
|
@@ -744,136 +748,231 @@ async function collectPaymentMethodParams(type, flags) {
|
|
|
744
748
|
}
|
|
745
749
|
|
|
746
750
|
// src/payment-methods/add.ts
|
|
747
|
-
var
|
|
748
|
-
var
|
|
751
|
+
var MANUAL_POLL_INTERVAL_MS = 3e3;
|
|
752
|
+
var MANUAL_POLL_TIMEOUT_MS = 15 * 60 * 1e3;
|
|
753
|
+
var DROPIN_POLL_INTERVAL_MS = 5e3;
|
|
754
|
+
var DROPIN_POLL_TIMEOUT_MS = 30 * 60 * 1e3;
|
|
755
|
+
var TERMINAL_STATUSES = /* @__PURE__ */ new Set(["ACTIVE", "FAILED", "EXPIRED"]);
|
|
749
756
|
function registerAddCommand(parent, deps) {
|
|
750
|
-
const cmd = parent.command("add").description("Add a payment method (
|
|
757
|
+
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(
|
|
758
|
+
"--mode <mode>",
|
|
759
|
+
'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)',
|
|
760
|
+
"manual"
|
|
761
|
+
).option(
|
|
762
|
+
"--email <email>",
|
|
763
|
+
"Manual mode: email for 3DS verification. Dropin mode: email used as the Drop-in session reference."
|
|
764
|
+
).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(
|
|
751
765
|
"--idempotency-key <key>",
|
|
752
|
-
"Idempotency key forwarded verbatim as the Idempotency-Key header"
|
|
766
|
+
"Idempotency key forwarded verbatim as the Idempotency-Key header (manual mode only)"
|
|
753
767
|
);
|
|
754
768
|
cmd.action(async () => {
|
|
755
769
|
const opts = cmd.optsWithGlobals();
|
|
756
770
|
const format = resolveFormat(opts.format);
|
|
757
771
|
const isYes = Boolean(opts.yes);
|
|
758
|
-
const
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
email: opts.email,
|
|
765
|
-
cardNumber: opts.cardNumber,
|
|
766
|
-
expiry: opts.expiry,
|
|
767
|
-
cvv: opts.cvv
|
|
768
|
-
};
|
|
769
|
-
const params = await collectPaymentMethodParams(type, flags);
|
|
770
|
-
let idempotencyKey = opts.idempotencyKey;
|
|
771
|
-
if (!idempotencyKey) {
|
|
772
|
-
if (isYes) {
|
|
773
|
-
throw new IdempotencyKeyRequiredError("payment-methods add");
|
|
774
|
-
}
|
|
775
|
-
idempotencyKey = await PromptEngine.resolveInput(void 0, {
|
|
776
|
-
message: "Idempotency key (unique per write, for safe retry):",
|
|
777
|
-
validate: (v) => v.trim().length > 0 || "Idempotency key is required"
|
|
778
|
-
});
|
|
772
|
+
const mode = String(opts.mode ?? "manual").toLowerCase();
|
|
773
|
+
if (mode !== "manual" && mode !== "dropin") {
|
|
774
|
+
throw new CliError(
|
|
775
|
+
"PARAM_INVALID",
|
|
776
|
+
`Unknown --mode "${opts.mode}". Expected "manual" or "dropin".`
|
|
777
|
+
);
|
|
779
778
|
}
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
const result = await deps.apiClient.post(
|
|
784
|
-
"/payment-methods/create",
|
|
785
|
-
{ type: "api-key", key: apiKey },
|
|
786
|
-
params,
|
|
787
|
-
extraHeaders
|
|
788
|
-
);
|
|
789
|
-
if (!result.success) {
|
|
790
|
-
throw CliError.fromApi(result, { auth: "api-key" });
|
|
779
|
+
if (mode === "dropin") {
|
|
780
|
+
await handleDropinMode(deps, opts, format);
|
|
781
|
+
return;
|
|
791
782
|
}
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
783
|
+
await handleManualMode(deps, opts, format, isYes);
|
|
784
|
+
});
|
|
785
|
+
}
|
|
786
|
+
async function handleManualMode(deps, opts, format, isYes) {
|
|
787
|
+
const apiKey = await PromptEngine.resolveInput(opts.apiKey, {
|
|
788
|
+
message: "API Key:",
|
|
789
|
+
type: "password"
|
|
790
|
+
});
|
|
791
|
+
const type = opts.type || "card";
|
|
792
|
+
const flags = {
|
|
793
|
+
email: opts.email,
|
|
794
|
+
cardNumber: opts.cardNumber,
|
|
795
|
+
expiry: opts.expiry,
|
|
796
|
+
cvv: opts.cvv
|
|
797
|
+
};
|
|
798
|
+
const params = await collectPaymentMethodParams(type, flags);
|
|
799
|
+
let idempotencyKey = opts.idempotencyKey;
|
|
800
|
+
if (!idempotencyKey) {
|
|
801
|
+
if (isYes) {
|
|
802
|
+
throw new IdempotencyKeyRequiredError("payment-methods add");
|
|
803
|
+
}
|
|
804
|
+
idempotencyKey = await PromptEngine.resolveInput(void 0, {
|
|
805
|
+
message: "Idempotency key (unique per write, for safe retry):",
|
|
806
|
+
validate: (v) => v.trim().length > 0 || "Idempotency key is required"
|
|
807
|
+
});
|
|
808
|
+
}
|
|
809
|
+
const extraHeaders = {
|
|
810
|
+
"Idempotency-Key": idempotencyKey
|
|
811
|
+
};
|
|
812
|
+
const result = await deps.apiClient.post(
|
|
813
|
+
"/payment-methods/create",
|
|
814
|
+
{ type: "api-key", key: apiKey },
|
|
815
|
+
params,
|
|
816
|
+
extraHeaders
|
|
817
|
+
);
|
|
818
|
+
if (!result.success) {
|
|
819
|
+
throw CliError.fromApi(result, { auth: "api-key" });
|
|
820
|
+
}
|
|
821
|
+
const pm = result.data;
|
|
822
|
+
notify(format, "success", "Payment method created");
|
|
823
|
+
const createdResult = {
|
|
824
|
+
data: pm,
|
|
825
|
+
text: () => {
|
|
826
|
+
const lines = [
|
|
827
|
+
["ID", pm.id],
|
|
828
|
+
["Type", pm.type],
|
|
829
|
+
["Status", pm.status]
|
|
830
|
+
];
|
|
831
|
+
if (pm.brand) lines.push(["Brand", pm.brand]);
|
|
832
|
+
if (pm.first6) lines.push(["First 6", pm.first6]);
|
|
833
|
+
if (pm.last4) lines.push(["Last 4", pm.last4]);
|
|
834
|
+
return Formatter.keyValue(lines);
|
|
835
|
+
}
|
|
836
|
+
};
|
|
837
|
+
const configManager = new ConfigManager();
|
|
838
|
+
await renderWithContext(createdResult, { format }, configManager);
|
|
839
|
+
notify(format, "info", "Complete 3DS verification via email to activate");
|
|
840
|
+
if (type === "card" && pm.status === "PENDING") {
|
|
841
|
+
const finalStatus = await poll3dsVerification(deps.apiClient, apiKey, pm.id, format);
|
|
842
|
+
if (finalStatus === "ACTIVE") {
|
|
843
|
+
const getResult = await deps.apiClient.get(
|
|
844
|
+
`/payment-methods/${pm.id}`,
|
|
845
|
+
{ type: "api-key", key: apiKey }
|
|
817
846
|
);
|
|
818
|
-
if (
|
|
819
|
-
const
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
if (degraded.last4) lines.push(["Last 4", degraded.last4]);
|
|
855
|
-
return Formatter.keyValue(lines);
|
|
856
|
-
}
|
|
857
|
-
};
|
|
858
|
-
await renderWithContext(degradedResult, { format }, configManager);
|
|
859
|
-
}
|
|
860
|
-
} else if (finalStatus === "FAILED") {
|
|
861
|
-
notify(format, "error", "3DS verification failed");
|
|
862
|
-
} else if (finalStatus === "TIMEOUT") {
|
|
863
|
-
notify(
|
|
864
|
-
format,
|
|
865
|
-
"info",
|
|
866
|
-
`Verification timed out (15 min). Check status with: agenzo-token-cli payment-methods get ${pm.id} --api-key <your_key>`
|
|
867
|
-
);
|
|
847
|
+
if (getResult.success) {
|
|
848
|
+
const activatedPm = getResult.data;
|
|
849
|
+
notify(format, "success", "Payment method activated");
|
|
850
|
+
const activatedResult = {
|
|
851
|
+
data: activatedPm,
|
|
852
|
+
text: () => {
|
|
853
|
+
const lines = [
|
|
854
|
+
["ID", activatedPm.id],
|
|
855
|
+
["Type", activatedPm.type],
|
|
856
|
+
["Status", activatedPm.status]
|
|
857
|
+
];
|
|
858
|
+
if (activatedPm.brand) lines.push(["Brand", activatedPm.brand]);
|
|
859
|
+
if (activatedPm.first6) lines.push(["First 6", activatedPm.first6]);
|
|
860
|
+
if (activatedPm.last4) lines.push(["Last 4", activatedPm.last4]);
|
|
861
|
+
return Formatter.keyValue(lines);
|
|
862
|
+
}
|
|
863
|
+
};
|
|
864
|
+
await renderWithContext(activatedResult, { format }, configManager);
|
|
865
|
+
} else {
|
|
866
|
+
notify(format, "success", "Payment method activated");
|
|
867
|
+
const degraded = { ...pm, status: "ACTIVE" };
|
|
868
|
+
const degradedResult = {
|
|
869
|
+
data: degraded,
|
|
870
|
+
text: () => {
|
|
871
|
+
const lines = [
|
|
872
|
+
["ID", degraded.id],
|
|
873
|
+
["Type", degraded.type],
|
|
874
|
+
["Status", degraded.status]
|
|
875
|
+
];
|
|
876
|
+
if (degraded.brand) lines.push(["Brand", degraded.brand]);
|
|
877
|
+
if (degraded.first6) lines.push(["First 6", degraded.first6]);
|
|
878
|
+
if (degraded.last4) lines.push(["Last 4", degraded.last4]);
|
|
879
|
+
return Formatter.keyValue(lines);
|
|
880
|
+
}
|
|
881
|
+
};
|
|
882
|
+
await renderWithContext(degradedResult, { format }, configManager);
|
|
868
883
|
}
|
|
884
|
+
} else if (finalStatus === "FAILED") {
|
|
885
|
+
notify(format, "error", "3DS verification failed");
|
|
886
|
+
} else if (finalStatus === "TIMEOUT") {
|
|
887
|
+
notify(
|
|
888
|
+
format,
|
|
889
|
+
"info",
|
|
890
|
+
`Verification timed out (15 min). Check status with: agenzo-token-cli payment-methods get ${pm.id} --api-key <your_key>`
|
|
891
|
+
);
|
|
869
892
|
}
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
async function handleDropinMode(deps, opts, format) {
|
|
896
|
+
const apiKey = await PromptEngine.resolveInput(opts.apiKey, {
|
|
897
|
+
message: "API Key:",
|
|
898
|
+
type: "password"
|
|
870
899
|
});
|
|
900
|
+
const email = await PromptEngine.resolveInput(opts.email, {
|
|
901
|
+
message: "Email:"
|
|
902
|
+
});
|
|
903
|
+
const configManager = new ConfigManager();
|
|
904
|
+
const sessionResult = await deps.apiClient.post(
|
|
905
|
+
"/payment-methods/dropin/create",
|
|
906
|
+
{ type: "api-key", key: apiKey },
|
|
907
|
+
{ email }
|
|
908
|
+
);
|
|
909
|
+
if (!sessionResult.success) {
|
|
910
|
+
throw CliError.fromApi(sessionResult, { auth: "api-key" });
|
|
911
|
+
}
|
|
912
|
+
const session = sessionResult.data;
|
|
913
|
+
const pmId = session.id;
|
|
914
|
+
notify(format, "success", "Drop-in session created");
|
|
915
|
+
const createdResult = {
|
|
916
|
+
data: session,
|
|
917
|
+
text: () => Formatter.keyValue([["Session ID", session.session_id || "-"]])
|
|
918
|
+
};
|
|
919
|
+
await renderWithContext(createdResult, { format }, configManager);
|
|
920
|
+
notify(
|
|
921
|
+
format,
|
|
922
|
+
"info",
|
|
923
|
+
"Use the Session ID to add the payment method in the browser via the Drop-in SDK"
|
|
924
|
+
);
|
|
925
|
+
const finalPm = await pollVerificationStatus(deps.apiClient, apiKey, pmId, {
|
|
926
|
+
intervalMs: DROPIN_POLL_INTERVAL_MS,
|
|
927
|
+
timeoutMs: DROPIN_POLL_TIMEOUT_MS
|
|
928
|
+
});
|
|
929
|
+
if (finalPm.status === "ACTIVE") {
|
|
930
|
+
notify(format, "success", "Payment method activated");
|
|
931
|
+
const activated = {
|
|
932
|
+
data: finalPm,
|
|
933
|
+
text: () => Formatter.keyValue([
|
|
934
|
+
["PM ID", finalPm.id],
|
|
935
|
+
["Brand", finalPm.brand ?? "-"],
|
|
936
|
+
["First 6", finalPm.first6 ?? "-"],
|
|
937
|
+
["Last 4", finalPm.last4 ?? "-"],
|
|
938
|
+
["Status", finalPm.status]
|
|
939
|
+
])
|
|
940
|
+
};
|
|
941
|
+
await renderWithContext(activated, { format }, configManager);
|
|
942
|
+
return;
|
|
943
|
+
}
|
|
944
|
+
if (finalPm.status === "FAILED") {
|
|
945
|
+
notify(format, "error", "Failed to add payment method");
|
|
946
|
+
await renderPmId(finalPm.id, format, configManager);
|
|
947
|
+
process.exitCode = 1;
|
|
948
|
+
return;
|
|
949
|
+
}
|
|
950
|
+
if (finalPm.status === "EXPIRED") {
|
|
951
|
+
notify(format, "error", "Session expired before the payment method was added");
|
|
952
|
+
await renderPmId(finalPm.id, format, configManager);
|
|
953
|
+
process.exitCode = 1;
|
|
954
|
+
return;
|
|
955
|
+
}
|
|
956
|
+
notify(
|
|
957
|
+
format,
|
|
958
|
+
"error",
|
|
959
|
+
"Adding payment method did not complete within 30 minutes. Re-run with the same email to resume."
|
|
960
|
+
);
|
|
961
|
+
await renderPmId(pmId, format, configManager);
|
|
962
|
+
process.exitCode = 1;
|
|
963
|
+
}
|
|
964
|
+
async function renderPmId(id, format, configManager) {
|
|
965
|
+
const result = {
|
|
966
|
+
data: { id },
|
|
967
|
+
text: () => Formatter.keyValue([["PM ID", id]])
|
|
968
|
+
};
|
|
969
|
+
await renderWithContext(result, { format }, configManager);
|
|
871
970
|
}
|
|
872
971
|
async function poll3dsVerification(apiClient, apiKey, paymentMethodId, format) {
|
|
873
972
|
const startTime = Date.now();
|
|
874
973
|
notify(format, "info", "Waiting for 3DS verification...");
|
|
875
|
-
while (Date.now() - startTime <
|
|
876
|
-
await sleep(
|
|
974
|
+
while (Date.now() - startTime < MANUAL_POLL_TIMEOUT_MS) {
|
|
975
|
+
await sleep(MANUAL_POLL_INTERVAL_MS);
|
|
877
976
|
const result = await apiClient.get(
|
|
878
977
|
"/payment-methods/verification/status",
|
|
879
978
|
{ type: "api-key", key: apiKey },
|
|
@@ -891,6 +990,21 @@ async function poll3dsVerification(apiClient, apiKey, paymentMethodId, format) {
|
|
|
891
990
|
}
|
|
892
991
|
return "TIMEOUT";
|
|
893
992
|
}
|
|
993
|
+
async function pollVerificationStatus(apiClient, apiKey, pmId, options) {
|
|
994
|
+
const startTime = Date.now();
|
|
995
|
+
while (Date.now() - startTime < options.timeoutMs) {
|
|
996
|
+
const result = await apiClient.get(
|
|
997
|
+
"/payment-methods/verification/status",
|
|
998
|
+
{ type: "api-key", key: apiKey },
|
|
999
|
+
{ payment_method_id: pmId }
|
|
1000
|
+
);
|
|
1001
|
+
if (result.success && TERMINAL_STATUSES.has(result.data.status)) {
|
|
1002
|
+
return result.data;
|
|
1003
|
+
}
|
|
1004
|
+
await sleep(options.intervalMs);
|
|
1005
|
+
}
|
|
1006
|
+
return { id: pmId, status: "PENDING" };
|
|
1007
|
+
}
|
|
894
1008
|
function sleep(ms) {
|
|
895
1009
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
896
1010
|
}
|
|
@@ -1032,6 +1146,81 @@ function registerDisableCommand(parent, deps) {
|
|
|
1032
1146
|
});
|
|
1033
1147
|
}
|
|
1034
1148
|
|
|
1149
|
+
// src/payment-methods/dropin-create.ts
|
|
1150
|
+
function registerDropinCreateCommand(parent, deps) {
|
|
1151
|
+
const cmd = parent.command("dropin-create").description("Mint a Drop-in session and return the session id (no polling)").option("--api-key <key>", "API Key for authentication").option("--email <email>", "Email used as the Drop-in session reference");
|
|
1152
|
+
cmd.action(async () => {
|
|
1153
|
+
const opts = cmd.optsWithGlobals();
|
|
1154
|
+
const format = resolveFormat(opts.format);
|
|
1155
|
+
const apiKey = await PromptEngine.resolveInput(opts.apiKey, {
|
|
1156
|
+
message: "API Key:",
|
|
1157
|
+
type: "password"
|
|
1158
|
+
});
|
|
1159
|
+
const email = await PromptEngine.resolveInput(opts.email, {
|
|
1160
|
+
message: "Email:"
|
|
1161
|
+
});
|
|
1162
|
+
const sessionResult = await deps.apiClient.post(
|
|
1163
|
+
"/payment-methods/dropin/create",
|
|
1164
|
+
{ type: "api-key", key: apiKey },
|
|
1165
|
+
{ email }
|
|
1166
|
+
);
|
|
1167
|
+
if (!sessionResult.success) {
|
|
1168
|
+
throw CliError.fromApi(sessionResult, { auth: "api-key" });
|
|
1169
|
+
}
|
|
1170
|
+
const session = sessionResult.data;
|
|
1171
|
+
notify(format, "success", "Drop-in session created");
|
|
1172
|
+
const configManager = new ConfigManager();
|
|
1173
|
+
const result = {
|
|
1174
|
+
data: session,
|
|
1175
|
+
text: () => Formatter.keyValue([
|
|
1176
|
+
["PM ID", session.id],
|
|
1177
|
+
["Session ID", session.session_id || "-"],
|
|
1178
|
+
["Merchant Trans ID", session.merchant_trans_id || "-"],
|
|
1179
|
+
["Status", session.status]
|
|
1180
|
+
])
|
|
1181
|
+
};
|
|
1182
|
+
await renderWithContext(result, { format }, configManager);
|
|
1183
|
+
notify(
|
|
1184
|
+
format,
|
|
1185
|
+
"info",
|
|
1186
|
+
"Use the Session ID to add the payment method via the Drop-in SDK, then poll: agenzo-token-cli payment-methods dropin-status <pm_id> --api-key <your_key>"
|
|
1187
|
+
);
|
|
1188
|
+
});
|
|
1189
|
+
}
|
|
1190
|
+
|
|
1191
|
+
// src/payment-methods/dropin-status.ts
|
|
1192
|
+
function registerDropinStatusCommand(parent, deps) {
|
|
1193
|
+
const cmd = parent.command("dropin-status <pm_id>").description("Query the current Drop-in binding status for a payment method (single check)").option("--api-key <key>", "API Key for authentication");
|
|
1194
|
+
cmd.action(async (pmId) => {
|
|
1195
|
+
const opts = cmd.optsWithGlobals();
|
|
1196
|
+
const format = resolveFormat(opts.format);
|
|
1197
|
+
const apiKey = await PromptEngine.resolveInput(opts.apiKey, {
|
|
1198
|
+
message: "API Key:",
|
|
1199
|
+
type: "password"
|
|
1200
|
+
});
|
|
1201
|
+
const result = await deps.apiClient.get(
|
|
1202
|
+
"/payment-methods/verification/status",
|
|
1203
|
+
{ type: "api-key", key: apiKey },
|
|
1204
|
+
{ payment_method_id: pmId }
|
|
1205
|
+
);
|
|
1206
|
+
if (!result.success) {
|
|
1207
|
+
throw CliError.fromApi(result, { auth: "api-key" });
|
|
1208
|
+
}
|
|
1209
|
+
const pm = result.data;
|
|
1210
|
+
const entries = [["ID", pm.id ?? pmId]];
|
|
1211
|
+
if (pm.brand) entries.push(["Brand", pm.brand]);
|
|
1212
|
+
if (pm.first6) entries.push(["First 6", pm.first6]);
|
|
1213
|
+
if (pm.last4) entries.push(["Last 4", pm.last4]);
|
|
1214
|
+
entries.push(["Status", pm.status]);
|
|
1215
|
+
const configManager = new ConfigManager();
|
|
1216
|
+
const cmdResult = {
|
|
1217
|
+
data: pm,
|
|
1218
|
+
text: () => Formatter.keyValue(entries)
|
|
1219
|
+
};
|
|
1220
|
+
await renderWithContext(cmdResult, { format }, configManager);
|
|
1221
|
+
});
|
|
1222
|
+
}
|
|
1223
|
+
|
|
1035
1224
|
// src/payment-tokens/create.ts
|
|
1036
1225
|
import { confirm, select as select2 } from "@inquirer/prompts";
|
|
1037
1226
|
var DEFAULT_NT_FEE_CENTS = 500;
|
|
@@ -1631,6 +1820,8 @@ async function main() {
|
|
|
1631
1820
|
registerListCommand(pmCmd, deps);
|
|
1632
1821
|
registerGetCommand(pmCmd, deps);
|
|
1633
1822
|
registerDisableCommand(pmCmd, deps);
|
|
1823
|
+
registerDropinCreateCommand(pmCmd, deps);
|
|
1824
|
+
registerDropinStatusCommand(pmCmd, deps);
|
|
1634
1825
|
const ptCmd = program.command("payment-tokens").description("Payment token management");
|
|
1635
1826
|
registerCreateCommand(ptCmd, deps);
|
|
1636
1827
|
registerListCommand2(ptCmd, deps);
|