微信支付v3版本之app支付

来源:互联网 发布:淘宝电器店铺名字logo 编辑:程序博客网 时间:2024/05/21 09:32

首次开发微信支付,大部分代码都是参考

http://blog.csdn.net/wangqiuyun/article/details/51241064


1.公共参数配置

CommonsConstants{

    public static final String APP_ID  = "......";//微信开放平台审核通过的应用APPID
    
    public static final String MCH_ID = "......";//商户号
    
    public static final String PARTNER_KEY = ".......";//API的key
    key设置路径:微信商户平台(pay.weixin.qq.com)-->账户设置-->API安全-->密钥设置   

    public static final String CREATE_ORDER_URL="https://api.mch.weixin.qq.com/pay/unifiedorder"; //生成支付订单的URL
    
    public static final String QUERY_ORDER_URL="https://api.mch.weixin.qq.com/pay/orderquery"; //查询订单的URL
    
    public static final String CLOSE_ORDER_URL="https://api.mch.weixin.qq.com/pay/closeorder"; //关闭订单的URL
    
    public static final String NOTIFY_URL="http://........./PayController/payResult.do";//异步通知地址  
    
    public static final String BODY="云禽通商城商品";//描述
    
    public static final String TRADE_TYPE="APP";//支付类型

2.Md5加密算法

import java.security.MessageDigest;

public class MD5Utils {

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


3.xml解析

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;

public class XMLUtil {

       /**
        * 解析xml,返回第一级元素键值对。如果第一级元素有子节点,则此节点的值是子节点的xml数据。
        * @param strxml
        * @return
        * @throws JDOMException
        * @throws IOException
        */
       public static Map doXMLParse(String strxml) throws JDOMException, IOException {
          strxml = strxml.replaceFirst("encoding=\".*\"", "encoding=\"UTF-8\"");

          if(null == strxml || "".equals(strxml)) {
             return null;
          }

          Map map = new HashMap();

          InputStream in = new ByteArrayInputStream(strxml.getBytes("UTF-8"));
          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 = XMLUtil.getChildrenText(children);
             }

             map.put(k, v);
          }

          //关闭流
          in.close();

          return map;
       }

       /**
        * 获取子结点的xml
        * @param children
        * @return String
        */
       public static String getChildrenText(List children) {
          StringBuffer sb = new StringBuffer();
          if(!children.isEmpty()) {
             Iterator it = children.iterator();
             while(it.hasNext()) {
                Element e = (Element) it.next();
                String name = e.getName();
                String value = e.getTextNormalize();
                List list = e.getChildren();
                sb.append("<" + name + ">");
                if(!list.isEmpty()) {
                   sb.append(XMLUtil.getChildrenText(list));
                }
                sb.append(value);
                sb.append("</" + name + ">");
             }
          }

          return sb.toString();
       }

4.支付工具类

mport java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Iterator;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.SortedMap;

public class WXPayUtils {


    /**
     * 生成count位的随机数
     *
     * @param count
     * @return
     */
    public static String getRandomString(int length) {  
        String base = "abcdefghijklmnopqrstuvwxyz0123456789";  
        Random random = new Random();  
        StringBuffer sb = new StringBuffer();  
        for (int i = 0; i < length; i++) {  
            int number = random.nextInt(base.length());  
            sb.append(base.charAt(number));  
        }  
        return sb.toString();  
    }  

    
    /**
     * 定义签名,微信根据参数字段的ASCII码值进行排序 加密签名,故使用SortMap进行参数排序
     * @param characterEncoding
     * @param parameters
     * @return
     */
    public static String createSign(String characterEncoding,SortedMap<String,String> parameters){
        StringBuffer sb = new StringBuffer();
        Set es = parameters.entrySet();
        Iterator it = es.iterator();
        while(it.hasNext()) {
            Map.Entry entry = (Map.Entry)it.next();
            String k = (String)entry.getKey();
            Object v = entry.getValue();
            if(null != v && !"".equals(v)
                    && !"sign".equals(k) && !"key".equals(k)) {
                sb.append(k + "=" + v + "&");
            }
        }
        sb.append("key=" + CommonsConstants.PARTNER_KEY);//最后加密时添加商户密钥,由于key值放在最后,所以不用添加到SortMap里面去,单独处理,编码方式采用UTF-8
        String sign = MD5Utils.MD5Encode(sb.toString(), characterEncoding).toUpperCase();
        return sign;
    }
    
    /**
     * 将封装好的参数转换成Xml格式类型的字符串
     * @param parameters
     * @return
     */
     public static String getRequestXml(SortedMap<String,String> parameters){
            StringBuffer sb = new StringBuffer();
            sb.append("<xml>");
            Set es = parameters.entrySet();
            Iterator it = es.iterator();
            while(it.hasNext()) {
                Map.Entry entry = (Map.Entry)it.next();
                String k = (String)entry.getKey();
                String v = (String)entry.getValue();
                if("sign".equalsIgnoreCase(k)){

                }
                else if ("attach".equalsIgnoreCase(k)||"body".equalsIgnoreCase(k)) {
                    sb.append("<"+k+">"+"<![CDATA["+v+"]]></"+k+">");
                }
                else {
                    sb.append("<"+k+">"+v+"</"+k+">");
                }
            }
            sb.append("<"+"sign"+">"+"<![CDATA["+parameters.get("sign")+"]]></"+"sign"+">");
            sb.append("</xml>");
            
            return sb.toString();
    }
    
     /**
        * 得到本地机器的IP
        * @return
        */
       public static String getHostIp(){
          String ip = "";
          try{
             ip = InetAddress.getLocalHost().getHostAddress();
          }catch(UnknownHostException e){
             e.printStackTrace();
          }
          return ip;
       }
       
       /**
         * 是否签名正确,规则是:按参数名称a-z排序,遇到空值的参数不参加签名。
         * @return boolean
         */  
        public static boolean isTenpaySign(String characterEncoding, SortedMap<Object, Object> packageParams, String API_KEY) {  
            StringBuffer sb = new StringBuffer();  
            Set es = packageParams.entrySet();  
            Iterator it = es.iterator();  
            while(it.hasNext()) {  
                Map.Entry entry = (Map.Entry)it.next();  
                String k = (String)entry.getKey();  
                String v = (String)entry.getValue();  
                if(!"sign".equals(k) && null != v && !"".equals(v)) {  
                    sb.append(k + "=" + v + "&");  
                }  
            }  
              
            sb.append("key=" + API_KEY);  
              
            //算出摘要  
            String mysign = MD5Utils.MD5Encode(sb.toString(), characterEncoding).toUpperCase();
            String tenpaySign = ((String)packageParams.get("sign"));  
            //System.out.println(tenpaySign + "    " + mysign);  
            return tenpaySign.equals(mysign);  
        }
}

5.http请求工具类

这个要用到

http://archive.apache.org/dist/httpcomponents/httpclient/binary/

我用的是httpcomponents-client-4.3.1-bin版本的


import java.io.BufferedReader;
import java.io.InputStreamReader;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.message.BasicHeader;
import org.apache.http.protocol.HTTP;
import org.apache.log4j.Logger;

public class HttpUtils {

    private static Logger logger = Logger.getLogger(HttpUtils.class);

public static String postHttplient(String url, String xmlInfo) {
        try {

            HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
            CloseableHttpClient closeableHttpClient = httpClientBuilder.build();
            HttpPost httpPost = new HttpPost(url);
            HttpClientContext context = HttpClientContext.create();
            StringEntity se = new StringEntity(xmlInfo, "utf-8");
            se.setContentType("text/xml");
            se.setContentEncoding(new BasicHeader(HTTP.CONTENT_TYPE,
                    "application/xml"));
            se.setContentEncoding("UTF-8");
            httpPost.setEntity(se);
            httpPost.setConfig(RequestConfig.DEFAULT);
            HttpResponse httpResponse = closeableHttpClient.execute(httpPost,
                    context);
            HttpEntity httpEntity = httpResponse.getEntity();
//            InputStream content = httpEntity.getContent();
            BufferedReader reader = new BufferedReader(new InputStreamReader(
                     httpEntity.getContent(),"utf-8"));
            StringBuffer stringBuffer = new StringBuffer();
            String str = "";
            while ((str = reader.readLine()) != null) {
                stringBuffer.append(str);
            }
            reader.close();
            return stringBuffer.toString();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

6.统一下单接口调用

安卓前端会传(String OrderId, String totalAmount)


Map<String, Object> resultMap = new HashMap<String, Object>();
        int result = 0;

           float sessionmoney = Float.parseFloat(totalAmount);
           String finalmoney = String.format("%.2f", sessionmoney);
            finalmoney = finalmoney.replace(".", "");
            int intMoney = Integer.parseInt(finalmoney);

           // 封装签名参数
            SortedMap<String, String> packageParams = new TreeMap<String, String>();
            packageParams.put("appid", CommonsConstants.APP_ID);
            packageParams.put("mch_id", CommonsConstants.MCH_ID);
            packageParams.put("nonce_str", WXPayUtils.getRandomString(32));
            packageParams.put("body", CommonsConstants.BODY);
            packageParams.put("notify_url", CommonsConstants.NOTIFY_URL);
            packageParams.put("out_trade_no", OrderId);
            packageParams.put("spbill_create_ip", WXPayUtils.getHostIp());
            packageParams.put("total_fee", total_fee);
            packageParams.put("trade_type", CommonsConstants.TRADE_TYPE);

           // 生成签名
            String sign = WXPayUtils.createSign("UTF-8", packageParams);// 生成签名
            packageParams.put("sign", sign);

            // 生成请求的xml字符串
            String requestXml = WXPayUtils.getRequestXml(packageParams);

            // 发送请求返回xml字符串
            String retStr = HttpUtils.postHttplient(
                    CommonsConstants.CREATE_ORDER_URL, requestXml);
            logger.info("微信返回的xml字符串"+retStr);
            Map map = XMLUtil.doXMLParse(retStr);
            
            String return_code = (String) map.get("return_code");
            String prepay_id = null;


if (return_code.contains("SUCCESS")) {
                prepay_id = (String) map.get("prepay_id");// 获取到prepay_id
                logger.info("微信返回的预支付Id"+prepay_id);
                long currentTimeMillis = System.currentTimeMillis();//生成时间戳
                long second = currentTimeMillis / 1000L;//(转换成秒)
                String seconds = String.valueOf(second).substring(0, 10);//(截取前10位)
                SortedMap<String, String> signParam = new TreeMap<String, String>();
                signParam.put("appid", CommonsConstants.APP_ID);//app_id
                signParam.put("partnerid", CommonsConstants.MCH_ID);//微信商户账号
                signParam.put("prepayid", prepay_id);//预付订单id
                signParam.put("package", "Sign=WXPay");//默认sign=WXPay
                String returnNonce_str = WXPayUtils.getRandomString(32);
                signParam.put("noncestr", returnNonce_str);//自定义不重复的长度不长于32位
                signParam.put("timestamp",seconds);//北京时间时间戳
                String signAgain = WXPayUtils.createSign("UTF-8", signParam);//再次生成签名
                signParam.put("sign", signAgain);
                
    返回给前端有有两种形式:

               一个是封装成对象返回           

               对象的package属性在java里面是关键字,这里面有修改一下属性名称  

                PayOrderPay payOrderPay = new PayOrderPay();
                payOrderPay.setAppid(CommonsConstants.APP_ID);
                payOrderPay.setPrepayid(prepay_id);
                payOrderPay.setPartnerid(CommonsConstants.MCH_ID);
                payOrderPay.setPackageEx("Sign=WXPay");
                payOrderPay.setNoncestr(returnNonce_str);
                payOrderPay.setSign(signAgain);
//              String timeStamp = Long.toString(new Date().getTime() / 1000);
                payOrderPay.setTimestamp(seconds);
                payOrderPay.setOutTradeNo(OrderId);
                
                
                //拼接参数返回给移动端
//                StringBuffer weiXinVo=new StringBuffer();
//                weiXinVo.append("appid=").append(CommonsConstants.APP_ID).
//                append("&&partnerid=").append(CommonsConstants.MCH_ID).
//                append("&&prepayid=").append(prepay_id).
//                append("&&package=Sign=WXPay").append("&&noncestr=").append(returnNonce_str).
//                append("&&timestamp=").append(seconds).
//                append("&&sign=").append(signAgain).append("&&outTradeNo=").append(sb.toString());
//                
//                System.out.println(weiXinVo.toString());
                resultMap.put("num", result);
                resultMap.put("payOrderPay", payOrderPay);
            }else{
                String return_msg = (String) map.get("return_msg");
                result = 0;
                resultMap.put("num", result);
                resultMap.put("reutrn_msg", return_msg);
            }

7.支付回调

这个回调地址必须是外网可以直接访问的

如果你的电脑是局域网的可以做地址映射,保证微信能够直接访问到你给的URL.

我用的springmvc,直接上代码

@Controller
@RequestMapping("/PayController")
public class PayController {

private static Logger logger = Logger.getLogger(PayController.class);

   @RequestMapping("/payResult")
    @ResponseBody
    public void  payNotifyUrl(HttpServletRequest request, HttpServletResponse response) throws Exception {
      BufferedReader reader = null;

        reader = request.getReader();
        String line = "";
        String xmlString = null;
        StringBuffer inputString = new StringBuffer();

        while ((line = reader.readLine()) != null) {
            inputString.append(line);
        }
        xmlString = inputString.toString();
        request.getReader().close();  
        logger.info("----接收到的数据如下:---"+xmlString);
        Map<String, String> map = new HashMap<String, String>();
        map = XMLUtil.doXMLParse(xmlString);
      //过滤空 设置 TreeMap  
        SortedMap<Object,Object> packageParams = new TreeMap<Object,Object>();        
        Iterator it = map.keySet().iterator();  
        while (it.hasNext()) {  
            String parameter = (String) it.next();  
            String parameterValue = map.get(parameter);  
              
            String v = "";  
            if(null != parameterValue) {  
                v = parameterValue.trim();  
            }  
            packageParams.put(parameter, v);  
        }
        String key = CommonsConstants.PARTNER_KEY; // key  
        logger.info(packageParams);  

               
        if (WXPayUtils.isTenpaySign("UTF-8", packageParams,key)) {
            
            String resXml = "";
            if ("SUCCESS".equals((String)packageParams.get("result_code"))) {
                //更新支付订单表里面的state=00 ,transaction_id,TIME_END_DTTM

            |*************************我自己的业务逻辑*****************************|

                String      out_trade_no = (String)packageParams.get("out_trade_no");
                String      time_end = (String)packageParams.get("time_end");
                String   total_fee = (String)packageParams.get("total_fee");
                String   transaction_id = (String)packageParams.get("transaction_id");
                 
                payOrderService.updatePayOrderStatus(out_trade_no, total_fee, time_end, transaction_id);
               
                logger.info("支付成功,业务参数为"+" out_trade_no = "+out_trade_no+" time_end= "+time_end+" total_fee= "+total_fee+" transaction_id= "+transaction_id); 

               |*************************我自己的业务逻辑*****************************|

                resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>"  
                         + "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";  
            }else {  
                //更新支付订单表里面的state=01

                    |*************************我自己的业务逻辑*****************************|             

                String      out_trade_no = (String)packageParams.get("out_trade_no");
                String      time_end = (String)packageParams.get("time_end");
                String   total_fee = (String)packageParams.get("total_fee");
                String   transaction_id = (String)packageParams.get("transaction_id");
                
                payOrderService.updatePayOrderStatus2(out_trade_no, total_fee, time_end, transaction_id);

              logger.info("支付失败,错误信息:" + packageParams.get("err_code")); 

              |*************************我自己的业务逻辑*****************************|

                resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"  
                        + "<return_msg><![CDATA[报文为空]]></return_msg>" + "</xml> ";  
            }
             BufferedOutputStream out = new BufferedOutputStream(  
                     response.getOutputStream());  
             out.write(resXml.getBytes());  
             out.flush();  
             out.close();
            
        } else {
            logger.info("通知签名验证失败");  
        }

    }

8.查询订单

                // 封装签名参数
                SortedMap<String, String> packageParams = new TreeMap<String, String>();
                packageParams.put("appid", CommonsConstants.APP_ID);
                packageParams.put("mch_id", CommonsConstants.MCH_ID);
                packageParams.put("nonce_str", WXPayUtils.getRandomString(32));
                packageParams.put("out_trade_no", outTradeNo);
                // 生成签名
                String sign = WXPayUtils.createSign("UTF-8", packageParams);
                packageParams.put("sign", sign);
                
                // 生成请求的xml字符串
                String requestXml = WXPayUtils.getRequestXml(packageParams);

                // 发送请求返回xml字符串
                String retStr = HttpUtils.postHttplient(
                                    CommonsConstants.QUERY_ORDER_URL, requestXml);
                            logger.info("微信返回的xml字符串" + retStr);
                try {
                    Map map = XMLUtil.doXMLParse(retStr);
                    String return_code = (String) map.get("return_code");
                    if("SUCCESS".equals(return_code)){
                        String result_code = (String) map.get("result_code");
                        if("SUCCESS".equals(result_code)){
                            String trade_state = (String) map.get("trade_state");
                            
                            if("NOTPAY".equals(trade_state)){
                                payOrderMapper.updatePayOrderStatus03(outTradeNo);
                                resultMap.put("state", "03");
                            }else if("REFUND".equals("trade_state")){
                                payOrderMapper.updatePayOrderStatus02(outTradeNo);
                                resultMap.put("state", "02");
                            }else if("CLOSED".equals("trade_state")){
                                payOrderMapper.updatePayOrderStatus04(outTradeNo);
                                resultMap.put("state", "04");
                            }else if("REVOKED".equals("trade_state")){
                                payOrderMapper.updatePayOrderStatus05(outTradeNo);
                                resultMap.put("state", "05");
                            }else if("SUCCESS".equals("trade_state")){
                                payOrderMapper.updatePayOrderStatus00(outTradeNo);
                                orderMapper.updateOrderStatus02(outTradeNo);
                                resultMap.put("state", "00");
                            }else if("USERPAYING".equals("trade_state")){
                                payOrderMapper.updatePayOrderStatus99(outTradeNo);
                                resultMap.put("state", "99");
                            }else if("PAYERROR".equals("trade_state")){
                                payOrderMapper.updatePayOrderStatus01(outTradeNo);
                                resultMap.put("state", "01");
                            }
                        }else{
                            String err_code = (String)map.get("err_code");
                            logger.info("微信返回err_code" +  err_code);
                            if("ORDERNOTEXIST".equals(err_code)){
                                payOrderMapper.updatePayOrderStatus06(outTradeNo);
                                resultMap.put("state", "06");
                            }else{
                                payOrderMapper.updatePayOrderStatus07(outTradeNo);
                                resultMap.put("state", "07");
                            }
                        }
                    }else{
                        logger.info("通知签名验证失败");  
                    }


0 0
原创粉丝点击