feat: 延期微信支付在微信内直接拉起
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 19m17s

- 微信支付下单带 app_type;拿到 pay_config 后与套餐购买一样拉起微信支付,继续支付同样处理
- 微信内打开时支付方式默认微信;取消支付保留同一请求号,再次确认不重复建单
This commit is contained in:
邹进
2026-11-07 21:57:26 +09:00
parent b035105f80
commit d13be3580f
6 changed files with 26 additions and 12 deletions

View File

@@ -1,4 +1,5 @@
import request from '@/utils/request.js';
import { APP_TYPE } from '@/utils/env.js';
// 实名即生效套餐沉默期 H5 接口(后端 Iteration/8-11,/api/c/v1/package-silence)。
// 资格、试算、下单关闭默认错误提示,由页面按沉默期错误码给出客户能看懂的文案。
@@ -24,10 +25,13 @@ export const packageSilenceApi = {
},
createOrder({ package_usage_id, days, payment_method, request_id }) {
const data = { package_usage_id, days, payment_method, request_id };
// 微信支付带上应用类型:后端在微信内直接下 JSAPI 单,返回 pay_config 拉起支付
if (payment_method === 'wechat') data.app_type = APP_TYPE;
return request({
url: `${BASE}/orders`,
method: 'POST',
data: { package_usage_id, days, payment_method, request_id },
data,
showError: false
});
},

View File

@@ -155,6 +155,7 @@
));
const showToast = (title) => uni.showToast({ title, icon: 'none', duration: 2500 });
const isInWechatBrowser = () => typeof navigator !== 'undefined' && /micromessenger/i.test(navigator.userAgent);
const loadEligibility = async () => {
loading.value = true;
@@ -166,7 +167,7 @@
assetApi.getInfo(userStore.state.identifier).catch(() => null)
]);
eligibility.value = data;
paymentOptions.value = getSilencePaymentOptions(info?.allowed_payment_methods);
paymentOptions.value = getSilencePaymentOptions(info?.allowed_payment_methods, isInWechatBrowser());
if (!paymentOptions.value.some((option) => option.value === paymentMethod.value)) {
paymentMethod.value = paymentOptions.value[0]?.value || '';
}
@@ -248,15 +249,17 @@
request_id: requestIds.get(days, paymentMethod.value)
});
lastOrderId.value = order?.id || 0;
if (!order?.qr_content) {
if (!order?.qr_content && !order?.pay_config) {
showToast('订单已创建,请在"我的延期订单"中继续支付');
openOrders();
return;
}
await openSilencePayment(order, SILENCE_REFRESH_TARGET.EXTENSION);
const result = await openSilencePayment(order, SILENCE_REFRESH_TARGET.EXTENSION);
// 微信内直接支付完成:立即查订单结果(不经过支付页回跳)
if (result?.paid) await checkLastOrder();
} catch (error) {
// 网络异常时后端可能已建单:保留 request_id,客户再次点击会拿到同一笔订单。
if (!error?.paymentResultUnknown) requestIds.reset();
// 网络异常时后端可能已建单、取消微信支付时订单仍待支付:保留 request_id,客户再次点击会拿到同一笔订单。
if (!error?.paymentResultUnknown && !error?.cancelled) requestIds.reset();
showToast(getSilenceErrorMessage(error, '下单失败,请稍后重试'));
if (Number(error?.code) === 1271) await loadEligibility();
} finally {

View File

@@ -52,7 +52,7 @@
];
});
const canContinuePay = computed(() => order.value?.status === 'pending_payment' && !!order.value?.qr_content);
const canContinuePay = computed(() => order.value?.status === 'pending_payment' && (!!order.value?.qr_content || !!order.value?.pay_config));
const canCancel = computed(() => order.value?.status === 'pending_payment');
const cancelling = ref(false);
@@ -91,7 +91,8 @@
const continuePay = async () => {
try {
await openSilencePayment(order.value, SILENCE_REFRESH_TARGET.ORDER_DETAIL);
const result = await openSilencePayment(order.value, SILENCE_REFRESH_TARGET.ORDER_DETAIL);
if (result?.paid) await load();
} catch (error) {
uni.showToast({ title: error?.message || '拉起支付失败', icon: 'none' });
}

View File

@@ -19,6 +19,7 @@ test('延期支付方式只取 wechat、alipay 与资产允许方式的交集,
assert.deepEqual(getSilencePaymentOptions(['wechat', 'wallet', 'alipay']).map((item) => item.value), ['alipay', 'wechat']);
assert.deepEqual(getSilencePaymentOptions(['wallet']), []);
assert.deepEqual(getSilencePaymentOptions(undefined), []);
assert.deepEqual(getSilencePaymentOptions(['alipay', 'wechat'], true).map((item) => item.value), ['wechat', 'alipay']);
});
test('延期天数校验:整数且在 min~max 内', () => {

View File

@@ -1,4 +1,4 @@
import { markPendingPaymentRefresh, openAlipayPayment } from '@/utils/payment.js';
import { isValidWechatPayConfig, markPendingPaymentRefresh, openAlipayPayment, wechatH5Pay } from '@/utils/payment.js';
import { isPaymentLink } from '@/utils/silence.js';
export const SILENCE_REFRESH_TARGET = {
@@ -19,6 +19,10 @@ const isInWechat = () => typeof navigator !== 'undefined' && /micromessenger/i.t
* 在微信外且为链接时直接跳转;其余情况进入延期支付页展示二维码与链接。
*/
export const openSilencePayment = (order, refreshTarget) => {
// 微信内直接支付(JSAPI):与套餐购买一致,用 pay_config 拉起微信支付
if (order?.pay_config && isValidWechatPayConfig(order.pay_config)) {
return wechatH5Pay(order.pay_config).then(() => ({ paid: true }));
}
const content = String(order?.qr_content || '').trim();
if (!content) return Promise.reject(new Error('支付内容为空'));

View File

@@ -67,10 +67,11 @@ export const getSilenceErrorMessage = (error, fallback = '操作失败,请稍
export const isSilenceFeatureDisabled = (error) => Number(error?.code) === 1274;
/** 延期可用支付方式:资产允许的支付方式与 wechat、alipay 取交集,支付宝优先 */
export const getSilencePaymentOptions = (allowedMethods) => {
/** 延期可用支付方式:资产允许的支付方式与 wechat、alipay 取交集;微信内打开时微信优先,否则支付宝优先 */
export const getSilencePaymentOptions = (allowedMethods, preferWechat = false) => {
const allowed = Array.isArray(allowedMethods) ? allowedMethods : [];
return SILENCE_PAYMENT_METHODS
const order = preferWechat ? ['wechat', 'alipay'] : SILENCE_PAYMENT_METHODS;
return order
.filter((method) => allowed.includes(method))
.map((value) => ({ value, label: SILENCE_PAYMENT_LABELS[value] }));
};