支付宝兼容

This commit is contained in:
2026-03-09 09:07:58 +08:00
parent 069677957e
commit b3836b8bf2
31 changed files with 2382 additions and 307 deletions
+176 -18
View File
@@ -176,6 +176,8 @@
<view class="form-item">
<text class="form-label">收货地区</text>
<!-- 非支付宝小程序:使用多列 picker -->
<!-- #ifndef MP-ALIPAY -->
<picker mode="multiSelector" :range="regionColumns" range-key="name" :value="regionIndexes"
@change="onRegionChange" @columnchange="onRegionColumnChange">
<view class="form-input region-selector">
@@ -187,6 +189,19 @@
<text class="arrow-icon"></text>
</view>
</picker>
<!-- #endif -->
<!-- 支付宝小程序:使用自定义弹窗 + picker-view -->
<!-- #ifdef MP-ALIPAY -->
<view class="form-input region-selector" @click="showRegionPicker = true">
<text v-if="addressForm.province && addressForm.city && addressForm.district"
class="region-text">
{{ addressForm.province }} {{ addressForm.city }} {{ addressForm.district }}
</text>
<text v-else class="input-placeholder">请选择省市区</text>
<text class="arrow-icon"></text>
</view>
<!-- #endif -->
</view>
<view class="form-item">
@@ -254,6 +269,49 @@
</view>
</view>
<!-- 支付宝小程序地区选择弹窗 -->
<!-- #ifdef MP-ALIPAY -->
<view class="popup-mask" v-if="showRegionPicker" @click="closeRegionPicker">
<view class="popup-container" @click.stop>
<view class="address-popup">
<view class="popup-header">
<text class="popup-title">选择省市区</text>
<view class="close-btn" @click="closeRegionPicker">
<text class="close-icon">✕</text>
</view>
</view>
<view class="form-section">
<picker-view :value="regionIndexes" @change="onAliRegionChange"
indicator-style="height: 50px;">
<picker-view-column>
<view v-for="item in regionColumns[0]" :key="item.code">
{{ item.name }}
</view>
</picker-view-column>
<picker-view-column>
<view v-for="item in regionColumns[1]" :key="item.code">
{{ item.name }}
</view>
</picker-view-column>
<picker-view-column>
<view v-for="item in regionColumns[2]" :key="item.code">
{{ item.name }}
</view>
</picker-view-column>
</picker-view>
</view>
<view class="popup-footer">
<view class="confirm-btn" @click="confirmAliRegion">
<text>确定</text>
</view>
</view>
</view>
</view>
</view>
<!-- #endif -->
</view>
</template>
@@ -340,6 +398,7 @@
const showSkuPopup = ref(false)
const showAddressPopup = ref(false)
const showAddressDisplay = ref(false) // 地址展示弹窗
const showRegionPicker = ref(false) // 支付宝地区选择弹窗
// 计算是否已有地址
const hasAddress = computed(() => {
@@ -489,6 +548,45 @@
addressForm.value.districtCode = district.code
}
// 支付宝小程序 picker-view 列变化
const onAliRegionChange = (e) => {
const newVal = e.detail.value || []
const oldVal = regionIndexes.value || []
// 找出发生变化的列
let column = -1
for (let i = 0; i < newVal.length; i++) {
if (newVal[i] !== oldVal[i]) {
column = i
break
}
}
if (column !== -1) {
onRegionColumnChange({
detail: {
column,
value: newVal[column]
}
})
}
}
// 关闭支付宝地区弹窗
const closeRegionPicker = () => {
showRegionPicker.value = false
}
// 支付宝地区选择“确定”
const confirmAliRegion = () => {
onRegionChange({
detail: {
value: regionIndexes.value
}
})
closeRegionPicker()
}
// 获取用户收货地址
const fetchUserAddress = async () => {
try {
@@ -767,7 +865,7 @@
})
}
// 创建订单并支付
// 创建订单并支付(接入商品多支付平台方案:微信 / 支付宝,小程序端)
const createOrder = async () => {
try {
uni.showLoading({
@@ -782,13 +880,24 @@
savedAddress.value.receiverAddress :
`${addressForm.value.province}${addressForm.value.city}${addressForm.value.district}${addressForm.value.receiverAddress}`
// 根据当前运行环境确定支付平台
let paymentPlatform = 'WECHAT' // 默认微信
// #ifdef MP-ALIPAY
paymentPlatform = 'ALIPAY'
// #endif
// #ifdef H5
// H5 预留 Antom 支付,这里暂时仍按微信处理,如需接入可改为 ANTOM 并补充 paymentType / osType
paymentPlatform = 'WECHAT'
// #endif
const orderData = {
skuId: selectedSku.value.skuId,
quantity:quantity.value,
quantity: quantity.value,
receiverName: addressData.receiverName,
receiverPhone: addressData.receiverPhone,
receiverAddress: fullAddress, // 传递完整地址(省市区+详细地址)
remark: addressForm.value.remark || ''
remark: addressForm.value.remark || '',
paymentPlatform // WECHAT / ALIPAY /(预留)ANTOM
}
console.log('创建订单数据:', orderData)
@@ -800,11 +909,12 @@
if (res && res.code === 200 && res.data) {
uni.hideLoading()
// 调用微信支付
// 统一获取平台订单号(商品统一支付订单号)
const outOrderNo = res.data.OutOrderNo || res.data.outOrderNo
// ====================== 微信小程序支付 ======================
// #ifdef MP-WEIXIN
const payParams = res.data
// 保存订单ID,用于取消订单
const orderId = payParams.OutOrderNo || res.data.OutOrderNo
uni.requestPayment({
timeStamp: payParams.timeStamp,
nonceStr: payParams.nonceStr,
@@ -831,26 +941,18 @@
},
fail: async (payErr) => {
console.error('支付失败:', payErr)
// 判断是用户取消还是支付失败
if (payErr.errMsg && payErr.errMsg.includes('cancel')) {
// 用户取消支付,调用取消订单接口
// 用户取消支付,这里预留调用取消订单接口
try {
// uni.showLoading({
// title: '正在取消订单...',
// mask: true
// })
// await cancelProductOrder(orderId)
uni.hideLoading()
// await cancelProductOrder(outOrderNo)
uni.showToast({
title: '支付已取消',
icon: 'none'
})
} catch (cancelError) {
console.error('取消订单失败:', cancelError)
uni.hideLoading()
uni.showToast({
title: '支付已取消',
icon: 'none'
@@ -865,6 +967,62 @@
}
}
})
// #endif
// ====================== 支付宝小程序支付 ======================
// #ifdef MP-ALIPAY
console.log(res.data,'支付宝支付参数');
const tradeNO = res.data.tradeNo
if (!tradeNO) {
uni.showToast({
title: '未获取到支付宝支付参数',
icon: 'none'
})
return
}
my.tradePay({
tradeNO,
success: (payRes) => {
console.log('支付宝支付结果:', payRes)
if (payRes.resultCode === '9000') {
uni.showToast({
title: '支付成功',
icon: 'success',
duration: 2000
})
resetForm()
setTimeout(() => {
uni.switchTab({
url: '/subPackages/business/device-orderList'
})
}, 2000)
} else {
uni.showToast({
title: '支付失败,请重试',
icon: 'none'
})
}
},
fail: () => {
uni.showToast({
title: '支付失败,请重试',
icon: 'none'
})
}
})
// #endif
// ====================== H5 环境(预留 Antom 支付) ======================
// #ifdef H5
uni.showToast({
title: '当前环境暂不支持购买,请使用微信或支付宝小程序',
icon: 'none'
})
// #endif
} else {
uni.hideLoading()
uni.showToast({
+89 -21
View File
@@ -335,25 +335,40 @@
navigateToOrderDetail(order);
};
// 立即支付
// 立即支付(对齐 device-goods.vue,多平台支付)
const handlePayment = async (order) => {
try {
uni.showLoading({
title: '正在创建支付...',
mask: true
});
console.log(order);
// 调用后端创建微信支付订单接口(使用订单号
// const res = await createWxPayment(order.orderNo);
const res = await createProductOrder({orderNo:order.orderNo});
console.log('订单列表立即支付,订单信息:', order);
// 根据当前运行环境确定支付平台(与 device-goods.vue 保持一致
let paymentPlatform = 'WECHAT'; // 默认微信
// #ifdef MP-ALIPAY
paymentPlatform = 'ALIPAY';
// #endif
// #ifdef H5
// H5 预留 Antom 支付,这里暂时仍按微信处理,如需接入可改为 ANTOM 并补充 paymentType / osType
paymentPlatform = 'WECHAT';
// #endif
// 使用商品多支付平台统一下单接口,对已有订单进行支付
const res = await createProductOrder({
orderNo: order.orderNo,
paymentPlatform
});
if (res && res.code === 200 && res.data) {
uni.hideLoading();
// 统一获取平台订单号(商品统一支付订单号)
const outOrderNo = res.data.OutOrderNo || res.data.outOrderNo;
// ====================== 微信小程序支付 ======================
// #ifdef MP-WEIXIN
const payParams = res.data;
// 调用微信支付
uni.requestPayment({
timeStamp: payParams.timeStamp,
nonceStr: payParams.nonceStr,
@@ -367,25 +382,25 @@
icon: 'success',
duration: 2000
});
// 刷新订单列表
const statusArray = orderStatusTabs[currentTab.value].status;
const statusValue = statusArray.length === 0 ? undefined : statusArray[0];
await loadOrderList(statusValue);
},
fail: async (err) => {
console.error('支付失败:', err);
fail: async (payErr) => {
console.error('支付失败:', payErr);
// 判断是用户取消还是支付失败
if (err.errMsg && err.errMsg.includes('cancel')) {
// 用户取消支付,调用取消订单接口
if (payErr.errMsg && payErr.errMsg.includes('cancel')) {
// 用户取消支付,这里预留调用取消订单接口
try {
// await cancelProductOrder(order.id || order.orderId);
// uni.showToast({
// title: '支付已取消',
// icon: 'none'
// });
// await cancelProductOrder(outOrderNo || order.orderNo);
uni.showToast({
title: '支付已取消',
icon: 'none'
});
// 刷新订单列表
const statusArray = orderStatusTabs[currentTab.value].status;
const statusValue = statusArray.length === 0 ? undefined : statusArray[0];
@@ -406,6 +421,59 @@
}
}
});
// #endif
// ====================== 支付宝小程序支付 ======================
// #ifdef MP-ALIPAY
console.log(res.data, '支付宝支付参数');
const tradeNO = res.data.tradeNo || res.data.tradeNO;
if (!tradeNO) {
uni.showToast({
title: '未获取到支付宝支付参数',
icon: 'none'
});
return;
}
my.tradePay({
tradeNO,
success: async (payRes) => {
console.log('支付宝支付结果:', payRes);
if (payRes.resultCode === '9000') {
uni.showToast({
title: '支付成功',
icon: 'success',
duration: 2000
});
// 支付成功后刷新订单列表
const statusArray = orderStatusTabs[currentTab.value].status;
const statusValue = statusArray.length === 0 ? undefined : statusArray[0];
await loadOrderList(statusValue);
} else {
uni.showToast({
title: '支付失败,请重试',
icon: 'none'
});
}
},
fail: () => {
uni.showToast({
title: '支付失败,请重试',
icon: 'none'
});
}
});
// #endif
// ====================== H5 环境(预留 Antom 支付) ======================
// #ifdef H5
uni.showToast({
title: '当前环境暂不支持购买,请使用微信或支付宝小程序',
icon: 'none'
});
// #endif
} else {
uni.hideLoading();
uni.showToast({
+178 -93
View File
@@ -176,9 +176,6 @@ import {
createCouponPayment,
cancelCouponPayment
} from '@/config/api/coupon.js'
// import {
// cancelMemberCardPayment
// } from '@/config/api/member.js'
import {
createMemberCardPayment,
getMemberCardsByPosition,
@@ -353,7 +350,48 @@ const selectProduct = (product) => {
const orderNo = ref('')
// 处理购买
// 获取当前支付平台(前端维度)
const getPaymentPlatform = () => {
// 小程序环境
// #ifdef MP-WEIXIN
return 'WECHAT'
// #endif
// #ifdef MP-ALIPAY
return 'ALIPAY'
// #endif
// H5 默认使用 ANTOM
// #ifdef H5
return 'ANTOM'
// #endif
return 'WECHAT'
}
// 支付宝 tradePay 兼容:优先 tradeNO,其次 orderStr
const alipayTradePay = ({ tradeNo, orderStr, onSuccess, onFail }) => {
// #ifdef MP-ALIPAY
const tradeNO = tradeNo
if (tradeNO) {
my.tradePay({
tradeNO,
success: onSuccess,
fail: onFail
})
return
}
if (orderStr) {
my.tradePay({
orderStr,
success: onSuccess,
fail: onFail
})
return
}
// #endif
onFail && onFail(new Error('未获取到支付宝支付参数'))
}
// 处理购买(会员卡 / 优惠券),内部使用商品多支付平台方案下的统一思路
const handleBuy = async () => {
if (!selectedProduct.value) {
uni.showToast({
@@ -363,63 +401,89 @@ const handleBuy = async () => {
return
}
// 会员卡购买
const paymentPlatform = getPaymentPlatform()
// 会员卡购买(按接口文档:POST /app/member/pay
if (currentTab.value === 'card') {
try {
uni.showLoading({
title: '正在创建订单...'
})
const res = await createMemberCardPayment(selectedProduct.value.id)
const res = await createMemberCardPayment(selectedProduct.value.id, paymentPlatform)
uni.hideLoading()
if (res.code === 200 && res.data) {
orderNo.value = res.data.OutOrderNo;
// 调起微信支付
uni.requestPayment({
timeStamp: res.data.timeStamp,
nonceStr: res.data.nonceStr,
package: res.data.packageValue || res.data.package,
signType: res.data.signType || 'MD5',
paySign: res.data.paySign,
success: (payRes) => {
uni.showToast({
title: '支付成功',
icon: 'success'
})
// 支付成功后,跳转到我的会员卡页面
setTimeout(() => {
uni.navigateTo({
url: '/subPackages/business/my-card'
})
}, 1500)
},
fail: (err) => {
console.error('支付失败:', err)
console.log('支付失败详细信息:', err.errMsg.includes('cancel'));
if (err.errMsg && err.errMsg.includes('cancel')) {
if (orderNo.value) {
cancelMemberCardPayment(orderNo.value)
.then(cancelRes => {
console.log('取消支付订单成功:', cancelRes);
})
.catch(cancelErr => {
console.error('取消支付订单失败:', cancelErr);
});
// 不同平台分别发起支付(字段按文档)
// 微信小程序
// #ifdef MP-WEIXIN
if (paymentPlatform === 'WECHAT') {
// 会员卡订单号:OutOrderNo(文档)
orderNo.value = res.data.OutOrderNo
uni.requestPayment({
timeStamp: res.data.timeStamp,
nonceStr: res.data.nonceStr,
package: res.data.package,
signType: res.data.signType || 'MD5',
paySign: res.data.paySign,
success: () => {
uni.showToast({ title: '支付成功', icon: 'success' })
setTimeout(() => {
uni.navigateTo({ url: '/subPackages/business/my-card' })
}, 1500)
},
fail: (err) => {
console.error('支付失败:', err)
if (err.errMsg && err.errMsg.includes('cancel')) {
// 取消支付(本项目取消接口走 device 侧 cancel
orderNo.value && cancelMemberCardPayment(orderNo.value).catch(() => {})
uni.showToast({ title: '已取消支付', icon: 'none' })
} else {
uni.showToast({ title: '支付失败', icon: 'none' })
}
uni.showToast({
title: '已取消支付',
icon: 'none'
})
} else {
uni.showToast({
title: '支付失败',
icon: 'none'
})
}
})
return
}
// #endif
// 支付宝小程序
// #ifdef MP-ALIPAY
if (paymentPlatform === 'ALIPAY') {
// 文档返回:tradeNo / outTradeNo;也兼容 orderStr
const tradeNo = res.data.tradeNo || res.data.tradeNO
const payForm = res.data.payForm || res.data.orderStr
alipayTradePay({
tradeNo,
orderStr: payForm,
onSuccess: (payRes) => {
if (payRes.resultCode === '9000') {
uni.showToast({ title: '支付成功', icon: 'success' })
setTimeout(() => {
uni.navigateTo({ url: '/subPackages/business/my-card' })
}, 1500)
} else {
uni.showToast({ title: '支付失败', icon: 'none' })
}
},
onFail: () => {
uni.showToast({ title: '支付失败', icon: 'none' })
}
})
return
}
// #endif
// H5 + Antom(文档里 Antom 预留,当前后端可能返回 cashierUrl / h5Url 之一)
// #ifdef H5
if (paymentPlatform === 'ANTOM') {
const cashierUrl = res.data.cashierUrl || res.data.h5Url
if (cashierUrl) {
window.location.href = cashierUrl
return
}
})
}
// #endif
} else {
uni.showToast({
title: res.msg || '创建订单失败',
@@ -437,62 +501,83 @@ const handleBuy = async () => {
return
}
// 优惠券购买
// 优惠券购买(按接口文档:POST /app/coupon/pay
if (currentTab.value === 'coupon') {
try {
uni.showLoading({
title: '正在创建订单...'
})
const res = await createCouponPayment(selectedProduct.value.couponId)
const res = await createCouponPayment(selectedProduct.value.couponId, paymentPlatform)
uni.hideLoading()
if (res.code === 200 && res.data) {
// 调起微信支付
uni.requestPayment({
timeStamp: res.data.timeStamp,
nonceStr: res.data.nonceStr,
package: res.data.packageValue || res.data.package,
signType: res.data.signType || 'MD5',
paySign: res.data.paySign,
success: (payRes) => {
uni.showToast({
title: '支付成功',
icon: 'success'
})
// 支付成功后,跳转到我的优惠券页面
setTimeout(() => {
uni.navigateTo({
url: '/subPackages/business/my-coupon'
})
}, 1500)
},
fail: (err) => {
console.error('支付失败:', err)
if (err.errMsg && err.errMsg.includes('cancel')) {
// 用户取消支付,调用取消接口
const orderNo = res.data.OutOrderNo;
if (orderNo) {
cancelCouponPayment(orderNo)
.then(cancelRes => {
console.log('取消支付订单成功:', cancelRes);
})
.catch(cancelErr => {
console.error('取消支付订单失败:', cancelErr);
});
// 微信小程序
// #ifdef MP-WEIXIN
if (paymentPlatform === 'WECHAT') {
uni.requestPayment({
timeStamp: res.data.timeStamp,
nonceStr: res.data.nonceStr,
package: res.data.package,
signType: res.data.signType || 'MD5',
paySign: res.data.paySign,
success: () => {
uni.showToast({ title: '支付成功', icon: 'success' })
setTimeout(() => {
uni.navigateTo({ url: '/subPackages/business/my-coupon' })
}, 1500)
},
fail: (err) => {
console.error('支付失败:', err)
if (err.errMsg && err.errMsg.includes('cancel')) {
const outOrderNo = res.data.OutOrderNo || res.data.outOrderNo
outOrderNo && cancelCouponPayment(outOrderNo).catch(() => {})
uni.showToast({ title: '已取消支付', icon: 'none' })
} else {
uni.showToast({ title: '支付失败', icon: 'none' })
}
uni.showToast({
title: '已取消支付',
icon: 'none'
})
} else {
uni.showToast({
title: '支付失败',
icon: 'none'
})
}
})
return
}
// #endif
// 支付宝小程序
// #ifdef MP-ALIPAY
if (paymentPlatform === 'ALIPAY') {
const tradeNo = res.data.tradeNo || res.data.tradeNO
const payForm = res.data.payForm || res.data.orderStr
alipayTradePay({
tradeNo,
orderStr: payForm,
onSuccess: (payRes) => {
if (payRes.resultCode === '9000') {
uni.showToast({ title: '支付成功', icon: 'success' })
setTimeout(() => {
uni.navigateTo({ url: '/subPackages/business/my-coupon' })
}, 1500)
} else {
uni.showToast({ title: '支付失败', icon: 'none' })
}
},
onFail: () => {
uni.showToast({ title: '支付失败', icon: 'none' })
}
})
return
}
// #endif
// H5 + Antom
// #ifdef H5
if (paymentPlatform === 'ANTOM') {
const cashierUrl = res.data.cashierUrl || res.data.h5Url
if (cashierUrl) {
window.location.href = cashierUrl
return
}
})
}
// #endif
} else {
uni.showToast({
title: res.msg || '创建订单失败',