129 lines
5.2 KiB
Python
129 lines
5.2 KiB
Python
#!/usr/bin/env python3
|
||
"""Ватчер Alipay на live-счёте Stripe (эскалация e-261005142309-390896).
|
||
|
||
Контекст: Alex включил тумблер Alipay в дашборде (preference=on), но
|
||
capability ещё not available — счёт ждёт аппрува Stripe («говорит что нужен
|
||
ещё аппрувал, ждем», m-516173789). Включить через API нельзя (rk_live
|
||
restricted, available=false).
|
||
|
||
Этот скрипт — детерминированный поллинг без LLM (systemd-таймер, 5 мин):
|
||
- пока alipay.available=false → тихо выходим (state=waiting);
|
||
- как только Stripe активирует capability → создаёт Checkout Session
|
||
$100 one_time (price_1UNCYGBT53G7FGSQya04h28r) с card+alipay и
|
||
идемпотентно (cli.py tg-notify -k) отправляет ссылку в чат;
|
||
- после успешной доставки пишет done-маркер и больше не работает.
|
||
|
||
Ключ читается из /root/coop/.env (STRIPE_LIVE_KEY, реестр KEYS-REGISTRY).
|
||
Состояние: /var/lib/simbiot-harness/alipay-watch.state.
|
||
Выход: 0 = ждём/отработал, 3 = ошибка конфигурации (для журнала).
|
||
"""
|
||
import json
|
||
import os
|
||
import subprocess
|
||
import sys
|
||
import urllib.error
|
||
import urllib.parse
|
||
import urllib.request
|
||
|
||
STATE = "/var/lib/simbiot-harness/alipay-watch.state"
|
||
ENV_FILE = "/root/coop/.env"
|
||
PRICE = "price_1UNCYGBT53G7FGSQya04h28r" # $100 USD one_time, live
|
||
NOTIFY_KEY = "alipay-live-100-link" # идемпотентность доставки (t-260930083937)
|
||
HARNESS = "/root/simbiot-harness"
|
||
VENV_PY = f"{HARNESS}/venv/bin/python"
|
||
|
||
|
||
def _stripe_key() -> str | None:
|
||
try:
|
||
with open(ENV_FILE) as f:
|
||
for line in f:
|
||
line = line.strip()
|
||
if line.startswith("STRIPE_LIVE_KEY="):
|
||
return line.split("=", 1)[1].strip().strip('"').strip("'")
|
||
except OSError:
|
||
pass
|
||
return os.environ.get("STRIPE_LIVE_KEY")
|
||
|
||
|
||
def _call(path: str, method: str = "GET", data: dict | None = None) -> dict:
|
||
key = _stripe_key()
|
||
if not key:
|
||
return {"_error": "no STRIPE_LIVE_KEY"}
|
||
body = urllib.parse.urlencode(data).encode() if data is not None else None
|
||
req = urllib.request.Request(
|
||
"https://api.stripe.com/v1/" + path,
|
||
headers={"Authorization": f"Bearer {key}"},
|
||
method=method,
|
||
data=body,
|
||
)
|
||
try:
|
||
with urllib.request.urlopen(req, timeout=25) as r:
|
||
return json.loads(r.read())
|
||
except urllib.error.HTTPError as e:
|
||
return {"_error": e.code, "body": e.read().decode()[:400]}
|
||
|
||
|
||
def _read_state() -> str:
|
||
try:
|
||
with open(STATE) as f:
|
||
return f.read().strip()
|
||
except OSError:
|
||
return ""
|
||
|
||
|
||
def _write_state(s: str) -> None:
|
||
os.makedirs(os.path.dirname(STATE), exist_ok=True)
|
||
with open(STATE, "w") as f:
|
||
f.write(s)
|
||
|
||
|
||
def main() -> int:
|
||
if _read_state() == "done":
|
||
return 0 # ссылка создана и доставлена — ватчер отработал
|
||
|
||
cfgs = _call("payment_method_configurations?limit=100")
|
||
for c in cfgs.get("data", []):
|
||
alipay = c.get("alipay") or {}
|
||
if alipay.get("available"):
|
||
# capability активна — создаём линк card+alipay
|
||
sess = _call("checkout/sessions", "POST", {
|
||
"mode": "payment",
|
||
"line_items[0][price]": PRICE,
|
||
"line_items[0][quantity]": "1",
|
||
"success_url": "https://cil.by/thanks",
|
||
"cancel_url": "https://cil.by/",
|
||
"payment_method_types[0]": "card",
|
||
"payment_method_types[1]": "alipay",
|
||
})
|
||
url = sess.get("url")
|
||
if not url:
|
||
# доступна, но сессия не собирается — оставить ждущим,
|
||
# причина в журнале таймера; следующий прогон повторит
|
||
_write_state("available-but-failed")
|
||
print("alipay available, session failed:",
|
||
sess.get("_error"), sess.get("body", "")[:200])
|
||
return 0
|
||
text = (f"Alipay активирован на live-счёте Stripe. "
|
||
f"Боевая ссылка $100 (card + Alipay): {url}")
|
||
r = subprocess.run(
|
||
[VENV_PY, f"{HARNESS}/cli.py", "tg-notify", text,
|
||
"-k", NOTIFY_KEY],
|
||
capture_output=True, text=True, timeout=60,
|
||
)
|
||
out = (r.stdout or "").strip()
|
||
print("notify:", out)
|
||
if out.startswith("OK") or "SKIP" in out:
|
||
_write_state("done")
|
||
return 0
|
||
_write_state("notify-failed")
|
||
return 0
|
||
print(f"waiting: alipay preference={alipay.get('display_preference', {}).get('value')} "
|
||
f"available={alipay.get('available')} (cfg {c.get('id')})")
|
||
_write_state("waiting")
|
||
return 0
|
||
print("no live payment_method_configuration found:", cfgs.get("_error", "?"))
|
||
return 3
|
||
|
||
|
||
if __name__ == "__main__":
|
||
sys.exit(main())
|