修改支付宝佣金配置场景

This commit is contained in:
db_huang 2026-10-31 17:34:45 +08:00
parent 29ae53e593
commit 2b01e0adec
10 changed files with 12 additions and 18 deletions

View File

@ -53,5 +53,4 @@ ALIPAY_NOTIFY_URL=https://your-domain.example.com/alipay_callback
ALIPAY_TRANSFER_API=alipay.fund.trans.uni.transfer ALIPAY_TRANSFER_API=alipay.fund.trans.uni.transfer
ALIPAY_TRANSFER_QUERY_API=alipay.fund.trans.common.query ALIPAY_TRANSFER_QUERY_API=alipay.fund.trans.common.query
ALIPAY_TRANSFER_SCENE_NAME=佣金报酬 ALIPAY_TRANSFER_SCENE_NAME=佣金报酬
ALIPAY_TRANSFER_JOB_TYPE=问答任务参与者
ALIPAY_HTTP_TIMEOUT_SECONDS=30 ALIPAY_HTTP_TIMEOUT_SECONDS=30

View File

@ -28,7 +28,6 @@ def validate_configuration() -> None:
("ALIPAY_APP_PRIVATE_KEY_PATH", settings.alipay_app_private_key_path), ("ALIPAY_APP_PRIVATE_KEY_PATH", settings.alipay_app_private_key_path),
("ALIPAY_PUBLIC_KEY_PATH 或 ALIPAY_ALIPAY_PUBLIC_CERT_PATH", settings.alipay_public_key_path or settings.alipay_alipay_public_cert_path), ("ALIPAY_PUBLIC_KEY_PATH 或 ALIPAY_ALIPAY_PUBLIC_CERT_PATH", settings.alipay_public_key_path or settings.alipay_alipay_public_cert_path),
("ALIPAY_TRANSFER_SCENE_NAME", settings.alipay_transfer_scene_name), ("ALIPAY_TRANSFER_SCENE_NAME", settings.alipay_transfer_scene_name),
("ALIPAY_TRANSFER_JOB_TYPE", settings.alipay_transfer_job_type),
) if not str(value or "").strip() ) if not str(value or "").strip()
] ]
if missing: if missing:
@ -269,13 +268,10 @@ def transfer(*, out_biz_no: str, amount_fen: int, account: str, payee_name: str,
model.product_code = "TRANS_ACCOUNT_NO_PWD" model.product_code = "TRANS_ACCOUNT_NO_PWD"
model.biz_scene = "DIRECT_TRANSFER" model.biz_scene = "DIRECT_TRANSFER"
model.transfer_scene_name = settings.alipay_transfer_scene_name model.transfer_scene_name = settings.alipay_transfer_scene_name
job_type = TransferSceneReportInfo()
job_type.info_type = "岗位类型"
job_type.info_content = settings.alipay_transfer_job_type[:32]
reward_description = TransferSceneReportInfo() reward_description = TransferSceneReportInfo()
reward_description.info_type = "报酬说明" reward_description.info_type = "佣金报酬说明"
reward_description.info_content = order_title[:32] reward_description.info_content = order_title[:32]
model.transfer_scene_report_infos = [job_type, reward_description] model.transfer_scene_report_infos = [reward_description]
model.order_title = order_title[:128] model.order_title = order_title[:128]
payee = Participant() payee = Participant()
payee.identity = account payee.identity = account

View File

@ -78,7 +78,6 @@ class Settings:
alipay_transfer_api = os.getenv("ALIPAY_TRANSFER_API", "alipay.fund.trans.uni.transfer") alipay_transfer_api = os.getenv("ALIPAY_TRANSFER_API", "alipay.fund.trans.uni.transfer")
alipay_transfer_query_api = os.getenv("ALIPAY_TRANSFER_QUERY_API", "alipay.fund.trans.common.query") alipay_transfer_query_api = os.getenv("ALIPAY_TRANSFER_QUERY_API", "alipay.fund.trans.common.query")
alipay_transfer_scene_name = os.getenv("ALIPAY_TRANSFER_SCENE_NAME", "").strip() alipay_transfer_scene_name = os.getenv("ALIPAY_TRANSFER_SCENE_NAME", "").strip()
alipay_transfer_job_type = os.getenv("ALIPAY_TRANSFER_JOB_TYPE", "").strip()
alipay_http_timeout_seconds = int(os.getenv("ALIPAY_HTTP_TIMEOUT_SECONDS", "30")) alipay_http_timeout_seconds = int(os.getenv("ALIPAY_HTTP_TIMEOUT_SECONDS", "30"))

View File

@ -3659,6 +3659,8 @@ def _alipay_error_detail(exc: AlipayTransferError, *, querying: bool = False) ->
return "当前服务器公网 IP 不在支付宝开放平台白名单,请配置服务器 IP 白名单后重试查询" return "当前服务器公网 IP 不在支付宝开放平台白名单,请配置服务器 IP 白名单后重试查询"
if exc.code == "isv.invalid-alipay-root-cert-sn": if exc.code == "isv.invalid-alipay-root-cert-sn":
return "支付宝根证书序列号无效,请检查证书模式配置后重试" return "支付宝根证书序列号无效,请检查证书模式配置后重试"
if exc.code == "TRANSFER_SCENE_REPORT_INFO_ILLEGAL":
return "支付宝转账失败(TRANSFER_SCENE_REPORT_INFO_ILLEGAL):佣金报酬场景必须上报“佣金报酬说明”,请确认服务器已部署最新代码并重启后端"
if exc.ambiguous: if exc.ambiguous:
return "支付宝请求超时,结果暂不明确;请稍后查询订单状态,勿重复创建转账" return "支付宝请求超时,结果暂不明确;请稍后查询订单状态,勿重复创建转账"
action = "查询" if querying else "转账" action = "查询" if querying else "转账"
@ -3679,8 +3681,8 @@ def _perform_alipay_refund(db: Session, record, kind: str, user: AdminUser, paye
if not identity["alipay_account"] or not identity["payee_name"]: if not identity["alipay_account"] or not identity["payee_name"]:
raise HTTPException(status_code=400, detail="缺少收款支付宝账号或收款人姓名") raise HTTPException(status_code=400, detail="缺少收款支付宝账号或收款人姓名")
amount_fen = identity["refund_amount_fen"] amount_fen = identity["refund_amount_fen"]
if amount_fen <= 0: if amount_fen < 10:
raise HTTPException(status_code=400, detail="返款金额必须大于0") raise HTTPException(status_code=400, detail="支付宝单笔转账金额不能低于0.10元")
if payer.single_limit_fen and amount_fen > payer.single_limit_fen: if payer.single_limit_fen and amount_fen > payer.single_limit_fen:
raise HTTPException(status_code=400, detail="返款金额超过付款账户单笔限额") raise HTTPException(status_code=400, detail="返款金额超过付款账户单笔限额")
order = db.scalar(select(AlipayRefundOrder).where( order = db.scalar(select(AlipayRefundOrder).where(

View File

@ -32,7 +32,6 @@ def test_transfer_parses_official_sdk_json_response(monkeypatch):
}) })
monkeypatch.setattr(alipay_provider, "_client", lambda: client) monkeypatch.setattr(alipay_provider, "_client", lambda: client)
monkeypatch.setattr(alipay_provider.settings, "alipay_transfer_scene_name", "佣金报酬") monkeypatch.setattr(alipay_provider.settings, "alipay_transfer_scene_name", "佣金报酬")
monkeypatch.setattr(alipay_provider.settings, "alipay_transfer_job_type", "问答任务参与者")
result = alipay_provider.transfer( result = alipay_provider.transfer(
out_biz_no="RF001", amount_fen=300, account="buyer@example.com", out_biz_no="RF001", amount_fen=300, account="buyer@example.com",
payee_name="测试收款人", order_title="测试返款", payee_name="测试收款人", order_title="测试返款",
@ -41,8 +40,7 @@ def test_transfer_parses_official_sdk_json_response(monkeypatch):
biz_content = json.loads(client.params["biz_content"]) biz_content = json.loads(client.params["biz_content"])
assert biz_content["transfer_scene_name"] == "佣金报酬" assert biz_content["transfer_scene_name"] == "佣金报酬"
assert biz_content["transfer_scene_report_infos"] == [ assert biz_content["transfer_scene_report_infos"] == [
{"info_content": "问答任务参与者", "info_type": "岗位类型"}, {"info_content": "测试返款", "info_type": "佣金报酬说明"},
{"info_content": "测试返款", "info_type": "报酬说明"},
] ]

View File

@ -60,9 +60,9 @@ export function TaskDetail({ task, close, onConfirmRefund, onSaveRefundAmount, a
setAmount(((task.refund_amount_fen ?? (task.selected_questions?.length ?? 0) * 50) / 100).toFixed(2)); setAmount(((task.refund_amount_fen ?? (task.selected_questions?.length ?? 0) * 50) / 100).toFixed(2));
setAmountError(""); setAmountError("");
}, [task.board_key, task.refund_amount_fen]); }, [task.board_key, task.refund_amount_fen]);
const validAmount = /^\d+(\.\d{1,2})?$/.test(amount) && Number(amount) > 0 && Number(amount) <= 999999.99; const validAmount = /^\d+(\.\d{1,2})?$/.test(amount) && Number(amount) >= 0.1 && Number(amount) <= 999999.99;
async function saveAmount(reset = false) { async function saveAmount(reset = false) {
if (!reset && !validAmount) return setAmountError("请输入大于0且最多两位小数的金额"); if (!reset && !validAmount) return setAmountError("支付宝转账金额最低0.10元,且最多两位小数");
setAmountBusy(true); setAmountBusy(true);
setAmountError(""); setAmountError("");
try { try {
@ -210,7 +210,7 @@ export function TaskDetail({ task, close, onConfirmRefund, onSaveRefundAmount, a
</div> </div>
<div className="task-detail-refund-amount"> <div className="task-detail-refund-amount">
<label>返款金额(元) <label>返款金额(元)
{!readOnly && task.refund_endpoint ? <input aria-label="返款金额" type="number" min="0.01" max="999999.99" step="0.01" value={amount} onChange={(event) => setAmount(event.target.value)} /> : <b>¥{((task.refund_amount_fen ?? 0) / 100).toFixed(2)}</b>} {!readOnly && task.refund_endpoint ? <input aria-label="返款金额" type="number" min="0.10" max="999999.99" step="0.01" value={amount} onChange={(event) => setAmount(event.target.value)} /> : <b>¥{((task.refund_amount_fen ?? 0) / 100).toFixed(2)}</b>}
</label> </label>
<span>默认:{task.refund_default_amount_fen / 50 || 0} 条 × 0.50 元 = ¥{((task.refund_default_amount_fen || 0) / 100).toFixed(2)}{task.refund_amount_custom ? "(已自定义)" : ""}</span> <span>默认:{task.refund_default_amount_fen / 50 || 0} 条 × 0.50 元 = ¥{((task.refund_default_amount_fen || 0) / 100).toFixed(2)}{task.refund_amount_custom ? "(已自定义)" : ""}</span>
{!readOnly && task.refund_endpoint && <> {!readOnly && task.refund_endpoint && <>
@ -364,8 +364,8 @@ export function TaskDetail({ task, close, onConfirmRefund, onSaveRefundAmount, a
<p>收款人:{task.payee_name || "未填写"}<br />支付宝账号:{task.alipay_account || "未填写"}</p> <p>收款人:{task.payee_name || "未填写"}<br />支付宝账号:{task.alipay_account || "未填写"}</p>
<fieldset className="refund-record-fields" disabled={refundBusy}> <fieldset className="refund-record-fields" disabled={refundBusy}>
<label>返款金额(元) <label>返款金额(元)
<input type="number" min="0.01" max="999999.99" step="0.01" value={amount} onChange={(event) => setAmount(event.target.value)} /> <input type="number" min="0.10" max="999999.99" step="0.01" value={amount} onChange={(event) => setAmount(event.target.value)} />
<small>默认按每条问答 0.50 元计算,可修改金额。</small> <small>默认按每条问答 0.50 元计算;支付宝单笔转账最低 0.10 元。</small>
</label> </label>
<label>付款账户 <label>付款账户
<select value={payerAccount} disabled={accountsBusy} onChange={(event) => setPayerAccount(event.target.value)}> <select value={payerAccount} disabled={accountsBusy} onChange={(event) => setPayerAccount(event.target.value)}>