天天看點

android 微信支付:統一下單接口擷取2.開發

1、什麼是[統一下單接口]?

首先我們要明白這個問題,需要先行看一下微信的官方文檔:

https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1

2.開發

//微信支付,生成統一訂單
    private void wxPay(String order_id) {
        String appid = WeChatInfo.WX_APP_ID;//app在微信申請的appid
        String mch_id = WeChatInfo.WX_MCH_ID;//申請支付功能的商戶号
        String nonce_str = WeChatField.getRandomString();//随機碼
        String body = "黃金月卡會員";//商品描述,随便寫
//        String out_trade_no = UtilData.generateOrderSN();//訂單編号
        String out_trade_no = order_id;//訂單編号
//        (Math.Round((decimal)order.Amount * 100, 0)).ToString()
        String total_fee = "1";//總金額
//        String time_start = UtilData.getCurrentTime();//時間戳,格式yyyyMMddHHmmss
        String attach = "會員";//附加資料:深圳分店
        String trade_type = "APP";//交易類型
        String notify_url = "http://www.xxxx.net";//通知回調位址,然後背景做個回調,無參的,必須放到商戶平台上配置添加
        String spbill_create_ip = WeChatField.getIPAddress(mContext);//裝置ip
        SortedMap<String, String> params = new TreeMap<String, String>();
        params.put("appid", appid);
        params.put("mch_id", mch_id);
        params.put("nonce_str", nonce_str);
        params.put("attach", attach);
        params.put("body", body);
        params.put("out_trade_no", out_trade_no);
        params.put("total_fee", total_fee );
        params.put("trade_type", trade_type);
        params.put("notify_url", notify_url);
        params.put("spbill_create_ip", spbill_create_ip);

        XLog.e(XLog.getTag(),"---------out_trade_no:"+out_trade_no);

        //擷取簽名sign,文檔有些參數是必須傳的,但是少了還是能請求成功xml
        String sign = WeChatField.getSign(params);
        //參數xml化
        String xmlParams = WeChatField.parseString2Xml(params, sign);
        XLog.e(XLog.getTag(),"------下單xml化---->" + "\n" + xmlParams);
        String urlStr = "https://api.mch.weixin.qq.com/pay/unifiedorder";//下單統一接口

        try {
            final byte[] xmlbyte = xmlParams.getBytes("UTF-8");
            URL url = new URL(urlStr);
            final HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setConnectTimeout(5000);
            conn.setDoOutput(true);// 允許輸出
            conn.setDoInput(true);
            conn.setUseCaches(false);// 不使用緩存
            conn.setRequestMethod("POST");
            conn.setRequestProperty("Connection", "Keep-Alive");// 維持長連接配接
            conn.setRequestProperty("Charset", "UTF-8");
            conn.setRequestProperty("Content-Length",
                    String.valueOf(xmlbyte.length));
            conn.setRequestProperty("Content-Type", "text/xml; charset=UTF-8");
            conn.setRequestProperty("X-ClientType", "2");//發送自定義的頭資訊
            conn.getOutputStream().write(xmlbyte);
            conn.getOutputStream().flush();
            conn.getOutputStream().close();

            if (conn.getResponseCode() != 200){
                XLog.e(XLog.getTag(),"----------請求url失敗-");
                return;
            }
            InputStream is = conn.getInputStream();// 擷取傳回資料
            // 使用輸出流來輸出字元(可選)
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            byte[] buf = new byte[1024];
            int len;
            while ((len = is.read(buf)) != -1) {
                out.write(buf, 0, len);
            }
            String string = out.toString("UTF-8");
            System.out.println(string);
            XLog.e("      微信傳回資料   ", "  ---   " + string);
            out.close();
            is.close();

            String xmlStr = ((String) string);
            XLog.e(XLog.getTag(),"----result--->" + string);
            XLog.e(XLog.getTag(),"----下單傳回--->" + "\n" + xmlStr);
            Map<String, String> map = WeChatField.decodeXml(xmlStr);
            Set set = map.entrySet();
            Iterator iterator = set.iterator();
            while (iterator.hasNext()) {
                Map.Entry entry = (Map.Entry) iterator.next();
                XLog.e(XLog.getTag(),"-----entry.getKey()/entry.getValue()---->" + entry.getKey() + "/" + entry.getValue());
            }
            if (map.get("return_code").equalsIgnoreCase("SUCCESS")) {
                if (map.get("return_msg").equalsIgnoreCase("OK")) {
                    PayReq req = new PayReq();
                    req.appId = WeChatInfo.WX_APP_ID;//APPID
                    req.partnerId = WeChatInfo.WX_MCH_ID;//商戶号
                    req.prepayId = map.get("prepay_id");
                    req.nonceStr = map.get("nonce_str");
                    String time = System.currentTimeMillis() / 1000 + "";
                    req.timeStamp = time;//時間戳,這次是截取long類型時間的前10位
                    req.packageValue = "Sign=WXPay";//參數是固定的,寫死的
                    SortedMap<String, String> sortedMap = new TreeMap<String, String>();//一定要注意鍵名,去掉下劃線,字母全是小寫
                    sortedMap.put("appid", WeChatInfo.WX_APP_ID);
                    sortedMap.put("partnerid", WeChatInfo.WX_MCH_ID);
                    sortedMap.put("prepayid", map.get("prepay_id"));
                    sortedMap.put("noncestr", map.get("nonce_str"));
                    sortedMap.put("timestamp", time);
                    sortedMap.put("package", "Sign=WXPay");
                    req.sign = WeChatField.getSign(sortedMap);//重新存除了sign字段之外,再次簽名,要不然喚起微信支付會傳回-1,特别坑爹的是,鍵名一定要去掉下劃線,不然傳回-1
                    //進行支付
                    doWxpay(req);
                } else {
                    Toast.makeText(mContext, "簽名失敗", Toast.LENGTH_SHORT).show();
                }

            } else {
                Toast.makeText(mContext, "交易失敗", Toast.LENGTH_SHORT).show();
            }

        } catch (MalformedURLException e) {
            e.printStackTrace();
        }catch (IOException e) {
            e.printStackTrace();
        }

    }
           

WeChatField.class

public class WeChatField {


    /**
     * 解析傳回的XML為鍵值對
     * @param content
     * @return
     */
    public static Map<String,String> decodeXml(String content) {

        try {
            Map<String, String> xml = new HashMap<String, String>();
            XmlPullParser parser = Xml.newPullParser();
            parser.setInput(new StringReader(content));
            int event = parser.getEventType();
            while (event != XmlPullParser.END_DOCUMENT) {

                String nodeName=parser.getName();
                switch (event) {
                    case XmlPullParser.START_DOCUMENT:

                        break;
                    case XmlPullParser.START_TAG:

                        if("xml".equals(nodeName)==false){
                            //執行個體化student對象
                            xml.put(nodeName,parser.nextText());
                        }
                        break;
                    case XmlPullParser.END_TAG:
                        break;
                }
                event = parser.next();
            }

            return xml;
        } catch (Exception e) {
            XLog.e(XLog.getTag(),e.toString());
        }
        return null;

    }


    /***
     * 将鍵值對xml化
     * @param map
     * @param sign
     * @return
     */
    public static String parseString2Xml(SortedMap<String, String> map, String sign) {
        StringBuffer sb = new StringBuffer();
        map.put("sign",sign);
        sb.append("<xml>");
        Set es = map.entrySet();
        Iterator iterator = es.iterator();
        while(iterator.hasNext()){
            Map.Entry entry = (Map.Entry)iterator.next();
            String k = (String)entry.getKey();
            String v = (String)entry.getValue();
            sb.append("<"+k+">"+v+"</"+k+">");
        }
        sb.append("</xml>");
        return sb.toString();
    }


    /**
     * 簽名獲得sign字段
     * @param params
     * @return
     */
    public static String getSign(SortedMap<String, String> params) {
        StringBuffer sb = new StringBuffer();
        Set es = params.entrySet();
        Iterator iterator = es.iterator();
        while (iterator.hasNext()){
            Map.Entry entry = (Map.Entry) iterator.next();
            String key = (String) entry.getKey();
            String value = (String) entry.getValue();
            if (null != value && !TextUtils.isEmpty(value) && !key.equals("key")){
                sb.append(key + "=" + value + "&");
            }
        }
        sb.append("key="+WeChatInfo.WX_PRIVATE_KEY);//商品平台API密鑰,32位的字母數字,找申請支付功能的人要,就在商戶平台那裡
        XLog.e(XLog.getTag(),"-------------------------------sign-------------->"+"\n"+sb.toString());
        String packageSign = MD5.getMessageDigest(sb.toString().getBytes()).toUpperCase();
        return packageSign;
    }

    /**
     * MD5加密
     * @return
     */
    public static String getRandomString() {
        Random random = new Random();
        return MD5.getMessageDigest(String.valueOf(random.nextInt(10000)).getBytes());
    }

    /**擷取裝置ip*/
    public static String getIPAddress(Context context) {
        NetworkInfo info = ((ConnectivityManager) context
                .getSystemService(Context.CONNECTIVITY_SERVICE)).getActiveNetworkInfo();
        if (info != null && info.isConnected()) {
            if (info.getType() == ConnectivityManager.TYPE_MOBILE) {//目前使用2G/3G/4G網絡
                try {
                    //Enumeration<NetworkInterface> en=NetworkInterface.getNetworkInterfaces();
                    for (Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements(); ) {
                        NetworkInterface intf = en.nextElement();
                        for (Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements(); ) {
                            InetAddress inetAddress = enumIpAddr.nextElement();
                            if (!inetAddress.isLoopbackAddress() && inetAddress instanceof Inet4Address) {
                                return inetAddress.getHostAddress();
                            }
                        }
                    }
                } catch (SocketException e) {
                    e.printStackTrace();
                }

            } else if (info.getType() == ConnectivityManager.TYPE_WIFI) {//目前使用無線網絡
                WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
                WifiInfo wifiInfo = wifiManager.getConnectionInfo();
                String ipAddress = intIP2StringIP(wifiInfo.getIpAddress());//得到IPV4位址
                return ipAddress;
            }
        } else {
            //目前無網絡連接配接,請在設定中打開網絡
        }
        return null;
    }



    /**
     * 将得到的int類型的IP轉換為String類型
     *
     * @param ip
     * @return
     */
    public static String intIP2StringIP(int ip) {
        return (ip & 0xFF) + "." +
                ((ip >> 8) & 0xFF) + "." +
                ((ip >> 16) & 0xFF) + "." +
                (ip >> 24 & 0xFF);
    }

}
           

PayReq.class

public class PayReq extends BaseReq {
    private static final String TAG = "MicroMsg.PaySdk.PayReq";
    private static final int EXTDATA_MAX_LENGTH = 1024;
    public String appId;
    public String partnerId;
    public String prepayId;
    public String nonceStr;
    public String timeStamp;
    public String packageValue;
    public String sign;
    public String extData;
    public PayReq.Options options;
    public String signType;

    public PayReq() {
    }

    public boolean checkArgs() {
        if (this.appId != null && this.appId.length() != 0) {
            if (this.partnerId != null && this.partnerId.length() != 0) {
                if (this.prepayId != null && this.prepayId.length() != 0) {
                    if (this.nonceStr != null && this.nonceStr.length() != 0) {
                        if (this.timeStamp != null && this.timeStamp.length() != 0) {
                            if (this.packageValue != null && this.packageValue.length() != 0) {
                                if (this.sign != null && this.sign.length() != 0) {
                                    if (this.extData != null && this.extData.length() > 1024) {
                                        Log.e("MicroMsg.PaySdk.PayReq", "checkArgs fail, extData length too long");
                                        return false;
                                    } else {
                                        return true;
                                    }
                                } else {
                                    Log.e("MicroMsg.PaySdk.PayReq", "checkArgs fail, invalid sign");
                                    return false;
                                }
                            } else {
                                Log.e("MicroMsg.PaySdk.PayReq", "checkArgs fail, invalid packageValue");
                                return false;
                            }
                        } else {
                            Log.e("MicroMsg.PaySdk.PayReq", "checkArgs fail, invalid timeStamp");
                            return false;
                        }
                    } else {
                        Log.e("MicroMsg.PaySdk.PayReq", "checkArgs fail, invalid nonceStr");
                        return false;
                    }
                } else {
                    Log.e("MicroMsg.PaySdk.PayReq", "checkArgs fail, invalid prepayId");
                    return false;
                }
            } else {
                Log.e("MicroMsg.PaySdk.PayReq", "checkArgs fail, invalid partnerId");
                return false;
            }
        } else {
            Log.e("MicroMsg.PaySdk.PayReq", "checkArgs fail, invalid appId");
            return false;
        }
    }

    public void toBundle(Bundle var1) {
        super.toBundle(var1);
        var1.putString("_wxapi_payreq_appid", this.appId);
        var1.putString("_wxapi_payreq_partnerid", this.partnerId);
        var1.putString("_wxapi_payreq_prepayid", this.prepayId);
        var1.putString("_wxapi_payreq_noncestr", this.nonceStr);
        var1.putString("_wxapi_payreq_timestamp", this.timeStamp);
        var1.putString("_wxapi_payreq_packagevalue", this.packageValue);
        var1.putString("_wxapi_payreq_sign", this.sign);
        var1.putString("_wxapi_payreq_extdata", this.extData);
        var1.putString("_wxapi_payreq_sign_type", this.signType);
        if (this.options != null) {
            this.options.toBundle(var1);
        }

    }

    public void fromBundle(Bundle var1) {
        super.fromBundle(var1);
        this.appId = a.b(var1, "_wxapi_payreq_appid");
        this.partnerId = a.b(var1, "_wxapi_payreq_partnerid");
        this.prepayId = a.b(var1, "_wxapi_payreq_prepayid");
        this.nonceStr = a.b(var1, "_wxapi_payreq_noncestr");
        this.timeStamp = a.b(var1, "_wxapi_payreq_timestamp");
        this.packageValue = a.b(var1, "_wxapi_payreq_packagevalue");
        this.sign = a.b(var1, "_wxapi_payreq_sign");
        this.extData = a.b(var1, "_wxapi_payreq_extdata");
        this.signType = a.b(var1, "_wxapi_payreq_sign_type");
        this.options = new PayReq.Options();
        this.options.fromBundle(var1);
    }

    public int getType() {
        return 5;
    }

    public static class Options {
        public static final int INVALID_FLAGS = -1;
        public String callbackClassName;
        public int callbackFlags = -1;

        public Options() {
        }

        public void toBundle(Bundle var1) {
            var1.putString("_wxapi_payoptions_callback_classname", this.callbackClassName);
            var1.putInt("_wxapi_payoptions_callback_flags", this.callbackFlags);
        }

        public void fromBundle(Bundle var1) {
            this.callbackClassName = a.b(var1, "_wxapi_payoptions_callback_classname");
            this.callbackFlags = a.a(var1, "_wxapi_payoptions_callback_flags");
        }
    }
}
           

定義訂單編号

/**
	 * 要求外部訂單号必須唯一。
	 * @return
	 */
	public static String getOutTradeNo() {
		SimpleDateFormat format = new SimpleDateFormat("MMddHHmmss", Locale.getDefault());
		Date date = new Date();
		String key = format.format(date);

		Random r = new Random();
		key = key + r.nextInt();
		key = key.substring(0, 15);
		return key;
	}
           

有什麼不懂,歡迎留言;

統一訂單接口,坑太多了,花了一天時間 搞定;