微信公众号支付开发过程
2018-02-27 17:46
260 查看
工作中接触微信公众号支付开发,记录一下开发的过程,公众号开发之后再补。
function onBridgeReady(){
WeixinJSBridge.invoke(
'getBrandWCPayRequest', {
"appId":"wx2421b1c4370ec43b", //公众号名称,由商户传入
"timeStamp":"1395712654", //时间戳,自1970年以来的秒数
"nonceStr":"e61463f8efa94090b1f366cccfbbb444", //随机串
"package":"prepay_id=u802345jgfjsdfgsdg888",
"signType":"MD5", //微信签名方式:
"paySign":"70EA570631E4BB79628FBCA90534C63FF7FADD89" //微信签名
},
function(res){
if(res.err_msg == "get_brand_wcpay_request:ok" ) {} // 使用以上方式判断前端返回,微信团队郑重提示:res.err_msg将在用户支付成功后返回 ok,但并不保证它绝对可靠。
}
);
}
if (typeof WeixinJSBridge == "undefined"){
if( document.addEventListener ){
document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);
}else if (document.attachEvent){
document.attachEvent('WeixinJSBridgeReady'
4000
, onBridgeReady);
document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);
}
}else{
onBridgeReady();
}
这是开发文档中前端调用支付接口的方法,appid是公共账号ID,package参数中的prepay_id由统一订单生成,等下再讲统一订单的过程,signType是签名方式默认为MD5需要与统一下单中的签名方式一致,签名生成的方法详见微信里面的规则。
生成签名的方法:
public static String getPaySignature(Doc doc) {
Arr keys=doc.keys();
String[] ss=new String[keys.len()];
for(int i=0;i<keys.len();i++) {
ss[i]=keys.gStr(i);
}
Arrays.sort(ss);
StringBuffer stringSignTemp=new StringBuffer();
for(int i=0;i<ss.length;i++) {
stringSignTemp.append("&"+ss[i]+"="+doc.g(ss[i]));
}
stringSignTemp.append("&key="+WeixinPayUtil.JSAPI_Secret);//WeixinPayUtil.JSAPI_Secret:商户平台API安全中的密钥,默认放在参数排序的最后再进行签名生成
stringSignTemp.deleteCharAt(0);
String sign=null;
try {
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] digest = md.digest(stringSignTemp.toString().getBytes());
sign = byteArrToHexStr(digest);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return sign;
}
接下来就是介绍统一下单的API,用于产生prepay_id,统一下单涉及的参数较多,注意参数里面的大小写,我因为统一下单下的appid参数和前端的appId参数名称有差别,被坑了好久才发现,需注意统一下单中的i是小写的,前端的i是大写的。
下面说明统一下单的参数和接口,以下参数都是公众号支付必填的,选填的不做介绍了
接口链接:https://api.mch.weixin.qq.com/pay/unifiedorder
appid:与前端参数中的appid一样为公众号ID
muc_id:商户号,商户平台的商户号
nonce_str:随即字符串,随机32位以内,生成较灵活无特殊的要求
sign:签名,将所有需要参数运用生成签名的方法生成,此处第一次签名生成,与前端中的签名不一样,我一开始开发的时候没注意
直接用统一下单的这个签名用于前端中
body:商品描述,如果有中文需要进行编码
out_trade_no:商品订单号,一般用自己项目生成的订单号即可,必须唯一
total_fee:标价金额,以分为单位
spbill_create_ip:终端IP,支付提交的用户端IP
notify_url:通知地址,支付成功回微信调用的回调函数,需要在回调函数中对HTTPSEVERLETREQUEST进行解析
如果接受到支付成功的信息则返回相应的XML提示微信
if(map.g("return_code")=="SUCCESS") {
System.out.println("-------------支付成功-------------");
return "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>";
}else {
System.out.println(map.g("return_msg"));
return "fail";
}
trade_type:交易类型,因为我是公众号支付,所以是JSAPI
/**
* 统一下单
*/
public String UnifiedOrder(Doc param) {
Doc UnifiedOrderParam=new Doc();
UnifiedOrderParam.s("appid",TokenThread.appid);
UnifiedOrderParam.s("body",param.g("body"));
UnifiedOrderParam.s("mch_id",Mch_Id);
UnifiedOrderParam.s("nonce_str",Uid.GetId());
UnifiedOrderParam.s("notify_url",Notify_Url);
UnifiedOrderParam.s("trade_type",Trade_Type);
UnifiedOrderParam.s("total_fee",param.g("total_fee"));
UnifiedOrderParam.s("openid",param.g("openid"));
UnifiedOrderParam.s("out_trade_no",param.g("OrderNo"));
UnifiedOrderParam.s("spbill_create_ip",Spbill_Create_Ip);
String sign=SignUtil.getPaySignature(UnifiedOrderParam);
UnifiedOrderParam.s("sign",sign);
//UnifiedOrderParam.del("key");
String str=MessageUtil.docToXml(UnifiedOrderParam,true);
return WeixinUtil.httpRequest(UnifiedOrder_URL, "POST",str);
}
Doc是以JSON为底层的类,用MAP存也行生成签名后将这个对象变为XML文件
public static String docToXml(Doc doc, boolean isCDATA) {
StringBuffer sb=new StringBuffer();
sb.append("<xml>\n");
docToXml(doc, isCDATA,sb,0);
sb.append("</xml>");
return sb.toString();
}
private static void docToXml(Doc doc, boolean isCDATA,StringBuffer sb,int space) {
Arr keys = doc.keys();
for (int i = 0; i < keys.len(); i++) {
String key = keys.gStr(i);
Doc tmp = doc.strDoc(key);
if (tmp.isNotNull()) {
sb.append(" <" + key + ">\n");
docToXml(tmp, isCDATA,sb,++space);
sb.append(" </" + key + ">\n");
} else {
sb.append(" <" + key + ">");
if (isCDATA) {
sb.append("<![CDATA[");
sb.append(doc.g(key));
sb.append("]]>");
} else {
sb.append(doc.g(key));
}
sb.append("</" + key + ">\n");
}
}
}
public static String httpRequest(String requestUrl, String requestMethod, String outputStr) {
String jsonObject = null;
StringBuffer buffer = new StringBuffer();
try {
// 创建SSLContext对象,并使用我们指定的信任管理器初始化
TrustManager[] tm = { new MyX509TrustManager() };
SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");
sslContext.init(null, tm, new SecureRandom());
// 从上述SSLContext对象中得到SSLSocketFactory兑现
SSLSocketFactory ssf = sslContext.getSocketFactory();
URL url = new URL(requestUrl);
HttpsURLConnection httpsUrlConn = (HttpsURLConnection) url.openConnection();
httpsUrlConn.setSSLSocketFactory(ssf);
httpsUrlConn.setDoOutput(true);
httpsUrlConn.setDoInput(true);
httpsUrlConn.setUseCaches(false);
// 设置请求方式(GET/Post)
httpsUrlConn.setRequestMethod(requestMethod);
if ("GET".equalsIgnor
c16f
eCase(requestMethod)) {
httpsUrlConn.connect();
}
// 当有数据需要提交时
if (null != outputStr) {
OutputStream outputStream = httpsUrlConn.getOutputStream();
// 注意编码格式,防止中文乱码
outputStream.write(outputStr.getBytes("UTF-8"));
outputStream.close();
}
// 将返回的输入流转换成字符串
InputStream inputStream = httpsUrlConn.getInputStream();
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
String str = null;
while ((str = bufferedReader.readLine()) != null) {
buffer.append(str);
}
bufferedReader.close();
inputStreamReader.close();
// 释放资源
inputStream.close();
inputStream = null;
httpsUrlConn.disconnect();
jsonObject = buffer.toString();
} catch (ConnectException ce) {
ce.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return jsonObject;
}
调用统一下单的接口地址产生前端接口所需要的参数
@RequestMapping("/getPay")
@ResponseBody
public String getPay(String OrderTotal,String OrderNo){
WeixinPayUtil wei=new WeixinPayUtil();
int OrderTotal1=Integer.parseInt(OrderTotal)*100;
String str=wei.UnifiedOrder(new Doc().s("body","TreeMan-shop").s("OrderNo",OrderNo).s("openid",((Doc)SessionUtil.getAttribute("Login")).g("openid")).s("total_fee",OrderTotal1));
String timeStamp = Long.toString(System.currentTimeMillis()/1000);
Doc data=MessageUtil.xmlToDoc(str);
Doc doc=new Doc();
doc.s("appId",TokenThread.appid);
doc.s("timeStamp",timeStamp);
doc.s("nonceStr",data.g("nonce_str"));
doc.s("package","prepay_id="+data.g("prepay_id"));
doc.s("signType","MD5");
String sign=SignUtil.getPaySignature(doc);
doc.s("prepay_id",data.g("prepay_id"));
doc.s("paySign",sign);
System.out.println(doc);
return doc.str();
}
前端上调用此方法生成参数传过去前端
if (!typeof WeixinJSBridge == "undefined"){
if( document.addEventListener ){
document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);
}else if (document.attachEvent){
document.attachEvent('WeixinJSBridgeReady', onBridgeReady);
document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);
}
}else{
onBridgeReady();
}
function onBridgeReady(){
jqxhr = $.ajax({
url : '${pageContext.request.contextPath}/WXPay/getPay',
dateType : 'json',
data:{
OrderTotal:OrderTotal.payment,
OrderNo:OrderNo
},
success : function(data){
bug(data);
var datadoc=JSON.parse(data);
WeixinJSBridge.invoke(
'getBrandWCPayRequest', {
"appId":datadoc.appId, //公众号名称,由商户传入
"timeStamp":datadoc.timeStamp, ///时间戳,自1970年以来的秒数
"nonceStr":datadoc.nonceStr, //随机串
"package":"prepay_id="+datadoc.prepay_id,
"signType":"MD5", //微信签名方式:
"paySign":datadoc.paySign //微信签名
},
function(res){
// 使用以上方式判断前端返回,微信团队郑重提示:res.err_msg将在用户支付成功后返回 ok,但并不保证它绝对可靠。
if(res.err_msg == "get_brand_wcpay_request:ok" ) {
//$('#myModal').modal('show');
useProduct();
JsonByAjax({Action:'Paycomplete', Params:{OrderNo:OrderNo}});
}else if(res.err_msg == "get_brand_wcpay_request:cancel"){//用户取消订单
}else{//支付失败
}
}
);
}
});
}
这就是微信公众号支付开发的全过程。过程也是很艰辛,很多坑,没有注意两次签名,参数大小写等问题,不过做出来还是很高兴的。
function onBridgeReady(){
WeixinJSBridge.invoke(
'getBrandWCPayRequest', {
"appId":"wx2421b1c4370ec43b", //公众号名称,由商户传入
"timeStamp":"1395712654", //时间戳,自1970年以来的秒数
"nonceStr":"e61463f8efa94090b1f366cccfbbb444", //随机串
"package":"prepay_id=u802345jgfjsdfgsdg888",
"signType":"MD5", //微信签名方式:
"paySign":"70EA570631E4BB79628FBCA90534C63FF7FADD89" //微信签名
},
function(res){
if(res.err_msg == "get_brand_wcpay_request:ok" ) {} // 使用以上方式判断前端返回,微信团队郑重提示:res.err_msg将在用户支付成功后返回 ok,但并不保证它绝对可靠。
}
);
}
if (typeof WeixinJSBridge == "undefined"){
if( document.addEventListener ){
document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);
}else if (document.attachEvent){
document.attachEvent('WeixinJSBridgeReady'
4000
, onBridgeReady);
document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);
}
}else{
onBridgeReady();
}
这是开发文档中前端调用支付接口的方法,appid是公共账号ID,package参数中的prepay_id由统一订单生成,等下再讲统一订单的过程,signType是签名方式默认为MD5需要与统一下单中的签名方式一致,签名生成的方法详见微信里面的规则。
生成签名的方法:
public static String getPaySignature(Doc doc) {
Arr keys=doc.keys();
String[] ss=new String[keys.len()];
for(int i=0;i<keys.len();i++) {
ss[i]=keys.gStr(i);
}
Arrays.sort(ss);
StringBuffer stringSignTemp=new StringBuffer();
for(int i=0;i<ss.length;i++) {
stringSignTemp.append("&"+ss[i]+"="+doc.g(ss[i]));
}
stringSignTemp.append("&key="+WeixinPayUtil.JSAPI_Secret);//WeixinPayUtil.JSAPI_Secret:商户平台API安全中的密钥,默认放在参数排序的最后再进行签名生成
stringSignTemp.deleteCharAt(0);
String sign=null;
try {
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] digest = md.digest(stringSignTemp.toString().getBytes());
sign = byteArrToHexStr(digest);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return sign;
}
接下来就是介绍统一下单的API,用于产生prepay_id,统一下单涉及的参数较多,注意参数里面的大小写,我因为统一下单下的appid参数和前端的appId参数名称有差别,被坑了好久才发现,需注意统一下单中的i是小写的,前端的i是大写的。
下面说明统一下单的参数和接口,以下参数都是公众号支付必填的,选填的不做介绍了
接口链接:https://api.mch.weixin.qq.com/pay/unifiedorder
appid:与前端参数中的appid一样为公众号ID
muc_id:商户号,商户平台的商户号
nonce_str:随即字符串,随机32位以内,生成较灵活无特殊的要求
sign:签名,将所有需要参数运用生成签名的方法生成,此处第一次签名生成,与前端中的签名不一样,我一开始开发的时候没注意
直接用统一下单的这个签名用于前端中
body:商品描述,如果有中文需要进行编码
out_trade_no:商品订单号,一般用自己项目生成的订单号即可,必须唯一
total_fee:标价金额,以分为单位
spbill_create_ip:终端IP,支付提交的用户端IP
notify_url:通知地址,支付成功回微信调用的回调函数,需要在回调函数中对HTTPSEVERLETREQUEST进行解析
如果接受到支付成功的信息则返回相应的XML提示微信
if(map.g("return_code")=="SUCCESS") {
System.out.println("-------------支付成功-------------");
return "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>";
}else {
System.out.println(map.g("return_msg"));
return "fail";
}
trade_type:交易类型,因为我是公众号支付,所以是JSAPI
/**
* 统一下单
*/
public String UnifiedOrder(Doc param) {
Doc UnifiedOrderParam=new Doc();
UnifiedOrderParam.s("appid",TokenThread.appid);
UnifiedOrderParam.s("body",param.g("body"));
UnifiedOrderParam.s("mch_id",Mch_Id);
UnifiedOrderParam.s("nonce_str",Uid.GetId());
UnifiedOrderParam.s("notify_url",Notify_Url);
UnifiedOrderParam.s("trade_type",Trade_Type);
UnifiedOrderParam.s("total_fee",param.g("total_fee"));
UnifiedOrderParam.s("openid",param.g("openid"));
UnifiedOrderParam.s("out_trade_no",param.g("OrderNo"));
UnifiedOrderParam.s("spbill_create_ip",Spbill_Create_Ip);
String sign=SignUtil.getPaySignature(UnifiedOrderParam);
UnifiedOrderParam.s("sign",sign);
//UnifiedOrderParam.del("key");
String str=MessageUtil.docToXml(UnifiedOrderParam,true);
return WeixinUtil.httpRequest(UnifiedOrder_URL, "POST",str);
}
Doc是以JSON为底层的类,用MAP存也行生成签名后将这个对象变为XML文件
public static String docToXml(Doc doc, boolean isCDATA) {
StringBuffer sb=new StringBuffer();
sb.append("<xml>\n");
docToXml(doc, isCDATA,sb,0);
sb.append("</xml>");
return sb.toString();
}
private static void docToXml(Doc doc, boolean isCDATA,StringBuffer sb,int space) {
Arr keys = doc.keys();
for (int i = 0; i < keys.len(); i++) {
String key = keys.gStr(i);
Doc tmp = doc.strDoc(key);
if (tmp.isNotNull()) {
sb.append(" <" + key + ">\n");
docToXml(tmp, isCDATA,sb,++space);
sb.append(" </" + key + ">\n");
} else {
sb.append(" <" + key + ">");
if (isCDATA) {
sb.append("<![CDATA[");
sb.append(doc.g(key));
sb.append("]]>");
} else {
sb.append(doc.g(key));
}
sb.append("</" + key + ">\n");
}
}
}
public static String httpRequest(String requestUrl, String requestMethod, String outputStr) {
String jsonObject = null;
StringBuffer buffer = new StringBuffer();
try {
// 创建SSLContext对象,并使用我们指定的信任管理器初始化
TrustManager[] tm = { new MyX509TrustManager() };
SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");
sslContext.init(null, tm, new SecureRandom());
// 从上述SSLContext对象中得到SSLSocketFactory兑现
SSLSocketFactory ssf = sslContext.getSocketFactory();
URL url = new URL(requestUrl);
HttpsURLConnection httpsUrlConn = (HttpsURLConnection) url.openConnection();
httpsUrlConn.setSSLSocketFactory(ssf);
httpsUrlConn.setDoOutput(true);
httpsUrlConn.setDoInput(true);
httpsUrlConn.setUseCaches(false);
// 设置请求方式(GET/Post)
httpsUrlConn.setRequestMethod(requestMethod);
if ("GET".equalsIgnor
c16f
eCase(requestMethod)) {
httpsUrlConn.connect();
}
// 当有数据需要提交时
if (null != outputStr) {
OutputStream outputStream = httpsUrlConn.getOutputStream();
// 注意编码格式,防止中文乱码
outputStream.write(outputStr.getBytes("UTF-8"));
outputStream.close();
}
// 将返回的输入流转换成字符串
InputStream inputStream = httpsUrlConn.getInputStream();
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
String str = null;
while ((str = bufferedReader.readLine()) != null) {
buffer.append(str);
}
bufferedReader.close();
inputStreamReader.close();
// 释放资源
inputStream.close();
inputStream = null;
httpsUrlConn.disconnect();
jsonObject = buffer.toString();
} catch (ConnectException ce) {
ce.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return jsonObject;
}
调用统一下单的接口地址产生前端接口所需要的参数
@RequestMapping("/getPay")
@ResponseBody
public String getPay(String OrderTotal,String OrderNo){
WeixinPayUtil wei=new WeixinPayUtil();
int OrderTotal1=Integer.parseInt(OrderTotal)*100;
String str=wei.UnifiedOrder(new Doc().s("body","TreeMan-shop").s("OrderNo",OrderNo).s("openid",((Doc)SessionUtil.getAttribute("Login")).g("openid")).s("total_fee",OrderTotal1));
String timeStamp = Long.toString(System.currentTimeMillis()/1000);
Doc data=MessageUtil.xmlToDoc(str);
Doc doc=new Doc();
doc.s("appId",TokenThread.appid);
doc.s("timeStamp",timeStamp);
doc.s("nonceStr",data.g("nonce_str"));
doc.s("package","prepay_id="+data.g("prepay_id"));
doc.s("signType","MD5");
String sign=SignUtil.getPaySignature(doc);
doc.s("prepay_id",data.g("prepay_id"));
doc.s("paySign",sign);
System.out.println(doc);
return doc.str();
}
前端上调用此方法生成参数传过去前端
if (!typeof WeixinJSBridge == "undefined"){
if( document.addEventListener ){
document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);
}else if (document.attachEvent){
document.attachEvent('WeixinJSBridgeReady', onBridgeReady);
document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);
}
}else{
onBridgeReady();
}
function onBridgeReady(){
jqxhr = $.ajax({
url : '${pageContext.request.contextPath}/WXPay/getPay',
dateType : 'json',
data:{
OrderTotal:OrderTotal.payment,
OrderNo:OrderNo
},
success : function(data){
bug(data);
var datadoc=JSON.parse(data);
WeixinJSBridge.invoke(
'getBrandWCPayRequest', {
"appId":datadoc.appId, //公众号名称,由商户传入
"timeStamp":datadoc.timeStamp, ///时间戳,自1970年以来的秒数
"nonceStr":datadoc.nonceStr, //随机串
"package":"prepay_id="+datadoc.prepay_id,
"signType":"MD5", //微信签名方式:
"paySign":datadoc.paySign //微信签名
},
function(res){
// 使用以上方式判断前端返回,微信团队郑重提示:res.err_msg将在用户支付成功后返回 ok,但并不保证它绝对可靠。
if(res.err_msg == "get_brand_wcpay_request:ok" ) {
//$('#myModal').modal('show');
useProduct();
JsonByAjax({Action:'Paycomplete', Params:{OrderNo:OrderNo}});
}else if(res.err_msg == "get_brand_wcpay_request:cancel"){//用户取消订单
}else{//支付失败
}
}
);
}
});
}
这就是微信公众号支付开发的全过程。过程也是很艰辛,很多坑,没有注意两次签名,参数大小写等问题,不过做出来还是很高兴的。
相关文章推荐
- 微信公众号支付开发全过程----JAVA
- 微信公众号支付开发全过程 --JAVA
- 微信公众号支付开发全过程(java版)
- 微信公众号支付开发全过程 --JAVA
- Java版微信公众号支付开发全过程
- 微信公众号支付开发的过程小结
- 【转】微信公众号支付开发全过程 --JAVA
- 微信公众号支付开发,希望能帮到大家
- [iOS]应用内支付(内购)的个人开发过程及坑!
- [iOS]应用内支付(内购)的个人开发过程及坑!
- 微信公众号支付开发
- 微信公众号开发中遇到的问题——支付回调,分享,获取openId(三)
- 微信公众号支付开发流程
- 移动支付--微信公众号支付开发
- 微信公众号分享、支付开发
- android支付宝支付开发过程
- nodejs开发微信公众号支付
- 微信公众号支付的开发经历 2016年java版
- JS微信公众号支付开发
- 关于http socket timeout 超时时间 未设置 导致线程一直在等待(线程饥饿),微信公众号开发过程遇到的。java