JSAPI微信支付开发流程和坑


首先,我先简要说明一下微信支付开发的流程

      众所周知,工欲善其事,必先利其器,微信官方推出了web微信开发工具,有windowslinux、版本的,根据自己的开发环境选择合适自己的,登陆公众平台-->开发-->开发工具。

根据官网的文档说明,先在微信公众平台里点击微信支付,填写测试授权或支付授权目录,支付测试状态下,设置测试目录,测试人的微信号添加到白名单,发起支付的页面目录必须与设置的精确匹配,而且该域名必须是通过备案的,自己可以写个简单的servlet验证token,如果验证通过,说明该域名是有效的,如果没有验证通过,则说明该域名肯定有问题(排除servlet写的有问题)这一步是可选的,不是必须的,反正,我是当时测过的。

接着,添加测试白名单,填写支付申请。

最后,记得在开发-->接口权限-->网页服务-->网页账号,修改网页授权回调页面域名,授权回调域名配置规范为全域名,比如需要网页授权的域名为:www.qq.com,配置以后此域名下面的页面http://www.qq.com/music.html 、 http://www.qq.com/login.html 都可以进行OAuth2.0鉴权。但http://pay.qq.com 、 http://music.qq.com 、 http://qq.com无法进行OAuth2.0鉴权

好了,到这里,微信支付的配置信息暂且告一段落,下面开始微信支付开发流程

1)先进行微信网页授权,获取code,引导关注者打开如下页面:

https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect若提示“该链接无法访问”,请检查参数是否填写错误。

参数说明:


部分代码:

<pre name="code" class="java"><pre name="code" class="java">@SuppressWarnings("deprecation")
	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		
		//共账号及商户相关参数
		String appid = "";
		//String backUri = "";
		String backUri = "";
		//授权后要跳转的链接所需的参数一般有会员号,金额,订单号之类,
		//最好自己带上一个加密字符串将金额加上一个自定义的key用MD5签名或者自己写的签名,
		//比如 Sign = %3D%2F%CS% 
		String totals = request.getParameter("totals");
		String orderNo=request.getParameter("orderNO");
		//String orderNo=appid+Sha1Util.getTimeStamp();
		backUri = backUri+"?orderNo="+orderNo+"&describe=test&money="+totals;
		
		//URLEncoder.encode 后可以在backUri 的url里面获取传递的所有参数
		backUri = URLEncoder.encode(backUri);
		//scope 参数视各自需求而定,这里用scope=snsapi_base 不弹出授权页面直接授权目的只获取统一支付接口的openid
		String url = "http://open.weixin.qq.com/connect/oauth2/authorize?" +
				"appid=" + appid+
				"&redirect_uri=" +
				 backUri+
				"&response_type=code&scope=snsapi_userinfo&state=123#wechat_redirect";
		response.setCharacterEncoding("UTF-8");  
		response.sendRedirect(url);
		
	}

 
 如果用户同意授权,页面将跳转至  
redirect_uri/?code=CODE&state=STATE 
。若用户禁止授权,则重定向后不会带上 
code 
参数,仅会带上 
state 
参数 
redirect_uri?state=STATE 

2)通过code换取openid

获取code后,请求以下链接获取access_token: https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code

  正确时返回的JSON数据包如下:

{

   "access_token":"ACCESS_TOKEN",

   "expires_in":7200,

   "refresh_token":"REFRESH_TOKEN",

   "openid":"OPENID",

   "scope":"SCOPE",

   "unionid": "o6_bmasdasdsad6_2sgVt7hMZOPfL"

}

 

错误时微信会返回JSON数据包如下(示例为Code无效错误):

{"errcode":40029,"errmsg":"invalid code"}在这里我遇到了第一坑,openid拿不到,究其原因是网页授权的域名写错了。

3)调用支付接口https://api.mch.weixin.qq.com/pay/unifiedorder

在这里我遇到了第二坑,prepay_id为空,最后查出来是因为商户密钥配错了

请求参数如下:

字段名

变量名

必填

类型

示例值

描述

公众账号ID

appid

String(32)

wxd678efh567hg6787

微信分配的公众账号ID(企业号corpid即为此appId

商户号

mch_id

String(32)

1230000109

微信支付分配的商户号

设备号

device_info

String(32)

013467007045764

终端设备号(门店号或收银设备ID),注意:PC网页或公众号内支付请传"WEB"

随机字符串

nonce_str

String(32)

5K8264ILTKCH16CQ2502SI8ZNMTM67VS

随机字符串,不长于32位。推荐随机数生成算法

签名

sign

String(32)

C380BEC2BFD727A4B6845133519F3AD6

签名,详见签名生成算法

商品描述

body

String(128)

Ipad mini  16G  白色

商品或支付单简要描述

商品详情

detail

String(8192)

Ipad mini  16G  白色

商品名称明细列表

附加数据

attach

String(127)

深圳分店

附加数据,在查询API和支付通知中原样返回,该字段主要用于商户携带订单的自定义数据

商户订单号

out_trade_no

String(32)

20150806125346

商户系统内部的订单号,32个字符内、可包含字母其他说明见商户订单号

货币类型

fee_type

String(16)

CNY

符合ISO 4217标准的三位字母代码,默认人民币:CNY,其他值列表详见货币类型

总金额

total_fee

Int

888

订单总金额,单位为分,详见支付金额

终端IP

spbill_create_ip

String(16)

123.12.12.123

APP和网页支付提交用户端ipNative支付填调用微信支付API的机器IP

交易起始时间

time_start

String(14)

20091225091010

订单生成时间,格式为yyyyMMddHHmmss,如2009122591010秒表示为20091225091010。其他详见时间规则

交易结束时间

time_expire

String(14)

20091227091010

订单失效时间,格式为yyyyMMddHHmmss,如2009122791010秒表示为20091227091010。其他详见时间规则

注意:最短失效时间间隔必须大于5分钟

商品标记

goods_tag

String(32)

WXG

商品标记,代金券或立减优惠功能的参数,说明详见代金券或立减优惠

通知地址

notify_url

String(256)

http://www.weixin.qq.com/wxpay/pay.php

接收微信支付异步通知回调地址,通知url必须为直接可访问的url,不能携带参数。

交易类型

trade_type

String(16)

JSAPI

取值如下:JSAPINATIVEAPP,详细说明见参数规定

商品ID

product_id

String(32)

12235413214070356458058

trade_type=NATIVE,此参数必传。此id为二维码中包含的商品ID,商户自行定义。

指定支付方式

limit_pay

String(32)

no_credit

no_credit--指定不能使用信用卡支付

用户标识

openid

String(128)

oUpF8uMuAJO_M2pxb1Q9zNjWeS6o

trade_type=JSAPI,此参数必传,用户在商户appid下的唯一标识。openid如何获取,可参考【获取openid】。企业号请使用【企业号OAuth2.0接口】获取企业号内成员userid,再调用【企业号useridopenid接口】进行转换

部分代码:

public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		//网页授权后获取传递的参数
		//String userId = request.getParameter("userId"); 	
		String orderNo = request.getParameter("orderNo"); 
		String money = request.getParameter("money");
		String code = request.getParameter("code");
		//金额转化为分为单位
		float sessionmoney = Float.parseFloat(money);
		int paymoney=(int) (sessionmoney*100);
		String finalmoney=String.valueOf((paymoney));
		
		//商户相关资料 
		String appid = "";
		String appsecret = "";
		String partner = "";// 商户号
		String partnerkey = "";//商户号密钥
		
		
		
		String openId ="";
		String URL = "https://api.weixin.qq.com/sns/oauth2/access_token?appid="+appid+"&secret="+appsecret+"&code="+code+"&grant_type=authorization_code";
		
		JSONObject jsonObject = CommonUtil.httpsRequest(URL, "GET", null);
		if (null != jsonObject) {
			openId = jsonObject.getString("openid");
			System.out.println(openId);
			 PrintWriter pw = response.getWriter(); 
			 //pw.print("openId----------->"+openId);
		}
		
		//获取openId后调用统一支付接口https://api.mch.weixin.qq.com/pay/unifiedorder
				String currTime = TenpayUtil.getCurrTime();
				//8位日期
				String strTime = currTime.substring(8, currTime.length());
				//四位随机数
				String strRandom = TenpayUtil.buildRandom(4) + "";
				//10位序列号,可以自行调整。
				String strReq = strTime + strRandom;
				
				
				//商户号
				String mch_id = partner;
				//子商户号  非必输
				//String sub_mch_id="";
				//设备号   非必输
				//String device_info="";
				//随机数 
				String nonce_str = strReq;
				//商品描述
				//String body = describe;
				
				//商品描述根据情况修改
				String body = orderNo;
				//附加数据
				//String attach = userId;
				//商户订单号
				String out_trade_no = orderNo;
				int intMoney = Integer.parseInt(finalmoney);
				
				//总金额以分为单位,不带小数点
				int total_fee = intMoney;
				//订单生成的机器 IP
				String spbill_create_ip = request.getRemoteAddr();
				//订 单 生 成 时 间   非必输
//				String time_start ="";
				//订单失效时间      非必输
//				String time_expire = "";
				//商品标记   非必输
//				String goods_tag = "";
				
				//这里notify_url是 支付完成后微信发给该链接信息,可以判断会员是否支付成功,改变订单状态等。
				String notify_url ="";
				
				String trade_type = "JSAPI";
				String openid = openId;
				//非必输
//				String product_id = "";
				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("attach", attach);  
				packageParams.put("out_trade_no", out_trade_no);  
				
				
				//这里写的金额为1 分到时修改
				//packageParams.put("total_fee", "1");  
				packageParams.put("total_fee", finalmoney);  
				packageParams.put("spbill_create_ip", spbill_create_ip);  
				packageParams.put("notify_url", notify_url);  
				
				packageParams.put("trade_type", trade_type);  
				packageParams.put("openid", openid);  

				RequestHandler reqHandler = new RequestHandler(request, response);
				reqHandler.init(appid, appsecret, partnerkey);
				
				String sign = reqHandler.createSign(packageParams);
				String xml="<xml>"+
						"<appid>"+appid+"</appid>"+
						"<mch_id>"+mch_id+"</mch_id>"+
						"<nonce_str>"+nonce_str+"</nonce_str>"+
						"<sign>"+sign+"</sign>"+
						"<body><![CDATA["+body+"]]></body>"+
						//"<attach>"+attach+"</attach>"+
						"<out_trade_no>"+out_trade_no+"</out_trade_no>"+
						//金额,这里写的1 分到时修改
						//"<total_fee>"+1+"</total_fee>"+
						"<total_fee>"+finalmoney+"</total_fee>"+
						"<spbill_create_ip>"+spbill_create_ip+"</spbill_create_ip>"+
						"<notify_url>"+notify_url+"</notify_url>"+
						"<trade_type>"+trade_type+"</trade_type>"+
						"<openid>"+openid+"</openid>"+
						"</xml>";
				System.out.println(xml);
				String allParameters = "";
				try {
					allParameters =  reqHandler.genPackage(packageParams);
				} catch (Exception e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				String createOrderURL = "https://api.mch.weixin.qq.com/pay/unifiedorder";
				String prepay_id="";
				try {
					prepay_id = new GetWxOrderno().getPayNo(createOrderURL, xml);
					PrintWriter pw = response.getWriter(); 
					
					if(prepay_id.equals("")){
						request.setAttribute("ErrorMsg", "统一支付接口获取预支付订单出错");
						String json="{\"status\":\"error\",\"message\":\"prepay_id is null\"}";
						
						 pw.print(json);
						//response.sendRedirect("error.jsp");
						
						
						return;
					}
				} catch (Exception e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				}
				SortedMap<String, String> finalpackage = new TreeMap<String, String>();
				String appid2 = appid;
				String timestamp = Sha1Util.getTimeStamp();
				String nonceStr2 = nonce_str;
				String prepay_id2 = "prepay_id="+prepay_id;
				String packages = prepay_id2;
				finalpackage.put("appId", appid2);  
				finalpackage.put("timeStamp", timestamp);  
				finalpackage.put("nonceStr", nonceStr2);  
				finalpackage.put("package", packages);  
				finalpackage.put("signType", "MD5");
				String finalsign = reqHandler.createSign(finalpackage);
				//System.out.println("pay.jsp?appid="+appid2+"&timeStamp="+timestamp+"&nonceStr="+nonceStr2+"&package="+packages+"&sign="+finalsign);
				response.sendRedirect("pay.jsp?appid="+appid2+"&timeStamp="+timestamp+"&nonceStr="+nonceStr2+"&package="+packages+"&sign="+finalsign+"&orderNO="+orderNo);
				return; 
	}

4)前台页面掉微信支付 js 接口

示例代码如下:

unction onBridgeReady(){
   WeixinJSBridge.invoke(
       'getBrandWCPayRequest', {
           "appId" : "",     //公众号名称,由商户传入     
           "timeStamp":" ",         //时间戳,自1970年以来的秒数     
           "nonceStr" : "", //随机串     
           "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', onBridgeReady); 
       document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);
   }
}else{
   onBridgeReady();
}

5)处理回调逻辑

3)步棸自己配置notify_url即为支付过后的回调地址,可以为action,静态页面等,自己可以在改回调地址里处理支付过后的业务逻辑

到这里,微信jsAPI开发流程已基本全部讲完,最后,在测试的过程中,微信支付是不允许一个订单经常改价钱的,微信支付默认以第一次提交订单的价钱为基准,以后再提交该订单如果价钱不是第一次的价钱,微信支付默认通不过(切记!我这个坑查了我一天),最后,祝大家开发顺利

 

 

 

 

 


       

  • 1
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值