在React单页应用中接入支付宝和微信支付,前端的主要职责是渲染支付表单、展示二维码并处理唤起逻辑,而支付参数生成与签名校验必须放在后端完成。PC端通常以扫码或跳转收银台为主,移动端则需要通过H5页面唤起支付App或跳转至手机收银台。本文从实际工程角度梳理支付宝和微信支付在React中的接入方式与端侧适配。

一、前期准备:商户参数配置与环境隔离
接入支付宝和微信支付前,需要分别登录支付宝开放平台和微信商户平台,完成企业认证并创建应用或商户号。支付宝需要获取应用ID、应用私钥、支付宝公钥和网关地址;微信支付需要获取商户号、API密钥、AppID和证书文件。这些敏感信息不能放置在React前端代码中,否则会泄露签名能力,导致资金风险。
在开发阶段建议启用沙箱环境。支付宝沙箱提供独立的买家与卖家账号,微信支付则通过申请测试商户号进行联调。前端项目可以通过环境变量区分沙箱地址与生产地址,例如在.env文件中配置REACT_APP_PAYMENT_BASE_URL。每个支付请求由后端接口发起,前端只负责接收后端返回的支付参数,不参与任何签名计算。
后端需要提供统一下单接口,例如/api/create-payment,根据订单号、金额、支付渠道和客户端类型返回对应数据。对于React项目,合理的做法是将支付流程封装为独立的服务模块,统一处理支付宝与微信支付的差异。下面是一个请求下单接口的基础封装。
// payService.js
export async function createPayment(payload) {
const response = await fetch('/api/create-payment', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
});
if (!response.ok) throw new Error('下单失败');
return response.json();
}
二、支付宝支付:电脑网站支付与手机网站支付的React实现
支付宝电脑网站支付采用表单跳转模式。后端调用alipay.trade.page.pay接口后,会返回一段完整的HTML表单,该表单包含隐藏参数并自动提交到支付宝收银台。在React组件中,可以通过dangerouslySetInnerHTML将这段HTML渲染到页面中,支付宝脚本会自动执行提交,用户随后跳转到支付宝登录页面完成支付。
手机网站支付使用alipay.trade.wap.pay接口,返回的同样是一段自动提交表单。移动端场景下,用户将在手机浏览器中打开H5页面,渲染该表单后会唤起支付宝App或者显示手机收银台。需要注意的是,支付宝在微信内置浏览器中会被拦截,因此当检测到用户处于微信环境时应提示复制链接到外部浏览器打开。下面是一个根据设备类型渲染支付宝表单的组件。
import { useState, useEffect } from 'react';
const AlipayPay = ({ orderId, amount, isMobile }) => {
const [payForm, setPayForm] = useState('');
useEffect(() => {
createPayment({
orderId,
amount,
channel: 'alipay',
scene: isMobile ? 'wap' : 'page',
}).then(res => setPayForm(res.payForm));
}, [orderId, amount, isMobile]);
if (!payForm) return <p>正在创建支付订单...</p>;
return <div dangerouslySetInnerHTML={{ __html: payForm }} />;
};
该组件先请求后端获取支付表单,然后通过dangerouslySetInnerHTML注入页面。注意这里不能使用字符串拼接直接放入JSX,否则表单无法自动提交。支付宝返回的表单自带自动提交脚本,React不需要干预提交动作。渲染后浏览器会执行脚本跳转收银台。
三、微信支付:Native扫码支付与H5支付的React实现
微信支付PC端常用Native扫码支付。后端调用统一下单接口时设置trade_type为NATIVE,返回code_url,前端将该地址转换为二维码展示。用户使用微信扫描二维码后在手机确认付款,PC页面通过轮询订单状态来获取支付结果。二维码生成可以使用qrcode.react等React组件库,避免手动解析图片。
移动端微信H5支付则返回mweb_url,前端直接通过window.location.href跳转到该地址,微信会在外部浏览器中打开并拉起微信支付。H5支付要求商户在微信商户平台配置支付授权目录,并且后端请求中需要设置正确的Referer。在React中,可以在支付按钮点击后调用后端接口,拿到mweb_url后执行跳转。下面给出一个同时处理Native二维码和H5跳转的组件。
import QRCode from 'qrcode.react';
const WxPay = ({ orderId, amount, isMobile, onPaid }) => {
const [payData, setPayData] = useState(null);
const startPay = async () => {
const res = await createPayment({
orderId,
amount,
channel: 'wechat',
scene: isMobile ? 'h5' : 'native',
});
if (isMobile && res.mwebUrl) {
window.location.href = res.mwebUrl;
} else {
setPayData(res);
}
};
useEffect(() => {
if (!payData || isMobile) return;
const timer = setInterval(async () => {
const status = await queryOrder(orderId);
if (status === 'paid') {
clearInterval(timer);
onPaid();
}
}, 3000);
return () => clearInterval(timer);
}, [payData, orderId, isMobile]);
return (
<div>
{isMobile ? (
<button onClick={startPay}>立即支付</button>
) : payData ? (
<div>
<QRCode value={payData.codeUrl} size={220} />
<p>请使用微信扫码支付</p>
</div>
) : (
<button onClick={startPay}>创建支付二维码</button>
)}
</div>
);
};
上面代码中isMobile为true时点击按钮直接跳转mweb_url;PC端则显示二维码,并启动3秒间隔的轮询查询。实际项目中应把queryOrder封装为服务函数,并在组件卸载时停止轮询,避免内存泄漏。微信Native支付没有前端回调,只能依赖轮询或后端异步通知。
四、PC端与移动端适配:UA判断、跳转处理与微信内限制
React单页应用需要根据用户设备选择不同的支付场景。工具函数可以通过navigator.userAgent判断移动端,也可以在创建订单时由后端根据请求头判断。前端判断主要用于决定调用哪个接口以及展示扫码还是按钮。以下是一个常用的移动端判断函数。
const isMobileDevice = () => {
const ua = navigator.userAgent;
return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(ua);
};
移动端场景中还存在微信内置浏览器的限制。微信会屏蔽支付宝的跳转,因此在检测到微信环境时,建议显示遮罩层提示用户点击右上角使用浏览器打开。微信H5支付虽然能在微信内发起,但部分Android机型可能无法正常拉起,此时同样需要引导至外部浏览器。可以使用navigator.userAgent判断是否包含MicroMessenger来识别微信环境。
另外,支付宝H5支付在iOS Safari中打开时,如果页面使用了自定义URL Scheme,必须确保在用户点击动作中触发,否则可能被系统拦截。React中的支付按钮应该放置在真实的用户点击事件里,避免在异步回调中自动跳转。响应式布局方面,支付页面在移动端不要把按钮放在position:fixed的底部,容易与键盘或安全区域冲突。
五、订单状态同步与安全防护:回调验签、幂等处理与轮询优化
无论支付宝还是微信支付,最终的支付结果确认都不能只依赖前端页面跳转。前端跳转只能作为用户体验优化,真正的订单状态更新必须由后端接收异步通知并验签。支付宝和微信的异步通知都是POST请求,后端验证签名通过后更新订单,然后返回success字符串。前端可以通过轮询查询订单状态来及时更新界面。
前端轮询需要注意频率和退出清理。使用setInterval时如果组件卸载没有清除,会继续请求接口造成资源浪费。可以将轮询逻辑封装为自定义hook,在useEffect的cleanup中清除定时器。对于高并发场景,可以考虑使用WebSocket或SSE替代短轮询,减少服务器压力。下面是一个通用轮询hook示例。
const useOrderPolling = (orderId, onSuccess) => {
useEffect(() => {
if (!orderId) return;
let timer = null;
const query = async () => {
try {
const res = await fetch(`/api/order/${orderId}`);
const data = await res.json();
if (data.status === 'paid') {
clearInterval(timer);
onSuccess(data);
}
} catch (err) {
// 忽略网络异常,等待下一次轮询
}
};
query();
timer = setInterval(query, 3000);
return () => clearInterval(timer);
}, [orderId]);
};
安全方面,确保所有支付参数由后端生成,前端不能修改金额、商品描述等关键字段。支付回调地址必须为公网可达的HTTPS地址,并在商户平台正确配置。如果使用沙箱环境联调,注意沙箱与正式环境的接口域不同,切换环境时要同步修改后端配置。订单金额单位也要统一,支付宝以元为单位,微信支付以分为单位,避免因单位混淆导致资损。
以上就是React中接入支付宝与微信支付并适配PC端和移动端的完整思路。前端重点在于封装支付组件、判断设备类型、处理跳转与轮询,后端则负责下单、验签和状态同步。将两端的职责划分清楚,可以显著减少支付模块的联调成本。