如何在 React Hooks 中匹配支付 callback URL
支付老李
结论
把 callback URL 中会变化的交易号设为路由参数:
/confirmPayment/:transId
以这个 URL 为例:
http://localhost:8081/confirmPayment/trans_202012201871?trxref=trans_202012201871&reference=trans_202012201871
React Router 会将路径中的 trans_202012201871 解析为 transId。问号后面的 trxref 和 reference 属于查询参数,不参与路由匹配,需要通过 URLSearchParams 读取。
React Router v6 配置
先声明路由:
import { BrowserRouter, Routes, Route } from "react-router-dom";
import ConfirmPayment from "./ConfirmPayment";
export default function App() {
return (
<BrowserRouter>
<Routes>
<Route
path="/confirmPayment/:transId"
element={<ConfirmPayment />}
/>
</Routes>
</BrowserRouter>
);
}
再在落地页组件中分别读取路径参数和查询参数:
import { useEffect } from "react";
import { useLocation, useParams } from "react-router-dom";
export default function ConfirmPayment() {
const { transId } = useParams();
const { search } = useLocation();
const query = new URLSearchParams(search);
const trxref = query.get("trxref");
const reference = query.get("reference");
useEffect(() => {
if (!transId || !trxref || !reference) {
return;
}
// 调用自己的后端接口,由后端向支付平台确认交易状态。
// 不要仅凭 URL 参数判断支付成功。
console.log({ transId, trxref, reference });
}, [transId, trxref, reference]);
return (
<main>
<h1>正在确认支付</h1>
<p>Transaction ID: {transId}</p>
<p>trxref: {trxref}</p>
<p>reference: {reference}</p>
</main>
);
}
较新版本的 React Router 还可以使用 useSearchParams,查询参数的读取会更简洁:
import { useParams, useSearchParams } from "react-router-dom";
export default function ConfirmPayment() {
const { transId } = useParams();
const [searchParams] = useSearchParams();
const trxref = searchParams.get("trxref");
const reference = searchParams.get("reference");
return (
<div>
<p>Transaction ID: {transId}</p>
<p>trxref: {trxref}</p>
<p>reference: {reference}</p>
</div>
);
}
React Router v5 配置
如果项目使用的是 React Router v5,路由需要这样写:
import { BrowserRouter, Route, Switch } from "react-router-dom";
import ConfirmPayment from "./ConfirmPayment";
export default function App() {
return (
<BrowserRouter>
<Switch>
<Route
exact
path="/confirmPayment/:transId"
component={ConfirmPayment}
/>
</Switch>
</BrowserRouter>
);
}
组件中仍可使用 useParams 和 useLocation:
import { useLocation, useParams } from "react-router-dom";
export default function ConfirmPayment() {
const { transId } = useParams();
const { search } = useLocation();
const query = new URLSearchParams(search);
const trxref = query.get("trxref");
const reference = query.get("reference");
return (
<div>
<p>Transaction ID: {transId}</p>
<p>trxref: {trxref}</p>
<p>reference: {reference}</p>
</div>
);
}
为什么要这样配置
这个 URL 包含路径和查询字符串两部分:
路径:
/confirmPayment/trans_202012201871
查询字符串:
?trxref=trans_202012201871&reference=trans_202012201871
动态路由参数只匹配路径部分:
/confirmPayment/:transId
所以 transId 的值是:
transId === "trans_202012201871"
查询字符串中的两个参数则分别是:
trxref === "trans_202012201871"
reference === "trans_202012201871"
transId 这个名称可以修改,但路由声明必须与 useParams() 中读取的名称一致。例如,将路由参数写成 :id 后,组件中也要读取 id。
支付确认时的注意事项
callback 页面适合接收跳转结果和展示状态,但不能只根据 transId、trxref 或 reference 判断支付是否成功,因为用户可以手动修改这些 URL 参数。
更可靠的处理流程是:
- React 页面从 URL 中读取交易标识。
- React 将交易标识发送到自己的后端。
- 后端使用服务端凭证向支付平台查询交易状态,或者检查已经收到的支付 Webhook。
- 后端核对金额、币种、订单号、收款方和交易状态。
- React 根据后端返回的可信结果,显示成功、失败或处理中。
部署服务器还需要支持 SPA 路由回退。用户直接访问 /confirmPayment/... 时,服务器应返回 React 应用的 index.html,否则刷新 callback 页面可能会出现 404。生产环境中的 callback 地址也应使用支付平台能够访问的 HTTPS 域名,不能使用 localhost。
备注:内容仅供参考。
