目录
- 一、微信支付介绍
- 1、微信扫码支付申请
- 2、开发文档
- 3、微信支付SDK
- 二、微信支付开发
- 2、微信支付前端整合
- 三、订单支付后处理
一、微信支付介绍
1、微信扫码支付申请
微信扫码支付是商户系统按微信支付协议生成支付二维码,用户再用微信“扫一扫”完成支付的模式。该模式适用于PC网站支付、实体店单品或订单支付、媒体广告支付等场景。
申请步骤:(了解)
第一步:注册公众号(类型须为:服务号)
请根据营业执照类型选择以下主体注册:个体工商户| 企业/公司| 政府| 媒体| 其他类型。
第二步:认证公众号
公众号认证后才可申请微信支付,认证费:300元/年。
第三步:提交资料申请微信支付
登录公众平台,点击左侧菜单【微信支付】,开始填写资料等待审核,审核时间为1-5个工作日内。
第四步:开户成功,登录商户平台进行验证
资料审核通过后,请登录联系人邮箱查收商户号和密码,并登录商户平台填写财付通备付金打的小额资金数额,完成账户验证。
第五步:在线签署协议
本协议为线上电子协议,签署后方可进行交易及资金结算,签署完立即生效。
2、开发文档
微信支付接口调用的整体思路:
按API要求组装参数,以XML方式发送(POST)给微信支付接口(URL),微信支付接口也是以XML方式给予响应。程序根据返回的结果(其中包括支付URL)生成二维码或判断订单状态。
在线微信支付开发文档:
https://pay.weixin.qq.com/wiki/doc/api/index.html
(1)appid:微信公众账号或开放平台APP的唯一标识
(2)mch_id:商户号 (配置文件中的partner)
(3)partnerkey:商户密钥
(4)sign:数字签名, 根据微信官方提供的密钥和一套算法生成的一个加密信息, 就是为了保证交易的安全性
3、微信支付SDK
我们主要会用到微信支付SDK的以下功能:
(1)获取随机字符串
WXPayUtil.generateNonceStr()
(2)MAP转换为XML字符串(自动添加签名)
WXPayUtil.generateSignedXml(param, partnerkey)
(3)XML字符串转换为MAP
WXPayUtil.xmlToMap(result)
二、微信支付开发
1、微信支付接口-生成微信支付二维码
场景:用户扫描商户展示在各种场景的二维码进行支付
使用案例:
线下:家乐福超市、7-11便利店、上品折扣线下店等
线上:大众点评网站、携程网站、唯品会、美丽说网站等
开发模式:
模式一:商户在后台给你生成二维码,用户打开扫一扫
模式二:商户后台系统调用微信支付统一下单API生成预付交易,将接口返回的链接生成二维码,用户扫码后输入密码完成支付交易。注意:该模式的预付单有效期为2小时,过期后无法支付。
微信支付:生成xml发送请求
操作模块:service_orders
(1)service_order引入依赖
<dependency>
<groupId>com.github.wxpay</groupId>
<artifactId>wxpay-sdk</artifactId>
<version>0.0.3</version>
</dependency>
(2)添加配置
在resource下面创建weipay.properties
#关联的公众号appid
weixin.pay.appid=wx74862e0dfcf69954
#商户号
weixin.pay.partner=1558950191
#商户key
weixin.pay.partnerkey=T6m9iK73b0kn9g5v426MKfHQH7X8rKwb
读取properties工具类
WeiPayProperties
@ConfigurationProperties(prefix = "weixin.pay")
@PropertySource(value = "classpath:weipay.properties")
@Component
@Data
public class WeiPayProperties {
private String appid;
private String partner;
private String partnerkey;
}
(3)复制工具类
(4)添加添加交易记录方法
添加PaymentMapper接口
public interface PaymentMapper extends BaseMapper<PaymentInfo> {
}
添加PaymentService接口
public interface PaymentService extends IService<PaymentInfo> {
/**
* 保存交易记录
* @param order
* @param paymentType 支付类型(1:微信 2:支付宝)
*/
void savePaymentInfo(OrderInfo order, Integer paymentType);
}
添加PaymentServiceImpl实现类
@Service
public class PaymentServiceImpl extends ServiceImpl<PaymentMapper, PaymentInfo> implements PaymentService {
@Override
public void savePaymentInfo(OrderInfo order, Integer paymentType) {
QueryWrapper<PaymentInfo> queryWrapper=new QueryWrapper<PaymentInfo>();
queryWrapper.eq("order_id",order.getId());
PaymentInfo paymentInfo1 = baseMapper.selectOne(queryWrapper);
if(paymentInfo1 != null){
return;
}
PaymentInfo paymentInfo=new PaymentInfo();
paymentInfo.setOutTradeNo(order.getOutTradeNo());
paymentInfo.setOrderId(order.getId());
paymentInfo.setPaymentType(paymentType);
paymentInfo.setTotalAmount(order.getAmount());
String subject = new DateTime(order.getReserveDate()).toString("yyyy-MM-dd")+"|"+order.getHosname()+"|"+order.getDepname()+"|"+order.getTitle();
paymentInfo.setSubject(subject);
paymentInfo.setPaymentStatus(PaymentStatusEnum.UNPAID.getStatus()); //支付状态
baseMapper.insert(paymentInfo);
}
}
(5)添加支付service接口与实现
添加WeixinService接口
@Service
public class WeixinServiceImpl implements WeixinService {
@Autowired
private OrderInfoService orderInfoService;
@Autowired
private PaymentService paymentService;
@Autowired
private WeiPayProperties weiPayProperties;
@Override
public String createNative(Long orderId) {
//1.根据订单id去数据库中获取订单信息
OrderInfo orderInfo = orderInfoService.getById(orderId);
//2.保存支付记录信息
paymentService.savePaymentInfo(orderInfo, PaymentTypeEnum.WEIXIN.getStatus());
//3.请求微信服务器获取微信支付的url地址
HttpClient httpClient=new HttpClient("https://api.mch.weixin.qq.com/pay/unifiedorder");
Map<String,String> paramMap=new HashMap<String,String>();
paramMap.put("appid",weiPayProperties.getAppid());
paramMap.put("mch_id",weiPayProperties.getPartner());
paramMap.put("nonce_str", WXPayUtil.generateNonceStr());
Date reserveDate = orderInfo.getReserveDate();
String reserveDateString = new DateTime(reserveDate).toString("yyyy/MM/dd");
String body = reserveDateString + "就诊"+ orderInfo.getDepname();
paramMap.put("body",body);
paramMap.put("out_trade_no",orderInfo.getOutTradeNo());
paramMap.put("total_fee","1"); //为了测试 整1分
paramMap.put("spbill_create_ip","127.0.0.1");
paramMap.put("notify_url","http://guli.shop/api/order/weixinPay/weixinNotify");
paramMap.put("trade_type","NATIVE");
try{
httpClient.setXmlParam(WXPayUtil.generateSignedXml(paramMap, weiPayProperties.getPartnerkey()));//设置超参数
httpClient.setHttps(true);//支持https协议
httpClient.post(); //发送请求
String xmlResult = httpClient.getContent();
Map<String, String> stringStringMap = WXPayUtil.xmlToMap(xmlResult);
return stringStringMap.get("code_url");
}catch (Exception ex){
return "";
}
}
}
(6)添加Controller方法
添加WeixinController类
@RestController
@RequestMapping("/api/order/weixin")
public class WeixinController {
@Autowired
private WeixinService weiPayService;
/**
* 下单 生成二维码
*/
@GetMapping("/{orderId}")
public R createNative(@PathVariable Long orderId){
String url= weiPayService.createNative(orderId);
return R.ok().data("url",url);
}
}
2、微信支付前端整合
(1)在wx.js定义方法
createNative(orderId) {
return request({
url: `/api/order/weixin/${orderId}`,
method: `get`
})
},
(2)安装npm install vue-qriously
前端入口文件中引入(nuxt)
import VueQriously from 'vue-qriously'
Vue.use(VueQriously)
(3)修改order/show.vue组件
<template>
<!-- header -->
<div class="nav-container page-component">
<!--左侧导航 #start -->
<div class="nav left-nav">
<div class="nav-item ">
<span class="v-link clickable dark" onclick="javascript:window.location='/user'">实名认证 </span>
</div>
<div class="nav-item selected">
<span class="v-link selected dark" onclick="javascript:window.location='/order'"> 挂号订单 </span>
</div>
<div class="nav-item ">
<span class="v-link clickable dark" onclick="javascript:window.location='/patient'"> 就诊人管理 </span>
</div>
<div class="nav-item ">
<span class="v-link clickable dark"> 修改账号信息 </span>
</div>
<div class="nav-item ">
<span class="v-link clickable dark"> 意见反馈 </span>
</div>
</div>
<!-- 左侧导航 #end -->
<!-- 右侧内容 #start -->
<div class="page-container">
<div class="order-detail">
<div class="title"> 挂号详情</div>
<div class="status-bar">
<div class="left-wrapper">
<div class="status-wrapper BOOKING_SUCCESS">
<span class="iconfont"></span> {{ orderInfo.param.orderStatusString }}
</div>
</div>
<div class="right-wrapper">
<img src="//img.114yygh.com/static/web/code_order_detail.png" class="code-img">
<div class="content-wrapper">
<div> 微信<span class="iconfont"></span>关注“北京114预约挂号”</div>
<div class="watch-wrapper"> 快速挂号,轻松就医</div>
</div>
</div>
</div>
<div class="info-wrapper">
<div class="title-wrapper">
<div class="block"></div>
<div>挂号信息</div>
</div>
<div class="info-form">
<el-form ref="form" :model="form">
<el-form-item label="就诊人信息:">
<div class="content"><span>{{ orderInfo.patientName }}</span></div>
</el-form-item>
<el-form-item label="就诊日期:">
<div class="content"><span>{{ orderInfo.reserveDate }} {{ orderInfo.reserveTime == 0 ? '上午' : '下午' }}</span></div>
</el-form-item>
<el-form-item label="就诊医院:">
<div class="content"><span>{{ orderInfo.hosname }} </span></div>
</el-form-item>
<el-form-item label="就诊科室:">
<div class="content"><span>{{ orderInfo.depname }} </span></div>
</el-form-item>
<el-form-item label="医生职称:">
<div class="content"><span>{{ orderInfo.title }} </span></div>
</el-form-item>
<el-form-item label="医事服务费:">
<div class="content">
<div class="fee">{{ orderInfo.amount }}元
</div>
</div>
</el-form-item>
<el-form-item label="挂号单号:">
<div class="content"><span>{{ orderInfo.outTradeNo }} </span></div>
</el-form-item>
<el-form-item label="挂号时间:">
<div class="content"><span>{{ orderInfo.createTime }}</span></div>
</el-form-item>
</el-form>
</div>
</div>
<div class="rule-wrapper mt40">
<div class="rule-title"> 注意事项</div>
<div>1、请确认就诊人信息是否准确,若填写错误将无法取号就诊,损失由本人承担;<br>
<span style="color:red">2、【取号】就诊当天需在{{ orderInfo.fetchTime }}在医院取号,未取号视为爽约,该号不退不换;</span><br>
3、【退号】在{{ orderInfo.quitTime }}前可在线退号 ,逾期将不可办理退号退费;<br>
4、北京114预约挂号支持自费患者使用身份证预约,同时支持北京市医保患者使用北京社保卡在平台预约挂号。请于就诊当日,携带预约挂号所使用的有效身份证件到院取号;<br>
5、请注意北京市医保患者在住院期间不能使用社保卡在门诊取号。
</div>
</div>
<div class="bottom-wrapper mt60" v-if="orderInfo.orderStatus == 0 || orderInfo.orderStatus == 1">
<div class="button-wrapper">
<div class="v-button white" @click="cancelOrder()">取消预约</div>
</div>
<div class="button-wrapper ml20" v-if="orderInfo.orderStatus == 0">
<div class="v-button" @click="pay()">支付</div>
</div>
</div>
</div>
</div>
<!-- 右侧内容 #end -->
<!-- 微信支付弹出框 -->
<el-dialog :visible.sync="dialogPayVisible" style="text-align: left" :append-to-body="true" width="500px" @close="closeDialog">
<div class="container">
<div class="operate-view" style="height: 350px;">
<div class="wrapper wechat">
<div>
<qriously :value="url" :size="220"/>
<div style="text-align: center;line-height: 25px;margin-bottom: 40px;">
请使用微信扫一扫<br/>
扫描二维码支付
</div>
</div>
</div>
</div>
</div>
</el-dialog>
</div>
<!-- footer -->
</template>
<script>
import '~/assets/css/hospital_personal.css'
import '~/assets/css/hospital.css'
import orderInfoApi from '@/api/order'
import wxApi from '@/api/wx'
export default {
data() {
return {
orderId: null,
orderInfo: {
param: {}
},
dialogPayVisible: false,
payObj: {},
timer: null, // 定时器名称
url:null
}
},
created() {
this.orderId = this.$route.query.orderId
this.init()
},
methods: {
pay(){
this.dialogPayVisible=true
//请求后端,拿到微信支付的地址,通过前端组件将地址转换成二维码
wxApi.createNative(this.orderId).then(response=>{
this.url = response.data.url;
})
},
init() {
orderInfoApi.getOrders(this.orderId).then(response => {
console.log(response.data);
this.orderInfo = response.data.orderInfo
})
}
}
}
</script>
<style>
.info-wrapper {
padding-left: 0;
padding-top: 0;
}
.content-wrapper {
color: #333;
font-size: 14px;
padding-bottom: 0;
}
.bottom-wrapper {
width: 100%;
}
.button-wrapper {
margin: 0;
}
.el-form-item {
margin-bottom: 5px;
}
.bottom-wrapper .button-wrapper {
margin-top: 0;
}
</style>
三、订单支付后处理
1、前端页面修改
添加定时器方法,每隔3秒去查询一次支付状态
(1)封装api方法
queryPayStatus(orderId) {
return request({
url: `/api/order/weixin/status/${orderId}`,
method: 'get'
})
},
(2)修改页面调用
import '~/assets/css/hospital_personal.css'
import '~/assets/css/hospital.css'
import orderInfoApi from '@/api/order'
import wxApi from '@/api/wx'
export default {
data() {
return {
orderId: null,
orderInfo: {
param: {}
},
dialogPayVisible: false,
payObj: {},
timer: null, // 定时器名称
url:null
}
},
created() {
this.orderId = this.$route.query.orderId
this.init()
},
methods: {
pay(){
this.dialogPayVisible=true
//请求后端,拿到微信支付的地址,通过前端组件将地址转换成二维码
wxApi.createNative(this.orderId).then(response=>{
this.url = response.data.url;
if(this.url == '') {
this.dialogPayVisible = false
this.$message.error("支付错误")
} else {
this.timer = setInterval(() => {
this.queryPayStatus(this.orderId)
}, 3000);
}
})
},
queryPayStatus(orderId) {
wxApi.queryPayStatus(orderId).then(response => {
if (response.message == '支付中') {
return
}
clearInterval(this.timer);
window.location.reload()
})
},
init() {
orderInfoApi.getOrders(this.orderId).then(response => {
console.log(response.data);
this.orderInfo = response.data.orderInfo
})
}
}
}
</script>
<style>
.info-wrapper {
padding-left: 0;
padding-top: 0;
}
.content-wrapper {
color: #333;
font-size: 14px;
padding-bottom: 0;
}
.bottom-wrapper {
width: 100%;
}
.button-wrapper {
margin: 0;
}
.el-form-item {
margin-bottom: 5px;
}
.bottom-wrapper .button-wrapper {
margin-top: 0;
}
</style>
2、创建查询支付状态接口
(1)添加WeixinService方法和实现
/**
* 根据订单号去微信第三方查询支付状态
*/
Map<String, String> queryPayStatus(Long orderId);
//实现方法
@Override
public Map<String, String> queryPayStatus(Long orderId) {
OrderInfo orderInfo = orderInfoService.getById(orderId);
HttpClient httpClient =new HttpClient("https://api.mch.weixin.qq.com/pay/orderquery");
Map<String,String> map = new HashMap<String,String>();
map.put("appid", weiPayProperties.getAppid());
map.put("mch_id", weiPayProperties.getPartner());
map.put("out_trade_no", orderInfo.getOutTradeNo());//商户订单号
map.put("nonce_str",WXPayUtil.generateNonceStr());
try{
httpClient.setXmlParam(WXPayUtil.generateSignedXml(map, weiPayProperties.getPartnerkey()));
httpClient.setHttps(true);
httpClient.post();
String content = httpClient.getContent();
Map<String, String> stringStringMap = WXPayUtil.xmlToMap(content);
return stringStringMap; //支付
} catch (Exception ex){
return null;
}
}
(2)添加PaymentService方法和实现
//更新支付状态
void paySuccess(Long orderId, Map<String, String> map);
@Autowired
private OrderInfoService orderInfoService;
//更新支付状态
@Transactional
@Override
public void paySuccess(Long orderId, Map<String,String> map) {
//更新订单表的订单状态
OrderInfo orderInfo=new OrderInfo();
orderInfo.setId(orderId);
orderInfo.setOrderStatus(OrderStatusEnum.PAID.getStatus());
orderInfoService.updateById(orderInfo);
//更新支付记录表的支付状态
UpdateWrapper updateWrapper=new UpdateWrapper();
updateWrapper.eq("order_id",orderId);
updateWrapper.set("trade_no",map.get("transaction_id")); //微信支付的订单号[微信服务器]
updateWrapper.set("payment_status", PaymentStatusEnum.PAID.getStatus());
updateWrapper.set("callback_time",new Date());
updateWrapper.set("callback_content", JSONObject.toJSONString(map));
paymentService.update(updateWrapper);
}
}
(3)添加Controller方法
@Autowired
private WeixinService weiPayService;
@GetMapping("/status/{orderId}")
public R getPayStatus(@PathVariable Long orderId){
Map<String,String> map=weiPayService.queryPayStatus(orderId);
if(map == null){
return R.error().message("查询失败");
}
//查询成功+支付成功
if("SUCCESS".equals(map.get("trade_state"))){ //支付成功
weiPayService.paySuccess(orderId,map);//更新了订单状态0 1 +支付记录表的支付状态:1 2
return R.ok();
}
//
return R.ok().message("支付中"); //支付失败
}