V3微信支付开发笔录

真是坑爹啊,微信支付到处都是坑,一不小心就栽里面了, 文档也不怎么全,经过一周的奋斗终于把微信支付功能搞定,在此写下自己当时走入的误区和一些需要注意的地方,希望后边开发的朋友们可以少走弯路,少被微信坑~~~~~

基本上微信支付需要根据流程图走:

基本步骤如下:

1.先调用统一下单接口,获取到perpay_id,在调用接口的时候需要自己写签名,签名前拼接的字符串是有顺序的,实在不清楚顺序可以使用官网上的签名在线工具进行校验,

根据校验结果进行填写保证OK,地址https://pay.weixin.qq.com/wiki/tools/signverify/,调用统一下单接口代码如下:

public Map<String, Object> doGetWXUnifiedOrder(HttpServletRequest request,HttpServletResponse response){
String url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
String appId = "";
String mchId = ""; //商户ID,这个是需要商户进行申请的,只有服务号可以
String key = ""; //登录商户端可以自己进行设置,32位的
String nonceStr = UUID.randomUUID().toString().replaceAll("-", "");
String timeStamp = Long.toString(System.currentTimeMillis()/1000);
String outTradeNo = "";//订单号,确保在商户系统唯一
String body = ""; //商品名称或者描述都可以
String totalFee = "";//订单总金额
String spbillCreateIp = getIp(request); //机器IP
String notifyUrl = ""; //接收微信支付成功通知
String tradeType = "JSAPI";//交易类型 JSAPI、NATIVE、APP
String openid =""; //用户的唯一标识,trade_type=JSAPI时必填

List<KV> xmlList = new ArrayList<KV>();
xmlList.add(new KV("appid", appId));
xmlList.add(new KV("body", body));
xmlList.add(new KV("mch_id", mchId));
xmlList.add(new KV("nonce_str", nonceStr.replaceAll("-", ""))); 
xmlList.add(new KV("notify_url", notifyUrl));
xmlList.add(new KV("openid", openId));
xmlList.add(new KV("out_trade_no", outTradeNo));
xmlList.add(new KV("spbill_create_ip", spbillCreateIp));
xmlList.add(new KV("total_fee", totalFee));
xmlList.add(new KV("trade_type", tradeType)); 

//生成签名
StringBuffer sb = new StringBuffer();
for (int i = 0; i < xmlList.size(); i++) {
KV kv = xmlList.get(i);
String k = kv.getKey();
String v = kv.getValue();
if(null != v && !"".equals(v)
&& !"sign".equals(k) && !"key".equals(k)) {
if("total_fee".equals(k)){
Double dd = Double.parseDouble(v)*100;
int y = dd.intValue();
sb.append(k + "=" + y + "&");
}else {
sb.append(k + "=" + v + "&");
}

}
}
sb.append("key=" + key); //自己的API密钥 
System.out.println("密钥---------"+sb);
String sign1 = MD5Util.MD5Encode(sb.toString(), "UTF-8").toUpperCase();
xmlList.add(new KV("sign", sign1));


//转换成xml
StringBuffer sb1 = new StringBuffer();
sb1.append("<xml>");
for (int i = 0; i < xmlList.size(); i++) {
KV kv = xmlList.get(i);
String k = kv.getKey();
String v = kv.getValue();
if ("attach".equalsIgnoreCase(k)||"body".equalsIgnoreCase(k)||"sign".equalsIgnoreCase(k)) {
sb1.append("<"+k+">"+"<![CDATA["+v+"]]></"+k+">");
}else {
if("total_fee".equals(k)){
Double dd = Double.parseDouble(v)*100;
int y = dd.intValue();
sb1.append("<"+k+">"+y+"</"+k+">");
}else {
sb1.append("<"+k+">"+v+"</"+k+">");

}
}
sb1.append("</xml>");
System.out.println("传递参数----------"+sb1);


URL url2;
try {
url2 = new URL(url);
HttpURLConnection conn = (HttpURLConnection) url2.openConnection();

conn.setConnectTimeout(30000); // 设置连接主机超时(单位:毫秒)

conn.setReadTimeout(30000); // 设置从主机读取数据超时(单位:毫秒)

conn.setDoOutput(true); // post请求参数要放在http正文内,顾设置成true,默认是false

conn.setDoInput(true); // 设置是否从httpUrlConnection读入,默认情况下是true

conn.setUseCaches(false); // Post 请求不能使用缓存

// 设定传送的内容类型是可序列化的java对象(如果不设此项,在传送序列化对象时,当WEB服务默认的不是这种类型时可能抛java.io.EOFException)

conn.setRequestProperty("Content-Type","application/x-www-form-urlencoded");

conn.setRequestMethod("POST");// 设定请求的方法为"POST",默认是GET

conn.setRequestProperty("Content-Length",sb1.length()+"");

String encode = "utf-8";

OutputStreamWriter out = new OutputStreamWriter(conn.getOutputStream(), encode);

out.write(sb1.toString());

out.flush();

out.close();

// 获取响应内容体

BufferedReader in = new BufferedReader(new InputStreamReader(

conn.getInputStream(), "UTF-8"));

String line = "";

StringBuffer strBuf = new StringBuffer();

while ((line = in.readLine()) != null) {

strBuf.append(line).append("\n");

}

in.close();
System.out.println("统一支付返回数据-------"+strBuf);
map = WeixinMessageUtil.parseXml(strBuf.toString());

map.put("timeStamp", timeStamp);
map.put("nonceStr", nonceStr);

if(map.get("result_code").equals("SUCCESS") && map.get("return_code").equals("SUCCESS")){

//统一下单成功后可以进入

//重新生成的签名主要是用于H5调用API支付的时候使用的,这个和统一下单返回的签名是不一样的
String paySignTemp = "appId="+appId+"&nonceStr="+nonceStr+"&package=prepay_id="+map.get("prepay_id")+"&signType=MD5&timeStamp="+timeStamp+"&key="+key;
System.out.println("paySignTemp--------"+paySignTemp);
String paySign = MD5Util.MD5Encode(paySignTemp, "UTF-8");
map.put("paySign", paySign);
System.out.println("paySign---------"+paySign);
//将订单预支付信息保存数据库
BasePrepayOrderInfo prepayOrder = (BasePrepayOrderInfo) basedao.findUniqueResult("from BasePrepayOrderInfo where orderCode='"+outTradeNo+"'");
if(prepayOrder == null){
prepayOrder = new BasePrepayOrderInfo();
prepayOrder.setOrderCode(outTradeNo);
prepayOrder.setPrepayId(map.get("prepay_id").toString());
prepayOrder.setPaySign(paySign);
basedao.save(prepayOrder);
}
}else {
System.out.println("统一下单失败");
}

//map.put("paySign", sign1);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}


//map.clear();
return map;
}

统一下单需要用到的一下接口

/**
* 获取ip
* @param request
* @return
*/
public static String getIp(HttpServletRequest request) {
if (request == null)
return "";
String ip = request.getHeader("X-Requested-For");
if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("X-Forwarded-For");
}
if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_CLIENT_IP");
}
if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_X_FORWARDED_FOR");
}
if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
return ip;
}

package com.ioif.util;

import java.security.MessageDigest;

public class MD5Util {
private static String byteArrayToHexString(byte b[]) {
StringBuffer resultSb = new StringBuffer();
for (int i = 0; i < b.length; i++)
resultSb.append(byteToHexString(b[i]));
return resultSb.toString();
}
private static String byteToHexString(byte b) {
int n = b;
if (n < 0)
n += 256;
int d1 = n / 16;
int d2 = n % 16;
return hexDigits[d1] + hexDigits[d2];
}
public static String MD5Encode(String origin, String charsetname) {
String resultString = null;
try {
resultString = new String(origin);
MessageDigest md = MessageDigest.getInstance("MD5");
if (charsetname == null || "".equals(charsetname))
resultString = byteArrayToHexString(md.digest(resultString
.getBytes()));
else
resultString = byteArrayToHexString(md.digest(resultString
.getBytes(charsetname)));
} catch (Exception exception) {
}
return resultString;
}
private static final String hexDigits[] = { "0", "1", "2", "3", "4", "5",
"6", "7", "8", "9", "a", "b", "c", "d", "e", "f" };
}

XML进行解析接口

public static Map<String, Object> parseXml(String xmlStr) throws Exception {
// 将解析结果存储在HashMap中
Map<String, Object> map = new HashMap<String, Object>();
InputStream is = new ByteArrayInputStream(xmlStr.getBytes("UTF-8"));
// 读取输入流
SAXBuilder builder = new SAXBuilder();
org.jdom.Document document = builder.build(is);
// 得到xml根元素
org.jdom.Element root = document.getRootElement();
// 得到根元素的所有子节点
List<org.jdom.Element> elementList = root.getChildren();
Iterator it = elementList.iterator();
while (it.hasNext()) {
org.jdom.Element e = (org.jdom.Element)it.next();
String k = e.getName();
String v = null;
List children = e.getChildren();
if(children.isEmpty()){
v = e.getTextNormalize();
}else{
v = getChildrenText(children);
}
map.put(k, v);
}
return map;
}
public static String getChildrenText(List children) {

StringBuffer sb = new StringBuffer();

if(!children.isEmpty()) {

Iterator it = children.iterator();

while(it.hasNext()) {

org.jdom.Element e = (org.jdom.Element) it.next();

String name = e.getName();

String value = e.getTextNormalize();

List list = e.getChildren();

sb.append("<" + name + ">");

if(!list.isEmpty()) {

sb.append(getChildrenText(list));

}

sb.append(value);

sb.append("</" + name + ">");

}

}

return sb.toString();

}

 

2.统一下单成功后可以使用H5去调用支付API,js代码如下

function onBridgeReady(orderCode,goodsName,orderPrice){
var orderQueryUrl = localUrl + "wx/orderQuery";
$.post(orderQueryUrl,{orderCode:orderCode,goodsName:goodsName,orderPrice:orderPrice},function(res){
if(res.return_code=="SUCCESS"&&res.result_code=="SUCCESS"){
pay(orderCode, goodsName, orderPrice);
}
},"json");
}

function pay(orderCode,goodsName,orderPrice){
var url = localUrl+"wx/unifiedorder";
$.post(url,{orderCode:orderCode,goodsName:goodsName,orderPrice:orderPrice},function(res){
if(res.result_code=="SUCCESS" && res.return_code=="SUCCESS"){
var appId = res.appid;
var mchId = res.mch_id;
var nonceStr = res.nonceStr;
var body = goodsName;
var outTradeNo = orderCode;
var totalFee = orderPrice;
alert("appid--"+appId+"---prepay_id--"+res.prepay_id+"----paySign"+res.sign);
WeixinJSBridge.invoke(
'getBrandWCPayRequest',{
"appId":appId,
"timeStamp":res.timeStamp,
"nonceStr":nonceStr,
"package":"prepay_id="+res.prepay_id,
"signType":"MD5",
"paySign":res.paySign //这一块使用的签名是统一下单完成后重新生成的
},function(data){
alert(data.err_msg)
//if(res.err_msg=="get_brand_wcpay_request:ok"){}
})
}else{
alert(res.return_msg)

},"json");
}
function callPay(orderCode,goodsName,orderPrice){
if(typeof(WeixinJSBridge) =="undefined"){
if(document.addEventListener){
document.addEventListener('WeixinJSBridgeReady',onBridgeReady(orderCode,goodsName,orderPrice),false);
}else if(document.attachEvent){
document.attachEvent("WeixinJSBridgeReady",onBridgeReady(orderCode,goodsName,orderPrice));
document.attachEvent("onWeixinJSBridgeReady",onBridgeReady(orderCode,goodsName,orderPrice));
}
}else{
onBridgeReady(orderCode,goodsName,orderPrice);
}
}

H5调用支付API有时候一闪而过,一方面就是因为签名失败,但是在JS端它只会报“get_brand_wcpay_request:fail”,

另一方面就是支付授权目录的问题,这个是需要配置到最后一级目录的,要不然验证不过,这就是微信最坑爹的部分

3.支付完成后微信会发生通支付自己后端写的通知接口,统一下单中notify_url写的路径是什么就会通知到哪里

通知接口代码如下:

public Map<String, Object> doUpdateOrderStatus(HttpServletRequest request,HttpServletResponse response) {
try {
// 获取微信支付完成后返回的参数
BufferedReader in = new BufferedReader(new InputStreamReader(
request.getInputStream(), "UTF-8"));
String line = "";
StringBuffer strBuf = new StringBuffer();
while ((line = in.readLine()) != null) {
strBuf.append(line).append("\n");
}
in.close();
map = WeixinMessageUtil.parseXml(strBuf.toString());
System.out.println("微信通知参数-------"+map);
Object returnCode = map.get("return_code");// 返回状态码
if ("SUCCESS".equals(returnCode)) {

String openId = map.get("openid").toString();
String ordercode = map.get("out_trade_no").toString();
///

成功后可以自行修改数据库数据

/

//修改完成后返回成功参数给微信,未接收到成功信息微信会隔一段时间发送一次通知
StringBuffer sb1 = new StringBuffer();
sb1.append("<xml>");
sb1.append("<return_code><![CDATA[SUCCESS]]></return_code>");
sb1.append("<return_msg><![CDATA[OK]]></return_msg>");
sb1.append("</xml>");
try {
response.setContentType("application/json; charset=utf-8");
PrintWriter out = response.getWriter();
// 将流传递给微信端
out.print(sb1);
// 关闭流
out.flush();
out.close();

} catch (IOException ex) {
ex.printStackTrace();
}
} else {
map.put("statue", "0");
map.put("error", map.get("return_msg"));
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

return map;
}

至此微信支付整个流程就开发完成了~~~~~希望以后别再碰到这玩意了


转载自:http://www.w2bc.com/article/76604

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值