
本文详解如何通过 REST API 将 React 表单中的动态金额(如用户输入的付款数额)安全、可靠地传递至 Django 视图,并注入到银行网关支付流程中,替代硬编码的 amount = 5000。
本文详解如何通过 rest api 将 react 表单中的动态金额(如用户输入的付款数额)安全、可靠地传递至 django 视图,并注入到银行网关支付流程中,替代硬编码的 `amount = 5000`。
在实际支付集成场景中,将金额(amount)硬编码在 Django 视图里(如 amount = 5000)完全不可行——它必须由前端用户实时输入、经校验后动态传入后端,再交由 azbankgateways 等支付 SDK 使用。要实现这一目标,关键在于将原函数式视图升级为 DRF 的 API 视图,并正确处理 JSON 请求体与 CSRF 防护。
✅ 步骤一:改造 Django 视图为 APIView(推荐)或 @api_view
原 go_to_gateway_view 是一个普通 Django 函数视图(request 为 HttpRequest),默认解析 application/x-www-form-urlencoded 或表单数据,无法直接读取 application/json 请求体中的字段。因此需改用 Django REST Framework 提供的 @api_view 装饰器:
# views.py
from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework import status
from django.views.decorators.csrf import csrf_exempt
from django.utils.decorators import method_decorator
from azbankgateways import bankfactories, models as bank_models
from azbankgateways.exceptions import AZBankGatewaysException
import logging
@api_view(['POST'])
def go_to_gateway_api(request):
# ✅ 安全获取 JSON 请求体中的 amount 字段
try:
amount = int(request.data.get('amount', 0))
if amount <= 0:
return Response({'error': 'Amount must be a positive integer'}, status=status.HTTP_400_BAD_REQUEST)
except (ValueError, TypeError):
return Response({'error': 'Invalid amount format'}, status=status.HTTP_400_BAD_REQUEST)
user_mobile_number = request.data.get('mobile', '+989112221234') # 可选,也可从前端传入
factory = bankfactories.BankFactory()
try:
bank = factory.auto_create()
bank.set_request(request)
bank.set_amount(amount) # ✅ 动态传入金额
bank.set_client_callback_url('/callback-gateway')
bank.set_mobile_number(user_mobile_number)
bank_record = bank.ready()
# 返回重定向 URL 或网关信息(非 render)
return Response({
'gateway_url': bank.get_gateway_url(), # 推荐:返回跳转链接,由前端跳转
'tracking_code': bank_record.tracking_code
}, status=status.HTTP_200_OK)
except AZBankGatewaysException as e:
logging.critical(f"Bank gateway error: {e}")
return Response({'error': 'Payment initialization failed'}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)⚠️ 注意:
- 不要使用 request.POST["amount"] —— 它只对 multipart/form-data 或 x-www-form-urlencoded 有效;JSON 请求需用 request.data(DRF 自动解析)。
- 移除 render() 和 HTML 模板逻辑,API 视图应返回结构化 JSON 数据,跳转由前端控制更灵活、安全。
- 若需保留原有 render 流程(不推荐),可改用 JsonResponse 并确保 Content-Type: application/json,但需自行处理 CSRF 和会话。
✅ 步骤二:配置 URL 路由(替换旧路由)
在 urls.py 中,将原函数视图路由替换为新 API 视图:
立即学习“前端免费学习笔记(深入)”;
# urls.py
from django.urls import path
from . import views
urlpatterns = [
# ❌ 移除旧路由(如果存在):
# path('go-to-gateway/', views.go_to_gateway_view, name='go-to-gateway'),
# ✅ 新增 API 路由:
path('api/go-to-gateway/', views.go_to_gateway_api, name='go-to-gateway-api'),
]✅ 步骤三:完善 React 前端提交逻辑(含 CSRF 处理)
你的 React 代码基本正确,但需优化两点:CSRF Token 获取方式(避免依赖 jQuery)和 错误处理健壮性:
// AmountForm.jsx
import { useState } from 'react';
function getCookie(name) {
let cookieString = document.cookie;
let cookies = cookieString.split('; ');
for (let cookie of cookies) {
let [cookieName, cookieValue] = cookie.split('=');
if (cookieName === name) return decodeURIComponent(cookieValue);
}
return null;
}
function AmountForm() {
const [amount, setAmount] = useState('');
const [loading, setLoading] = useState(false);
const [error, setError] = useState('');
const handleSubmit = async (event) => {
event.preventDefault();
setLoading(true);
setError('');
const csrftoken = getCookie('csrftoken');
if (!csrftoken) {
setError('CSRF token missing. Please reload the page.');
setLoading(false);
return;
}
try {
const response = await fetch('http://127.0.0.1:8000/api/go-to-gateway/', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': csrftoken,
},
body: JSON.stringify({ amount: parseInt(amount, 10) }),
});
if (!response.ok) {
const errData = await response.json();
throw new Error(errData.error || 'Failed to initialize payment');
}
const result = await response.json();
console.log('Gateway ready:', result);
// ✅ 前端跳转至网关(比服务端 render 更可控)
window.location.href = result.gateway_url;
} catch (err) {
setError(err.message);
console.error(err);
} finally {
setLoading(false);
}
};
return (
<form onSubmit={handleSubmit}>
<label>
支付金额(单位:里亚尔):
<input
type="number"
value={amount}
onChange={(e) => setAmount(e.target.value)}
min="1000"
required
/>
</label>
<button type="submit" disabled={loading}>
{loading ? '处理中...' : '前往支付'}
</button>
{error && <p style={{ color: 'red' }}>{error}</p>}
</form>
);
}
export default AmountForm;✅ 关键注意事项总结
- CSRF 安全:Django 默认启用 CSRF 保护。确保前端请求携带有效的 X-CSRFToken(从 csrftoken Cookie 读取),且后端视图未被 @csrf_exempt 错误移除防护。
- 数据验证:永远不要信任前端输入!后端必须校验 amount 类型、范围与业务逻辑(如最小值、是否整数)。
- 响应设计:API 应返回轻量 JSON(如网关 URL),而非渲染页面——这符合前后端分离原则,也便于错误调试与移动端适配。
- 调试技巧:在 Django 视图开头添加 print(request.data) 和 print(request.content_type),确认请求体被正确解析。
完成以上改造后,React 输入的任意合法金额即可实时驱动 Django 支付流程,彻底告别硬编码,构建真正灵活、可维护的支付集成方案。
















