@agenzo/token-cli 0.3.1 → 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 +223 -113
- package/dist/index.js.map +1 -1
- package/package.json +8 -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
|
@@ -744,136 +744,231 @@ async function collectPaymentMethodParams(type, flags) {
|
|
|
744
744
|
}
|
|
745
745
|
|
|
746
746
|
// src/payment-methods/add.ts
|
|
747
|
-
var
|
|
748
|
-
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"]);
|
|
749
752
|
function registerAddCommand(parent, deps) {
|
|
750
|
-
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(
|
|
751
761
|
"--idempotency-key <key>",
|
|
752
|
-
"Idempotency key forwarded verbatim as the Idempotency-Key header"
|
|
762
|
+
"Idempotency key forwarded verbatim as the Idempotency-Key header (manual mode only)"
|
|
753
763
|
);
|
|
754
764
|
cmd.action(async () => {
|
|
755
765
|
const opts = cmd.optsWithGlobals();
|
|
756
766
|
const format = resolveFormat(opts.format);
|
|
757
767
|
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
|
-
});
|
|
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
|
+
);
|
|
779
774
|
}
|
|
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" });
|
|
775
|
+
if (mode === "dropin") {
|
|
776
|
+
await handleDropinMode(deps, opts, format);
|
|
777
|
+
return;
|
|
791
778
|
}
|
|
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
|
-
|
|
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 }
|
|
817
842
|
);
|
|
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
|
-
);
|
|
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);
|
|
868
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
|
+
);
|
|
869
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"
|
|
895
|
+
});
|
|
896
|
+
const email = await PromptEngine.resolveInput(opts.email, {
|
|
897
|
+
message: "Email:"
|
|
870
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);
|
|
871
966
|
}
|
|
872
967
|
async function poll3dsVerification(apiClient, apiKey, paymentMethodId, format) {
|
|
873
968
|
const startTime = Date.now();
|
|
874
969
|
notify(format, "info", "Waiting for 3DS verification...");
|
|
875
|
-
while (Date.now() - startTime <
|
|
876
|
-
await sleep(
|
|
970
|
+
while (Date.now() - startTime < MANUAL_POLL_TIMEOUT_MS) {
|
|
971
|
+
await sleep(MANUAL_POLL_INTERVAL_MS);
|
|
877
972
|
const result = await apiClient.get(
|
|
878
973
|
"/payment-methods/verification/status",
|
|
879
974
|
{ type: "api-key", key: apiKey },
|
|
@@ -891,6 +986,21 @@ async function poll3dsVerification(apiClient, apiKey, paymentMethodId, format) {
|
|
|
891
986
|
}
|
|
892
987
|
return "TIMEOUT";
|
|
893
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
|
+
}
|
|
894
1004
|
function sleep(ms) {
|
|
895
1005
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
896
1006
|
}
|