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

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

基本步骤如下:

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”,

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

调用支付API中途放弃支付,再重新支付调用统一下单会在微信端报错订单号重复,这时就需要换个新的订单号进行统一下单

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;
}

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

V3微信支付开发笔录的更多相关文章

  1. PHP微信支付开发实例

    这篇文章主要为大家详细介绍了PHP微信支付开发过程,具有一定的参考价值,感兴趣的小伙伴们可以参考一下 PHP微信支付开发过程,分享给大家,供大家参考,具体内容如下 1.开发环境 Thinkphp 3. ...

  2. 微信支付开发(11) Native支付

    关键字:微信公众平台 微信支付 Native原生支付作者:方倍工作室原文:http://www.cnblogs.com/txw1958/p/wxpay-native.html 由于微信支付接口更新,本 ...

  3. Android开发 --微信支付开发(转载!)(开发工具:Eclipse)

    Android_APP 微信支付接口开发 日期:2015-10-06 12:47:33 作者: 来源: 人气:3549 1.首先说一下我们在开发微信支付接口的时候遇到最多和最疑惑的问题,那就是明明 a ...

  4. 微信支付开发+{ping++}微信支付托管

    ------------------------微信支付接口------------------------------- 微信支付开发并没有想象中的那么难,主要是微信提供了sdk. 微信公众号必须是 ...

  5. PHP微信支付开发之扫描支付(模式二)后如何回调

    其实在写这篇文章的时候感觉自己已经落伍了,不过笔者在百度上搜索"微信支付开发之扫描支付(模式二)后如何回调"寻找答案时,发现依旧有很多朋友没有解决这个问题,所以就把自己的解决思路分 ...

  6. PHP微信支付开发

    此链接https://pay.weixin.qq.com/wiki/doc/api/H5.php?chapter=15_2,是微信官方的示例,无效,报错. 1.申请微信支付的开通条件?什么样的账号可以 ...

  7. 微信支付开发出现redirect_uri参数错误的解决方法

    我们在进行微信支付开发的时候会遇到出现“redirect_uri参数错误”这种情况,怎么办呢?下面就是我总结出现这种“redirect_uri参数错误”的七种可能情况,以及解决方式. 1.可能原因①: ...

  8. 微信支付开发 c# SDK JSAPI支付开发的流程和微信大坑

    微信支付开发流程 1. 开通微信支付功能 省略 2. 下载微信的C#版的微信SDK 下载连接:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chap ...

  9. 小D课堂-SpringBoot 2.x微信支付在线教育网站项目实战_1-1.SpringBoot整合微信支付开发在线教育视频站点介绍

    笔记 第一章项目介绍和前期准备 1.SpringBoot整合微信支付开发在线教育视频站点介绍     简介: 课程介绍,和小D课堂在线教育项目搭建开发 1.课程大纲介绍         2.微信支付项 ...

随机推荐

  1. selenium webdriver学习(五)------------iframe的处理(转)

    selenium webdriver学习(五)------------iframe的处理 博客分类: Selenium-webdriver 如何定位frame中元素  有时候我们在定位一个页面元素的时 ...

  2. WPF 之 DataTemplate 实例(摘抄)

     

  3. Java JDBC学习实战(二): 管理结果集

    在我的上一篇博客<Java JDBC学习实战(一): JDBC的基本操作>中,简要介绍了jdbc开发的基本流程,并详细介绍了Statement和PreparedStatement的使用:利 ...

  4. hdu 1045 Fire Net(dfs)

    Fire Net Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Su ...

  5. mysql-python安装

    操作系统:ubuntu16.04-gnome 首先要安装mysql数据 sudo apt install mysql-server 我们使用pip进行安装第三方模块 系统python版本为2.7.12 ...

  6. vue+element-ui 字体自适应不同屏幕

    项目背景:屏幕自适应问题,当在不同分辨率的屏幕上显示页面时,页面的字体需要根据屏幕大小来自适应,想到使用rem作为字体的单位 vue-cli脚手架下的index.html中写入以下js脚本 <s ...

  7. Python--day46--mysql触发器

    触发器:当对某张表做:增删改操作时,可以使用触发器自定义关联行为 1,为什么需要创建mysql触发器? 比如说我往tb1表里面插入一条数据的时候,同时需要往日志表tb2中插入这条数据,这时候就需要创造 ...

  8. 1626 - Brackets sequence——[动态规划]

    Let us define a regular brackets sequence in the following way: Empty sequence is a regular sequence ...

  9. node第一个参数必须是err

    Node.js 约定回调函数第一个参数必须是错误对象err: 问题:Node.js约定回调函数第一个参数必须是错误对象err,如果没有错误该参数就是null 原因:异步执行分成两段,在两段之间抛出异常 ...

  10. VScode快捷键(最全)

    按 Press 功能 Function Ctrl + Shift + P,F1 显示命令面板 Show Command Palette Ctrl + P 快速打开 Quick Open Ctrl + ...