一、基本设置
1、进入微信公众平台->微信支付->开发配置 设置授权目录,这个url精确到支付地址的上一级即可
2、公众号设置->功能设置 设置js接口安全域名,网页授权域名
二、获取网页授权
1、用户同意授权,获取code
- String APPID = "";
- String uri="";
- String REDIRECT_URI =URLEncoder.encode(uri);;
- String code = "code";
- String SCOPE = "snsapi_base";
- String url = "https://open.weixin.qq.com/connect/oauth2/authorize?" +
- "appid=" + APPID +
- "&redirect_uri=" +
- REDIRECT_URI+
- "&response_type="+code+"&scope="+SCOPE+"&state=123#wechat_redirect";
- response.sendRedirect(url);
code state 作为参数跳转到了设置的REDIRECT_URI中
2、通过code换取网页授权access_token
- SortedMap<String, String> paramMap1=new TreeMap<String, String>();
- paramMap1.put("appid",Global.getResource("tuji.official_accounts.app_id"));
- paramMap1.put("secret",Global.getResource("tuji.official_accounts.app_secret") );
- paramMap1.put("code",code);
- paramMap1.put("grant_type","authorization_code");
- Map<String, String> resultMap=WeiXinUtil.getAccessToken(paramMap1);
String openId=resultMap.get("openid");
获取到 openid;
三、统一下单
- SortedMap<String,Object> parmMap=new TreeMap<String, Object>();
- parmMap.put("openid",openId ); Map<String, Object> resultMap=new HashMap<String,Object>();
- String urlStr="https://api.mch.weixin.qq.com/pay/unifiedorder";
- //参数
- parmMap.put("out_trade_no",hOrder.getOrderNumber());
- parmMap.put("appid",Global.getResource("tuji.official_accounts.app_id"));
- parmMap.put("mch_id",Global.getResource("weixin.mac_id"));//商户号
- //parmMap.put("device_info",WeiXinUtil.DEVICE_INFO_WEB );//设备号
- parmMap.put("nonce_str",create_nonce_str());//随机字符串
- //parmMap.put("sign_type","MD5" );//签名类型,默认为MD5,支持HMAC-SHA256和MD5。
- parmMap.put("body",hOrder.getGoodsName());//商品简单描述,这里放的是商品名称
- int costPrice=0;
- JSONArray jsonArray=new JSONArray();
- JSONObject subJson=new JSONObject();
- Double d =hOrder.getTotalPrice();
- costPrice=HotelUtil.yuan2fen(d);
- subJson.put("goods_id",hOrder.getGoodId()+"");
- subJson.put("goods_name",hOrder.getGoodsName());
- subJson.put("quantity",hOrder.getGoodsNumber().intValue());//商品数量
- subJson.put("price",costPrice);
- jsonArray.add(subJson);
- JSONObject jsonObject=new JSONObject();
- //jsonObject.put("cost_price",608800);//cost_price Int 可选 32 订单原价,商户侧一张小票订单可能被分多次支付,订单原价 用于记录整张小票的支付金额。当订单原价与支付金额不相等则被判定为拆单,无法享受优惠
- //jsonObject.put("receipt_id","wx123" );//receipt_id String 可选 32 商家小票ID
- jsonObject.put("goods_detail", jsonArray);
- parmMap.put("detail",jsonObject.toString());//商品详细列表,使用Json格式,传输签名前请务必使用CDATA标签将JSON文本串保 护起来。
- //parmMap.put("attach","lalala" );//附加数据,在查询API和支付通知中原样返回,可作为自定义参数使用。
- parmMap.put("total_fee",costPrice );//订单总金额,单位为分
- //parmMap.put("fee_type","CNY");//符合ISO 4217标准的三位字母代码,默认人民币:CNY
- //parmMap.put("goods_tag", );//商品标记,使用代金券或立减优惠功能时需要的参数
- parmMap.put("trade_type","JSAPI");//取值如下:JSAPI,NATIVE,APP
- //parmMap.put("product_id", );//rade_type=NATIVE时(即扫码支付),此参数必传。此参数为二维码中包含的商品ID,商户自行定义。
- //parmMap.put("limit_pay","no_credit" );//上传此参数no_credit--可限制用户不能使用信用卡支付
- parmMap.put("notify_url",Global.getResource("weixin.notify_url"));//异步接收微信支付结果通知的回调地址,通知url必须为外网可访问的url,不能携带参数。
- String spbill_create_ip =getIp2(request);
- parmMap.put("spbill_create_ip",spbill_create_ip );//APP和网页支付提交用户端ip,Native支付填调用微信支付API的机器IP。
- //传入的参数
- /*parmMap.put("time_start", );//订单生成时间,格式为yyyyMMddHHmmss,如2009年12月25日9点10分10秒表示为20091225091010。
- parmMap.put("time_expire", );//订单失效时间,格式为yyyyMMddHHmmss,如2009年12月27日9点10分10秒表示为20091227091010。
- parmMap.put("out_trade_no", );//商户系统内部订单号,要求32个字符内、且在同一个商户号下唯一。
- parmMap.put("openid", );//trade_type=JSAPI时(即公众号支付),此参数必传,此参数为微信用户在商户对应appid下的唯一标识。
- */
- //做签名
- //String sign=getSign(parmMap);
- String sign=WeiXinUtil.createSign(parmMap,Global.getResource("weixin.key"));//做签名的key是商户平台设置的
- parmMap.put("sign",sign);//签名
- String xmlInfo="";
- try {
- // xmlInfo=unifiedorderMap2Xml(parmMap);
- xmlInfo=XMLUtil.arraySortToXml(parmMap, true);
- xmlInfo=new String(xmlInfo.toString().getBytes(), "ISO8859-1");//此处解决body为中文的问题
- } catch (Exception e) {
- throw new WxPayException("unifiedorderMap2Xml错误");
- }
- String resultStr="";
- try {
- resultStr=XmlPostUtil.post(urlStr, xmlInfo);
- } catch (Exception e) {
- throw new WxPayException("XmlPostUtil.post错误");
- }
- try {
- //resultMap=PayCommonUtil.getMapFromXML(resultStr);
- resultMap=XMLUtil.doXMLParse(resultStr);
- } catch (Exception e) {
- throw new WxPayException("getMapFromXML错误");
- }
- return resultMap;
下单用到的工具类
- /**
- * 签名 sortMap。
- */
- public static String createSign(SortedMap<String, Object> paramMap,String key) {
- List list = new ArrayList(paramMap.keySet());
- Object[] ary = list.toArray();
- Arrays.sort(ary);
- list = Arrays.asList(ary);
- String str = "";
- for(int i=0;i<list.size();i++){
- str+=list.get(i)+"="+paramMap.get(list.get(i)+"")+"&";
- }
- str+="key="+key;
- str = MD5Util.MD5Encode(str, "UTF-8").toUpperCase();
- return str;
- }
- /**
- * SortedMap转成xml
- * @param parm
- * @param isAddCDATA isAddCDATA true 带<![CDATA[]];false 不带
- * @return
- */
- public static String arraySortToXml(SortedMap<String, Object> parm, boolean isAddCDATA) {
- StringBuffer strbuff = new StringBuffer("<xml>");
- if (parm != null && !parm.isEmpty()) {
- for (Entry<String, Object> entry : parm.entrySet()) {
- strbuff.append("<").append(entry.getKey()).append(">");
- if (isAddCDATA) {
- strbuff.append("<![CDATA[");
- if (StringUtils.isNotEmpty(entry.getValue())) {
- strbuff.append(entry.getValue());
- }
- strbuff.append("]]>");
- } else {
- if (StringUtils.isNotEmpty(entry.getValue())) {
- strbuff.append(entry.getValue());
- }
- }
- strbuff.append("</").append(entry.getKey()).append(">");
- }
- }
- return strbuff.append("</xml>").toString();
- }
- /**
- * 发送xml数据请求到server端
- *
- * @param url
- * xml请求数据地址
- * @param xmlString
- * 发送的xml数据流
- * @return null发送失败,否则返回响应内容
- */
- public static String post(String url, String xmlString) {
- // 关闭
- System.setProperty("org.apache.commons.logging.Log", "org.apache.commons.logging.impl.SimpleLog");
- System.setProperty("org.apache.commons.logging.simplelog.showdatetime", "true");
- System.setProperty("org.apache.commons.logging.simplelog.log.org.apache.commons.httpclient", "stdout");
- // 创建httpclient工具对象
- HttpClient client = new HttpClient();
- // 创建post请求方法
- PostMethod myPost = new PostMethod(url);
- // 设置请求超时时间
- client.setConnectionTimeout(300 * 1000);
- String responseString = null;
- try {
- // 设置请求头部类型
- myPost.setRequestHeader("Content-Type", "text/xml");
- myPost.setRequestHeader("charset", "UTF-8");
- // 设置请求体,即xml文本内容,注:这里写了两种方式,一种是直接获取xml内容字符串,一种是读取xml文件以流的形式
- myPost.setRequestBody(xmlString);
- // InputStream
- // body=this.getClass().getResourceAsStream(xmlFileName);
- // myPost.setRequestBody(body);
- // myPost.setRequestEntity(new
- // StringRequestEntity(xmlString,"text/xml","utf-8"));
- int statusCode = client.executeMethod(myPost);
- if (statusCode == HttpStatus.SC_OK) {
- BufferedInputStream bis = new BufferedInputStream(myPost.getResponseBodyAsStream());
- byte[] bytes = new byte[1024];
- ByteArrayOutputStream bos = new ByteArrayOutputStream();
- int count = 0;
- while ((count = bis.read(bytes)) != -1) {
- bos.write(bytes, 0, count);
- }
- byte[] strByte = bos.toByteArray();
- responseString = new String(strByte, 0, strByte.length, "UTF-8");
- bos.close();
- bis.close();
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- myPost.releaseConnection();
- return responseString;
- }
- /**
- * 解析xml,返回第一级元素键值对。如果第一级元素有子节点,则此节点的值是子节点的xml数据。
- *
- * @param strxml
- * @return
- * @throws JDOMException
- * @throws IOException
- */
- public static Map doXMLParse(String strxml) throws Exception {
- if (null == strxml || "".equals(strxml)) {
- return null;
- }
- Map m = new HashMap();
- InputStream in = String2Inputstream(strxml);
- SAXBuilder builder = new SAXBuilder();
- Document doc = builder.build(in);
- Element root = doc.getRootElement();
- List list = root.getChildren();
- Iterator it = list.iterator();
- while (it.hasNext()) {
- Element e = (Element) it.next();
- String k = e.getName();
- String v = "";
- List children = e.getChildren();
- if (children.isEmpty()) {
- v = e.getTextNormalize();
- } else {
- v = getChildrenText(children);
- }
- m.put(k, v);
- }
- // 关闭流
- in.close();
- return m;
- }
获取到 prepay_id=resultMap.get("prepay_id");
四、调用支付控件
- $(".submit")
- .bind(
- "click",
- function() {
- var whitch = $(".selected div").html();
- if (whitch == "微信支付") {
- 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() {
- var appId = "${appId}";
- var timeStamp = "${timeStamp}";
- var nonceStr = "${nonceStr}";
- var package1 = "${package}";
- var signType = "${signType}";
- var paySign = "${paySign}";
- WeixinJSBridge.invoke('getBrandWCPayRequest', {
- "appId" : appId,//公众号名称,由商户传入
- "timeStamp" : timeStamp,//时间戳,自1970年以来的秒数
- "nonceStr" : nonceStr,//随机串
- "package" : package1,
- "signType" : signType,//微信签名方式:
- "paySign" : paySign
- //微信签名
- }, function(res) {
- if (res.err_msg == "get_brand_wcpay_request:ok") {
- window.location.href="${ctx}/api/v1/hotel/paySuccess";
- }else if(res.err_msg == "get_brand_wcpay_request:fail"){
- alert('支付失败');
- }else if(res.err_msg == "get_brand_wcpay_request:cancel"){
- alert('支付取消');
- }else{
- alert(res.err_msg);
- }
- });
- }
五、支付结果通知
/**
* 下单回掉地址
* @return
*/
@RequestMapping(value="notifyUrl",method=RequestMethod.POST)
@ResponseBody
public String notifyUrl(HttpServletRequest request,HttpServletResponse response,Model model){
System.out.println("==开始进入h5支付回调方法==");
String xml_review_result = WeiXinUtil.getXmlRequest(request);
System.out.println("微信支付结果:"+xml_review_result);
Map<String, String> resultMap = null;
try {
resultMap = XMLUtil.doXMLParse(xml_review_result);
System.out.println("resultMap:"+resultMap);
if(resultMap != null && resultMap.size() > 0){
String sign_receive = (String)resultMap.get("sign");//返回的签名
System.out.println("sign_receive:"+sign_receive);
resultMap.remove("sign");//删除签名
String checkSign = WeiXinUtil.getSign(resultMap,Global.getResource("weixin.key"));//生成新签名
System.out.println("checkSign:"+checkSign);
//签名校验成功
if(checkSign != null && sign_receive != null &&
checkSign.equals(sign_receive.trim())){
System.out.println("weixin receive check Sign sucess");
try{
//获得返回结果
String return_code = (String)resultMap.get("return_code");
if("SUCCESS".equals(return_code)){//交易成功
String out_trade_no = (String)resultMap.get("out_trade_no");//获取商户的订单号
System.out.println("weixin pay sucess,out_trade_no:"+out_trade_no);
//处理支付成功以后的逻辑,处理订单,相关的商品 减去相应数量
String resultXml="<xml><return_code><![CDATA[SUCCESS]]></return_code>"+ "<return_msg><![CDATA[OK]]></return_msg></xml>";
return resultXml;
}else{//交易失败
String resultXml="<xml><return_code><![CDATA[FAIL]]></return_code>"+
"<return_msg><![CDATA[FAIL]]></return_msg></xml>";
return resultXml;
}
}catch(Exception e){
e.printStackTrace();
}
}else{
//签名校验失败
System.out.println("weixin receive check Sign fail");
String checkXml = "<xml><return_code><![CDATA[FAIL]]></return_code>"+
"<return_msg><![CDATA[check sign fail]]></return_msg></xml>";
return checkXml;
}
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 下单回掉地址
* @return
*/
@RequestMapping(value="notifyUrl",method=RequestMethod.POST)
@ResponseBody
public String notifyUrl(HttpServletRequest request,HttpServletResponse response,Model model){
System.out.println("==开始进入h5支付回调方法==");
String xml_review_result = WeiXinUtil.getXmlRequest(request);
System.out.println("微信支付结果:"+xml_review_result);
Map<String, String> resultMap = null;
try {
resultMap = XMLUtil.doXMLParse(xml_review_result);
System.out.println("resultMap:"+resultMap);
if(resultMap != null && resultMap.size() > 0){
String sign_receive = (String)resultMap.get("sign");//返回的签名
System.out.println("sign_receive:"+sign_receive);
resultMap.remove("sign");//删除签名
String checkSign = WeiXinUtil.getSign(resultMap,Global.getResource("weixin.key"));//生成新签名
System.out.println("checkSign:"+checkSign);
//签名校验成功
if(checkSign != null && sign_receive != null &&
checkSign.equals(sign_receive.trim())){
System.out.println("weixin receive check Sign sucess");
try{
//获得返回结果
String return_code = (String)resultMap.get("return_code");
if("SUCCESS".equals(return_code)){//交易成功
String out_trade_no = (String)resultMap.get("out_trade_no");//获取商户的订单号
System.out.println("weixin pay sucess,out_trade_no:"+out_trade_no);
//处理支付成功以后的逻辑,处理订单,相关的商品 减去相应数量
String resultXml="<xml><return_code><![CDATA[SUCCESS]]></return_code>"+
"<return_msg><![CDATA[OK]]></return_msg></xml>";
return resultXml;
}else{//交易失败
String resultXml="<xml><return_code><![CDATA[FAIL]]></return_code>"+
"<return_msg><![CDATA[FAIL]]></return_msg></xml>";
return resultXml;
}
}catch(Exception e){
e.printStackTrace();
}
}else{
//签名校验失败
System.out.println("weixin receive check Sign fail");
String checkXml = "<xml><return_code><![CDATA[FAIL]]></return_code>"+
"<return_msg><![CDATA[check sign fail]]></return_msg></xml>";
return checkXml;
}
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}