您的位置:首页 > 移动开发 > 微信开发

微信公众号支付开发过程

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{//支付失败
           
           }
       }
   ); 
}
});

}
这就是微信公众号支付开发的全过程。过程也是很艰辛,很多坑,没有注意两次签名,参数大小写等问题,不过做出来还是很高兴的。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: