fix payment checkout fallback to manual transfer
This commit is contained in:
@@ -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");
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user