微信支付(APP支付)-服务端开发(一)
微信支付,首先需要注册一个商户平台公众账号,(网址:https://pay.weixin.qq.com/index.php/home/d_login)
目前微信支付的接入方式有四种方式:公众号支付,APP支付,扫描支付,刷卡支付。本文中我将详细讲解一下APP支付。
微信支付→APP支付官方文档:https://pay.weixin.qq.com/wiki/doc/api/app.php?chapter=8_1
主要流程如下:(https://pay.weixin.qq.com/wiki/doc/api/app.php?chapter=8_3)
商户系统和微信支付系统主要交互说明:
步骤1:用户在商户APP中选择商品,提交订单,选择微信支付。
步骤2:商户后台收到用户支付单,调用微信支付统一下单接口。参见【统一下单API】。
步骤3:统一下单接口返回正常的prepay_id,再按签名规范重新生成签名后,将数据传输给APP。参与签名的字段名为appId,partnerId,prepayId,nonceStr,timeStamp,package。注意:package的值格式为Sign=WXPay
步骤4:商户APP调起微信支付。api参见本章节【app端开发步骤说明】
步骤5:商户后台接收支付通知。api参见【支付结果通知API】
步骤6:商户后台查询支付结果。,api参见【查询订单API】
其中与后台相关的主要为步骤2和步骤6。虽然都有官方文档的说明,但是开发过程中,难免还会遇到不少的坑,比如签名问题,大小写问题等。
我首先讲解一下步骤2(统一下单API:https://pay.weixin.qq.com/wiki/doc/api/app.php?chapter=9_1):
统一下单接口地址:URL地址:https://api.mch.weixin.qq.com/pay/unifiedorder,请求参数,返回参数等文档中都有详细描述,就不再一一说明,直接上代码:
@RequestMapping(value = "/createOrder", method = {RequestMethod.GET, RequestMethod.POST})
public String createOrder(Map<String, String> model) throws Exception{
log.debug("WeChatPayController.createOrder,parameter[{trade_no,subject,total_fee},{"
+ request.getParameter("trade_no")
+ ","
+ request.getParameter("subject")
+ ","
+ request.getParameter("total_fee") + "}]");
WeChatRsp response = new WeChatRsp();
WeChat weChat = new WeChat();
String orderNo = request.getParameter("trade_no"); //订单号
String money = request.getParameter("total_fee"); //订单金额
String body = request.getParameter("subject"); //商品描述根据情况修改
//金额转化为分为单位
float sessionmoney = Float.parseFloat(money);
String finalmoney = String.format("%.2f", sessionmoney);
finalmoney = finalmoney.replace(".", "");
//商户相关资料
String appid = CommonUtils.getPropertiesValue("config", "appid");
String appsecret = CommonUtils.getPropertiesValue("config", "appsecret");
String partner = CommonUtils.getPropertiesValue("config", "partnerId");
String partnerkey = CommonUtils.getPropertiesValue("config", "partnerkey");
//商户号
String mch_id = partner;
//随机数
Random random = new Random();
String nonce_str = cn.emagsoftware.utils.MD5Util.getMD5String(String.valueOf(random.nextInt(10000)));
//商户订单号
String out_trade_no = orderNo;
int intMoney = Integer.parseInt(finalmoney);
//总金额以分为单位,不带小数点
int total_fee = intMoney;
//订单生成的机器 IP
String spbill_create_ip = request.getRemoteAddr();
System.out.println("订单生成的机器IP:"+spbill_create_ip);
//这里notify_url是 支付完成后微信发给该链接信息,可以判断会员是否支付成功,改变订单状态等。
String notify_url = CommonUtils.getPropertiesValue("config", "weChat_notify_url");
//交易类型
String trade_type = CommonUtils.getPropertiesValue("config", "trade_type");
SortedMap<String, String> packageParams = new TreeMap<String, String>();
packageParams.put("appid", appid);
packageParams.put("mch_id", mch_id);
packageParams.put("nonce_str", nonce_str);
packageParams.put("body", body);
packageParams.put("out_trade_no", out_trade_no);
packageParams.put("total_fee", total_fee+"");
packageParams.put("spbill_create_ip", spbill_create_ip);
packageParams.put("notify_url", notify_url);
packageParams.put("trade_type", trade_type);
HttpServletResponse httpServletResponse = null;
RequestHandler reqHandler = new RequestHandler(request, httpServletResponse);
reqHandler.init(appid, appsecret, partnerkey);
String sign = reqHandler.createSign(packageParams);
String xml="<xml>"+
"<appid>"+appid+"</appid>"+
"<body><![CDATA["+body+"]]></body>"+
"<mch_id>"+mch_id+"</mch_id>"+
"<nonce_str>"+nonce_str+"</nonce_str>"+
"<notify_url>"+notify_url+"</notify_url>"+
"<out_trade_no>"+out_trade_no+"</out_trade_no>"+
"<sign>"+sign+"</sign>"+
"<spbill_create_ip>"+spbill_create_ip+"</spbill_create_ip>"+
"<total_fee>"+total_fee+"</total_fee>"+
"<trade_type>"+trade_type+"</trade_type>"+
"</xml>";
log.debug("xml = "+xml);
String createOrderURL = WECHAT_CREATE_ORDER_URL;
String prepay_id="";
//获取预支付交易号
try {
prepay_id = new GetWxOrderno().getPayNo(createOrderURL, xml, "prepay_id");
} catch (Exception e1) {
e1.printStackTrace();
response.setResultCode(Constant.ERROR_CODE_9998);
response.setResultMessage(Constant.ERROR_MESSAGE.get(Constant.ERROR_CODE_9998));
}
if (prepay_id!=null& !prepay_id.equals("")) {
response.setResultCode(Constant.SUCCESS_CODE);
response.setResultMessage(Constant.ERROR_MESSAGE.get(Constant.SUCCESS_CODE));
weChat.setAppid(appid);
weChat.setPrepayid(prepay_id);
String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
String nonce_string = cn.emagsoftware.utils.MD5Util.getMD5String(timestamp);
weChat.setTimestamp(timestamp);
weChat.setNoncestr(nonce_string);
weChat.setPackages("Sign=WXPay");
weChat.setPartnerid(partner);
//二次签名
SortedMap<String, String> finalpackage = new TreeMap<String, String>();
finalpackage.put("appid", appid);
finalpackage.put("timestamp", timestamp);
finalpackage.put("noncestr", nonce_string);
finalpackage.put("package", "Sign=WXPay");
finalpackage.put("partnerid", mch_id);
finalpackage.put("prepayid", prepay_id);
String finalsign = reqHandler.createSign(finalpackage);
weChat.setSign(finalsign);
response.setData(weChat);
}else {
log.debug("预支付交易号生成失败。。。");
response.setResultCode(Constant.ERROR_CODE_9998);
response.setResultMessage(Constant.ERROR_MESSAGE.get(Constant.ERROR_CODE_9998));
}
try{
model.put(Constant.RETURN_MESSAGE, JsonUtils.getJSONString(response));
log.debug("WeChatPayController.createOrder.response=="+JsonUtils.getJSONString(response));
} catch (Exception ex) {
log.error("VersionController.getVersion", ex);
}
return RET_JSP;
}
APP端发起支付请求之后,会发送订单号给服务端程序,服务端拿到订单号之后,根据统一下单地址,发送xml文件给微信,微信接受处理后,如果返回成功,则同时会返回一个预支付交易会话标识(prepay_id),拿到这个标识之后,服务端进行二次签名,生成sign,签名参数如下:
//二次签名
SortedMap<String, String> finalpackage = new TreeMap<String, String>();
finalpackage.put("appid", appid); //appid
finalpackage.put("timestamp", timestamp); //时间戳 十位
finalpackage.put("noncestr", nonce_string); //随机字符串
finalpackage.put("package", "Sign=WXPay"); //固定值
finalpackage.put("partnerid", mch_id); //商户id(微信商户平台获取)
finalpackage.put("prepayid", prepay_id); //第一次请求微信,成功后,返回的参数
String finalsign = reqHandler.createSign(finalpackage); //生成签名
weChat.setSign(finalsign); //生成签名后,放入对象中
response.setData(weChat); //参数返回给前端
第一次发送统一下单的时候,官方文档中都有说明,哪些参数是必须的,哪些参数不是必须的,以及参数类型,此处不再一一解释,不理解的可以参考官方文档。重点说一下二次签名:
二次签名的时候涉及到的参数有appid,timestamp,noncestr,partnerid,prepayid,package,这6个参数全部是小写(大小写不同,MD5加密结果不一致,二次签名官方没有文档,比较坑)
APP端获取到服务端传递的参数后,调起支付接口(https://pay.weixin.qq.com/wiki/doc/api/app.php?chapter=9_12&index=2),这一点是APP端的操作,不在详解。
APP端支付成功后,会再次发送请求到服务器端,确定订单是否付款成功,服务端需要再次向微信发起请求,查询订单,具体操作查看下一章节。
注释:开发中遇到任务问题(服务端),欢迎咨询,我也是初次开发微信支付,希望可以帮到你。
微信支付(APP支付)-服务端开发(一)的更多相关文章
- Day01_搭建环境&CMS服务端开发
学成在线 第1天 讲义-项目概述 CMS接口开发 1 项目的功能构架 1.1 项目背景 受互联网+概念的催化,当今中国在线教育市场的发展可谓是百花齐放.如火如荼. 按照市场领域细分为:学前教育.K12 ...
- 个人公众号服务端开发Demo
公众号出来很久了,也可以个人申请.知道公众号的服务端开发其实很简单,接口调用封装,数据存取,不外如是. 人一旦懒了,真的是 “无可救药” 了...现简单描述晚到的公众号HelloWorld 思路 公众 ...
- 在线教学、视频会议 Webus Fox(2) 服务端开发手册
上次在<在线教学.视频会议软件 Webus Fox(1)文本.语音.视频聊天及电子白板基本用法>里介绍了软件的基本用法.本文主要介绍服务器端如何配置.开发. 1. 配置 1.1 IIS配置 ...
- Swift3.0服务端开发(一) 完整示例概述及Perfect环境搭建与配置(服务端+iOS端)
本篇博客算是一个开头,接下来会持续更新使用Swift3.0开发服务端相关的博客.当然,我们使用目前使用Swift开发服务端较为成熟的框架Perfect来实现.Perfect框架是加拿大一个创业团队开发 ...
- Android 服务端开发之开发环境配置
Android 服务端开发之开发环境配置 这里是在Eclipse的基础上安装PhpEclipse插件方法,PHPEclipse是Eclipse的 一个用于开发PHP的插件.当然也可以采用Java开发a ...
- 网站的优化----首页优化---app调取服务端数据
高并发经常会发生在有大活跃用户量来访问网站的某个点,例如用户高聚集的业务场景中,如:抢购,促销等.为了让用户流畅的访问网站,来根据自己的业务设计适合系统的处理方案. //对于APP网站首页数据,通常是 ...
- Swift3.0服务端开发(三) Mustache页面模板与日志记录
本篇博客主要介绍如果在Perfect工程中引入和使用Mustache页面模板与日志记录系统.Mustache页面模板类似于PHP中的smarty模板引擎或者Java中的JSTL标签.当然Mustach ...
- Swift3.0服务端开发(五) 记事本的开发(iOS端+服务端)
前边以及陆陆续续的介绍了使用Swift3.0开发的服务端应用程序的Perfect框架.本篇博客就做一个阶段性的总结,做一个完整的实例,其实这个实例在<Swift3.0服务端开发(一)>这篇 ...
- 如何有效快速提高Java服务端开发人员的技术水平?
我相信很多工作了3-5年的开发人员都会经常问自己几个问题: 1.为什么总是感觉技术没有质的提高? 2.如何能够有效和快速的提高自身的技术水平? 3.如何进入到一个牛逼的大公司,认识牛逼的人? 这篇文章 ...
随机推荐
- 【BZOJ2839】集合计数&&【BZOJ3622】已经没有什么好害怕的了
再谈容斥原理来两道套路几乎一致的题目[BZOJ2839]集合计数Description一个有N个元素的集合有2^N个不同子集(包含空集),现在要在这2^N个集合中取出若干集合(至少一个),使得它们的交 ...
- 【UER #1】DZY Loves Graph(待卡常数)
题解: 正解是可持久化并查集 但这个显然是lct可以维护的 但这常数是个问题啊??? #include <bits/stdc++.h> using namespace std; struc ...
- SVM(支持向量机)分类算法
SVM算法比较复杂,数学功底要求很高. 详见七月大神博客<支持向量机通俗导论(理解SVM的三层境界)>
- 【Java】 剑指offer(27) 二叉树的镜像
本文参考自<剑指offer>一书,代码采用Java语言. 更多:<剑指Offer>Java实现合集 题目 请完成一个函数,输入一个二叉树,该函数输出它的镜像. 思路 画图可 ...
- Centos7与Windows10添加Windows10启动项并设置为默认启动
在Centos7下root登陆 编辑 /boot/grub2/grub.cfg vim /boot/grub2/grub.cfg 在第一行添加 menuentry "Windows10&qu ...
- MySQL 5.7.14 net start mysql 服务无法启动
解决方法: 1.mysqld --initialize 初始化data目录 2.重新输入net start mysql命令 补充,服务停止的方法:net stop mysql
- Mybatis Generator xml格式配置
Mybatis Generator可以使用Maven方式和Java方法,使用Maven这里是配置文件: <?xml version="1.0" encoding=" ...
- CentOS 6.4在运行XFS时系统crash的bug分析
最近有一台CentOS 6.4的服务器发生多次crash,kernel version 是Linux 2.6.32-431.29.2.el6.x86_64.从vmcore-dmesg日志内容及cras ...
- html 转成 pdf 进行预览、下载、打印
html 页面转成 pdf,直接看代码: 参考地址: https://github.com/linwalker/render-html-to-pdf 给出代码 方便粘贴: var downPdf = ...
- Python中应用SQL及SQLAlchemy(一)
以SQLit3为例: import sqlite3 conn = sqlite3.connect('db.sqlite3') #获取游标对象 cur = conn.cursor() #执行一系列SQL ...