java微信小程序调用支付接口

简介:微信小程序支付这里的坑还是有的,所以提醒各位在编写的一定要注意!!!

1.首先呢,你需要准备**openid,appid**,还有申请微信支付后要设置一个**32位的密钥**,需要先生成一个**sign**,得到**prepay_id**,然后再得到一个**paySign**,总之就是很墨迹,下面献上我的controller


//微信下单支付

@ResponseBody

@RequestMapping("doOrder")

public void doOrder(HttpServletRequest request, HttpServletResponse response) throws IOException, JDOMException {

request.setCharacterEncoding("UTF-8");

response.setCharacterEncoding("UTF-8");

//得到openid

String openid = request.getParameter("openid");

int fee = 0;

//得到小程序传过来的价格,注意这里的价格必须为整数,1代表1分,所以传过来的值必须*100;

if (null != request.getParameter("price")) {

fee = Integer.parseInt(request.getParameter("price").toString());

}

System.out.println(request.getParameter("price"));

System.out.println(fee);

//订单编号

String did = request.getParameter("did");

//订单标题

String title = request.getParameter("title");

//时间戳

String times = System.currentTimeMillis() + "";

SortedMap<Object, Object> packageParams = new TreeMap<Object, Object>();

packageParams.put("appid", "wxa**********2e2");

packageParams.put("mch_id", "1486425722");

packageParams.put("nonce_str", times);//时间戳

packageParams.put("body", title);//支付主体

packageParams.put("out_trade_no", did);//编号

packageParams.put("total_fee", fee);//价格

// packageParams.put("spbill_create_ip", getIp2(request));这里之前加了ip,但是总是获取sign失败,原因不明,之后就注释掉了

packageParams.put("notify_url", base+"/notify");//支付回调接口,用于支付成功后处理业务逻辑,小程序端支付success不能保证100%回调成功,建议采用后端异步回调处理方式,回调方法在最后

packageParams.put("trade_type", "JSAPI");//这个api有,固定的

packageParams.put("openid", openid);//openid

//获取sign

String sign = PayCommonUtil.createSign("UTF-8", packageParams, "x********************************4");//最后这个是自己设置的32位密钥

packageParams.put("sign", sign);

//转成XML

String requestXML = PayCommonUtil.getRequestXml(packageParams);

System.out.println(requestXML);

//得到含有prepay_id的XML

String resXml = HttpUtil.postData("https://api.mch.weixin.qq.com/pay/unifiedorder", requestXML);

System.out.println(resXml);

//解析XML存入Map

Map map = XMLUtil.doXMLParse(resXml);

System.out.println(map);

// String return_code = (String) map.get("return_code");

//得到prepay_id

String prepay_id = (String) map.get("prepay_id");

SortedMap<Object, Object> packageP = new TreeMap<Object, Object>();

packageP.put("appId", "wxa**********2e2");//!!!注意,这里是appId,上面是appid,真怀疑写这个东西的人。。。

packageP.put("nonceStr", times);//时间戳

packageP.put("package", "prepay_id=" + prepay_id);//必须把package写成 "prepay_id="+prepay_id这种形式

packageP.put("signType", "MD5");//paySign加密

packageP.put("timeStamp", (System.currentTimeMillis() / 1000) + "");

//得到paySign

String paySign = PayCommonUtil.createSign("UTF-8", packageP, "x********************************4");

packageP.put("paySign", paySign);

//将packageP数据返回给小程序

Gson gson = new Gson();

String json = gson.toJson(packageP);

PrintWriter pw = response.getWriter();

System.out.println(json);

pw.write(json);

pw.close();

}

```

2.下面是需要用到的工具类

(1).生成sign以及得到sign后生成XML工具类PayCommonUtil


public class PayCommonUtil {

/**

    * 是否签名正确,规则是:按参数名称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 = MD5.MD5Encode(sb.toString(), characterEncoding).toLowerCase(); 

        String tenpaySign = ((String)packageParams.get("sign")).toLowerCase(); 


        //System.out.println(tenpaySign + "    " + mysign); 

        return tenpaySign.equals(mysign); 

    } 


    /**

    * @author

    * @Description:sign签名

    * @param characterEncoding

    *            编码格式

    * @param parameters

    *            请求参数

    * @return

    */ 

    public static String createSign(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 = entry.getKey().toString(); 

            String v = entry.getValue().toString(); 

            if (null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) { 

                sb.append(k + "=" + v + "&"); 

            } 

        } 

        sb.append("key=" + API_KEY); 

        String sign = MD5.MD5Encode(sb.toString(), characterEncoding).toUpperCase(); 

        return sign; 

    } 


    /**

    * @author

    * @Description:将请求参数转换为xml格式的string

    * @param parameters

    *            请求参数

    * @return

    */ 

    public static String getRequestXml(SortedMap<Object, Object> 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 = entry.getKey().toString(); 

            String v = entry.getValue().toString(); 

            if ("attach".equalsIgnoreCase(k) || "body".equalsIgnoreCase(k) || "sign".equalsIgnoreCase(k)) { 

                sb.append("<" + k + ">" + "<![CDATA[" + v + "]]></" + k + ">"); 

            } else { 

                sb.append("<" + k + ">" + v + "</" + k + ">"); 

            } 

        } 

        sb.append("</xml>"); 

        return sb.toString(); 

    } 


    /**

    * 取出一个指定长度大小的随机正整数.

    * 

    * @param length

    *            int 设定所取出随机数的长度。length小于11

    * @return int 返回生成的随机数。

    */ 

    public static int buildRandom(int length) { 

        int num = 1; 

        double random = Math.random(); 

        if (random < 0.1) { 

            random = random + 0.1; 

        } 

        for (int i = 0; i < length; i++) { 

            num = num * 10; 

        } 

        return (int) ((random * num)); 

    } 


    /**

    * 获取当前时间 yyyyMMddHHmmss

    * 

    * @return String

    */ 

    public static String getCurrTime() { 

        Date now = new Date(); 

        SimpleDateFormat outFormat = new SimpleDateFormat("yyyyMMddHHmmss"); 

        String s = outFormat.format(now); 

        return s; 

    } 

}

```

(2).访问官方接口得到含有prepay_id的XML工具类HttpUtil


public class HttpUtil {

//private static final Log logger = Logs.get(); 

    private final static int CONNECT_TIMEOUT = 5000; // in milliseconds 

    private final static String DEFAULT_ENCODING = "UTF-8"; 


    public static String postData(String urlStr, String data){ 

        return postData(urlStr, data, null); 

    } 


    public static String postData(String urlStr, String data, String contentType){ 

        BufferedReader reader = null; 

        try { 

            URL url = new URL(urlStr); 

            URLConnection conn = url.openConnection(); 

            conn.setDoOutput(true); 

            conn.setConnectTimeout(CONNECT_TIMEOUT); 

            conn.setReadTimeout(CONNECT_TIMEOUT); 

            if(contentType != null) 

                conn.setRequestProperty("content-type", contentType); 

            OutputStreamWriter writer = new OutputStreamWriter(conn.getOutputStream(), DEFAULT_ENCODING); 

            if(data == null) 

                data = ""; 

            writer.write(data); 

            writer.flush(); 

            writer.close();   


            reader = new BufferedReader(new InputStreamReader(conn.getInputStream(), DEFAULT_ENCODING)); 

            StringBuilder sb = new StringBuilder(); 

            String line = null; 

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

                sb.append(line); 

                sb.append("\r\n"); 

            } 

            return sb.toString(); 

        } catch (IOException e) { 

            //logger.error("Error connecting to " + urlStr + ": " + e.getMessage()); 

        } finally { 

            try { 

                if (reader != null) 

                    reader.close(); 

            } catch (IOException e) { 

            } 

        } 

        return null; 

    } 

}

```

(3).解析XML工具类


public class XMLUtil {

public static Map doXMLParse(String strxml) throws JDOMException, IOException { 

        strxml = strxml.replaceFirst("encoding=\".*\"", "encoding=\"UTF-8\""); 


        if(null == strxml || "".equals(strxml)) { 

            return null; 

        } 


        Map m = 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); 

            } 


            m.put(k, v); 

        } 


        //关闭流 

        in.close(); 


        return m; 

    } 


    /**

    * 获取子结点的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).MD5加密工具类


public class MD5 {

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.小程序支付函数


gopay: function () {

    var that = this

    wx.request({

      url: app.baseurl + 'doOrder',

      data: {

        'openid': wx.getStorageSync('openids'),

        'title': that.data.title,

        'did': that.data.did,

        'price': that.data.price*100

      },

      method: 'POST',

      header: {

        "content-type": 'application/x-www-form-urlencoded'

      },

      success: function (res) {

        console.log(res.data)

        console.log(res.data.timeStamp)

        console.log(res.data.nonceStr)

        console.log(res.data.package)

        console.log(res.data.paySign)

        wx.requestPayment({

          timeStamp: res.data.timeStamp,

          nonceStr: res.data.nonceStr,

          package: res.data.package,

          signType: res.data.signType,

          paySign: res.data.paySign,

          success: function (res) {

          console.log('支付调用成功',res)

          },

          fail: function (res) {

            console.log(res)

          }

        })

      }

    })

  }

```

4、支付成功回调


/**

    * 此函数会被执行多次,如果支付状态已经修改为已支付,则下次再调的时候判断是否已经支付,如果已经支付了,则什么也执行

    * @param request

    * @param response

    * @return

    * @throws IOException

    * @throws JDOMException

    */

    @RequestMapping(value = "notify")

    @ResponseBody

    public String notify(HttpServletRequest request, HttpServletResponse response) throws IOException {

    String resXml = "";

        System.out.println("微信支付回调");

//resultxml中含用户订单号等信息,解析后用于处理订单

        Map<String, String> params = PayCommonUtil.doXMLParse(resultxml);

        if (!PayCommonUtil.isTenpaySign(params)) {

            // 支付失败

            resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"

                    + "<return_msg><![CDATA[报文为空]]></return_msg>" + "</xml> ";

        } else {

            System.out.println("==========付款成功==========");

            // ------------------------------

            // 处理业务开始

            // ------------------------------

            // 此处处理订单状态,结合自己的订单数据完成订单状态的更新

            // ------------------------------

//通知微信.异步确认成功.必写.不然会一直通知后台.八次之后就认为交易失败了

resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>"

                    + "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";

        }

        BufferedOutputStream out = new BufferedOutputStream(

                response.getOutputStream());

        out.write(resXml.getBytes());

        out.flush();

        out.close();

    }

```

结束啦,小程序支付的java后台就这些。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335