@stripe/link-cli 0.15.0 → 0.16.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.
Files changed (3) hide show
  1. package/README.md +18 -0
  2. package/dist/cli.js +38 -19
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -310,6 +310,24 @@ card SpendRequest instead; do not create an LPT request.
310
310
  | Rolling creation rate | 200 per 60 days |
311
311
 
312
312
 
313
+ Use `mpp pay` to complete purchases on merchants that use the [Machine Payments Protocol](https://mpp.dev). The spend request must use `credential_type: "shared_payment_token"` and you must approve it before paying. The SPT is one-time-use — if payment fails, create a new spend request.
314
+
315
+ ```bash
316
+ link-cli mpp pay https://climate.stripe.dev/api/contribute \
317
+ --spend-request-id lsrq_001 \
318
+ --method POST \
319
+ --data '{"amount":100}' \
320
+ --header "X-Custom: value"
321
+ ```
322
+
323
+ In agent mode (`--format json`), the full flow returns the payment continuation twice: as `_next.pay_argv` (`{ "command": "mpp", "args": [...] }`) and as `_next.pay_command`. Prefer `pay_argv` and invoke it directly, passing each `args` entry as its own process argument. The URL, body and headers can carry merchant-controlled text, so `pay_command` is shell-quoted for callers that must go through a shell — pass it to the shell verbatim, without unquoting or re-splitting it.
324
+
325
+ Use `mpp decode` to validate a raw `WWW-Authenticate` header and extract the `network_id` needed for `shared_payment_token` spend requests:
326
+
327
+ ```bash
328
+ link-cli mpp decode \
329
+ --challenge 'Payment id="ch_001", realm="merchant.example", method="stripe", intent="charge", request="..."'
330
+ ```
313
331
 
314
332
  ### Report outcomes
315
333
 
package/dist/cli.js CHANGED
@@ -593,7 +593,9 @@ var BalancesResource = class extends BaseResource {
593
593
  var paymentMethodSchema = z2.looseObject({
594
594
  id: z2.string(),
595
595
  type: z2.string(),
596
- is_default: z2.boolean()
596
+ is_default: z2.boolean(),
597
+ name: z2.string(),
598
+ nickname: z2.optional(z2.string().nullable())
597
599
  });
598
600
  var paymentMethodsResponseSchema = z2.looseObject({
599
601
  payment_details: z2.array(paymentMethodSchema)
@@ -649,7 +651,7 @@ var ReportResource = class extends BaseResource {
649
651
  var shippingAddressSchema = z4.looseObject({
650
652
  id: z4.string(),
651
653
  is_default: z4.boolean(),
652
- nickname: z4.string().nullable(),
654
+ nickname: z4.optional(z4.string().nullable()),
653
655
  address: z4.looseObject({}).nullable()
654
656
  });
655
657
  var shippingAddressesResponseSchema = z4.looseObject({
@@ -2799,7 +2801,7 @@ function buildHeaders(data, headers) {
2799
2801
  if (key) result[key] = value;
2800
2802
  }
2801
2803
  if (!Object.keys(result).some((key) => key.toLowerCase() === "user-agent")) {
2802
- result["User-Agent"] = `link-cli/${"0.15.0"}`;
2804
+ result["User-Agent"] = `link-cli/${"0.16.0"}`;
2803
2805
  }
2804
2806
  return result;
2805
2807
  }
@@ -3658,6 +3660,21 @@ function createDemoCli(authRepo2, spendRequestRepo2, createPaymentMethodsResourc
3658
3660
  // src/commands/mpp/index.tsx
3659
3661
  import { Cli as Cli4, z as z14 } from "incur";
3660
3662
 
3663
+ // src/utils/shell-quote.ts
3664
+ var SAFE_RE = /^[A-Za-z0-9_@%+=:,./-]+$/;
3665
+ function shellQuote(value) {
3666
+ if (value === "") {
3667
+ return "''";
3668
+ }
3669
+ if (SAFE_RE.test(value)) {
3670
+ return value;
3671
+ }
3672
+ return `'${value.replaceAll("'", `'\\''`)}'`;
3673
+ }
3674
+ function shellCommand(parts) {
3675
+ return parts.map(shellQuote).join(" ");
3676
+ }
3677
+
3661
3678
  // src/commands/mpp/decode-view.tsx
3662
3679
  import { Box as Box10, Text as Text12 } from "ink";
3663
3680
  import { jsx as jsx14, jsxs as jsxs10 } from "react/jsx-runtime";
@@ -3829,20 +3846,22 @@ function createMppCli(repository, paymentMethodsFactory, authStorage2, envAccess
3829
3846
  request_approval: true,
3830
3847
  test: opts.test || void 0
3831
3848
  });
3832
- const nextFlags = [`--spend-request-id ${spendRequest.id}`];
3833
- if (method) nextFlags.push(`-X ${method}`);
3834
- if (data) nextFlags.push(`-d '${data}'`);
3849
+ const nextArgs = ["pay", url, "--spend-request-id", spendRequest.id];
3850
+ if (method) nextArgs.push("-X", method);
3851
+ if (data) nextArgs.push("-d", data);
3835
3852
  if (headers) {
3836
- for (const h of headers) nextFlags.push(`-H '${h}'`);
3853
+ for (const h of headers) nextArgs.push("-H", h);
3837
3854
  }
3838
- const nextCommand = `mpp pay ${url} ${nextFlags.join(" ")}`;
3855
+ const nextCommand = `mpp ${shellCommand(nextArgs)}`;
3856
+ const pollCommand = `spend-request retrieve ${shellQuote(spendRequest.id)} --interval 2 --max-attempts 300`;
3839
3857
  yield {
3840
3858
  ...spendRequest,
3841
- instruction: `Present the approval_url to the user and ask them to approve in the Link app. Then call \`spend-request retrieve ${spendRequest.id} --interval 2 --max-attempts 300\` to poll until approved. Once approved, run the _next.command to complete payment. Do not wait for the user to reply \u2014 start polling immediately.`,
3859
+ instruction: `Present the approval_url to the user and ask them to approve in the Link app. Then call \`${pollCommand}\` to poll until approved. Once approved, run _next.pay_argv (preferred \u2014 invoke it directly without a shell) or _next.pay_command to complete payment. Do not wait for the user to reply \u2014 start polling immediately.`,
3842
3860
  _next: {
3843
- poll_command: `spend-request retrieve ${spendRequest.id} --interval 2 --max-attempts 300`,
3861
+ poll_command: pollCommand,
3844
3862
  pay_command: nextCommand,
3845
- until: "status changes from pending_approval, then run pay_command"
3863
+ pay_argv: { command: "mpp", args: nextArgs },
3864
+ until: "status changes from pending_approval, then run pay_argv"
3846
3865
  }
3847
3866
  };
3848
3867
  }
@@ -4094,7 +4113,7 @@ var PaymentMethodsList = ({
4094
4113
  return /* @__PURE__ */ jsxs13(Box13, { flexDirection: "column", children: [
4095
4114
  /* @__PURE__ */ jsx19(Text15, { bold: true, children: "Payment Methods" }),
4096
4115
  /* @__PURE__ */ jsx19(Box13, { flexDirection: "column", marginTop: 1, children: methods.map((pm) => {
4097
- const label = pm.card_details?.brand ?? pm.bank_account_details?.bank_name ?? "Bank account";
4116
+ const label = pm.name ?? pm.card_details?.brand ?? pm.bank_account_details?.bank_name ?? "Bank account";
4098
4117
  const last4 = pm.card_details?.last4 ?? pm.bank_account_details?.last4;
4099
4118
  const suffix = pm.nickname ? `(${pm.nickname})` : "";
4100
4119
  const agenticCap = pm.capabilities?.agentic_payments;
@@ -6243,9 +6262,9 @@ function buildRequiresActionResult(request) {
6243
6262
  const isAutoResume2 = nextAction?.resolution === "auto_resume";
6244
6263
  return {
6245
6264
  ...request,
6246
- instruction: isAutoResume2 ? `The spend request requires 3D Secure verification. Present action_url (${nextAction?.action_url}) to the user, then call \`spend-request retrieve ${request.id} --interval 2 --max-attempts 300\` to poll until it resolves. Do not create a new spend request \u2014 this one resumes automatically once the challenge is completed.` : `The spend request requires action (${nextAction?.type}): ${nextAction?.display_message}${nextAction?.action_url ? ` URL: ${nextAction.action_url}` : ""} Have the user complete this, then create a new spend request.`,
6265
+ instruction: isAutoResume2 ? `The spend request requires 3D Secure verification. Present action_url (${nextAction?.action_url}) to the user, then call \`spend-request retrieve ${shellQuote(request.id)} --interval 2 --max-attempts 300\` to poll until it resolves. Do not create a new spend request \u2014 this one resumes automatically once the challenge is completed.` : `The spend request requires action (${nextAction?.type}): ${nextAction?.display_message}${nextAction?.action_url ? ` URL: ${nextAction.action_url}` : ""} Have the user complete this, then create a new spend request.`,
6247
6266
  _next: isAutoResume2 ? {
6248
- command: `spend-request retrieve ${request.id} --interval 2 --max-attempts 300`,
6267
+ command: `spend-request retrieve ${shellQuote(request.id)} --interval 2 --max-attempts 300`,
6249
6268
  until: "status changes from requires_action"
6250
6269
  } : void 0
6251
6270
  };
@@ -6503,9 +6522,9 @@ function createSpendRequestCli(repository, authStorage2, envAccessToken2) {
6503
6522
  }
6504
6523
  yield {
6505
6524
  ...created,
6506
- instruction: `Present the approval_url to the user and ask them to approve in the Link app. Then call \`spend-request retrieve ${created.id} --interval 2 --max-attempts 300\` to poll until approved. Do not wait for the user to reply \u2014 start polling immediately.`,
6525
+ instruction: `Present the approval_url to the user and ask them to approve in the Link app. Then call \`spend-request retrieve ${shellQuote(created.id)} --interval 2 --max-attempts 300\` to poll until approved. Do not wait for the user to reply \u2014 start polling immediately.`,
6507
6526
  _next: {
6508
- command: `spend-request retrieve ${created.id} --interval 2 --max-attempts 300`,
6527
+ command: `spend-request retrieve ${shellQuote(created.id)} --interval 2 --max-attempts 300`,
6509
6528
  until: "status changes from pending_approval"
6510
6529
  }
6511
6530
  };
@@ -6616,9 +6635,9 @@ function createSpendRequestCli(repository, authStorage2, envAccessToken2) {
6616
6635
  }
6617
6636
  yield {
6618
6637
  ...approval,
6619
- instruction: `Present the approval_url to the user and ask them to approve in the Link app. Then call \`spend-request retrieve ${id} --interval 2 --max-attempts 300\` to poll until approved. Do not wait for the user to reply \u2014 start polling immediately.`,
6638
+ instruction: `Present the approval_url to the user and ask them to approve in the Link app. Then call \`spend-request retrieve ${shellQuote(id)} --interval 2 --max-attempts 300\` to poll until approved. Do not wait for the user to reply \u2014 start polling immediately.`,
6620
6639
  _next: {
6621
- command: `spend-request retrieve ${id} --interval 2 --max-attempts 300`,
6640
+ command: `spend-request retrieve ${shellQuote(id)} --interval 2 --max-attempts 300`,
6622
6641
  until: "status changes from pending_approval"
6623
6642
  }
6624
6643
  };
@@ -7580,7 +7599,7 @@ function cacheUpdateInfo(value, ttlMs = UPDATE_CACHE_TTL_MS) {
7580
7599
  }
7581
7600
 
7582
7601
  // src/cli.tsx
7583
- var cliVersion = "0.15.0";
7602
+ var cliVersion = "0.16.0";
7584
7603
  var cliName = "@stripe/link-cli";
7585
7604
  var defaultHeaders = {
7586
7605
  "User-Agent": `link-cli/${cliVersion}`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stripe/link-cli",
3
- "version": "0.15.0",
3
+ "version": "0.16.0",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "link-cli": "./dist/cli.js"
@@ -45,7 +45,7 @@
45
45
  "tsx": "^4.23.5",
46
46
  "typescript": "^5.9.3",
47
47
  "vitest": "^4.1.10",
48
- "@stripe/link-sdk": "0.2.0",
48
+ "@stripe/link-sdk": "0.2.1",
49
49
  "@stripe/link-typescript-config": "0.0.0"
50
50
  },
51
51
  "scripts": {