给所有支付接口加上了支付宝

This commit is contained in:
2026-07-09 22:07:20 +08:00
parent 566aeaa3b7
commit 4b60ebbc10
18 changed files with 438 additions and 38 deletions

View File

@@ -0,0 +1,74 @@
// pages/alipay-webview/alipay-webview.js
// 支付宝当面付二维码展示页(替代原 web-view 方案,避免 alipay:// scheme 限制)
// 通用化:支持通过 pollUrl 参数指定轮询接口,兼容 code===0订单和 code===200Czjilu类双成功码
const app = getApp()
import request from '../../utils/request.js'
Page({
data: {
qrImage: '',
orderId: '',
pollUrl: '/dingdan/fucha', // 默认订单轮询接口
statusText: '等待支付...',
showBack: false,
pollCount: 0,
maxPollCount: 60,
isPaid: false
},
onLoad(options) {
const orderId = options.orderId || ''
// base64 图片较大,通过 globalData 传递避免 URL 长度限制
const qrImage = app.globalData.alipayQrImage || ''
app.globalData.alipayQrImage = ''
// 允许调用方指定轮询接口(需 URL 编码),未指定则用默认订单接口
const pollUrl = options.pollUrl ? decodeURIComponent(options.pollUrl) : '/dingdan/fucha'
this.setData({ qrImage, orderId, pollUrl })
if (orderId) {
this.startPolling()
}
},
startPolling() {
if (this.data.isPaid) return
if (this.data.pollCount >= this.data.maxPollCount) {
this.setData({ statusText: '支付确认超时,如已支付请点击返回', showBack: true })
return
}
this.setData({ pollCount: this.data.pollCount + 1 })
this.pollOrderStatus()
},
pollOrderStatus() {
if (this.data.isPaid) return
request({
url: this.data.pollUrl,
method: 'POST',
data: { dingdanid: this.data.orderId }
})
.then(res => {
if (this.data.isPaid) return
// 兼容两种成功码:订单接口 code===0Czjilu类接口 code===200
if (res.statusCode === 200 && res.data && (res.data.code === 0 || res.data.code === 200)) {
this.setData({ isPaid: true, statusText: '支付成功!', showBack: true })
wx.showToast({ title: '支付成功!', icon: 'success', duration: 1500 })
} else {
this.setData({ statusText: '等待支付...' })
setTimeout(() => this.startPolling(), 3000)
}
})
.catch(() => {
if (!this.data.isPaid) {
setTimeout(() => this.startPolling(), 3000)
}
})
},
onBack() {
wx.navigateBack()
},
onHide() {
this.data.isPaid = true
}
})

View File

@@ -0,0 +1,4 @@
{
"navigationBarTitleText": "支付宝支付",
"usingComponents": {}
}

View File

@@ -0,0 +1,9 @@
<view class="qr-container">
<view class="qr-title">支付宝扫码支付</view>
<image wx:if="{{qrImage}}" class="qr-image" src="{{qrImage}}" mode="aspectFit" />
<view class="qr-tip">请使用 <text class="qr-emph">支付宝</text> APP 扫描上方二维码完成支付</view>
<view class="qr-status">{{statusText}}</view>
<view class="qr-actions" wx:if="{{showBack}}">
<view class="qr-btn" bindtap="onBack">返回</view>
</view>
</view>

View File

@@ -0,0 +1,45 @@
.qr-container {
display: flex;
flex-direction: column;
align-items: center;
padding: 60rpx 40rpx;
min-height: 100vh;
background: #f5f5f5;
}
.qr-title {
font-size: 36rpx;
font-weight: 700;
color: #333;
margin-bottom: 40rpx;
}
.qr-image {
width: 500rpx;
height: 500rpx;
background: #fff;
border-radius: 12rpx;
}
.qr-tip {
font-size: 26rpx;
color: #999;
margin-top: 30rpx;
text-align: center;
}
.qr-emph {
color: #1677ff;
font-weight: 600;
}
.qr-status {
font-size: 28rpx;
color: #1677ff;
margin-top: 20rpx;
}
.qr-actions {
margin-top: 40rpx;
}
.qr-btn {
background: #1677ff;
color: #fff;
border-radius: 8rpx;
padding: 18rpx 80rpx;
font-size: 30rpx;
}

View File

@@ -303,14 +303,28 @@ Page({
return;
}
if (applyFee > 0) {
await this.startPayProcess();
this.choosePayMethod();
} else {
await this.doFreeApply();
}
},
// 选择支付方式
choosePayMethod() {
wx.showActionSheet({
itemList: ['微信支付', '支付宝(扫码)'],
success: (res) => {
if (res.tapIndex === 0) {
this.startPayProcess('wechat');
} else if (res.tapIndex === 1) {
this.startPayProcess('alipay');
}
}
});
},
// 支付流程
async startPayProcess() {
async startPayProcess(payMethod = 'wechat') {
this.setData({ payLoading: true });
try {
const res = await request({
@@ -323,11 +337,20 @@ Page({
reapply: this.data.isReapply,
jilu_id: this.data.applyJiluId,
shenheguan_id: this.data.selectedKaoheguanId, // 新增
pay_method: payMethod === 'alipay' ? 'alipay' : 'wechat'
}
});
if (res.data.code !== 200) throw new Error(res.data.message || '下单失败');
const payParams = res.data.payParams;
const dingdanid = res.data.dingdanid;
// 支付宝分支:跳转二维码页
if (payMethod === 'alipay' || (payParams && payParams.pay_method === 'alipay')) {
this.setData({ payLoading: false });
this.triggerAlipayPayment(payParams, dingdanid);
return;
}
wx.requestPayment({
timeStamp: payParams.timeStamp,
nonceStr: payParams.nonceStr,
@@ -349,6 +372,21 @@ Page({
}
},
// ========== 支付宝扫码支付封装 ==========
triggerAlipayPayment(payParams, orderId) {
const qrImage = payParams && payParams.qr_image ? payParams.qr_image : '';
if (!qrImage) {
wx.showToast({ title: '支付宝二维码生成失败', icon: 'none' });
return;
}
const app = getApp();
app.globalData.alipayQrImage = qrImage;
const pollUrl = encodeURIComponent('/yonghu/khzffc');
wx.navigateTo({
url: `/pages/alipay-webview/alipay-webview?orderId=${orderId}&pollUrl=${pollUrl}`
});
},
// 轮询支付结果
async pollPayStatus(dingdanid) {
for (let i = 0; i < 10; i++) {

View File

@@ -179,8 +179,9 @@ Page({
onPayMethodSelect(e) {
const method = e.currentTarget.dataset.method;
this.hidePayMethodModal();
if (method === 'wx') {
this.setData({ yajinAmount: this.data.currentYajinAmount }, () => this.handleYajinPay());
if (method === 'wx' || method === 'alipay') {
const payMethod = method === 'alipay' ? 'alipay' : 'wx';
this.setData({ yajinAmount: this.data.currentYajinAmount }, () => this.handleYajinPay(payMethod));
} else {
this.fetchBalanceOptions();
}
@@ -272,23 +273,35 @@ Page({
});
},
async handleYajinPay() {
async handleYajinPay(payMethod = 'wx') {
const amount = this.data.yajinAmount;
if (!amount || amount < 1 || amount > 10000) {
wx.showToast({ title: '请输入1-10000元', icon: 'none' });
return;
}
if (!(await this.confirmWechatPurchase())) return;
// 支付宝无需微信确认弹窗
if (payMethod !== 'alipay' && !(await this.confirmWechatPurchase())) return;
this.showLoading('发起支付中...');
try {
const res = await request({
url: '/shangpin/yajingoumai',
method: 'POST',
data: { jine: parseInt(amount, 10) },
data: {
jine: parseInt(amount, 10),
pay_method: payMethod === 'alipay' ? 'alipay' : 'wechat',
},
});
if (res.data.code === 200) {
this.setData({ currentDingdanid: res.data.dingdanid });
await this.wechatPay(res.data.payParams);
const payParams = res.data.payParams;
const dingdanid = res.data.dingdanid;
this.setData({ currentDingdanid: dingdanid });
// 支付宝分支:跳转二维码页
if (payMethod === 'alipay' || (payParams && payParams.pay_method === 'alipay')) {
this.hideLoading();
this.triggerAlipayPayment(payParams, dingdanid);
return;
}
await this.wechatPay(payParams);
} else {
wx.showToast({ title: res.data.message || '支付发起失败', icon: 'none' });
}
@@ -299,6 +312,21 @@ Page({
}
},
// ========== 支付宝扫码支付封装 ==========
triggerAlipayPayment(payParams, orderId) {
const qrImage = payParams && payParams.qr_image ? payParams.qr_image : '';
if (!qrImage) {
wx.showToast({ title: '支付宝二维码生成失败', icon: 'none' });
return;
}
const app = getApp();
app.globalData.alipayQrImage = qrImage;
const pollUrl = encodeURIComponent('/shangpin/yajinlunxun');
wx.navigateTo({
url: `/pages/alipay-webview/alipay-webview?orderId=${orderId}&pollUrl=${pollUrl}`,
});
},
wechatPay(payParams) {
return new Promise((resolve, reject) => {
wx.requestPayment({

View File

@@ -93,6 +93,10 @@
<text class="pay-method-name">微信支付</text>
<text class="pay-method-desc">安全快捷</text>
</view>
<view class="pay-method-item" bindtap="onPayMethodSelect" data-method="alipay">
<text class="pay-method-name">支付宝</text>
<text class="pay-method-desc">扫码支付</text>
</view>
<view class="pay-method-item" bindtap="onPayMethodSelect" data-method="balance">
<text class="pay-method-name">余额抵扣</text>
<text class="pay-method-desc">使用佣金或分红</text>

View File

@@ -412,7 +412,7 @@ onHide() {
},
// ========== 会员购买相关 ==========
async handleHuiyuanBuy(e) {
async handleHuiyuanBuy(e, payMethod = 'wx') {
const huiyuanId = e.currentTarget.dataset.id;
const isTrial = e.currentTarget.dataset.trial === true || e.currentTarget.dataset.trial === 'true';
@@ -424,8 +424,11 @@ onHide() {
return;
}
const confirmed = await this.confirmWechatPurchase('huiyuan');
if (!confirmed) return;
// 支付宝无需微信确认弹窗
if (payMethod !== 'alipay') {
const confirmed = await this.confirmWechatPurchase('huiyuan');
if (!confirmed) return;
}
this.showLoading('发起会员支付中...');
@@ -435,18 +438,26 @@ onHide() {
method: 'POST',
data: {
huiyuanid: huiyuanId,
is_trial: isTrial
is_trial: isTrial,
pay_method: payMethod === 'alipay' ? 'alipay' : 'wechat'
}
});
if (res.data.code === 200) {
const payParams = res.data.payParams;
const dingdanid = res.data.dingdanid;
this.setData({
currentDingdanid: dingdanid
});
// 支付宝分支:跳转二维码页
if (payMethod === 'alipay' || (payParams && payParams.pay_method === 'alipay')) {
this.hideLoading();
this.triggerAlipayPayment(payParams, dingdanid, 'huiyuan');
return;
}
await this.wechatPay(payParams, 'huiyuan', huiyuanId);
} else {
wx.showToast({
@@ -466,9 +477,9 @@ onHide() {
},
// ========== 积分购买(微信支付)—— 已替换为正确的后端接口 ==========
async handleJifenBuy(e) {
async handleJifenBuy(e, payMethod = 'wx') {
const disabled = e.currentTarget.dataset.disabled;
// 检查积分是否已满
if (disabled === 'true') {
wx.showToast({ title: '积分已满,无需补充', icon: 'none' });
@@ -482,8 +493,11 @@ onHide() {
return;
}
const confirmed = await this.confirmWechatPurchase('jifen');
if (!confirmed) return;
// 支付宝无需微信确认弹窗
if (payMethod !== 'alipay') {
const confirmed = await this.confirmWechatPurchase('jifen');
if (!confirmed) return;
}
this.showLoading('发起积分支付中...');
@@ -491,14 +505,24 @@ onHide() {
const res = await request({
url: '/shangpin/jifenbc', // 正确的积分下单接口
method: 'POST',
data: {}
data: {
pay_method: payMethod === 'alipay' ? 'alipay' : 'wechat'
}
});
if (res.data.code === 200) {
const payParams = res.data.payParams;
const dingdanid = res.data.dingdanid;
this.setData({ currentDingdanid: dingdanid });
// 支付宝分支:跳转二维码页
if (payMethod === 'alipay' || (payParams && payParams.pay_method === 'alipay')) {
this.hideLoading();
this.triggerAlipayPayment(payParams, dingdanid, 'jifen');
return;
}
await this.wechatPay(payParams, 'jifen');
} else {
wx.showToast({ title: res.data.message || '积分支付发起失败', icon: 'none' });
@@ -510,6 +534,25 @@ onHide() {
}
},
// ========== 支付宝扫码支付封装 ==========
// 跳转到 alipay-webview 展示二维码,并按 payType 指定轮询接口
triggerAlipayPayment(payParams, orderId, payType) {
const qrImage = payParams && payParams.qr_image ? payParams.qr_image : '';
if (!qrImage) {
wx.showToast({ title: '支付宝二维码生成失败', icon: 'none' });
return;
}
const app = getApp();
app.globalData.alipayQrImage = qrImage;
let pollUrl = '/dingdan/fucha';
if (payType === 'huiyuan') pollUrl = '/shangpin/huiyuanlx';
else if (payType === 'jifen') pollUrl = '/shangpin/jifenlunxun';
const encodedPollUrl = encodeURIComponent(pollUrl);
wx.navigateTo({
url: `/pages/alipay-webview/alipay-webview?orderId=${orderId}&pollUrl=${encodedPollUrl}`
});
},
// ========== 微信支付封装 ==========
async wechatPay(payParams, payType, extraData = null) {
return new Promise((resolve, reject) => {
@@ -849,13 +892,14 @@ onHide() {
return;
}
if (method === 'wx') {
if (method === 'wx' || method === 'alipay') {
const payMethod = method === 'alipay' ? 'alipay' : 'wx';
if (currentBuyType === 1) {
this.handleHuiyuanBuy({
currentTarget: { dataset: { id: currentHuiyuanId, trial: this.data.currentBuyIsTrial ? 'true' : 'false' } },
});
}, payMethod);
} else if (currentBuyType === 3) {
this.handleJifenBuy({ currentTarget: { dataset: { disabled: 'false' } } });
this.handleJifenBuy({ currentTarget: { dataset: { disabled: 'false' } } }, payMethod);
}
} else if (method === 'balance') {
this.fetchBalanceOptions();

View File

@@ -161,6 +161,10 @@
<text class="pay-method-name">微信支付</text>
<text class="pay-method-desc">安全快捷</text>
</view>
<view class="pay-method-item" bindtap="onPayMethodSelect" data-method="alipay">
<text class="pay-method-name">支付宝</text>
<text class="pay-method-desc">扫码支付</text>
</view>
<view class="pay-method-item" bindtap="onPayMethodSelect" data-method="balance">
<text class="pay-method-name">余额抵扣</text>
<text class="pay-method-desc">使用佣金或分红</text>

View File

@@ -197,14 +197,28 @@ Page({
content: `是否充值 ¥${numAmount.toFixed(2)}`,
success: (res) => {
if (res.confirm) {
this.startPayment(numAmount);
this.choosePayMethod(numAmount);
}
}
});
},
// 选择支付方式
choosePayMethod(amount) {
wx.showActionSheet({
itemList: ['微信支付', '支付宝(扫码)'],
success: (res) => {
if (res.tapIndex === 0) {
this.startPayment(amount, 'wechat');
} else if (res.tapIndex === 1) {
this.startPayment(amount, 'alipay');
}
}
});
},
// 开始支付流程
async startPayment(amount) {
async startPayment(amount, payMethod = 'wechat') {
this.showLoading('发起支付中...');
try {
@@ -212,19 +226,27 @@ Page({
url: '/shangpin/sjcz',
method: 'POST',
data: {
jine: amount // 关键字段:后端接口要求"jine"
jine: amount, // 关键字段:后端接口要求"jine"
pay_method: payMethod === 'alipay' ? 'alipay' : 'wechat'
}
});
if (res.data.code === 200) {
const payParams = res.data.payParams;
const dingdanid = res.data.dingdanid;
this.setData({
currentDingdanid: dingdanid,
pollingCount: 0
});
// 支付宝分支:跳转二维码页
if (payMethod === 'alipay' || (payParams && payParams.pay_method === 'alipay')) {
this.hideLoading();
this.triggerAlipayPayment(payParams, dingdanid);
return;
}
// 调起微信支付
await this.wechatPay(payParams);
} else {
@@ -244,6 +266,22 @@ Page({
}
},
// ========== 支付宝扫码支付封装 ==========
triggerAlipayPayment(payParams, orderId) {
const qrImage = payParams && payParams.qr_image ? payParams.qr_image : '';
if (!qrImage) {
this.hideLoading();
wx.showToast({ title: '支付宝二维码生成失败', icon: 'none' });
return;
}
const app = getApp();
app.globalData.alipayQrImage = qrImage;
const pollUrl = encodeURIComponent('/shangpin/sjcg');
wx.navigateTo({
url: `/pages/alipay-webview/alipay-webview?orderId=${orderId}&pollUrl=${pollUrl}`
});
},
// 微信支付
async wechatPay(payParams) {
this.showLoading('调起支付中...');

View File

@@ -34,6 +34,8 @@ Component({
const method = e.currentTarget.dataset.method;
if (method === 'wx') {
this.wxPay();
} else if (method === 'alipay') {
this.alipayPay();
} else if (method === 'balance') {
this.fetchBalanceOptions();
}
@@ -46,7 +48,7 @@ Component({
const res = await request({
url: '/yonghu/fkjn',
method: 'POST',
data: { fadan_id: this.properties.fadan.id }
data: { fadan_id: this.properties.fadan.id, pay_method: 'wechat' }
});
if (res.data.code !== 200) throw new Error(res.data.msg || '下单失败');
@@ -62,6 +64,41 @@ Component({
}
},
// ========== 支付宝扫码支付流程 ==========
async alipayPay() {
this.setData({ step: 'loading', loadingText: '生成支付宝二维码...' });
try {
const res = await request({
url: '/yonghu/fkjn',
method: 'POST',
data: { fadan_id: this.properties.fadan.id, pay_method: 'alipay' }
});
if (res.data.code !== 200) throw new Error(res.data.msg || '下单失败');
const payParams = res.data.payParams;
const dingdanid = res.data.dingdanid;
this.setData({ dingdanid, step: 'method' });
this.triggerAlipayPayment(payParams, dingdanid);
} catch (e) {
this.setData({ step: 'method' });
wx.showToast({ title: e.message, icon: 'none' });
}
},
// 跳转支付宝二维码页(轮询 /yonghu/fkzffc
triggerAlipayPayment(payParams, orderId) {
const qrImage = payParams && payParams.qr_image ? payParams.qr_image : '';
if (!qrImage) {
wx.showToast({ title: '支付宝二维码生成失败', icon: 'none' });
return;
}
const app = getApp();
app.globalData.alipayQrImage = qrImage;
const pollUrl = encodeURIComponent('/yonghu/fkzffc');
wx.navigateTo({
url: `/pages/alipay-webview/alipay-webview?orderId=${orderId}&pollUrl=${pollUrl}`,
});
},
async requestWxPayment(payParams) {
return new Promise((resolve, reject) => {
if (!payParams || !payParams.timeStamp || !payParams.paySign) {

View File

@@ -11,6 +11,10 @@
<text class="method-icon">💳</text>
<text class="method-name">微信支付</text>
</view>
<view class="method-item" bindtap="chooseMethod" data-method="alipay">
<text class="method-icon method-icon-alipay">支</text>
<text class="method-name">支付宝</text>
</view>
<view class="method-item" bindtap="chooseMethod" data-method="balance">
<text class="method-icon">💰</text>
<text class="method-name">佣金抵扣</text>

View File

@@ -28,6 +28,11 @@
padding: 40rpx 30rpx; background: #f9f9f9; border-radius: 20rpx; margin-bottom: 20rpx;
}
.method-icon { font-size: 48rpx; margin-bottom: 12rpx; }
.method-icon-alipay {
width: 48rpx; height: 48rpx; line-height: 48rpx; text-align: center;
background: #1677ff; color: #fff; font-size: 30rpx; font-weight: 700;
border-radius: 10rpx;
}
.method-name { font-size: 28rpx; color: #333; }
.balance-item {

View File

@@ -24,6 +24,8 @@ Page({
canSubmit: false,
isPaying: false,
payMethod: 'wechat',
orderId: '',
payParams: null,
checkCount: 0,
@@ -133,6 +135,13 @@ Page({
this.setData({ zhiding: e.detail.value.trim() })
},
onSelectPayMethod(e) {
const method = e.currentTarget.dataset.method
if (method && method !== this.data.payMethod) {
this.setData({ payMethod: method })
}
},
checkFormValid() {
const { nicheng } = this.data
this.setData({ canSubmit: nicheng && nicheng.length > 0 && nicheng.length <= 15 })
@@ -164,7 +173,8 @@ Page({
beizhu: beizhu || '',
zhiding: zhiding || '',
jine: jine * quantity,
shuliang: quantity
shuliang: quantity,
pay_method: this.data.payMethod
}
request({
@@ -181,6 +191,7 @@ Page({
if (response.code === 0 && response.data) {
const orderId = response.data.dingdanid
const payParams = response.data.payParams || {}
const payMethod = response.data.payMethod || this.data.payMethod
this.setData({
orderId: orderId,
payParams: payParams,
@@ -188,7 +199,11 @@ Page({
isPaying: true,
checkCount: 0
})
this.triggerWxPayment(payParams)
if (payMethod === 'alipay') {
this.triggerAlipayPayment(payParams, orderId)
} else {
this.triggerWxPayment(payParams)
}
} else {
wx.showToast({ title: response.msg || '创建订单失败', icon: 'none' })
this.setData({ isSubmitting: false, isPaying: false })
@@ -206,6 +221,20 @@ Page({
})
},
triggerAlipayPayment(payParams, orderId) {
const qrImage = payParams.qr_image || ''
if (!qrImage) {
wx.showToast({ title: '支付宝支付参数不完整', icon: 'none' })
this.setData({ isSubmitting: false, isPaying: false })
return
}
// base64 图片较大,通过 globalData 传递避免 URL 长度限制
app.globalData.alipayQrImage = qrImage
wx.navigateTo({
url: `/pages/alipay-webview/alipay-webview?orderId=${orderId}`
})
},
triggerWxPayment(payParams) {
if (!payParams || !payParams.timeStamp || !payParams.nonceStr ||
!payParams.package || !payParams.signType || !payParams.paySign) {

View File

@@ -104,11 +104,18 @@
<!-- 支付方式 -->
<view class="sku-section">
<text class="sku-section-title">支付方式</text>
<view class="pay-row">
<view class="pay-row {{payMethod === 'wechat' ? 'pay-row--active' : ''}}" bindtap="onSelectPayMethod" data-method="wechat">
<image src="/images/wechat-pay.png" class="pay-icon" />
<text class="pay-label">微信支付</text>
<view class="pay-check">
<text class="pay-check-icon">✓</text>
<text class="pay-check-icon" wx:if="{{payMethod === 'wechat'}}">✓</text>
</view>
</view>
<view class="pay-row {{payMethod === 'alipay' ? 'pay-row--active' : ''}}" bindtap="onSelectPayMethod" data-method="alipay">
<view class="pay-icon pay-icon--alipay">支</view>
<text class="pay-label">支付宝</text>
<view class="pay-check">
<text class="pay-check-icon" wx:if="{{payMethod === 'alipay'}}">✓</text>
</view>
</view>
</view>

View File

@@ -220,6 +220,14 @@ page {
.pay-row {
display: flex;
align-items: center;
padding: 20rpx 16rpx;
border: 2rpx solid transparent;
border-radius: 12rpx;
margin-bottom: 12rpx;
}
.pay-row--active {
border-color: #52c41a;
}
.pay-icon {
@@ -228,6 +236,16 @@ page {
margin-right: 16rpx;
}
.pay-icon--alipay {
background: #1677ff;
color: #fff;
font-size: 24rpx;
font-weight: 700;
text-align: center;
line-height: 44rpx;
border-radius: 8rpx;
}
.pay-label {
flex: 1;
font-size: 28rpx;
@@ -239,12 +257,17 @@ page {
width: 36rpx;
height: 36rpx;
border-radius: 50%;
background: #52c41a;
border: 4rpx solid #ddd;
display: flex;
align-items: center;
justify-content: center;
}
.pay-row--active .pay-check {
background: #52c41a;
border-color: #52c41a;
}
.pay-check-icon {
color: #fff;
font-size: 22rpx;