uniapp H5如何支持微信扫描支付
1.背景
通常使用到微信扫描支付的场景,基本是在PC端,或者Android大屏端, 如京东商城PC端购物,或者在大街上饮料机购买饮料,都会用到扫码支付。那么如何
2.疑问:uniapp H5如何支持微信扫描支付
在流程图的第2步,商家后台向微信后台申请统一支付订单,微信后台会返回一个支付url(形式通常是weixin://wxpay/bizpayurl?pr=xxxxxx), 这个支付url就是支付的入口, 这个支付url在浏览器通常是不能访问的,它往往呈现在用户面前的是一个二维码, 那么如何转换呢?
3.两种方法转换
3.1 方法1: 在客户端转换
在浏览器端,收到收到url后,使用 js (如QRCode.js) 对支付url 进行转换,并显示相应的图片。
3.2 方法2: 在服务端转换
在商家后台端,收到url后,用相应的库(java的通常使用com.google.zxing),把支付url 转换成二维码图片,然后浏览器端显示该图片。
由于方法2,更具普适性,后面的样例代码只提供方法2的,方法1的自行度娘了。
使用微信客户端,扫描以上二维码,扫码的结果是 weixin://wxpay/bizpayurl?pr=f9qZqIJzz ,微信客户端会识别该url, 并按流程唤起微信支付。
4.参考代码
4.1 uniApp端
<template>
<view class="container">
<top-layout topTitle="支付"></top-layout>
<main-layout :alignLeft="false">
<template v-slot:contentPage>
<total-layout btnText='确认支付' :isShowPrice="false" :btnBackFunc="goBack" :btnWantToFunc="confirmPay"
:price="totalPrice">
<template v-slot:page>
<view class="list" :style="{height: windowHeight + 'px'}">
<view class="price-area">
<view class="price-title">应付金额:</view>
<view class="shop-price">
<text>¥</text>
<text class="shop-price-text">{{ integerPriceStr }}</text>
<text>{{ decimalPriceStr }}</text>
</view>
</view>
<uni-section style="margin-top: 8px;" title="请选择支付方式" type="line">
<view class="uni-list">
<radio-group @change="payModeChange">
<label class="uni-list-cell uni-list-cell-pd" v-for="(item, index) in payModes" :key="item.value">
<view>
<radio :value="item.value" :checked="index === currPayModeIndex" />
</view>
<image class="payImg" :src="item.imgUrl" mode="aspectFill"></image>
<view>{{item.text}}</view>
</label>
</radio-group>
</view>
</uni-section>
</view>
<uni-popup ref="popupQrCode" type="bottom" :is-mask-click="false" background-color="white" :round="10">
<view class="payCode-title">请使用微信扫码完成支付</view>
<view style="text-align: center;">
<image class="payCode-img" :src="payCodeImgUrl" mode="widthFix"></image>
</view>
<view class="woodsbtn_cancle uni-bg-red" @click="cancelQrCodePay">取消支付</view>
<view class="woodsbtn_finish" @click="finishPay">支付完成,通知商家</view>
</uni-popup>
</template>
</total-layout>
</template>
</main-layout>
</view>
</template>
export default {
data() {
return {
windowHeight: 0,
orderName: "", //订单名称
orderNo: "", //订单号
orderType: 0, //订单类型, 0-未知,10-商城订单
totalPrice: 0, //订单合计
totalNum: 0, //订单商品总数量
integerPriceStr: "0",
decimalPriceStr: ".00",
goods: [],
payModes: [],
currPayModeIndex: 0,
payCodeImgUrl: "",
loginPageUrl: "",
};
},
onLoad(options) {
const sys = uni.getSystemInfoSync()
this.windowHeight = sys.windowHeight
this.payModes = [{
text: '微信扫码支付',
value: "20",
imgUrl: "/static/images/pay/wx.png",
}];
},
confirmPay(orderNo, user) {
let that = this
let orderData = {}
orderData.memberId = user.id;
orderData.memberName = user.name;
orderData.orderNo = orderNo; //订单号
orderData.payMode = that.payModes[that.currPayModeIndex].value; //支付方式
//正式请求支付信息
request({
url: '/mallOrder/requestPay',
data: orderData,
method: 'POST'
}).then((res) => {
if (res.code === '0') {
let orderPayInfo = res.data;
console.log('商城订单请求支付成功.订单号=' + orderPayInfo.orderNo);
if (orderPayInfo.payMode === 20) {
//微信扫码支付
if (orderPayInfo.payStatus === 10) {
//10-未支付, 弹出扫码窗口。
console.info('支付URL=' + orderPayInfo.payCodeUrl);
if (orderPayInfo.payCodeImgUrl) {
that.payCodeImgUrl = config.QRCODE_BASE_URL + orderPayInfo.payCodeImgUrl;
}
that.openPopupQrCode()
} else {
uni.showToast({
title: '支付失败!' + res.msg,
icon: 'error',
duration: 2000
});
}
}
} else {
uni.showToast({
title: '提交商城订单支付失败.' + res.msg,
icon: 'error',
duration: 2000
});
}
});
},
}
4.2 java 后台端
/**
* 生成二维码到Image
* @author madifu
* @param text 文字内容
* @param logoPath LOGO文件路径,如果没有LOGO可填空白
* @param note 说明
* @return = null 表示失败
* @throws WriterException
* @throws IOException
*/
private static BufferedImage drawQRCodeImage(String text, String logoPath, String note ) throws WriterException, IOException {
MultiFormatWriter multiFormatWriter = new MultiFormatWriter();
// 参数顺序分别为:编码内容,编码类型,生成图片宽度,生成图片高度,设置参数
BitMatrix bm = multiFormatWriter.encode(text, BarcodeFormat.QR_CODE, QRCODE_SIZE, QRCODE_SIZE, hints);
BufferedImage outImage = new BufferedImage(QRCODE_SIZE, QRCODE_SIZE, BufferedImage.TYPE_INT_RGB);
// 开始利用二维码数据创建Bitmap图片,分别设为黑(0xFFFFFFFF)白(0xFF000000)两色
for (int x = 0; x < QRCODE_SIZE; x++) {
for (int y = 0; y < QRCODE_SIZE; y++) {
outImage.setRGB(x, y, bm.get(x, y) ? 0xFF000000 : 0xFFFFFFFF);
}
}
int width = outImage.getWidth();
int height = outImage.getHeight();
if (checkImageExist(logoPath)) {
// 构建绘图对象
Graphics2D g = outImage.createGraphics();
// 读取Logo图片
BufferedImage logo = ImageIO.read(new File(logoPath));
// 开始绘制logo图片
g.setBackground(Color.WHITE);
g.clearRect(width * 2 / 5, height * 2 / 5, width * 2 / 10, height * 2 / 10);
g.drawImage(logo, width * 2 / 5, height * 2 / 5, width * 2 / 10, height * 2 / 10, null);
g.dispose();
logo.flush();
}
// 自定义文本描述
if (!StringUtils.isEmpty(note)) {
// 新的图片,把带logo的二维码下面加上文字
BufferedImage noteImage = new BufferedImage(QRCODE_SIZE, QRCODE_SIZE+40, BufferedImage.TYPE_4BYTE_ABGR);
Graphics2D outg = noteImage.createGraphics();
// 画二维码到新的面板
outg.drawImage(outImage, 0, 0, outImage.getWidth(), outImage.getHeight(), null);
// 画文字到新的面板
outg.setColor(Color.BLACK);
outg.setFont(new Font("宋体", Font.BOLD, 30)); // 字体、字型、字号
int strWidth = outg.getFontMetrics().stringWidth(note);
if (strWidth >= QRCODE_SIZE) {
// //长度过长就截取前面部分
// 长度过长就换行
String note1 = note.substring(0, note.length() / 2);
String note2 = note.substring(note.length() / 2, note.length());
int strWidth1 = outg.getFontMetrics().stringWidth(note1);
int strWidth2 = outg.getFontMetrics().stringWidth(note2);
outg.drawString(note1, (QRCODE_SIZE - strWidth1)/ 2, height + (noteImage.getHeight() - height) / 2 + 12);
BufferedImage noteImage2 = new BufferedImage(QRCODE_SIZE, QRCODE_SIZE+80, BufferedImage.TYPE_4BYTE_ABGR);
Graphics2D outg2 = noteImage2.createGraphics();
outg2.drawImage(noteImage, 0, 0, noteImage.getWidth(), noteImage.getHeight(), null);
outg2.setColor(Color.BLACK);
outg2.setFont(new Font("宋体", Font.BOLD, 30)); // 字体、字型、字号
outg2.drawString(note2, 200 - strWidth2 / 2,
noteImage.getHeight() + (noteImage2.getHeight() - noteImage.getHeight()) / 2 + 5);
outg2.dispose();
noteImage2.flush();
noteImage = noteImage2;
} else {
//outg.drawString(note, 200 - strWidth / 2, height + (outImage.getHeight() - height) / 2 + 12); // 画文字
outg.drawString(note, (QRCODE_SIZE - strWidth) / 2, height + (noteImage.getHeight() - height) / 2 + 12); // 画文字
}
outg.dispose();
noteImage.flush();
outImage = noteImage;
}
outImage.flush();
return outImage;
}