Files changed (2) hide show
  1. i18n.py +4 -4
  2. managed_auth.py +7 -22
i18n.py CHANGED
@@ -427,7 +427,7 @@ TEXTS: dict[str, dict[str, str]] = {
427
  "payment_channel_label": "本次付款方式",
428
  "payment_channel_alipay": "Alipay",
429
  "payment_channel_wechat": "WeChat",
430
- "payment_scan_dual": "请使用与本次付款方式一致的二维码(当前选择:{channel})。付款后将订单号发送给管理员,核实到账后会增加对应版本额度。",
431
  "payment_notice_product": "购买{tier}:支付 {amount} {currency} 可增加{credits}篇该版本论文额度。",
432
  "payment_paid_product": "付款已确认,已增加{credits}篇{tier}论文额度。现在可以继续生成论文。",
433
  "quota_exhausted": "当前所选版本额度不足,请购买相应版本额度。",
@@ -675,7 +675,7 @@ TEXTS: dict[str, dict[str, str]] = {
675
  "payment_channel_label": "Payment method for this order",
676
  "payment_channel_alipay": "Alipay",
677
  "payment_channel_wechat": "WeChat",
678
- "payment_scan_dual": "Use the QR code that matches the selected payment method (current: {channel}). After payment, send the order number to the administrator; the matching plan credits will be added after receipt is verified.",
679
  "payment_notice_product": "Purchase {tier}: pay {currency} {amount} to add {credits} paper credits for this plan.",
680
  "payment_paid_product": "Payment was confirmed. {credits} {tier} paper credits were added. You can generate again now.",
681
  "quota_exhausted": "The selected plan has no remaining credits. Purchase credits for that plan to continue.",
@@ -923,7 +923,7 @@ TEXTS: dict[str, dict[str, str]] = {
923
  "payment_channel_label": "Способ оплаты для этого заказа",
924
  "payment_channel_alipay": "Alipay",
925
  "payment_channel_wechat": "WeChat",
926
- "payment_scan_dual": "Используйте QR-код, соответствующий выбранному способу оплаты (сейчас: {channel}). После оплаты отправьте номер заказа администратору; лимит нужной версии будет добавлен после проверки поступления средств.",
927
  "payment_notice_product": "Покупка {tier}: оплата {amount} {currency} добавит {credits} квоты на статьи этой версии.",
928
  "payment_paid_product": "Платёж подтверждён. Добавлено {credits} квоты {tier}. Теперь можно продолжить создание статьи.",
929
  "quota_exhausted": "Лимит выбранной версии исчерпан. Приобретите лимит для этой версии, чтобы продолжить.",
@@ -1171,7 +1171,7 @@ TEXTS: dict[str, dict[str, str]] = {
1171
  "payment_channel_label": "Осы тапсырыстың төлем тәсілі",
1172
  "payment_channel_alipay": "Alipay",
1173
  "payment_channel_wechat": "WeChat",
1174
- "payment_scan_dual": "Таңдалған төлем тәсіліне сәйкес QR-кодты пайдаланыңыз (қазір: {channel}). Төлемнен кейін тапсырыс нөмірін әкімшіге жіберіңіз; түсім расталғаннан кейін тиісті нұсқа лимиті қосылады.",
1175
  "payment_notice_product": "{tier} нұсқасын сатып алу: {amount} {currency} төлемі осы нұсқаға {credits} мақала лимитін қосады.",
1176
  "payment_paid_product": "Төлем расталды. {tier} нұсқасына {credits} мақала лимиті қосылды. Енді мақала жасауды жалғастыруға болады.",
1177
  "quota_exhausted": "Таңдалған нұсқаның лимиті таусылды. Жалғастыру үшін осы нұсқаға лимит сатып алыңыз.",
 
427
  "payment_channel_label": "本次付款方式",
428
  "payment_channel_alipay": "Alipay",
429
  "payment_channel_wechat": "WeChat",
430
+ "payment_scan_dual": "支付宝和微信二维码对应同一订单。请直接使用任一方式完成付款;无需预先选择支付方式。付款后将订单号发送给管理员,核实真实到账后会增加对应版本额度。",
431
  "payment_notice_product": "购买{tier}:支付 {amount} {currency} 可增加{credits}篇该版本论文额度。",
432
  "payment_paid_product": "付款已确认,已增加{credits}篇{tier}论文额度。现在可以继续生成论文。",
433
  "quota_exhausted": "当前所选版本额度不足,请购买相应版本额度。",
 
675
  "payment_channel_label": "Payment method for this order",
676
  "payment_channel_alipay": "Alipay",
677
  "payment_channel_wechat": "WeChat",
678
+ "payment_scan_dual": "The Alipay and WeChat QR codes belong to the same order. Pay with either method; no payment-method selection is required in advance. After payment, send the order number to the administrator. The matching plan credits will be added after the actual receipt is verified.",
679
  "payment_notice_product": "Purchase {tier}: pay {currency} {amount} to add {credits} paper credits for this plan.",
680
  "payment_paid_product": "Payment was confirmed. {credits} {tier} paper credits were added. You can generate again now.",
681
  "quota_exhausted": "The selected plan has no remaining credits. Purchase credits for that plan to continue.",
 
923
  "payment_channel_label": "Способ оплаты для этого заказа",
924
  "payment_channel_alipay": "Alipay",
925
  "payment_channel_wechat": "WeChat",
926
+ "payment_scan_dual": "QR-коды Alipay и WeChat относятся к одному и тому же заказу. Оплатите любым способом — заранее выбирать способ оплаты не нужно. После оплаты отправьте номер заказа администратору; квота нужной версии будет добавлена после подтверждения фактического поступления средств.",
927
  "payment_notice_product": "Покупка {tier}: оплата {amount} {currency} добавит {credits} квоты на статьи этой версии.",
928
  "payment_paid_product": "Платёж подтверждён. Добавлено {credits} квоты {tier}. Теперь можно продолжить создание статьи.",
929
  "quota_exhausted": "Лимит выбранной версии исчерпан. Приобретите лимит для этой версии, чтобы продолжить.",
 
1171
  "payment_channel_label": "Осы тапсырыстың төлем тәсілі",
1172
  "payment_channel_alipay": "Alipay",
1173
  "payment_channel_wechat": "WeChat",
1174
+ "payment_scan_dual": "Alipay және WeChat QR-кодтары бір тапсырысқа тиесілі. Алдын ала төлем тәсілін таңдаудың қажеті жоқ; кез келген тәсілмен төлеңіз. Төлемнен кейін тапсырыс нөмірін әкімшіге жіберіңіз; нақты түсім расталғаннан кейін тиісті нұсқа лимиті қосылады.",
1175
  "payment_notice_product": "{tier} нұсқасын сатып алу: {amount} {currency} төлемі осы нұсқаға {credits} мақала лимитін қосады.",
1176
  "payment_paid_product": "Төлем расталды. {tier} нұсқасына {credits} мақала лимиті қосылды. Енді мақала жасауды жалғастыруға болады.",
1177
  "quota_exhausted": "Таңдалған нұсқаның лимиті таусылды. Жалғастыру үшін осы нұсқаға лимит сатып алыңыз.",
managed_auth.py CHANGED
@@ -618,32 +618,19 @@ def _render_payment_purchase_body(language: str) -> None:
618
  if (existing_sku and existing_sku != package_sku) or (existing_order and order_sku != package_sku):
619
  st.session_state["sp_payment_order"] = None
620
  st.session_state["sp_payment_request_id"] = ""
 
621
  else:
622
  st.warning(tr(language, "payment_notice"))
623
 
624
- frozen_order_channel = (
625
- str((existing_order or {}).get("payment_channel") or "").strip().upper()
626
- if isinstance(existing_order, dict)
627
- else ""
628
- )
629
- if frozen_order_channel in {"ALIPAY", "WECHAT"}:
630
- st.session_state["sp_payment_channel"] = frozen_order_channel
631
- payment_channel = st.radio(
632
- tr(language, "payment_channel_label"),
633
- ("ALIPAY", "WECHAT"),
634
- index=0 if str(st.session_state.get("sp_payment_channel") or "ALIPAY").upper() != "WECHAT" else 1,
635
- format_func=lambda value: tr(language, f"payment_channel_{value.lower()}"),
636
- horizontal=True,
637
- disabled=bool(existing_order),
638
- key="sp_payment_channel",
639
- )
640
-
641
- if st.button(tr(language, "payment_create"), key="sp_create_payment_order"):
642
  st.session_state["sp_payment_request_id"] = (
643
  f"pay_{int(time.time() * 1000)}_{secrets.token_urlsafe(12)}"
644
  )
645
  st.session_state["sp_payment_package_sku"] = package_sku
646
- st.session_state["sp_payment_order_channel"] = str(payment_channel).upper()
647
 
648
  request_id = str(st.session_state.get("sp_payment_request_id") or "").strip()
649
  order = st.session_state.get("sp_payment_order")
@@ -652,7 +639,6 @@ def _render_payment_purchase_body(language: str) -> None:
652
  order_result = create_payment_order(
653
  request_id,
654
  package_sku or None,
655
- str(st.session_state.get("sp_payment_order_channel") or payment_channel),
656
  )
657
  order = dict(order_result.get("order") or {})
658
  if not order.get("merchant_order_no") or not order.get("qr_code"):
@@ -668,7 +654,6 @@ def _render_payment_purchase_body(language: str) -> None:
668
  qr_code = str(order.get("qr_code") or "").strip()
669
  if qr_code:
670
  payment_provider = str(order.get("payment_provider") or "").strip().upper()
671
- payment_channel = str(order.get("payment_channel") or st.session_state.get("sp_payment_order_channel") or "ALIPAY").strip().upper()
672
  if payment_provider == "MANUAL_ALIPAY":
673
  alipay_bytes = _qr_png_bytes(qr_code)
674
  wechat_path = Path(__file__).resolve().parent / "static" / "payment_wechat_qr.png"
@@ -722,7 +707,7 @@ def _render_payment_purchase_body(language: str) -> None:
722
  """,
723
  unsafe_allow_html=True,
724
  )
725
- st.caption(tr(language, "payment_scan_dual", channel=tr(language, f"payment_channel_{payment_channel.lower()}")))
726
  else:
727
  st.image(_qr_png_bytes(qr_code), width=280)
728
  order_no = str(order.get("merchant_order_no") or "").strip()
 
618
  if (existing_sku and existing_sku != package_sku) or (existing_order and order_sku != package_sku):
619
  st.session_state["sp_payment_order"] = None
620
  st.session_state["sp_payment_request_id"] = ""
621
+ existing_order = None
622
  else:
623
  st.warning(tr(language, "payment_notice"))
624
 
625
+ # R4-1D: opening the purchase dialog is the explicit purchase action.
626
+ # Create one trusted order for the selected server-authoritative SKU;
627
+ # users no longer preselect a payment channel or click a second create button.
628
+ if not existing_order and not str(st.session_state.get("sp_payment_request_id") or "").strip():
 
 
 
 
 
 
 
 
 
 
 
 
 
 
629
  st.session_state["sp_payment_request_id"] = (
630
  f"pay_{int(time.time() * 1000)}_{secrets.token_urlsafe(12)}"
631
  )
632
  st.session_state["sp_payment_package_sku"] = package_sku
633
+ st.session_state["sp_payment_order_channel"] = ""
634
 
635
  request_id = str(st.session_state.get("sp_payment_request_id") or "").strip()
636
  order = st.session_state.get("sp_payment_order")
 
639
  order_result = create_payment_order(
640
  request_id,
641
  package_sku or None,
 
642
  )
643
  order = dict(order_result.get("order") or {})
644
  if not order.get("merchant_order_no") or not order.get("qr_code"):
 
654
  qr_code = str(order.get("qr_code") or "").strip()
655
  if qr_code:
656
  payment_provider = str(order.get("payment_provider") or "").strip().upper()
 
657
  if payment_provider == "MANUAL_ALIPAY":
658
  alipay_bytes = _qr_png_bytes(qr_code)
659
  wechat_path = Path(__file__).resolve().parent / "static" / "payment_wechat_qr.png"
 
707
  """,
708
  unsafe_allow_html=True,
709
  )
710
+ st.caption(tr(language, "payment_scan_dual"))
711
  else:
712
  st.image(_qr_png_bytes(qr_code), width=280)
713
  order_no = str(order.get("merchant_order_no") or "").strip()