152 lines
5.5 KiB
Python
152 lines
5.5 KiB
Python
import json
|
|
|
|
import pytest
|
|
from hashlib import md5
|
|
from sqlalchemy import create_engine, inspect, text
|
|
|
|
from app import alipay_provider
|
|
from app.main import _migrate_alipay_transfer_attempts
|
|
|
|
|
|
class FakeClient:
|
|
def __init__(self, payload):
|
|
self.payload = payload
|
|
|
|
def execute(self, _request):
|
|
return json.dumps(self.payload, ensure_ascii=False)
|
|
|
|
|
|
def test_transfer_parses_official_sdk_json_response(monkeypatch):
|
|
monkeypatch.setattr(alipay_provider, "_client", lambda: FakeClient({
|
|
"code": "10000",
|
|
"msg": "Success",
|
|
"out_biz_no": "RF001",
|
|
"order_id": "ALI001",
|
|
"status": "SUCCESS",
|
|
}))
|
|
result = alipay_provider.transfer(
|
|
out_biz_no="RF001", amount_fen=300, account="buyer@example.com",
|
|
payee_name="测试收款人", order_title="测试返款",
|
|
)
|
|
assert result == {"out_biz_no": "RF001", "trade_no": "ALI001", "status": "SUCCESS"}
|
|
|
|
|
|
def test_transfer_preserves_provider_business_error(monkeypatch):
|
|
monkeypatch.setattr(alipay_provider, "_client", lambda: FakeClient({
|
|
"code": "40004",
|
|
"msg": "Business Failed",
|
|
"sub_code": "PAYEE_NOT_EXIST",
|
|
"sub_msg": "收款账户不存在",
|
|
}))
|
|
with pytest.raises(alipay_provider.AlipayTransferError) as error:
|
|
alipay_provider.transfer(
|
|
out_biz_no="RF002", amount_fen=300, account="missing@example.com",
|
|
payee_name="测试收款人", order_title="测试返款",
|
|
)
|
|
assert error.value.code == "PAYEE_NOT_EXIST"
|
|
assert error.value.ambiguous is False
|
|
|
|
|
|
def test_old_sdk_error_envelope_is_recovered_from_response_exception():
|
|
raw = (
|
|
'[request-id]response shape maybe illegal. '
|
|
'{"alipay_fund_trans_common_query_response":{'
|
|
'"code":"40006","msg":"Insufficient Permissions",'
|
|
'"sub_code":"isv.illegal-client-ip",'
|
|
'"sub_msg":"当前访问IP不在开放平台设置"},'
|
|
'"alipay_cert_sn":"abc","sign":"signature"}'
|
|
)
|
|
|
|
error = alipay_provider._provider_error_from_exception(RuntimeError(raw))
|
|
|
|
assert error is not None
|
|
assert error.code == "isv.illegal-client-ip"
|
|
assert str(error) == "当前访问IP不在开放平台设置"
|
|
assert error.ambiguous is False
|
|
|
|
|
|
def test_query_parses_processing_status(monkeypatch):
|
|
monkeypatch.setattr(alipay_provider, "_client", lambda: FakeClient({
|
|
"code": "10000",
|
|
"msg": "Success",
|
|
"out_biz_no": "RF003",
|
|
"order_id": "ALI003",
|
|
"status": "DEALING",
|
|
}))
|
|
result = alipay_provider.query(out_biz_no="RF003")
|
|
assert result["status"] == "DEALING"
|
|
assert result["trade_no"] == "ALI003"
|
|
|
|
|
|
def test_legacy_transfer_table_is_migrated_without_losing_rows():
|
|
engine = create_engine("sqlite:///:memory:")
|
|
with engine.begin() as connection:
|
|
connection.execute(text("""
|
|
CREATE TABLE alipay_transfer_attempts (
|
|
id INTEGER PRIMARY KEY,
|
|
target_kind VARCHAR(32) NOT NULL,
|
|
target_id INTEGER NOT NULL,
|
|
amount_fen INTEGER NOT NULL,
|
|
account VARCHAR(128) NOT NULL,
|
|
payee_name VARCHAR(64) NOT NULL,
|
|
created_by_id INTEGER NOT NULL,
|
|
status VARCHAR(32) NOT NULL DEFAULT 'pending',
|
|
created_at DATETIME
|
|
)
|
|
"""))
|
|
connection.execute(text("""
|
|
INSERT INTO alipay_transfer_attempts
|
|
(id, target_kind, target_id, amount_fen, account, payee_name, created_by_id)
|
|
VALUES (1, 'answer_submission', 42, 100, 'buyer@example.com', '测试收款人', 7)
|
|
"""))
|
|
|
|
_migrate_alipay_transfer_attempts(engine)
|
|
columns = {column["name"] for column in inspect(engine).get_columns("alipay_transfer_attempts")}
|
|
assert {"order_no", "shop_name", "payer_account", "operator_name", "error_message", "out_biz_no"} <= columns
|
|
with engine.connect() as connection:
|
|
row = connection.execute(text("SELECT * FROM alipay_transfer_attempts WHERE id = 1")).mappings().one()
|
|
assert row["amount_fen"] == 100
|
|
assert row["out_biz_no"].startswith("LEGACY-1-")
|
|
assert row["status"] == "failed"
|
|
assert "旧版本记录" in row["error_message"]
|
|
engine.dispose()
|
|
|
|
|
|
def test_certificate_serials_use_issuer_and_decimal_serial_number():
|
|
class FakeName:
|
|
def rfc4514_string(self):
|
|
return "CN=Alipay,O=Example,C=CN"
|
|
|
|
class FakeCertificate:
|
|
issuer = FakeName()
|
|
serial_number = 0xA12B
|
|
|
|
class SignatureAlgorithmOid:
|
|
dotted_string = "1.2.840.113549.1.1.11"
|
|
|
|
signature_algorithm_oid = SignatureAlgorithmOid()
|
|
|
|
expected = md5(b"CN=Alipay,O=Example,C=CN41259").hexdigest()
|
|
assert alipay_provider._certificate_serial(FakeCertificate()) == expected
|
|
assert alipay_provider._root_certificate_serial([FakeCertificate(), FakeCertificate()]) == f"{expected}_{expected}"
|
|
|
|
|
|
def test_root_certificate_serial_ignores_non_rsa_certificates():
|
|
class FakeName:
|
|
def rfc4514_string(self):
|
|
return "CN=Alipay,O=Example,C=CN"
|
|
|
|
class FakeCertificate:
|
|
issuer = FakeName()
|
|
serial_number = 0xA12B
|
|
|
|
def __init__(self, oid):
|
|
self.signature_algorithm_oid = type("Oid", (), {"dotted_string": oid})()
|
|
|
|
rsa = FakeCertificate("1.2.840.113549.1.1.11")
|
|
sm2 = FakeCertificate("1.2.156.10197.1.501")
|
|
ecc = FakeCertificate("1.2.840.10045.4.3.3")
|
|
expected = md5(b"CN=Alipay,O=Example,C=CN41259").hexdigest()
|
|
|
|
assert alipay_provider._root_certificate_serial([sm2, rsa, ecc]) == expected
|