Files
dukang/apps/mini-user/src/pages/pay/index.tsx
T
jacy 233ed0af3b
CI / verify (pull_request) Has been cancelled
v3.5.1 版本更新
2026-08-19 15:54:51 +08:00

247 lines
7.6 KiB
TypeScript

import { useCallback, useEffect, useState } from 'react';
import { View, Text } from '@tarojs/components';
import '../../styles/order.css';
import Taro, { useDidShow, useRouter } from '@tarojs/taro';
import PageShell from '../../components/PageShell';
import SubPageHeader from '../../components/SubPageHeader';
import WechatLoginButton from '../../components/WechatLoginButton';
import { ensurePayReady } from '../../lib/pay-ready';
import {
authorizeWechatForPay,
fetchClientConfig,
fetchUserProfile,
isWechatAuthRequiredError,
needsWechatAuthForPay,
payOrder,
saveWechatLoginResult,
} from '../../lib/pay-wechat';
import { applyWechatLoginResult } from '../../lib/wechat-auth';
import { isWechatEnv } from '../../lib/weixin';
import { goLogin } from '../../lib/auth-nav';
import { request, toast } from '../../lib/api';
export default function PayPage() {
const router = useRouter();
const orderId = router.params.orderId ?? '';
const [loading, setLoading] = useState(false);
const [authLoading, setAuthLoading] = useState(false);
const [mockMode, setMockMode] = useState(true);
const [needsWechatAuth, setNeedsWechatAuth] = useState(false);
const [msg, setMsg] = useState('');
const [orderNo, setOrderNo] = useState('');
const [payAmount, setPayAmount] = useState('—');
const [deliveryType, setDeliveryType] = useState('');
const returnPath = orderId
? `/pages/pay/index?orderId=${orderId}`
: '/pages/pay/index';
const refreshPayReadiness = useCallback(async () => {
try {
const [config, profile] = await Promise.all([fetchClientConfig(), fetchUserProfile()]);
setMockMode(config.mockPay);
setNeedsWechatAuth(needsWechatAuthForPay(config, profile));
return profile;
} catch {
return null;
}
}, []);
useDidShow(() => {
void refreshPayReadiness();
});
useEffect(() => {
if (!orderId) return;
if (process.env.TARO_ENV === 'weapp') {
void ensurePayReady(returnPath);
}
}, [orderId, returnPath]);
useEffect(() => {
if (!orderId) {
setOrderNo('');
setPayAmount('—');
return;
}
request<{
orderNo?: string;
payAmount?: number | string;
totalAmount?: number | string;
deliveryType?: string;
}>(`/trade/orders/${orderId}`)
.then((order) => {
setOrderNo(order.orderNo || '');
setDeliveryType(order.deliveryType || '');
const amount = Number(order.payAmount ?? order.totalAmount ?? 0);
if (Number.isFinite(amount) && amount > 0) {
setPayAmount(amount.toFixed(2));
}
})
.catch((e) => {
setOrderNo('');
setDeliveryType('');
toast(e instanceof Error ? e.message : '加载订单失败');
});
}, [orderId]);
async function wechatAuthorize() {
setAuthLoading(true);
setMsg('');
try {
if (!isWechatEnv()) {
setMsg('请在微信内打开以授权微信支付');
return;
}
if (process.env.TARO_ENV === 'weapp') {
const ready = await ensurePayReady(returnPath);
if (ready) await refreshPayReadiness();
return;
}
const result = await authorizeWechatForPay();
if (result) {
if (result.needBindPhone && result.wxSessionKey) {
goLogin(returnPath, { bindMode: '1', wxSessionKey: result.wxSessionKey });
return;
}
if (saveWechatLoginResult(result) || applyWechatLoginResult(result)) {
setMsg('');
await refreshPayReadiness();
toast('微信授权成功', 'success');
}
}
} catch (e) {
setMsg(e instanceof Error ? e.message : '微信授权失败');
} finally {
setAuthLoading(false);
}
}
async function pay() {
if (!orderId) {
toast('订单不存在');
return;
}
if (needsWechatAuth) {
setMsg('请先完成微信授权后再支付');
if (process.env.TARO_ENV === 'h5') {
await wechatAuthorize();
} else {
await ensurePayReady(returnPath);
}
return;
}
const ready = await ensurePayReady(returnPath);
if (!ready) return;
setLoading(true);
setMsg('');
try {
const status = await payOrder(orderId);
if (status === 'pending') {
toast('支付结果确认中,请稍后在订单列表查看');
} else {
toast('支付成功', 'success');
}
if (deliveryType === 'ON_SITE_PICKUP') {
// 现场提货支付即完成 → 订单详情;from=pay 返回强制回首页,避免 navigateBack 退出小程序
Taro.reLaunch({ url: `/pages/order-detail/index?id=${orderId}&from=pay` });
} else {
Taro.reLaunch({ url: '/pages/orders/index?tab=paid&from=pay' });
}
} catch (e) {
if (isWechatAuthRequiredError(e)) {
setNeedsWechatAuth(true);
setMsg('微信支付需要先完成微信授权');
return;
}
const message = e instanceof Error ? e.message : '支付失败';
if (message.includes('取消')) {
setMsg('已取消支付');
return;
}
setMsg(message);
toast(message);
} finally {
setLoading(false);
}
}
return (
<PageShell variant="sub" className="pay-page" hasFixedFooter>
<SubPageHeader title="收银台" />
<View className="sub-page-body">
<View className="pay-status">
<View className="pay-status-icon">
<Text>¥</Text>
</View>
<Text className="pay-status-title">
{needsWechatAuth ? '需完成微信授权' : '待支付'}
</Text>
<Text className="pay-status-amount">¥{payAmount}</Text>
</View>
{needsWechatAuth ? (
<View className="pay-wechat-auth-card">
<Text className="pay-wechat-auth-title">尚未授权微信</Text>
<Text className="pay-wechat-auth-desc">
授权后可安全调起微信支付,不会重复扣款
</Text>
<WechatLoginButton
loading={authLoading}
onClick={() => void wechatAuthorize()}
/>
</View>
) : null}
<View className="order-card">
<View className="order-row">
<Text className="order-row-label">订单号</Text>
<Text className="order-row-value">{orderNo || '—'}</Text>
</View>
<View className="order-row">
<Text className="order-row-label">支付方式</Text>
<Text className="order-row-value">微信支付</Text>
</View>
<View className="order-row">
<Text className="order-row-label">说明</Text>
<Text className="order-row-value">
{mockMode ? 'Mock 模式由服务端直接标记已付款' : '将调起微信收银台'}
</Text>
</View>
</View>
{msg ? (
<Text className="pay-wechat-auth-msg" style={{ marginTop: 12 }}>
{msg}
</Text>
) : null}
</View>
<View className="pay-bar">
<View
className="order-confirm-submit"
style={{ flex: 1, opacity: loading || needsWechatAuth ? 0.7 : 1 }}
onClick={() => {
if (loading) return;
if (needsWechatAuth) {
void wechatAuthorize();
return;
}
void pay();
}}
>
<Text>
{loading
? '支付中…'
: needsWechatAuth
? authLoading
? '授权中…'
: '微信一键授权'
: '立即支付'}
</Text>
</View>
</View>
</PageShell>
);
}