feat: 实现 Mock 支付功能,修复支付激活会员问题
- 当未配置 ALIPAY_* 环境变量时,自动启用 Mock 支付模式 - Mock 支付页面点击按钮后调用 /api/payment/mock-confirm 激活会员 - 更新 AGENTS.md 文档说明 Mock 支付配置和使用方法
This commit is contained in:
@@ -610,9 +610,55 @@ pub async fn payment_page(
|
||||
let return_url = format!("{}/payment/success?order_no={}", base_url, order_no);
|
||||
|
||||
let Some(config) = AlipayConfig::from_env() else {
|
||||
let jwt_for_mock = token.clone();
|
||||
let mock_html = format!(r#"<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head><meta charset="utf-8"><title>模拟支付</title></head>
|
||||
<body style="font-family:-apple-system;padding:40px;text-align:center">
|
||||
<h2 style="color:#52c41a">模拟支付环境</h2>
|
||||
<p style="color:#666">当前为沙箱模拟环境,无需真实支付</p>
|
||||
<p style="color:#999;font-size:14px">订单号: {}</p>
|
||||
<p style="color:#999;font-size:14px">套餐: {}</p>
|
||||
<button id="confirmBtn" onclick="confirmMockPay()" style="padding:12px 32px;font-size:16px;background:#1677ff;color:#fff;border:none;border-radius:4px;cursor:pointer">确认模拟支付</button>
|
||||
<p id="result"></p>
|
||||
<p><a href="/payment" style="color:#1677ff">返回重试</a></p>
|
||||
<script>
|
||||
function confirmMockPay() {{
|
||||
var btn = document.getElementById('confirmBtn');
|
||||
btn.disabled = true;
|
||||
btn.textContent = '处理中...';
|
||||
fetch('/api/payment/mock-confirm', {{
|
||||
method: 'POST',
|
||||
headers: {{
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': 'Bearer {}'
|
||||
}},
|
||||
body: JSON.stringify({{order_id: '{}'}})
|
||||
}})
|
||||
.then(r => r.json())
|
||||
.then(data => {{
|
||||
if (data.success) {{
|
||||
document.getElementById('result').innerHTML = '<span style="color:#52c41a">支付成功!</span>';
|
||||
setTimeout(() => window.location.href = '/payment?mock=1', 1500);
|
||||
}} else {{
|
||||
document.getElementById('result').innerHTML = '<span style="color:#f5222d">失败: ' + (data.error || '未知错误') + '</span>';
|
||||
btn.disabled = false;
|
||||
btn.textContent = '重试';
|
||||
}}
|
||||
}})
|
||||
.catch(e => {{
|
||||
document.getElementById('result').innerHTML = '<span style="color:#f5222d">网络错误</span>';
|
||||
btn.disabled = false;
|
||||
btn.textContent = '重试';
|
||||
}});
|
||||
}}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
"#, order_no, pkg.display_name, jwt_for_mock, order_no);
|
||||
return Ok(HttpResponse::Ok()
|
||||
.content_type("text/html; charset=utf-8")
|
||||
.body(format_error_html("支付配置不完整,请联系管理员", &order_no)));
|
||||
.body(mock_html));
|
||||
};
|
||||
|
||||
let total_amount_str = format!("{:.2}", pkg.amount as f64 / 100.0);
|
||||
@@ -665,9 +711,55 @@ pub async fn alipay_pay_page(
|
||||
let return_url = format!("{}/payment/success?order_no={}", base_url, query.order_no);
|
||||
|
||||
let Some(config) = AlipayConfig::from_env() else {
|
||||
let jwt_for_mock = token.clone();
|
||||
let mock_html = format!(r#"<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head><meta charset="utf-8"><title>模拟支付</title></head>
|
||||
<body style="font-family:-apple-system;padding:40px;text-align:center">
|
||||
<h2 style="color:#52c41a">模拟支付环境</h2>
|
||||
<p style="color:#666">当前为沙箱模拟环境,无需真实支付</p>
|
||||
<p style="color:#999;font-size:14px">订单号: {}</p>
|
||||
<p style="color:#999;font-size:14px">套餐: {}</p>
|
||||
<button id="confirmBtn" onclick="confirmMockPay()" style="padding:12px 32px;font-size:16px;background:#1677ff;color:#fff;border:none;border-radius:4px;cursor:pointer">确认模拟支付</button>
|
||||
<p id="result"></p>
|
||||
<p><a href="/payment" style="color:#1677ff">返回重试</a></p>
|
||||
<script>
|
||||
function confirmMockPay() {{
|
||||
var btn = document.getElementById('confirmBtn');
|
||||
btn.disabled = true;
|
||||
btn.textContent = '处理中...';
|
||||
fetch('/api/payment/mock-confirm', {{
|
||||
method: 'POST',
|
||||
headers: {{
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': 'Bearer {}'
|
||||
}},
|
||||
body: JSON.stringify({{order_id: '{}'}})
|
||||
}})
|
||||
.then(r => r.json())
|
||||
.then(data => {{
|
||||
if (data.success) {{
|
||||
document.getElementById('result').innerHTML = '<span style="color:#52c41a">支付成功!</span>';
|
||||
setTimeout(() => window.location.href = '/payment?mock=1', 1500);
|
||||
}} else {{
|
||||
document.getElementById('result').innerHTML = '<span style="color:#f5222d">失败: ' + (data.error || '未知错误') + '</span>';
|
||||
btn.disabled = false;
|
||||
btn.textContent = '重试';
|
||||
}}
|
||||
}})
|
||||
.catch(e => {{
|
||||
document.getElementById('result').innerHTML = '<span style="color:#f5222d">网络错误</span>';
|
||||
btn.disabled = false;
|
||||
btn.textContent = '重试';
|
||||
}});
|
||||
}}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
"#, query.order_no, pkg.display_name, jwt_for_mock, query.order_no);
|
||||
return Ok(HttpResponse::Ok()
|
||||
.content_type("text/html; charset=utf-8")
|
||||
.body(format_error_html("支付配置不完整,请联系管理员", &query.order_no)));
|
||||
.body(mock_html));
|
||||
};
|
||||
|
||||
let total_amount_str = format!("{:.2}", pkg.amount as f64 / 100.0);
|
||||
|
||||
Reference in New Issue
Block a user