尚医通day16-网站怎么接入微信扫码支付?
第01章-准备工作
1、微信支付产品介绍
付款码支付、JSAPI支付、小程序支付、Native支付、APP支付、刷脸支付
1.1、付款码支付
用户展示微信钱包内的“付款码”给商家,商家扫描后直接完成支付,适用于线下面对面收银的场景。
1.2、JSAPI支付
- 线下场所:商户展示一个支付二维码,用户使用微信扫描二维码后,输入需要支付的金额,完成支付。
- 公众号场景:用户在微信内进入商家公众号,打开某个页面,选择某个产品,完成支付。
- PC网站场景:在网站中展示二维码,用户使用微信扫描二维码,输入需要支付的金额,完成支付。
特点:用户在客户端输入支付金额
1.3、小程序支付
在微信小程序平台内实现支付的功能。
1.4、Native支付
Native支付是指商户展示支付二维码,用户再用微信“扫一扫”完成支付的模式。这种方式适用于PC网站。
特点:商家预先指定支付金额
1.5、APP支付
商户通过在移动端独立的APP应用程序中集成微信支付模块,完成支付。
1.6、刷脸支付
用户在刷脸设备前通过摄像头刷脸、识别身份后进行的一种支付方式。
2、接口版本
微信支付企业主流的API版本有v2和v3,课程中我们使用微信支付APIv3。
V2和V3的比较

相比较而言,APIv2比APIv3安全性更高,但是APIv2中有一些功能在APIv3中尚未完整实现,具体参考如下API字典页面:API字典概览 | 微信支付商户平台文档中心 (qq.com)

3、接入指引
3.1、获取开发参数
如果需要独立申请和开通微信支付功能,可以参考如下官方文档。开通微信支付后,才能获取相关的开发参数以及商户公钥和商户私钥文件。
参考资料:微信支付接入指引 - 微信支付商户平台 (qq.com)
3.2、配置开发参数
在service-order服务的resources目录中创建wxpay.properties
这个文件定义了在“接入指引”的步骤中我们提前准备的微信支付相关的参数,例如商户号、APPID、API秘钥等等
# 微信支付相关参数
wxpay:
mch-id: 1558950191 #商户号
mch-serial-no: 34345964330B66427E0D3D28826C4993C77E631F # 商户API证书序列号
private-key-path: D:/project/yygh/cert/apiclient_key.pem # 商户私钥文件
api-v3-key: UDuLFDcmy5Eb6o0nTNZdu6ek4DDh4K8B # APIv3密钥
appid: wx74862e0dfcf69954 # APPID
notify-url: https://7d92-115-171-63-135.ngrok.io/api/order/wxpay/payment/notify # 接收支付结果通知地址
notify-refund-url: http://agxnyzl04y90.ngrok.xiaomiqiu123.top/api/order/wxpay/refunds/notify # 接收退款结果通知地址
3.3、复制商户私钥
将商户私钥文件复制到配置文件指定的目录下:
资料:资料>微信支付>商户证书>apiclient_key.pem
private-key-path: D:/project/yygh/cert/apiclient_key.pem # 商户私钥文件
3.4、证书密钥使用说明(了解)
参考文档:APIv3证书与密钥使用说明
一个完整的请求和响应的流程:
- 商户使用商户私钥对请求进行签名,发送给微信支付平台,平台使用商户公钥进行签名验证。
- 微信支付平台使用平台私钥对响应进行签名,商户使用微信支付平台公钥对响应进行验签。

第02章-订单支付
1、微信支付平台证书的获取
1.1、引入SDK
参考文档:SDK&工具
我们可以使用官方提供的 SDK wechatpay-java
在service-order微服务中添加依赖:
<!--微信支付APIv3-->
<dependency>
<groupId>com.github.wechatpay-apiv3</groupId>
<artifactId>wechatpay-java</artifactId>
<version>0.2.6</version>
</dependency>
1.2、读取支付参数
在config 包中 创建 WxPayConfig.java
package com.atguigu.syt.order.config;
@Configuration
@PropertySource("classpath:wxpay.properties") //读取配置文件
@ConfigurationProperties(prefix="wxpay") //读取wxpay节点
@Data
public class WxPayConfig {
// 商户号
private String mchId;
// 商户API证书序列号
private String mchSerialNo;
// 商户私钥文件
private String privateKeyPath;
// APIv3密钥
private String apiV3Key;
// APPID
private String appid;
// 接收支付结果通知地址
private String notifyUrl;
// 接收退款结果通知地址
private String notifyRefundUrl;
}
1.3、自动更新微信支付平台证书
在 API 请求过程中,客户端需使用微信支付平台证书,验证服务器应答的真实性和完整性。我们使用自动更新平台证书的配置类 RSAAutoCertificateConfig。每个商户号只能创建一个 RSAAutoCertificateConfig。
在WxPayConfig中添加如下方法:
/**
* 获取微信支付配置对象
* @return
*/
@Bean
public RSAAutoCertificateConfig getConfig(){
return new RSAAutoCertificateConfig.Builder()
.merchantId(mchId)
.privateKeyFromPath(privateKeyPath)
.merchantSerialNumber(mchSerialNo)
.apiV3Key(apiV3Key)
.build();
}
RSAAutoCertificateConfig 通过 RSAAutoCertificateProvider 自动下载微信支付平台证书。 同时,RSAAutoCertificateProvider 会启动一个后台线程,定时更新证书(目前设计为60分钟),以实现证书过期时的新老证书平滑切换。
常见错误:引入商户私钥后如果项目无法启动,则需要升级JDK版本,并重新配置idea编译和运行环境到最新版本的JDK。建议升级到1.8.0_300以上

2、生成支付二维码
2.1、Native支付流程
参考文档:业务流程时序图

2.2、Controller
在service-order中创建FrontWXPayController
package com.atguigu.syt.order.controller.front;
@Api(tags = "微信支付接口")
@RestController
@RequestMapping("/front/order/wxpay")
public class FrontWXPayController {
@Resource
private WxPayService wxPayService;
@Resource
private AuthContextHolder authContextHolder;
@ApiOperation("获取支付二维码url")
@ApiImplicitParam(name = "outTradeNo",value = "订单号", required = true)
@GetMapping("/auth/nativePay/{outTradeNo}")
public Result<String> nativePay(@PathVariable String outTradeNo, HttpServletRequest request, HttpServletResponse response) {
//校验用户登录状态
authContextHolder.checkAuth(request, response);
String codeUrl = wxPayService.createNative(outTradeNo);
return Result.ok(codeUrl);
}
}
2.3、Service
SDK参考代码:wechatpay-java/NativePayServiceExample.java at main · wechatpay-apiv3/wechatpay-java · GitHub

具体代码示例如下:

Native下单API参数参考:Native下单API
接口:OrderInfoService
/**
* 根据订单号获取订单
* @param outTradeNo
* @return
*/
OrderInfo selectByOutTradeNo(String outTradeNo);
实现:OrderInfoServiceImpl
@Override
public OrderInfo selectByOutTradeNo(String outTradeNo) {
LambdaQueryWrapper<OrderInfo> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(OrderInfo::getOutTradeNo, outTradeNo);
return baseMapper.selectOne(queryWrapper);
}
接口:WxPayService
package com.atguigu.syt.order.service;
public interface WxPayService {
/**
* 获取支付二维码utl
* @param outTradeNo
* @return
*/
String createNative(String outTradeNo);
}
实现:WxPayServiceImpl
package com.atguigu.syt.order.service.impl;
@Service
@Slf4j
public class WxPayServiceImpl implements WxPayService {
@Resource
private RSAAutoCertificateConfig rsaAutoCertificateConfig;
@Resource
private WxPayConfig wxPayConfig;
@Resource
private OrderInfoService orderInfoService;
@Override
public String createNative(String outTradeNo) {
// 初始化服务
NativePayService service = new NativePayService.Builder().config(rsaAutoCertificateConfig).build();
// 调用接口
try {
//获取订单
OrderInfo orderInfo = orderInfoService.selectByOutTradeNo(outTradeNo);
PrepayRequest request = new PrepayRequest();
// 调用request.setXxx(val)设置所需参数,具体参数可见Request定义
request.setAppid(wxPayConfig.getAppid());
request.setMchid(wxPayConfig.getMchId());
request.setDescription(orderInfo.getTitle());
request.setOutTradeNo(outTradeNo);
request.setNotifyUrl(wxPayConfig.getNotifyUrl());
Amount amount = new Amount();
//amount.setTotal(orderInfo.getAmount().multiply(new BigDecimal(100)).intValue());
amount.setTotal(1);//1分钱
request.setAmount(amount);
// 调用接口
PrepayResponse prepayResponse = service.prepay(request);
return prepayResponse.getCodeUrl();
} catch (HttpException e) { // 发送HTTP请求失败
// 调用e.getHttpRequest()获取请求打印日志或上报监控,更多方法见HttpException定义
log.error(e.getHttpRequest().toString());
throw new GuiguException(ResultCodeEnum.FAIL);
} catch (ServiceException e) { // 服务返回状态小于200或大于等于300,例如500
// 调用e.getResponseBody()获取返回体打印日志或上报监控,更多方法见ServiceException定义
log.error(e.getResponseBody());
throw new GuiguException(ResultCodeEnum.FAIL);
} catch (MalformedMessageException e) { // 服务返回成功,返回体类型不合法,或者解析返回体失败
// 调用e.getMessage()获取信息打印日志或上报监控,更多方法见MalformedMessageException定义
log.error(e.getMessage());
throw new GuiguException(ResultCodeEnum.FAIL);
}
}
}
可见,使用 SDK 并不需要计算请求签名和验证应答签名。
3、前端整合
3.1、api
创建api/wxpay.js
import request from '~/utils/request'
export default {
//获取支付二维码url
nativePay(outTradeNo) {
return request({
url: `/front/order/wxpay/auth/nativePay/${outTradeNo}`,
method: 'get'
})
},
}
3.2、修改show.vue
引入api
import wxpayApi from '~/api/wxpay'
添加data数据
codeUrl: null, //微信支付二维码
isPayShow: false, //不显示登录二维码组件
payText: '支付'
修改按钮
将
<div class="v-button" @click="pay()">支付</div>
修改为
<div class="v-button" @click="pay()">{{payText}}</div>
添加方法
//支付
pay() {
//防止重复提交
if(this.isPayShow) return
this.isPayShow = true
this.payText = '支付中.....'
//显示二维码
wxpayApi.nativePay(this.orderInfo.outTradeNo).then((response) => {
this.codeUrl = response.data
this.dialogPayVisible = true
})
},
//关闭对话框
closeDialog(){
//恢复支付按钮
this.isPayShow = false
this.payText = '支付'
}
用二维码组件替换img图片
<img src="二维码链接" alt="" />
替换成
<qriously :value="codeUrl" :size="220"/>

第03章-查询支付结果
1、支付查单
参考文档:微信支付查单接口
商户可以主动调用微信支付查单接口,同步订单状态。
调用查询订单接口,如果支付成功则更新订单状态并添加交易记录,如果支付尚未成功则轮询查单
1.1、更新订单状态
OrderInfoService接口
/**
* 根据订单号更新订单状态
* @param outTradeNo
* @param status
*/
void updateStatus(String outTradeNo, Integer status);
OrderInfoServiceImpl实现
@Override
public void updateStatus(String outTradeNo, Integer status) {
LambdaQueryWrapper<OrderInfo> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(OrderInfo::getOutTradeNo, outTradeNo);
OrderInfo orderInfo = new OrderInfo();
orderInfo.setOrderStatus(status);
baseMapper.update(orderInfo, queryWrapper);
}
1.2、添加交易记录
PaymentInfoService接口
/**
* 保存交易记录
* @param orderInfo
* @param transaction
*/
void savePaymentInfo(OrderInfo orderInfo, Transaction transaction);
实现:PaymentInfoServiceImpl
@Override
public void savePaymentInfo(OrderInfo orderInfo, Transaction transaction) {
PaymentInfo paymentInfo = new PaymentInfo();
paymentInfo.setOrderId(orderInfo.getId());
paymentInfo.setPaymentType(PaymentTypeEnum.WEIXIN.getStatus());
paymentInfo.setOutTradeNo(orderInfo.getOutTradeNo());//数据库字段的长度改成32
paymentInfo.setSubject(orderInfo.getTitle());
paymentInfo.setTotalAmount(orderInfo.getAmount());
paymentInfo.setPaymentStatus(PaymentStatusEnum.PAID.getStatus());
paymentInfo.setTradeNo(transaction.getTransactionId());
paymentInfo.setCallbackTime(new Date());
paymentInfo.setCallbackContent(transaction.toString());
baseMapper.insert(paymentInfo);
}
1.3、查单Controller
FrontWXPayController
@ApiOperation("查询支付状态")
@ApiImplicitParam(name = "outTradeNo",value = "订单id", required = true)
@GetMapping("/queryPayStatus/{outTradeNo}")
public Result queryPayStatus(@PathVariable String outTradeNo) {
//调用查询接口
boolean success = wxPayService.queryPayStatus(outTradeNo);
if (success) {
return Result.ok().message("支付成功");
}
return Result.ok().message("支付中").code(250);
}
1.4、查单Service
接口:WxPayService
/**
* 查询订单支付状态
* @param outTradeNo
* @return
*/
boolean queryPayStatus(String outTradeNo);
实现:WxPayServiceImpl
@Resource
private PaymentInfoService paymentInfoService;
@Override
public boolean queryPayStatus(String outTradeNo) {
// 初始化服务
NativePayService service = new NativePayService.Builder().config(rsaAutoCertificateConfig).build();
// 调用接口
try {
QueryOrderByOutTradeNoRequest request = new QueryOrderByOutTradeNoRequest();
// 调用request.setXxx(val)设置所需参数,具体参数可见Request定义
request.setOutTradeNo(outTradeNo);
request.setMchid(wxPayConfig.getMchId());
// 调用接口
Transaction transaction = service.queryOrderByOutTradeNo(request);
Transaction.TradeStateEnum tradeState = transaction.getTradeState();
//支付成功
if(tradeState.equals(Transaction.TradeStateEnum.SUCCESS)){
OrderInfo orderInfo = orderInfoService.selectByOutTradeNo(outTradeNo);
//更新订单状态
orderInfoService.updateStatus(outTradeNo, OrderStatusEnum.PAID.getStatus());
//记录支付日志
paymentInfoService.savePaymentInfo(orderInfo, transaction);
//通知医院修改订单状态
//组装参数
HashMap<String, Object> paramsMap = new HashMap<>();
paramsMap.put("hoscode", orderInfo.getHoscode());
paramsMap.put("hosOrderId", orderInfo.getHosOrderId());
paramsMap.put("timestamp", HttpRequestHelper.getTimestamp());
paramsMap.put("sign", HttpRequestHelper.getSign(paramsMap, "8af52af00baf6aec434109fc17164aae"));
//发送请求
JSONObject jsonResult = HttpRequestHelper.sendRequest(paramsMap, "http://localhost:9998/order/updatePayStatus");
//解析响应
if(jsonResult.getInteger("code") != 200) {
log.error("查单失败,"
+ "code:" + jsonResult.getInteger("code")
+ ",message:" + jsonResult.getString("message")
);
throw new GuiguException(ResultCodeEnum.FAIL.getCode(), jsonResult.getString("message"));
}
//返回支付结果
return true;//支付成功
}
return false;//支付中
} catch (HttpException e) { // 发送HTTP请求失败
// 调用e.getHttpRequest()获取请求打印日志或上报监控,更多方法见HttpException定义
log.error(e.getHttpRequest().toString());
throw new GuiguException(ResultCodeEnum.FAIL);
} catch (ServiceException e) { // 服务返回状态小于200或大于等于300,例如500
// 调用e.getResponseBody()获取返回体打印日志或上报监控,更多方法见ServiceException定义
log.error(e.getResponseBody());
throw new GuiguException(ResultCodeEnum.FAIL);
} catch (MalformedMessageException e) { // 服务返回成功,返回体类型不合法,或者解析返回体失败
// 调用e.getMessage()获取信息打印日志或上报监控,更多方法见MalformedMessageException定义
log.error(e.getMessage());
throw new GuiguException(ResultCodeEnum.FAIL);
}
}
2、前端整合
2.1、修改request.js
utils/request.js的响应拦截器中增加对250状态的判断
}else if (response.data.code !== 200) {
修改为
}else if (response.data.code !== 200 && response.data.code !== 250) {
2.2、api
在api/wxpay.js中添加的方法
//查询订单
queryPayStatus(outTradeNo) {
return request({
url: `/front/order/wxpay/queryPayStatus/${outTradeNo}`,
method: 'get'
})
},
3.3、show.vue轮询查单
js中修改pay方法:每隔3秒查单
添加queryPayStatus方法
完善closeDialog方法:停止定时器
//发起支付
pay(){
if(this.isPayShow) return
this.isPayShow = true
this.payText = '支付中.....'
wxpayApi.nativePay(this.orderInfo.outTradeNo).then((response) => {
this.codeUrl = response.data
this.dialogPayVisible = true
//每隔3秒查单
this.timer = setInterval(()=>{
console.log('轮询查单:' + new Date())
this.queryPayStatus()
}, 3000)
})
},
//查询订单状态
queryPayStatus(){
wxpayApi.queryPayStatus(this.orderInfo.outTradeNo).then(response => {
if(response.code == 250){
return
}
//清空定时器
clearInterval(this.timer)
window.location.reload()
})
},
//关闭对话框
closeDialog(){
//停止定时器
clearInterval(this.timer)
//恢复支付按钮
this.isPayShow = false
this.payText = '支付'
}
3、查单应答超时
3.1、测试应答超时
//应答超时
//设置响应超时,支付成功后没有及时响应,可能继续轮询查单,此时数据库会记录多余的支付日志
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
//返回支付结果
return true;//支付成功
3.2、处理重复通知
支付成功后,判断订单状态:
OrderInfo orderInfo = orderInfoService.selectByOutTradeNo(outTradeNo);
//处理支付成功后重复查单
//保证接口调用的幂等性:无论接口被调用多少次,产生的结果是一致的
Integer orderStatus = orderInfo.getOrderStatus();
if (OrderStatusEnum.UNPAID.getStatus().intValue() != orderStatus.intValue()) {
return true;//支付成功、关单、。。。
}
//更新订单状态
//记录支付日志
......
尚医通day16-网站怎么接入微信扫码支付?的更多相关文章
- ASP.NET Core Web 支付功能接入 微信-扫码支付篇
这篇文章将介绍ASP.NET Core中使用 开源项目 Payment,实现接入微信-扫码支付及异步通知功能. 开发环境:Win 10 x64.VS2017 15.6.4..NET Core SDK ...
- 【转载】ASP.NET Core Web 支付功能接入 微信-扫码支付篇
转自:http://www.cnblogs.com/essenroc/p/8630730.html 这篇文章将介绍ASP.NET Core中使用 开源项目 Payment,实现接入微信-扫码支付及异步 ...
- ASP.NET Core Web 支付功能接入 微信-扫码支付篇(转)
原文 https://www.cnblogs.com/essenroc/p/8630730.html // 随着版本更迭,新版本可能无法完全适用,请参考仓库内的示例. 这篇文章将介绍ASP.NET C ...
- thinkphp.2 thinkphp5微信支付 微信公众号支付 thinkphp 微信扫码支付 thinkphp 微信企业付款5
前面已经跑通了微信支付的流程,接下来吧微信支付和微信企业付款接入到thinkphp中,版本是3.2 把微信支付类.企业付款类整合到一起放到第三方类库,这里我把微信支付帮助类和企业付款类放到同一个文件了 ...
- PHP PC端微信扫码支付【模式二】详细教程-附带源码(转)
博主写这破玩意儿的时候花了大概快两天时间才整体的弄懂逻辑,考虑了一下~还是把所有代码都放出来给大家~抱着开源大无私的精神!谁叫我擅长拍黄片呢?同时也感谢我刚入行时候那些无私帮过我的程序员们! 首先还是 ...
- 【移动支付】.NET微信扫码支付接入(模式二-NATIVE)
一.前言 经过两三天的琢磨总算完成了微信扫码支付功能,不得不感叹几句: 微信提供的DEMO不错,直接复制粘贴就可以跑起来了: 微信的配置平台我真是服了.公众平台.商户平台.开放平台,一个平 ...
- 微信扫码支付PHP接入总结
微信扫码支付分为两种模式, 模式一比较复杂,需要公众号配置回调地址. 模式二比较简单,只需要在代码中配置回调地址就可以了. 我这次使用的是模式二. 需要配置参数, const APPID = 'xxx ...
- JAVA微信扫码支付模式二功能实现完整例子
概述 本例子实现微信扫码支付模式二的支付功能,应用场景是,web网站微信扫码支付.实现从点击付费按钮.到弹出二维码.到用户用手机微信扫码支付.到手机上用户付费成功.web网页再自动调整到支付成功后的页 ...
- Net MVC微信扫码支付
微信扫码支付+Asp.Net MVC 这里的扫码支付指的是PC网站上面使用微信支付,也就是官方的模式二,网站是Asp.net MVC,整理如下. 一.准备工作 使用的微信API中的统一下单方法,关键的 ...
- 微信公众号支付|微信H5支付|微信扫码支付|小程序支付|APP微信支付解决方案总结
最近负责的一些项目开发,都用到了微信支付(微信公众号支付.微信H5支付.微信扫码支付.APP微信支付).在开发的过程中,在调试支付的过程中,或多或少都遇到了一些问题,今天总结下,分享,留存. 先说注意 ...
随机推荐
- 集合-ArrayList 源码分析
1.概述 ArrayList 是一种变长的集合类,基于定长数组实现.ArrayList 允许空值和重复元素,当往 ArrayList 中添加的元素数量大于其底层数组容量时,其会通过扩容机制重新生成一个 ...
- 垃圾回收之CMS、G1、ZGC对比
ZGC(The Z Garbage Collector)是JDK 11中推出的一款低延迟垃圾回收器,它的设计目标包括: 停顿时间不超过10ms: 停顿时间不会随着堆的大小,或者活跃对象的大小而增加: ...
- kubernetes核心实战(三)--- ReplicationController
5.ReplicationController ReplicationController 确保在任何时候都有特定数量的 Pod 副本处于运行状态.换句话说,ReplicationController ...
- [软件设计&系统建模] Web软件通用能力模块
0 基础工具 1 日志 2 权限 3 文件处理(下载/上传) 4 对象池 对象池 数据库连接池 线程池 5 微服务 服务网关 配置中心 注册中心 服务调用 服务熔断 健康检测 Actuator 6 缓 ...
- DVWA上low级别反射型,存储型,DOM型XSS攻击获取用户cookie
1.什么是反射型 XSS 攻击? 反射型 XSS 是指应用程序通过 Web 请求获取不可信赖的数据,并在未检验数据是否存在恶意代码的情况下,将其发送给用户. 反射型 XSS 一般可以由攻击者构造带有恶 ...
- XSS的攻击
https://blog.csdn.net/m0_55854679/article/details/123028852
- Flask 上下文是什么 ?
哈喽大家好,我是咸鱼.今天我们来聊聊什么是 Flask 上下文 咸鱼在刚接触到这个概念的时候脑子里蹦出的第一个词是 CPU 上下文 今天咸鱼希望通过这篇文章,让大家能够对 Flask 上下文设计的 ...
- 原来这就是所谓的 JSR!
相信大家在学习 Java 的过程中,或多或少都见过 JSR 这个词.本篇文章就科普下什么是 JSR. 什么是 JSR ? JSR(Java Specification Requests),是指 Jav ...
- 如何使用Webpack工具构建项目
起步 webpack 用于编译 JavaScript 模块.一旦完成 安装,你就可以通过 webpack CLI 或 API 与其配合交互.如果你还不熟悉 webpack,请阅读 核心概念 和 对比, ...
- JAVA注解@Scheduled 不执行
spring boot项目需要在启动类加上注解 @EnableScheduling 定义一个接口 StockTask.java 1 public interface StockTask { 2 pub ...