【原创】微信小程序支付java后台案例(公众号支付同适用)(签名错误问题)
前言
1.微信小程序支付官方接口文档:[点击查看微信开放平台api开发文档]
2.遇到的坑:预支付统一下单签名结果返回[签名错误]失败,建议用官方[签名验证工具]检查签名是否存在问题.
3.遇到的坑:签名格式正确但统一下单接口依旧返回签名错误,解决方法=>去微信[商户平台]重新生成商户支付api密钥(文中提到的appKey),使用新的密钥进行调用接口。一般第一次生成的密钥是不可用的。

4.涉及的数据库表结构在最后面贴上
1.1 小程序支付说明

1.2 小程序轻量级支付模板(引用码云作者egan)引入jar包
<dependency>
<groupId>com.egzosn</groupId>
<artifactId>pay-java-common</artifactId>
<version>2.12.1</version>
</dependency>
<dependency>
<groupId>com.egzosn</groupId>
<artifactId>pay-java-wx</artifactId>
<version>2.12.1</version>
</dependency>
1.3 初始化微信支付配置存储类
private WxPayService wxUserPayService;
/**
* 微信支付必要信息初始化
* @param appId
*/
private void wxUserPayServiceInit(String appId) {
if (wxUserPayService == null) {
WxPayConfigStorage wxPayConfigStorage = new WxPayConfigStorage();
wxPayConfigStorage.setMchId(mchId);//支付商户号
wxPayConfigStorage.setAppid(appId);//小程序appid
// wxPayConfigStorage.setKeyPublic("转账公钥,转账时必填");
wxPayConfigStorage.setSecretKey(appKey);// 商户支付密钥
wxPayConfigStorage.setNotifyUrl(domain + "/wx/userpaycallback");//支付结果回调接口地址,必须外网可访问的完整url地址
wxPayConfigStorage.setSignType("MD5");
wxPayConfigStorage.setInputCharset("utf-8"); // https证书设置,退款必须 方式一
/*HttpConfigStorage httpConfigStorage = new HttpConfigStorage();
httpConfigStorage.setKeystorePath(keystorePath);//apiclient_cert.p12证书存放路径,填写绝对路径
httpConfigStorage.setStorePassword(mchId);
httpConfigStorage.setPath(true); httpConfigStorage.setMaxTotal(20);
httpConfigStorage.setDefaultMaxPerRoute(10);
wxUserPayService = new WxPayService(wxPayConfigStorage, httpConfigStorage);*/
wxUserPayService = new WxPayService(wxPayConfigStorage);
}
}
1.4 付款前生成微信预支付交易单,返回正确的预支付交易参数信息,供给微信小程序前端开发者调用付款API
String title = "开通会员";//支付下单标题
double total_fee = vipConfigInfoService.getInfo().getPrice();//实际支付金额
// 测试默认都是
// if (settingActive.equals("test")) {
// total_fee = 0.01;
// } // 添加支付记录信息,生成商户订单号,这里用支付记录id
String out_trade_no = payService.representVip(type, appId, openId, userId, total_fee, title); wxUserPayServiceInit(appId);//初始化微信支付配置存储类
PayOrder order = new PayOrder(title, type, new BigDecimal(total_fee), out_trade_no);
order.setTransactionType(WxTransactionType.JSAPI);
order.setOpenid(openId);
order.setSpbillCreateIp(spbill_create_ip);//终端ip,调用微信支付API的机器IP
order.setAddition(type);
JSONObject result = wxUserPayService.unifiedOrder(order);//微信统一下单接口,此方法在引入的jar中,直接传入必填的信息调用即可
System.out.println("请求微信支付统一下单结果:" + result.toString());
String timeStamp = System.currentTimeMillis() / 1000 + "";
JSONObject json = new JSONObject();
json.put("nonceStr", result.getString("nonce_str"));
json.put("package", "prepay_id=" + result.getString("prepay_id"));
json.put("timeStamp", timeStamp);
String stringSignTemp = "appId=" + appId + "&nonceStr=" + result.getString("nonce_str")
+ "&package=prepay_id=" + result.getString("prepay_id") + "&signType=MD5&timeStamp="
+ timeStamp;
json.put("paySign", wxUserPayService.createSign(stringSignTemp, "utf-8")); return json;//接口方法返回预支付交易信息,小程序前端获取返回参数,调用对应的付款接口
1.5 支付回调(付款后微信服务器后台回调支付结果信息)
/**
*
* 支付回调接口
*/
@RequestMapping("/userpaycallback")
public String wxUserPaycallback(HttpServletRequest request) {
try { Map<String, Object> params = wxUserPayService.getParameter2Map(request.getParameterMap(),
request.getInputStream());
if (null == params) {
throw new Exception("回调参数为空");
}
// 校验
if (!wxUserPayService.verify(params)) {
throw new Exception("校验失败");
}
payDone(params);//支付处理
return wxUserPayService.getPayOutMessage("SUCCESS", "成功").toMessage(); } catch (Exception e) {
e.printStackTrace();
return wxUserPayService.getPayOutMessage("FALL", "失败").toMessage();
}
} /**
* 支付结果处理
*/
private void payDone(Map<String, Object> dataMap) throws Exception {
System.out.println("支付回调:" + JSON.toJSONString(dataMap));
// {"transaction_id":"4200000109201805293331420304","nonce_str":"402880e963a9764b0163a979a16e0002","bank_type":"CFT","openid":"oXI6G5Jc4D44y2wixgxE3OPwpDVg","sign":"262978D36A3093ACBE4B55707D6EA7B2","fee_type":"CNY","mch_id":"1491307962","cash_fee":"10","out_trade_no":"14913079622018052909183048768217","appid":"wxa177427bc0e60aab","total_fee":"10","trade_type":"JSAPI","result_code":"SUCCESS","time_end":"20180529091834","is_subscribe":"N","return_code":"SUCCESS"}
String result_code = dataMap.get("result_code").toString();//支付结果code
String out_trade_no = dataMap.get("out_trade_no").toString();//商户交易订单id,此为支付记录id if (!result_code.equals("SUCCESS")) {
throw new Exception("支付回调失败:" + JSON.toJSONString(dataMap));
}
Pay pay = payService.getById(out_trade_no);//获取支付记录
if (pay == null || pay.getStatus() != 10) {//10=可用/待支付 20=支付成功 30=支付失败
throw new Exception("支付记录已经处理");
}
payService.pay(out_trade_no, result_code.equals("SUCCESS") ? 20 : 30);//根据回调结果更改支付结果状态
// 升级成会员的数据处理
if(result_code.equals("SUCCESS")) {//支付结果SUCCESS,支付成功开通会员,进行下一步操作
Document doc=new Document();
doc.put("vipStatus", 20);//状态20开通会员
doc.put("vipCreateTime", new Date());
doc.put("expireTime", StringUtil.getMonthNextOrBeforeDate(3));//设置过期时间为3个月后
userService.updateUserVip(pay.getUserId(),doc);
} }
2.1数据库表
支付记录表Pay
|
字段 |
名称 |
类型 |
备注 |
|---|---|---|---|
| _id | 唯一标识 | String | |
| type | 支付订单类型 | String | |
| orderId | 关联订单Id | String | |
| remark | 备注 | String | |
| openId | 微信用户openId | String | |
| account | 金额 | double | |
| status | 状态 | int | 10=可用/待支付 20=支付成功 30=支付失败 |
| createTime | 创建时间 | Date | |
| callBackTime | 回调时间 | Date |
3.1需要引用的类class
import com.egzosn.pay.common.bean.PayOrder;
import com.egzosn.pay.common.util.XML;
import com.egzosn.pay.wx.api.WxPayConfigStorage;
import com.egzosn.pay.wx.api.WxPayService;
import com.egzosn.pay.wx.bean.WxTransactionType;
结尾:此示例是用户开通vip的接口方法,重要的代码都有贴上。有疑问欢迎各位留言或联系邮箱laifw@foxmail.com,请多多指教
【原创】微信小程序支付java后台案例(公众号支付同适用)(签名错误问题)的更多相关文章
- 微信小程序登录JAVA后台
代码地址如下:http://www.demodashi.com/demo/12736.html 登录流程时序登录流程时序 具体的登录说明查看 小程序官方API 项目的结构图: springboot项目 ...
- 微信小程序与Java后台通信
一.写在前面 最近接触了小程序的开发,后端选择Java,因为小程序的代码运行在腾讯的服务器上,而我们自己编写的Java代码运行在我们自己部署的服务器上,所以一开始不是很明白小程序如何与后台进行通信的, ...
- 微信小程序与Java后台的通信
一.写在前面 最近接触了小程序的开发,后端选择Java,因为小程序的代码运行在腾讯的服务器上,而我们自己编写的Java代码运行在我们自己部署的服务器上,所以一开始不是很明白小程序如何与后台进行通信的, ...
- 微信小程序内判断是否关注公众号(JAVA)
微信小程序内判断是否关注公众号(JAVA) 思路来源(第二种): https://blog.csdn.net/Yanheeee/article/details/117295643 /** * 总体思路 ...
- 微信小程序:java后台获取openId
一.功能描述 openId是某个微信账户对应某个小程序或者公众号的唯一标识,但openId必须经过后台解密才能获取(之前实现过前台解密,可是由于微信小程序的种种限制,前台解密无法在小程序发布后使用) ...
- 微信小程序与java后台交互
java后台使用的ssm框架,小程序连接的本地接口.跟正常的web访问没什么区别,也是后台获取url,返回json数据:只是小程序前台请求的url要带上http://localhost:80801. ...
- 原创:微信小程序亲测体验,公众号入口曝光!
扫描即可体验知乐微信小程序,并且看到入口 你可以在这里看到相应的小程序:微信小程序商店 发现内有历史列表入口 真实小程序 搜索 操作栏 放置到桌面示意图必须搜索全称,才可以搜索到小程序 推荐给朋友,可 ...
- 微信小程序知识总结及案例集锦
微信小程序知识总结及案例集锦 微信小程序的发展会和微信公众号一样,在某个时间点爆发 学习路径 微信小程序最好的教程肯定是官方的文档啦,点击这里直达 微信官方文档 认真跟着文档看一遍,相信有vue前端经 ...
- 微信小程序需要https后台的创业机会思考
最近比较关注微信小程序,而且微信小程序的后台必须强制要求https, https相对http成本要高很多了. 这里我感觉有2个商机 (1)提供https 中转服务器 ,按流量来收费 (2) 微信小程序 ...
- 原创:微信小程序开发要点总结
废话不多少,下面是对我从开发微信小程序的第一步开始到发布的总结,觉得对您有帮助的话,可以赞赏下,以对我表示鼓励. 一:首先注册登录微信公众平台,这个平台很重要,以后查文档全在上面看.https://m ...
随机推荐
- Scratch3 二次开发系列
Scratch3.0来啦!!! Scratch做为图像化编程的首选语言,拖过积木块搭建实现动画游戏的制作.Scratch3添加了音乐.画笔.视频侦测.文字朗读.翻译等选择性下载扩展积木,可实现积 ...
- 【攻略】百度货币识别API,搞定防诈骗的应用小程序
1.需求及方案: 近两年用外币进行诈骗的案件很多.例如:2015年12月,一安徽诈骗团伙,用不值1角人民币的50印蒂(intis,秘鲁旧货币,1991年发行新货币后已停止流通,目前无货币价值,仅有&q ...
- 判断list集合不为空
在java开发中新手容易将判断一个list集合是否为空,只以If(list!=null)去判断,且容易和isEmpty()混淆,但是,list集合为空还是为null,是有区别的. 先看一下下面的例子, ...
- TensorFlow笔记-线程和队列
线程和队列 在使用TensorFlow进行异步计算时,队列是一种强大的机制. 为了感受一下队列,让我们来看一个简单的例子.我们先创建一个“先入先出”的队列(FIFOQueue),并将其内部所有元素初始 ...
- 盘一盘 synchronized (一)—— 从打印Java对象头说起
Java对象头的组成 Java对象的对象头由 mark word 和 klass pointer 两部分组成, mark word存储了同步状态.标识.hashcode.GC状态等等. klass ...
- 小白学python-day05-IDE、格式化输出、For While循环、断点、continue、break
今天是day05,以下是学习总结. 但行努力,莫问前程. ----------------------------------------------------------------------- ...
- webstorm mac 版破解
一.打开终端,输入: sudo vim /etc/hosts 回车后输入密码,编辑 hosts 文件,如图: 二.进入编辑模式(按 i 键),在最后一行添加如下代码: 0.0.0.0 account. ...
- SQL数据同步到ElasticSearch(三)- 使用Logstash+LastModifyTime同步数据
在系列开篇,我提到了四种将SQL SERVER数据同步到ES中的方案,本文将采用最简单的一种方案,即使用LastModifyTime来追踪DB中在最近一段时间发生了变更的数据. 安装Java 安装部分 ...
- helm安装MINIO文件服务器
MinIO Quickstart Guide MinIO 是一个基于Apache License v2.0开源协议的对象存储服务.它兼容亚马逊S3云存储服务接口,非常适合于存储大容量非结构化的数据,例 ...
- 算法与数据结构基础 - 广度优先搜索(BFS)
BFS基础 广度优先搜索(Breadth First Search)用于按离始节点距离.由近到远渐次访问图的节点,可视化BFS 通常使用队列(queue)结构模拟BFS过程,关于queue见:算法与数 ...