Add WeChat in-app JSAPI recharge for g2.tkmind.cn.

Use JSAPI with bound openid inside WeChat instead of QR long-press, and include related auth redirect and admin UX fixes.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
john
2026-06-16 16:20:04 +08:00
parent bb70634a96
commit f818dd0307
15 changed files with 260 additions and 32 deletions
+19 -10
View File
@@ -3,18 +3,14 @@ import { createPortal } from 'react-dom';
import QRCode from 'qrcode';
import { createRechargeOrder, getBillingConfig, getRechargeOrder } from '../api/client';
import type { BillingConfig, RechargeOrder } from '../types';
import { invokeWechatJsapiPay, isWeChatBrowser } from '../utils/wechatPay';
function formatYuan(cents: number) {
return `¥${(cents / 100).toFixed(cents % 100 === 0 ? 0 : 2)}`;
}
function isWeChatBrowser() {
return /MicroMessenger/i.test(navigator.userAgent);
}
function detectPayScene(): 'native' | 'h5' {
// 微信内置浏览器走扫码:H5 收银台需商户单独开通,长按识别二维码即可支付
if (isWeChatBrowser()) return 'native';
function detectPayScene(): 'native' | 'h5' | 'jsapi' {
if (isWeChatBrowser()) return 'jsapi';
return /Android|iPhone|iPad|iPod|Mobile/i.test(navigator.userAgent) ? 'h5' : 'native';
}
@@ -156,6 +152,14 @@ export function RechargeModal({
try {
const created = await createRechargeOrderWithFallback(selectedCents);
setOrder(created);
if (created.payMode === 'jsapi' && created.jsapiParams) {
startPolling(created.id);
const result = await invokeWechatJsapiPay(created.jsapiParams);
if (result === 'cancel') {
setError('已取消支付,可重新发起');
}
return;
}
if (created.payMode === 'h5' && created.h5Url && !redirectedRef.current) {
redirectedRef.current = true;
window.location.href = created.h5Url;
@@ -236,10 +240,15 @@ export function RechargeModal({
{order?.payMode === 'native' && qrDataUrl && (
<div className="recharge-qr-panel">
<img src={qrDataUrl} alt="微信支付二维码" className="recharge-qr" />
<p className="recharge-muted">使</p>
{expireLabel && <p className="recharge-muted"> {expireLabel} </p>}
</div>
)}
{order?.payMode === 'jsapi' && (
<div className="recharge-h5-panel">
<p className="recharge-muted">
{isWeChatBrowser()
? '长按下方二维码,选择「识别图中二维码」完成支付'
: '请使用微信扫一扫完成支付'}
{polling ? '请在微信支付弹窗中完成付款…' : '点击「微信支付」后将直接调起微信收银台'}
</p>
{expireLabel && <p className="recharge-muted"> {expireLabel} </p>}
</div>