mirror of
https://github.com/usestrix/strix.git
synced 2026-10-08 03:08:08 +00:00
feat(cli): pass a Stripe payment method to the top-up wallet client
This commit is contained in:
parent
268769fc45
commit
2df99a2873
3 changed files with 48 additions and 3 deletions
|
|
@ -71,6 +71,8 @@ strix cloud billing topup --credits 20 --yes # --yes skips the confirmation pr
|
|||
strix cloud billing topup --credits 20 --no-pay # print the 402 challenge without paying
|
||||
```
|
||||
|
||||
Card payments need a payer-side Stripe payment method. Pass it with `--payment-method pm_...` or set `MPPX_STRIPE_PAYMENT_METHOD`, together with the payer key in `MPPX_STRIPE_SECRET_KEY`. A funded `mppx` account (`npx mppx account create`) works for stablecoin payments without those variables.
|
||||
|
||||
If no wallet is available, print the challenge with `--no-pay` and ask the user to top up in the dashboard instead.
|
||||
|
||||
Automatic top-ups (admin): `strix cloud billing auto-topup` shows the setting. Enable it with:
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ from __future__ import annotations
|
|||
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
|
|
@ -219,6 +220,15 @@ def _build_parser(group: str, verb_label: str, cmd: Cmd) -> argparse.ArgumentPar
|
|||
action="store_true",
|
||||
help="Print the payment challenge instead of paying it.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--payment-method",
|
||||
default=None,
|
||||
metavar="PM_ID",
|
||||
help=(
|
||||
"Stripe payment method for the card payment, for example pm_card_visa "
|
||||
"in test mode. Defaults to MPPX_STRIPE_PAYMENT_METHOD."
|
||||
),
|
||||
)
|
||||
return parser
|
||||
|
||||
|
||||
|
|
@ -362,8 +372,17 @@ def _topup(
|
|||
|
||||
url = f"{http.app_url()}/api/v1/billing/topup"
|
||||
auth_header = f"Authorization: Bearer {http.api_token(token)}"
|
||||
result = subprocess.run( # noqa: S603
|
||||
[npx, "--yes", "mppx", url, "-J", json.dumps(body), "-H", auth_header],
|
||||
check=False,
|
||||
command = [npx, "--yes", "mppx", url, "-J", json.dumps(body), "-H", auth_header]
|
||||
payment_method = getattr(args, "payment_method", None) or os.environ.get(
|
||||
"MPPX_STRIPE_PAYMENT_METHOD"
|
||||
)
|
||||
if payment_method:
|
||||
command += ["-M", f"paymentMethod={payment_method}"]
|
||||
elif not os.environ.get("MPPX_ACCOUNT") and not os.environ.get("MPPX_STRIPE_SECRET_KEY"):
|
||||
console.print(
|
||||
"[dim]Tip: card payments need a wallet. Pass --payment-method, or set "
|
||||
"MPPX_STRIPE_SECRET_KEY and MPPX_STRIPE_PAYMENT_METHOD, or create an "
|
||||
"mppx account first with `npx mppx account create`.[/]"
|
||||
)
|
||||
result = subprocess.run(command, check=False) # noqa: S603
|
||||
return http.EXIT_OK if result.returncode == 0 else http.EXIT_PAYMENT
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@ from __future__ import annotations
|
|||
|
||||
import io
|
||||
import json
|
||||
import shutil
|
||||
import subprocess
|
||||
import webbrowser
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
|
|
@ -300,6 +302,28 @@ def test_topup_success_without_payment(monkeypatch: pytest.MonkeyPatch, capsys:
|
|||
assert json.loads(capsys.readouterr().out) == receipt
|
||||
|
||||
|
||||
def test_topup_passes_payment_method_to_wallet(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
challenge = {"payment_requirements": [{"amount": 500}]}
|
||||
monkeypatch.setattr(
|
||||
http, "request", lambda *_a, **_k: FakeResponse(status_code=402, payload=challenge)
|
||||
)
|
||||
monkeypatch.setattr(http, "api_token", lambda *_a, **_k: "tok")
|
||||
monkeypatch.setattr(shutil, "which", lambda _name: "/usr/bin/npx")
|
||||
commands: list[list[str]] = []
|
||||
|
||||
def fake_run(command: list[str], **_kwargs: Any) -> Any:
|
||||
commands.append(command)
|
||||
return type("Result", (), {"returncode": 0})()
|
||||
|
||||
monkeypatch.setattr(subprocess, "run", fake_run)
|
||||
code = cloud.run_cloud(
|
||||
["billing", "topup", "--credits", "5", "--yes", "--payment-method", "pm_card_visa"]
|
||||
)
|
||||
assert code == 0
|
||||
assert "-M" in commands[0]
|
||||
assert "paymentMethod=pm_card_visa" in commands[0]
|
||||
|
||||
|
||||
def test_render_json_mode_when_not_a_tty() -> None:
|
||||
assert render.json_mode(flag=True) is True
|
||||
# Under pytest, stdout is captured and is not a terminal.
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue