Spaces:
Running
Running
A5-R3-R5C dormant commercial lifecycle
#114
by GuXiaopeng - opened
- app.py +6 -2
- i18n.py +12 -0
- managed_auth.py +86 -7
app.py
CHANGED
|
@@ -663,11 +663,15 @@ def _process_pending_launch(lang_code: str) -> None:
|
|
| 663 |
except Exception as exc:
|
| 664 |
st.session_state["sp_pending_launch"] = None
|
| 665 |
raw = str(exc or "")
|
| 666 |
-
if "
|
|
|
|
|
|
|
|
|
|
|
|
|
| 667 |
st.session_state["sp_payment_required"] = True
|
| 668 |
st.error(tr(lang_code, "quota_exhausted"))
|
| 669 |
render_payment_purchase(lang_code)
|
| 670 |
-
elif "a5_product_task_path_not_authorized" in raw:
|
| 671 |
st.error(tr(lang_code, "product_generation_not_active"))
|
| 672 |
else:
|
| 673 |
st.error(raw)
|
|
|
|
| 663 |
except Exception as exc:
|
| 664 |
st.session_state["sp_pending_launch"] = None
|
| 665 |
raw = str(exc or "")
|
| 666 |
+
if "product_entitlement_required" in raw:
|
| 667 |
+
st.session_state["sp_payment_required"] = True
|
| 668 |
+
st.error(tr(lang_code, "product_entitlement_required"))
|
| 669 |
+
render_payment_purchase(lang_code)
|
| 670 |
+
elif "payment_required" in raw:
|
| 671 |
st.session_state["sp_payment_required"] = True
|
| 672 |
st.error(tr(lang_code, "quota_exhausted"))
|
| 673 |
render_payment_purchase(lang_code)
|
| 674 |
+
elif "a5_product_task_path_not_active" in raw or "a5_product_task_path_not_authorized" in raw:
|
| 675 |
st.error(tr(lang_code, "product_generation_not_active"))
|
| 676 |
else:
|
| 677 |
st.error(raw)
|
i18n.py
CHANGED
|
@@ -395,6 +395,9 @@ TEXTS: dict[str, dict[str, str]] = {
|
|
| 395 |
"logout_short": "退出",
|
| 396 |
"payment_dialog_title": "支付宝付款",
|
| 397 |
"payment_notice": "今日免费额度和长期额度均已用完。支付9.9元可增加3篇长期有效额度。",
|
|
|
|
|
|
|
|
|
|
| 398 |
"quota_exhausted": "今日免费额度和长期额度均已用完。请使用支付宝购买长期额度。",
|
| 399 |
"payment_create": "生成支付宝付款二维码",
|
| 400 |
"payment_creating": "正在创建支付宝订单……",
|
|
@@ -602,6 +605,9 @@ TEXTS: dict[str, dict[str, str]] = {
|
|
| 602 |
"logout_short": "Sign out",
|
| 603 |
"payment_dialog_title": "Alipay Payment",
|
| 604 |
"payment_notice": "Your daily free quota and long-term quota are exhausted. Pay CNY 9.9 to add three long-term papers.",
|
|
|
|
|
|
|
|
|
|
| 605 |
"quota_exhausted": "Your daily free quota and long-term quota are exhausted. Purchase long-term quota with Alipay.",
|
| 606 |
"payment_create": "Create Alipay QR code",
|
| 607 |
"payment_creating": "Creating the Alipay order…",
|
|
@@ -809,6 +815,9 @@ TEXTS: dict[str, dict[str, str]] = {
|
|
| 809 |
"logout_short": "Выйти",
|
| 810 |
"payment_dialog_title": "Оплата через Alipay",
|
| 811 |
"payment_notice": "Ежедневная бесплатная и долгосрочная квоты исчерпаны. Оплата 9,9 юаня добавляет квоту на три статьи без срока действия.",
|
|
|
|
|
|
|
|
|
|
| 812 |
"quota_exhausted": "Ежедневная бесплатная и долгосрочная квоты исчерпаны. Приобретите долгосрочную квоту через Alipay.",
|
| 813 |
"payment_create": "Создать QR-код для оплаты",
|
| 814 |
"payment_creating": "Создаётся заказ Alipay…",
|
|
@@ -1016,6 +1025,9 @@ TEXTS: dict[str, dict[str, str]] = {
|
|
| 1016 |
"logout_short": "Шығу",
|
| 1017 |
"payment_dialog_title": "Alipay арқылы төлеу",
|
| 1018 |
"payment_notice": "Күнделікті тегін және ұзақ мерзімді квота таусылды. 9,9 юань төлегенде мерзімі шектелмеген үш мақала квотасы қосылады.",
|
|
|
|
|
|
|
|
|
|
| 1019 |
"quota_exhausted": "Күнделікті тегін және ұзақ мерзімді квота таусылды. Alipay арқылы ұзақ мерзімді квота сатып алыңыз.",
|
| 1020 |
"payment_create": "Төлемге арналған QR-код жасау",
|
| 1021 |
"payment_creating": "Alipay тапсырысы жасалуда…",
|
|
|
|
| 395 |
"logout_short": "退出",
|
| 396 |
"payment_dialog_title": "支付宝付款",
|
| 397 |
"payment_notice": "今日免费额度和长期额度均已用完。支付9.9元可增加3篇长期有效额度。",
|
| 398 |
+
"product_entitlement_required": "当前所选版本额度不足,请使用支付宝购买对应版本额度。",
|
| 399 |
+
"payment_notice_product": "购买{tier}:支付 {amount} {currency} 可增加{credits}篇长期有效额度。",
|
| 400 |
+
"payment_paid_product": "支付成功,已增加{credits}篇{tier}长期有效额度。请重新点击生成论文。",
|
| 401 |
"quota_exhausted": "今日免费额度和长期额度均已用完。请使用支付宝购买长期额度。",
|
| 402 |
"payment_create": "生成支付宝付款二维码",
|
| 403 |
"payment_creating": "正在创建支付宝订单……",
|
|
|
|
| 605 |
"logout_short": "Sign out",
|
| 606 |
"payment_dialog_title": "Alipay Payment",
|
| 607 |
"payment_notice": "Your daily free quota and long-term quota are exhausted. Pay CNY 9.9 to add three long-term papers.",
|
| 608 |
+
"product_entitlement_required": "The selected plan has no remaining credits. Purchase credits for this plan with Alipay.",
|
| 609 |
+
"payment_notice_product": "Purchase {tier}: pay {currency} {amount} to add {credits} long-term paper credits.",
|
| 610 |
+
"payment_paid_product": "Payment succeeded. {credits} long-term {tier} paper credits were added. Start the paper again.",
|
| 611 |
"quota_exhausted": "Your daily free quota and long-term quota are exhausted. Purchase long-term quota with Alipay.",
|
| 612 |
"payment_create": "Create Alipay QR code",
|
| 613 |
"payment_creating": "Creating the Alipay order…",
|
|
|
|
| 815 |
"logout_short": "Выйти",
|
| 816 |
"payment_dialog_title": "Оплата через Alipay",
|
| 817 |
"payment_notice": "Ежедневная бесплатная и долгосрочная квоты исчерпаны. Оплата 9,9 юаня добавляет квоту на три статьи без срока действия.",
|
| 818 |
+
"product_entitlement_required": "Квота выбранного тарифа исчерпана. Приобретите квоту этого тарифа через Alipay.",
|
| 819 |
+
"payment_notice_product": "Покупка тарифа {tier}: оплата {amount} {currency} добавит {credits} долгосрочных квот на статьи.",
|
| 820 |
+
"payment_paid_product": "Оплата прошла успешно. Добавлено {credits} долгосрочных квот тарифа {tier}. Запустите создание статьи повторно.",
|
| 821 |
"quota_exhausted": "Ежедневная бесплатная и долгосрочная квоты исчерпаны. Приобретите долгосрочную квоту через Alipay.",
|
| 822 |
"payment_create": "Создать QR-код для оплаты",
|
| 823 |
"payment_creating": "Создаётся заказ Alipay…",
|
|
|
|
| 1025 |
"logout_short": "Шығу",
|
| 1026 |
"payment_dialog_title": "Alipay арқылы төлеу",
|
| 1027 |
"payment_notice": "Күнделікті тегін және ұзақ мерзімді квота таусылды. 9,9 юань төлегенде мерзімі шектелмеген үш мақала квотасы қосылады.",
|
| 1028 |
+
"product_entitlement_required": "Таңдалған нұсқаның квотасы таусылды. Осы нұсқаға арналған квотаны Alipay арқылы сатып алыңыз.",
|
| 1029 |
+
"payment_notice_product": "{tier} нұсқасын сатып алу: {amount} {currency} төлемі {credits} ұзақ мерзімді мақала квотасын қосады.",
|
| 1030 |
+
"payment_paid_product": "Төлем сәтті өтті. {tier} нұсқасына {credits} ұзақ мерзімді мақала квотасы қосылды. Мақала жасауды қайта бастаңыз.",
|
| 1031 |
"quota_exhausted": "Күнделікті тегін және ұзақ мерзімді квота таусылды. Alipay арқылы ұзақ мерзімді квота сатып алыңыз.",
|
| 1032 |
"payment_create": "Төлемге арналған QR-код жасау",
|
| 1033 |
"payment_creating": "Alipay тапсырысы жасалуда…",
|
managed_auth.py
CHANGED
|
@@ -135,7 +135,7 @@ def _clear_generation_session_state() -> None:
|
|
| 135 |
"is_running", "start_pipeline", "resume_pipeline", "run_mode",
|
| 136 |
"rag_engine", "up_amj_ref", "up_amj_data", "up_asq_ref",
|
| 137 |
"up_asq_data", "sp_payment_required", "sp_payment_order",
|
| 138 |
-
"sp_payment_request_id",
|
| 139 |
):
|
| 140 |
st.session_state.pop(key, None)
|
| 141 |
|
|
@@ -261,6 +261,11 @@ def _normalize_product_ui_projection(value: Any) -> dict[str, Any]:
|
|
| 261 |
raise RuntimeError("product_ui_projection_invalid_word_policy") from exc
|
| 262 |
if minimum <= 0 or maximum < minimum:
|
| 263 |
raise RuntimeError("product_ui_projection_invalid_word_policy")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 264 |
products[tier] = {
|
| 265 |
"product_tier": tier,
|
| 266 |
"product_policy_version": str(raw.get("product_policy_version") or ""),
|
|
@@ -269,13 +274,21 @@ def _normalize_product_ui_projection(value: Any) -> dict[str, Any]:
|
|
| 269 |
"min_body_words": minimum,
|
| 270 |
"max_body_words": maximum,
|
| 271 |
"counting_contract_version": str(raw.get("counting_contract_version") or ""),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 272 |
}
|
| 273 |
if not all((
|
| 274 |
products[tier]["product_policy_version"],
|
| 275 |
products[tier]["word_count_policy_version"],
|
| 276 |
products[tier]["body_metric"],
|
| 277 |
products[tier]["counting_contract_version"],
|
| 278 |
-
|
|
|
|
|
|
|
|
|
|
| 279 |
raise RuntimeError("product_ui_projection_incomplete")
|
| 280 |
|
| 281 |
try:
|
|
@@ -454,12 +467,19 @@ def logout_alipay_session(on_logout: Callable[[str, str], None] | None = None) -
|
|
| 454 |
_clear_generation_session_state()
|
| 455 |
|
| 456 |
|
| 457 |
-
def create_payment_order(
|
|
|
|
|
|
|
|
|
|
| 458 |
normalized = str(idempotency_key or "").strip()
|
|
|
|
|
|
|
|
|
|
|
|
|
| 459 |
result = _edge_call(
|
| 460 |
"pay_create",
|
| 461 |
normalized,
|
| 462 |
-
|
| 463 |
key_prefix="sp_pay_create",
|
| 464 |
)
|
| 465 |
if result is None:
|
|
@@ -493,22 +513,65 @@ def _qr_png_bytes(value: str) -> bytes:
|
|
| 493 |
|
| 494 |
|
| 495 |
def _render_payment_purchase_body(language: str) -> None:
|
| 496 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 497 |
|
| 498 |
if st.button(tr(language, "payment_create"), key="sp_create_payment_order"):
|
| 499 |
st.session_state["sp_payment_request_id"] = (
|
| 500 |
f"pay_{int(time.time() * 1000)}_{secrets.token_urlsafe(12)}"
|
| 501 |
)
|
|
|
|
| 502 |
|
| 503 |
request_id = str(st.session_state.get("sp_payment_request_id") or "").strip()
|
| 504 |
order = st.session_state.get("sp_payment_order")
|
| 505 |
if request_id and not order:
|
| 506 |
st.info(tr(language, "payment_creating"))
|
| 507 |
-
order_result = create_payment_order(request_id)
|
| 508 |
order = dict(order_result.get("order") or {})
|
| 509 |
if not order.get("merchant_order_no") or not order.get("qr_code"):
|
| 510 |
raise RuntimeError("payment_order_missing_qr_code")
|
|
|
|
|
|
|
| 511 |
st.session_state["sp_payment_order"] = order
|
|
|
|
|
|
|
|
|
|
| 512 |
|
| 513 |
order = st.session_state.get("sp_payment_order") or {}
|
| 514 |
qr_code = str(order.get("qr_code") or "").strip()
|
|
@@ -519,6 +582,9 @@ def _render_payment_purchase_body(language: str) -> None:
|
|
| 519 |
status_result = payment_order_status(order.get("merchant_order_no", ""))
|
| 520 |
updated = dict(status_result.get("order") or {})
|
| 521 |
st.session_state["sp_payment_order"] = updated
|
|
|
|
|
|
|
|
|
|
| 522 |
if updated.get("status") == "paid" and updated.get("credit_applied"):
|
| 523 |
user = status_result.get("user")
|
| 524 |
if isinstance(user, dict):
|
|
@@ -529,10 +595,23 @@ def _render_payment_purchase_body(language: str) -> None:
|
|
| 529 |
st.session_state["sp_payment_required"] = False
|
| 530 |
st.session_state["sp_payment_order"] = None
|
| 531 |
st.session_state["sp_payment_request_id"] = ""
|
| 532 |
-
st.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 533 |
elif updated.get("status") in {"closed", "failed"}:
|
| 534 |
st.session_state["sp_payment_order"] = None
|
| 535 |
st.session_state["sp_payment_request_id"] = ""
|
|
|
|
| 536 |
st.warning(tr(language, "payment_expired"))
|
| 537 |
|
| 538 |
|
|
|
|
| 135 |
"is_running", "start_pipeline", "resume_pipeline", "run_mode",
|
| 136 |
"rag_engine", "up_amj_ref", "up_amj_data", "up_asq_ref",
|
| 137 |
"up_asq_data", "sp_payment_required", "sp_payment_order",
|
| 138 |
+
"sp_payment_request_id", "sp_payment_package_sku",
|
| 139 |
):
|
| 140 |
st.session_state.pop(key, None)
|
| 141 |
|
|
|
|
| 261 |
raise RuntimeError("product_ui_projection_invalid_word_policy") from exc
|
| 262 |
if minimum <= 0 or maximum < minimum:
|
| 263 |
raise RuntimeError("product_ui_projection_invalid_word_policy")
|
| 264 |
+
try:
|
| 265 |
+
amount_fen = int(raw.get("amount_fen"))
|
| 266 |
+
credits_granted = int(raw.get("credits_granted"))
|
| 267 |
+
except (TypeError, ValueError) as exc:
|
| 268 |
+
raise RuntimeError("product_ui_projection_invalid_billing_policy") from exc
|
| 269 |
products[tier] = {
|
| 270 |
"product_tier": tier,
|
| 271 |
"product_policy_version": str(raw.get("product_policy_version") or ""),
|
|
|
|
| 274 |
"min_body_words": minimum,
|
| 275 |
"max_body_words": maximum,
|
| 276 |
"counting_contract_version": str(raw.get("counting_contract_version") or ""),
|
| 277 |
+
"package_sku": str(raw.get("package_sku") or ""),
|
| 278 |
+
"billing_policy_version": str(raw.get("billing_policy_version") or ""),
|
| 279 |
+
"amount_fen": amount_fen,
|
| 280 |
+
"currency": str(raw.get("currency") or ""),
|
| 281 |
+
"credits_granted": credits_granted,
|
| 282 |
}
|
| 283 |
if not all((
|
| 284 |
products[tier]["product_policy_version"],
|
| 285 |
products[tier]["word_count_policy_version"],
|
| 286 |
products[tier]["body_metric"],
|
| 287 |
products[tier]["counting_contract_version"],
|
| 288 |
+
products[tier]["package_sku"],
|
| 289 |
+
products[tier]["billing_policy_version"],
|
| 290 |
+
products[tier]["currency"],
|
| 291 |
+
)) or amount_fen <= 0 or credits_granted <= 0:
|
| 292 |
raise RuntimeError("product_ui_projection_incomplete")
|
| 293 |
|
| 294 |
try:
|
|
|
|
| 467 |
_clear_generation_session_state()
|
| 468 |
|
| 469 |
|
| 470 |
+
def create_payment_order(
|
| 471 |
+
idempotency_key: str,
|
| 472 |
+
package_sku: str | None = None,
|
| 473 |
+
) -> dict[str, Any]:
|
| 474 |
normalized = str(idempotency_key or "").strip()
|
| 475 |
+
payload: dict[str, Any] = {"idempotency_key": normalized}
|
| 476 |
+
normalized_sku = str(package_sku or "").strip()
|
| 477 |
+
if normalized_sku:
|
| 478 |
+
payload["package_sku"] = normalized_sku
|
| 479 |
result = _edge_call(
|
| 480 |
"pay_create",
|
| 481 |
normalized,
|
| 482 |
+
payload,
|
| 483 |
key_prefix="sp_pay_create",
|
| 484 |
)
|
| 485 |
if result is None:
|
|
|
|
| 513 |
|
| 514 |
|
| 515 |
def _render_payment_purchase_body(language: str) -> None:
|
| 516 |
+
projection = get_product_ui_projection()
|
| 517 |
+
product_mode = projection.get("mode") == "a5_product"
|
| 518 |
+
selected_product: dict[str, Any] | None = None
|
| 519 |
+
package_sku = ""
|
| 520 |
+
if product_mode:
|
| 521 |
+
selected_tier = str(st.session_state.get("sp_product_tier") or "STANDARD").strip().upper()
|
| 522 |
+
raw_product = (projection.get("products") or {}).get(selected_tier)
|
| 523 |
+
if not isinstance(raw_product, dict):
|
| 524 |
+
raise RuntimeError("product_payment_projection_missing_tier")
|
| 525 |
+
selected_product = dict(raw_product)
|
| 526 |
+
package_sku = str(selected_product.get("package_sku") or "").strip()
|
| 527 |
+
amount_fen = int(selected_product.get("amount_fen") or 0)
|
| 528 |
+
credits = int(selected_product.get("credits_granted") or 0)
|
| 529 |
+
if not package_sku or amount_fen <= 0 or credits <= 0:
|
| 530 |
+
raise RuntimeError("product_payment_projection_invalid")
|
| 531 |
+
amount_text = f"{amount_fen / 100:.2f}".rstrip("0").rstrip(".")
|
| 532 |
+
currency = str(selected_product.get("currency") or "").strip()
|
| 533 |
+
if not currency:
|
| 534 |
+
raise RuntimeError("product_payment_projection_invalid_currency")
|
| 535 |
+
tier_name = tr(language, f"product_tier_{selected_tier.lower()}")
|
| 536 |
+
st.warning(
|
| 537 |
+
tr(
|
| 538 |
+
language,
|
| 539 |
+
"payment_notice_product",
|
| 540 |
+
tier=tier_name,
|
| 541 |
+
amount=amount_text,
|
| 542 |
+
currency=currency,
|
| 543 |
+
credits=credits,
|
| 544 |
+
)
|
| 545 |
+
)
|
| 546 |
+
existing_order = st.session_state.get("sp_payment_order")
|
| 547 |
+
existing_sku = str(st.session_state.get("sp_payment_package_sku") or "").strip()
|
| 548 |
+
order_sku = str((existing_order or {}).get("package_sku") or "").strip() if isinstance(existing_order, dict) else ""
|
| 549 |
+
if (existing_sku and existing_sku != package_sku) or (existing_order and order_sku != package_sku):
|
| 550 |
+
st.session_state["sp_payment_order"] = None
|
| 551 |
+
st.session_state["sp_payment_request_id"] = ""
|
| 552 |
+
else:
|
| 553 |
+
st.warning(tr(language, "payment_notice"))
|
| 554 |
|
| 555 |
if st.button(tr(language, "payment_create"), key="sp_create_payment_order"):
|
| 556 |
st.session_state["sp_payment_request_id"] = (
|
| 557 |
f"pay_{int(time.time() * 1000)}_{secrets.token_urlsafe(12)}"
|
| 558 |
)
|
| 559 |
+
st.session_state["sp_payment_package_sku"] = package_sku
|
| 560 |
|
| 561 |
request_id = str(st.session_state.get("sp_payment_request_id") or "").strip()
|
| 562 |
order = st.session_state.get("sp_payment_order")
|
| 563 |
if request_id and not order:
|
| 564 |
st.info(tr(language, "payment_creating"))
|
| 565 |
+
order_result = create_payment_order(request_id, package_sku or None)
|
| 566 |
order = dict(order_result.get("order") or {})
|
| 567 |
if not order.get("merchant_order_no") or not order.get("qr_code"):
|
| 568 |
raise RuntimeError("payment_order_missing_qr_code")
|
| 569 |
+
if product_mode and str(order.get("package_sku") or "") != package_sku:
|
| 570 |
+
raise RuntimeError("payment_order_package_mismatch")
|
| 571 |
st.session_state["sp_payment_order"] = order
|
| 572 |
+
projected = order_result.get("product_ui")
|
| 573 |
+
if isinstance(projected, dict):
|
| 574 |
+
st.session_state["sp_product_ui_projection"] = _normalize_product_ui_projection(projected)
|
| 575 |
|
| 576 |
order = st.session_state.get("sp_payment_order") or {}
|
| 577 |
qr_code = str(order.get("qr_code") or "").strip()
|
|
|
|
| 582 |
status_result = payment_order_status(order.get("merchant_order_no", ""))
|
| 583 |
updated = dict(status_result.get("order") or {})
|
| 584 |
st.session_state["sp_payment_order"] = updated
|
| 585 |
+
projected = status_result.get("product_ui")
|
| 586 |
+
if isinstance(projected, dict):
|
| 587 |
+
st.session_state["sp_product_ui_projection"] = _normalize_product_ui_projection(projected)
|
| 588 |
if updated.get("status") == "paid" and updated.get("credit_applied"):
|
| 589 |
user = status_result.get("user")
|
| 590 |
if isinstance(user, dict):
|
|
|
|
| 595 |
st.session_state["sp_payment_required"] = False
|
| 596 |
st.session_state["sp_payment_order"] = None
|
| 597 |
st.session_state["sp_payment_request_id"] = ""
|
| 598 |
+
st.session_state["sp_payment_package_sku"] = ""
|
| 599 |
+
if product_mode and selected_product is not None:
|
| 600 |
+
tier_name = tr(language, f"product_tier_{str(selected_product.get('product_tier') or '').lower()}")
|
| 601 |
+
st.success(
|
| 602 |
+
tr(
|
| 603 |
+
language,
|
| 604 |
+
"payment_paid_product",
|
| 605 |
+
tier=tier_name,
|
| 606 |
+
credits=int(selected_product.get("credits_granted") or 0),
|
| 607 |
+
)
|
| 608 |
+
)
|
| 609 |
+
else:
|
| 610 |
+
st.success(tr(language, "payment_paid"))
|
| 611 |
elif updated.get("status") in {"closed", "failed"}:
|
| 612 |
st.session_state["sp_payment_order"] = None
|
| 613 |
st.session_state["sp_payment_request_id"] = ""
|
| 614 |
+
st.session_state["sp_payment_package_sku"] = ""
|
| 615 |
st.warning(tr(language, "payment_expired"))
|
| 616 |
|
| 617 |
|