fix payment checkout fallback to manual transfer

This commit is contained in:
2569718930@qq.com
2026-06-10 15:58:36 +08:00
parent f171049f60
commit 35bfd0670c
4 changed files with 88 additions and 8 deletions
+2 -2
View File
@@ -193,8 +193,8 @@ export function createAccountCopy(isEn: boolean): Record<string, string> {
? "Your wallet needs a small amount of POL for gas fees; USDC alone may not complete authorization or payment. Please confirm your wallet is on Polygon network and keep some POL before paying."
: "钱包里需要少量 POL 作为 gas 手续费;只有 USDC 可能无法完成授权或支付。请确认当前钱包在 Polygon 网络,并预留一点 POL 后再支付。",
paymentManualDesc: isEn
? "Option 2: Transfer directly to the platform's receiver contract without binding a wallet. After the transfer, submit the transaction hash (Tx Hash) and the system will verify and activate Pro automatically."
: "方式二:无需将钱包绑定到账号,直接向平台收款合约转账。转账完成后提交交易哈希(Tx Hash)系统会自动验签并开通 Pro。",
? "Option 2: Transfer directly to the platform's receiver address without binding a wallet. After the transfer, submit the transaction hash (Tx Hash) and the system will verify and activate Pro automatically."
: "方式二:无需将钱包绑定到账号,直接向平台收款地址转账。转账完成后提交交易哈希(Tx Hash)系统会自动验签并开通 Pro。",
paymentManualTitle: isEn
? "Manual Transfer (No Wallet Binding)"
: "手动转账(无需绑定钱包)",
@@ -480,11 +480,13 @@ export function usePaymentFlow(params: UsePaymentFlowParams) {
) ||
latestTokens.find((token) => Number(token.chain_id || targetChainId) === targetChainId);
if (selectedLatestToken?.supports_contract_checkout === false) {
throw new Error(
setPaymentInfo(
isEn
? `${selectedLatestToken.chain_name || chainIdToDisplayName(targetChainId)} ${selectedLatestToken.symbol || "USDC"} supports manual transfer only.`
: `${selectedLatestToken.chain_name || chainIdToDisplayName(targetChainId)} ${selectedLatestToken.symbol || "USDC"} 仅支持手动转账。`,
: `${selectedLatestToken.chain_name || chainIdToDisplayName(targetChainId)} ${selectedLatestToken.symbol || "USDC"} 当前仅支持手动转账。`,
);
await createManualPaymentIntent();
return;
}
const expectedReceiver = String(selectedLatestToken?.receiver_contract || latestConfig.receiver_contract || "").toLowerCase();
assertExpectedPaymentReceiver(expectedReceiver, "payment receiver contract");
+31 -3
View File
@@ -109,6 +109,21 @@ def _env_bool(name: str, default: bool = False) -> bool:
return str(raw).strip().lower() in {"1", "true", "yes", "on"}
def _config_bool(value: Any, default: bool = False) -> bool:
if value is None:
return default
if isinstance(value, bool):
return value
if isinstance(value, (int, float)):
return bool(value)
text = str(value).strip().lower()
if text in {"1", "true", "yes", "on"}:
return True
if text in {"0", "false", "no", "off"}:
return False
return default
def _env_int(name: str, default: int) -> int:
raw = os.getenv(name)
if raw is None:
@@ -650,9 +665,22 @@ class PaymentContractCheckoutService:
)
except Exception:
confirmations = None
supports_direct_transfer = bool(row.get("supports_direct_transfer", True))
supports_contract_checkout = bool(
row.get("supports_contract_checkout", row.get("supports_contract", chain_id == self.chain_id))
supports_direct_transfer = _config_bool(
row.get("supports_direct_transfer"),
True,
)
contract_support_raw = row.get(
"supports_contract_checkout",
row.get("supports_contract"),
)
same_direct_receiver = bool(
receiver_contract
and direct_receiver_address
and receiver_contract == direct_receiver_address
)
supports_contract_checkout = _config_bool(
contract_support_raw,
bool(chain_id == self.chain_id and receiver_contract and not same_direct_receiver),
)
return PaymentTokenConfig(
code=code,
+51 -1
View File
@@ -1,4 +1,10 @@
from src.payments.contract_checkout import PaymentContractCheckoutService, PaymentIntentRecord
import pytest
from src.payments.contract_checkout import (
PaymentCheckoutError,
PaymentContractCheckoutService,
PaymentIntentRecord,
)
ETHEREUM_USDC = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
@@ -69,6 +75,50 @@ def test_config_payload_exposes_polygon_and_ethereum_payment_routes(
assert eth_token["supports_contract_checkout"] is False
def test_matching_direct_receiver_disables_contract_checkout_by_default(
monkeypatch, tmp_path
):
_setup_multichain_env(monkeypatch, tmp_path)
service = PaymentContractCheckoutService()
polygon_token = next(
row for row in service.get_config_payload()["tokens"] if row["chain_id"] == 137
)
assert polygon_token["direct_receiver_address"] == RECEIVER.lower()
assert polygon_token["supports_direct_transfer"] is True
assert polygon_token["supports_contract_checkout"] is False
with pytest.raises(PaymentCheckoutError) as exc:
service.create_intent("user-1", "pro_monthly", payment_mode="strict")
assert exc.value.status_code == 400
assert "manual transfer only" in exc.value.detail
def test_explicit_contract_checkout_support_overrides_matching_direct_receiver(
monkeypatch, tmp_path
):
_setup_multichain_env(monkeypatch, tmp_path)
monkeypatch.setenv(
"POLYWEATHER_PAYMENT_ACCEPTED_TOKENS_JSON",
(
"["
f'{{"code":"usdc_polygon","symbol":"USDC","name":"USDC on Polygon",'
f'"chain_id":137,"chain_code":"polygon","chain_name":"Polygon",'
f'"address":"{POLYGON_USDC}","decimals":6,'
f'"receiver_contract":"{RECEIVER}",'
f'"direct_receiver_address":"{RECEIVER}",'
f'"supports_contract_checkout":true,'
f'"supports_direct_transfer":true,"is_default":true}}'
"]"
),
)
service = PaymentContractCheckoutService()
token = service.get_config_payload()["tokens"][0]
assert token["supports_contract_checkout"] is True
def test_create_direct_intent_can_target_ethereum_usdc(monkeypatch, tmp_path):
_setup_multichain_env(monkeypatch, tmp_path)
service = PaymentContractCheckoutService()