1. 程式人生 > >踩坑: 微信小程式支付流程(統一下單, 支付回撥)

踩坑: 微信小程式支付流程(統一下單, 支付回撥)

公司最近開發小程式,涉及到支付功能. 現在支付功能已經做完,特此記錄一下自己踩坑經驗:

眾所周知,微信小程式目前只能使用微信支付, 而且微信小程式支付相對於app支付,h5支付都要簡單一些,但是該支付文件對java這語言是非常不友好的,居然沒有demo, 網上雖說有很多部落格,但是找了好多都是跑不通, 亂七八糟的很多都跑不通, 以下 程式碼不是自己寫的,大多都是這兒超一點哪兒抄一頓,但是能跑通,親測沒毛病,如果有毛病可以留言交流.

廢話不多說,先說準備工作:

1. 首先登入微信公眾平臺, 保證自己的小程式已經開通了微信支付功能:

2. 登入微信商戶平臺:https://pay.weixin.qq.com

該步驟需要獲取兩個引數, 一個是商戶號, 一個是支付祕鑰, 如下圖所示

注意祕鑰自己要保護好,相當於支付密碼,每次簽名都需要該引數, 該引數只能設定的時候看得見,其餘的時候是沒法看得見.所以要記好了

4. 準備工作搞好後就開始走流程了,這裡說明下流程

微信小程式發起支付的請求到開發者伺服器, 後臺預下單返回一個prepay_id, 還有其他亂七八糟的引數.然後微信小程式呼叫支付方法進行支付, 最後微信伺服器會發起回撥函式到開發者伺服器.

先貼一段微信的測試程式碼,

微信統一下單必須要openId引數

//index.js
//獲取應用例項
var app = getApp()
Page({
  data: {
    motto: 'Hello World',
    userInfo: {}
  },
  onLoad: function () {
    console.log('onLoad')
    
  },
  //
  payoff: function(e){
    var that = this;
      console.log('zhixingle')
    wx.login({
      success: function(res) {
          wx.getUserInfo({
             
              success: function(re) {
                  that.getAddress(re,res.code);
              },
              fail: function () {
                  console.log('失敗了')
              }
          })
      }
      
    });
    
  },
  // 獲取使用者的收貨地址
    getAddress: function (re,code) {
      var that = this;
      wx.chooseAddress({
          success: function (add) {
              that.getOpenId(re, add.userName,add.provinceName,add.cityName,add.countyName,
              add.detailInfo,add.telNumber,code);
          }
      })
  },
  //獲取openid
  getOpenId: function(re, userName, provinceName, cityName, countryName, detailInfo, telNumber,code){
    var that = this;
    wx.request({ 
        // 開發者伺服器地址
        url: 'http://1d7a111.iok.la:10534/api/getUnionId', 
        method: 'POST',
        header: {
           'content-type': 'application/x-www-form-urlencoded'
        },
        data: { encryptedData: re.encryptedData, iv: re.iv, code: code },
        success: function(res) {
            var openId = res.data.userInfo.openId;
            var unionId = res.data.userInfo.unionId;
            // console.log('res>', openId);
            that.loginPlatform(userName, provinceName, cityName, countryName, detailInfo, 

telNumber,unionId, openId);
          
        }
    })
  },

  // 登入
    loginPlatform: function (userName, provinceName, cityName, countryName, detailInfo, telNumber,unionId, openId) {
        var that = this;
        wx.request({
            url: 'http://1d7a111.iok.la:12534/api/login/platform',
            method: 'POST',
            header: {
                'content-type': 'application/json'
            },
            data: { unionId: unionId, platform: 'WECHAT' },
            success: function(res) {
                console.log('res', res.data.token);
                var token = res.data.token;
                that.xiadan(userName, provinceName, cityName, countryName, detailInfo, telNumber,openId, token);
            }
        })
    },
  //下單
    xiadan: function (userName, provinceName, cityName, countryName, detailInfo, telNumber,openId, token){
    console.log('openId', openId);
      console.log('token', token);
    var that = this;
    wx.request({
        url: 'http://1d7a01111.iok.la:12534/api/v1/weixin/payment', 
        method: 'POST',
        header: {
            'content-type': 'application/json',
            'authorization': 'Bearer ' + token
        },
        data: {
            openId: openId,
            cfId: '5b32f553aff8ca411f839eb4', 
            planId: '5b32f5cbaff8ca111f839eb7',
            quantity: 2,
            orderRemark: '說點啥備註好?',
            name: userName,
            phone: telNumber,
            province: provinceName,
            city: cityName,
            district: countryName,
            address: detailInfo
        },
        success: function(res) {
            console.log('res>>>',res)
            that.requestPayment(res.data.data);
        }
    })
  },

  //申請支付
  requestPayment: function(obj){
      console.log('obj>>',obj)
    wx.requestPayment({
      'timeStamp': obj.timeStamp,
      'nonceStr': obj.nonceStr,
      'package': obj.package,
      'signType': obj.signType,
      'paySign': obj.paySign,
      'success':function(res){
      },
      'fail':function(res){
      }
    })
  }  
})
因為我這邊需求是要獲取微信使用者的收貨地址,然後unionId是登入獲取token,openId是支付的必須引數,所以程式碼寫的有點亂,不是專業的

接著貼後臺程式碼:


import com.thoughtworks.xstream.XStream;
import org.apache.log4j.Logger;
import org.dom4j.DocumentException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Map;

/**
 * @Author: YFei
 * @Date: Created in 18:10 2018/7/25
 * @Description:
 */
@RestController
@RequestMapping(value = "/api/v1")
public class WXAppletPayCtrl {
   
    private static final long serialVersionUID = 1L;
    private static final Logger L = Logger.getLogger(WXAppletPayCtrl.class);

    @Value("${wxapplet.config.weixinpay.notifyurl}")
    private String notify_url;
    //交易型別
    private final String trade_type = "JSAPI";
    //統一下單API介面連結
    private final String url = "https://api.mch.weixin.qq.com/pay/unifiedorder";

    /**
     *
     * @param request
     * @return
     * @throws UnsupportedEncodingException
     * @throws DocumentException
     */
    @RequestMapping(
            value = "/weixin/payment",
            method = RequestMethod.POST
    )
    public Map payment(@Valid @RequestBody NewWXOrderRequest request, HttpServletRequest httpServletRequest) {
        Map map = new HashMap();
        String money = "10";
        String title = "商品名字";
       
        try {
            OrderInfo order = new OrderInfo();
            order.setAppid(Configure.getAppID());
            order.setMch_id(Configure.getMch_id());
            order.setNonce_str(RandomStringGenerator.getRandomStringByLength(32));
            order.setBody(title);
            order.setOut_trade_no(RandomStringGenerator.getRandomStringByLength(32));
            order.setTotal_fee(Integer.parseInt(money));     // 該金錢其實10 是 0.1元
            order.setSpbill_create_ip("127.0.0.1");
            order.setNotify_url(notify_url);
            order.setTrade_type(trade_type);
            order.setOpenid(request.getOpenId());
            order.setSign_type("MD5");
            //生成簽名
            String sign = Signature.getSign(order);
            order.setSign(sign);

            String result = HttpRequest.sendPost(url, order);
            System.out.println(result);
            XStream xStream = new XStream();
            xStream.alias("xml", OrderReturnInfo.class);

            OrderReturnInfo returnInfo = (OrderReturnInfo)xStream.fromXML(result);
            // 二次簽名
            if ("SUCCESS".equals(returnInfo.getReturn_code()) && returnInfo.getReturn_code().equals(returnInfo.getResult_code())) {
                SignInfo signInfo = new SignInfo();
                signInfo.setAppId(Configure.getAppID());
                long time = System.currentTimeMillis()/1000;
                signInfo.setTimeStamp(String.valueOf(time));
                signInfo.setNonceStr(RandomStringGenerator.getRandomStringByLength(32));
                signInfo.setRepay_id("prepay_id="+returnInfo.getPrepay_id());
                signInfo.setSignType("MD5");
                //生成簽名
                String sign1 = Signature.getSign(signInfo);
                Map payInfo = new HashMap();
                payInfo.put("timeStamp", signInfo.getTimeStamp());
                payInfo.put("nonceStr", signInfo.getNonceStr());
                payInfo.put("package", signInfo.getRepay_id());
                payInfo.put("signType", signInfo.getSignType());
                payInfo.put("paySign", sign1);
                map.put("status", 200);
                map.put("msg", "統一下單成功!");
                map.put("data", payInfo);

                // 此處可以寫喚起支付前的業務邏輯
                
                // 業務邏輯結束
                return map;
            }
            map.put("status", 500);
            map.put("msg", "統一下單失敗!");
            map.put("data", null);
            return map;
        } catch (Exception e) {
            e.printStackTrace();
            L.error("-------", e);
        }
        return null;
    }

    /**
     * 微信小程式支付成功回撥函式
     * @param request
     * @param response
     * @throws Exception
     */
    @RequestMapping(value = "/weixin/callback")
    public void wxNotify(HttpServletRequest request,HttpServletResponse response) throws Exception{
        BufferedReader br = new BufferedReader(new InputStreamReader((ServletInputStream)request.getInputStream()));
        String line = null;
        StringBuilder sb = new StringBuilder();
        while((line = br.readLine()) != null){
            sb.append(line);
        }
        br.close();
        //sb為微信返回的xml
        String notityXml = sb.toString();
        String resXml = "";
        System.out.println("接收到的報文:" + notityXml);

        Map map = PayUtil.doXMLParse(notityXml);

        String returnCode = (String) map.get("return_code");
        if("SUCCESS".equals(returnCode)){
            //驗證簽名是否正確
            Map<String, String> validParams = PayUtil.paraFilter(map);  //回撥驗籤時需要去除sign和空值引數
            String validStr = PayUtil.createLinkString(validParams);//把陣列所有元素,按照“引數=引數值”的模式用“&”字元拼接成字串
            String sign = PayUtil.sign(validStr, Configure.getKey(), "utf-8").toUpperCase();//拼裝生成伺服器端驗證的簽名
            // 因為微信回撥會有八次之多,所以當第一次回撥成功了,那麼我們就不再執行邏輯了
           
            //根據微信官網的介紹,此處不僅對回撥的引數進行驗籤,還需要對返回的金額與系統訂單的金額進行比對等
            if(sign.equals(map.get("sign"))){
                /**此處新增自己的業務邏輯程式碼start**/
                  // bla bla bla....
                /**此處新增自己的業務邏輯程式碼end**/
                //通知微信伺服器已經支付成功
                resXml = "<xml>" + "<return_code><![CDATA[SUCCESS]]></return_code>"
                        + "<return_msg><![CDATA[OK]]></return_msg>" + "</xml> ";
            } else {
                System.out.println("微信支付回撥失敗!簽名不一致");
            }
        }else{
            resXml = "<xml>" + "<return_code><![CDATA[FAIL]]></return_code>"
                    + "<return_msg><![CDATA[報文為空]]></return_msg>" + "</xml> ";
        }
        System.out.println(resXml);
        System.out.println("微信支付回撥資料結束");

        BufferedOutputStream out = new BufferedOutputStream(
                response.getOutputStream());
        out.write(resXml.getBytes());
        out.flush();
        out.close();
    }

}

以上是統一下單的介面和微信支付成功的回撥介面, 注意的如果springboot專案中的springsecurity,一定要注意放行回撥地址, 否則回撥會失敗.

現在上一下相關的工具類和配置檔案

簽名類:

import com.thoughtworks.xstream.annotations.XStreamAlias;
import org.apache.log4j.Logger;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Map;


/**
 * 簽名
 * @author zuoliangzhu
 *
 */
public class Signature {
	private static final Logger L = Logger.getLogger(Signature.class);
	/**
     * 簽名演算法
     * @param o 要參與簽名的資料物件
     * @return 簽名
     * @throws IllegalAccessException
     */
    public static String getSign(Object o) throws IllegalAccessException {
        ArrayList<String> list = new ArrayList<String>();
        Class cls = o.getClass();
        Field[] fields = cls.getDeclaredFields();
        for (Field f : fields) {
            f.setAccessible(true);
            if (f.get(o) != null && f.get(o) != "") {
            	String name = f.getName();
            	XStreamAlias anno = f.getAnnotation(XStreamAlias.class);
            	if(anno != null)
            		name = anno.value();
                list.add(name + "=" + f.get(o) + "&");
            }
        }
        int size = list.size();
        String [] arrayToSort = list.toArray(new String[size]);
        Arrays.sort(arrayToSort, String.CASE_INSENSITIVE_ORDER);
        StringBuilder sb = new StringBuilder();
        for(int i = 0; i < size; i ++) {
            sb.append(arrayToSort[i]);
        }
        String result = sb.toString();
        result += "key=" + Configure.getKey();
        System.out.println("簽名資料:"+result);
        result = MD5.MD5Encode(result).toUpperCase();
        return result;
    }

    public static String getSign(Map<String,Object> map){
        ArrayList<String> list = new ArrayList<String>();
        for(Map.Entry<String,Object> entry:map.entrySet()){
            if(entry.getValue()!=""){
                list.add(entry.getKey() + "=" + entry.getValue() + "&");
            }
        }
        int size = list.size();
        String [] arrayToSort = list.toArray(new String[size]);
        Arrays.sort(arrayToSort, String.CASE_INSENSITIVE_ORDER);
        StringBuilder sb = new StringBuilder();
        for(int i = 0; i < size; i ++) {
            sb.append(arrayToSort[i]);
        }
        String result = sb.toString();
        result += "key=" + Configure.getKey();
        //Util.log("Sign Before MD5:" + result);
        result = MD5.MD5Encode(result).toUpperCase();
        //Util.log("Sign Result:" + result);
        return result;
    }
}

配置類

public class Configure {
    // 商戶支付祕鑰
	private static String key = "xxxxxxNOBVmszxxxxxxxxxxxxxxxxxxx";

	//小程式ID	
	private static String appID = "wx42ebbFFFFFFFFFF";
	//商戶號
	private static String mch_id = "1499111112";
	// 小程式的secret
	private static String secret = "xxxxxxxxxxxxxxxxxxx";

	public static String getSecret() {
		return secret;
	}

	public static void setSecret(String secret) {
		Configure.secret = secret;
	}

	public static String getKey() {
		return key;
	}

	public static void setKey(String key) {
		Configure.key = key;
	}

	public static String getAppID() {
		return appID;
	}

	public static void setAppID(String appID) {
		Configure.appID = appID;
	}

	public static String getMch_id() {
		return mch_id;
	}

	public static void setMch_id(String mch_id) {
		Configure.mch_id = mch_id;
	}

}

httpRequest工具

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;
import com.thoughtworks.xstream.io.xml.XmlFriendlyNameCoder;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;

import javax.net.ssl.X509TrustManager;
import java.io.IOException;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;

public class HttpRequest {
	//連線超時時間,預設10秒
    private static final int socketTimeout = 10000;

    //傳輸超時時間,預設30秒
    private static final int connectTimeout = 30000;
	/**
	 * post請求
	 * @throws IOException 
	 * @throws ClientProtocolException 
	 * @throws NoSuchAlgorithmException 
	 * @throws KeyStoreException 
	 * @throws KeyManagementException 
	 * @throws UnrecoverableKeyException 
	 */
	public static String sendPost(String url, Object xmlObj) throws ClientProtocolException, IOException, UnrecoverableKeyException, KeyManagementException, KeyStoreException, NoSuchAlgorithmException {
		

        
		HttpPost httpPost = new HttpPost(url);
		//解決XStream對出現雙下劃線的bug
        XStream xStreamForRequestPostData = new XStream(new DomDriver("UTF-8", new XmlFriendlyNameCoder("-_", "_")));
        xStreamForRequestPostData.alias("xml", xmlObj.getClass());
        //將要提交給API的資料物件轉換成XML格式資料Post給API
        String postDataXML = xStreamForRequestPostData.toXML(xmlObj);

        //得指明使用UTF-8編碼,否則到API伺服器XML的中文不能被成功識別
        StringEntity postEntity = new StringEntity(postDataXML, "UTF-8");
        httpPost.addHeader("Content-Type", "text/xml");
        httpPost.setEntity(postEntity);

        //設定請求器的配置
        RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(socketTimeout).setConnectTimeout(connectTimeout).build();
        httpPost.setConfig(requestConfig);
        
        HttpClient httpClient = HttpClients.createDefault();
        HttpResponse response = httpClient.execute(httpPost);
        HttpEntity entity = response.getEntity();
        String result = EntityUtils.toString(entity, "UTF-8");
        return result;
	}
	/**
	 * 自定義證書管理器,信任所有證書
	 * @author pc
	 *
	 */
	public static class MyX509TrustManager implements X509TrustManager {
		@Override
		public void checkClientTrusted(
				java.security.cert.X509Certificate[] arg0, String arg1)
				throws CertificateException {
			// TODO Auto-generated method stub

		}
		@Override
		public void checkServerTrusted(
				java.security.cert.X509Certificate[] arg0, String arg1)
				throws CertificateException {
			// TODO Auto-generated method stub
			
		}
		@Override
		public java.security.cert.X509Certificate[] getAcceptedIssuers() {
			// TODO Auto-generated method stub
			return null;
		}
      }
}

MD5加密工具


import java.security.MessageDigest;

/**
 * User: rizenguo
 * Date: 2014/10/23
 * Time: 15:43
 */
public class MD5 {
    private final static String[] hexDigits = {"0", "1", "2", "3", "4", "5", "6", "7",
            "8", "9", "a", "b", "c", "d", "e", "f"};

    /**
     * 轉換位元組陣列為16進位制字串
     * @param b 位元組陣列
     * @return 16進位制字串
     */
    public static String byteArrayToHexString(byte[] b) {
        StringBuilder resultSb = new StringBuilder();
        for (byte aB : b) {
            resultSb.append(byteToHexString(aB));
        }
        return resultSb.toString();
    }

    /**
     * 轉換byte到16進位制
     * @param b 要轉換的byte
     * @return 16進位制格式
     */
    private static String byteToHexString(byte b) {
        int n = b;
        if (n < 0) {
            n = 256 + n;
        }
        int d1 = n / 16;
        int d2 = n % 16;
        return hexDigits[d1] + hexDigits[d2];
    }

    /**
     * MD5編碼
     * @param origin 原始字串
     * @return 經過MD5加密之後的結果
     */
    public static String MD5Encode(String origin) {
        String resultString = null;
        try {
            resultString = origin;
            MessageDigest md = MessageDigest.getInstance("MD5");
            resultString = byteArrayToHexString(md.digest(resultString.getBytes()));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return resultString;
    }

}

預訂單實體類

package com.moyutang.common.utils.wxUtil;

/**
 * 預訂單
 * 
 * @author zuoliangzhu
 *
 */

public class OrderInfo {
	private String appid;// 小程式ID	
	private String mch_id;// 商戶號
	private String nonce_str;// 隨機字串
	private String sign_type;//簽名型別
	private String sign;// 簽名
	private String body;// 商品描述
	private String out_trade_no;// 商戶訂單號
	private int total_fee;// 標價金額 ,單位為分
	private String spbill_create_ip;// 終端IP
	private String notify_url;// 通知地址
	private String trade_type;// 交易型別
	private String openid;//使用者標識	

	public String getSign_type() {
		return sign_type;
	}

	public void setSign_type(String sign_type) {
		this.sign_type = sign_type;
	}

	public String getOpenid() {
		return openid;
	}

	public void setOpenid(String openid) {
		this.openid = openid;
	}

	public String getAppid() {
		return appid;
	}

	public void setAppid(String appid) {
		this.appid = appid;
	}

	public String getMch_id() {
		return mch_id;
	}

	public void setMch_id(String mch_id) {
		this.mch_id = mch_id;
	}

	public String getNonce_str() {
		return nonce_str;
	}

	public void setNonce_str(String nonce_str) {
		this.nonce_str = nonce_str;
	}

	public String getSign() {
		return sign;
	}

	public void setSign(String sign) {
		this.sign = sign;
	}

	public String getBody() {
		return body;
	}

	public void setBody(String body) {
		this.body = body;
	}

	public String getOut_trade_no() {
		return out_trade_no;
	}

	public void setOut_trade_no(String out_trade_no) {
		this.out_trade_no = out_trade_no;
	}

	public int getTotal_fee() {
		return total_fee;
	}

	public void setTotal_fee(int total_fee) {
		this.total_fee = total_fee;
	}

	public String getSpbill_create_ip() {
		return spbill_create_ip;
	}

	public void setSpbill_create_ip(String spbill_create_ip) {
		this.spbill_create_ip = spbill_create_ip;
	}

	public String getNotify_url() {
		return notify_url;
	}

	public void setNotify_url(String notify_url) {
		this.notify_url = notify_url;
	}

	public String getTrade_type() {
		return trade_type;
	}

	public void setTrade_type(String trade_type) {
		this.trade_type = trade_type;
	}

}
package com.moyutang.common.utils.wxUtil;

public class OrderReturnInfo {
    private String return_code;
    private String return_msg;
    private String result_code;
    private String appid;
    private String mch_id;
    private String nonce_str;
    private String sign;
    private String prepay_id;
    private String trade_type;
	public String getReturn_code() {
		return return_code;
	}
	public void setReturn_code(String return_code) {
		this.return_code = return_code;
	}
	public String getReturn_msg() {
		return return_msg;
	}
	public void setReturn_msg(String return_msg) {
		this.return_msg = return_msg;
	}
	public String getResult_code() {
		return result_code;
	}
	public void setResult_code(String result_code) {
		this.result_code = result_code;
	}
	public String getAppid() {
		return appid;
	}
	public void setAppid(String appid) {
		this.appid = appid;
	}
	public String getMch_id() {
		return mch_id;
	}
	public void setMch_id(String mch_id) {
		this.mch_id = mch_id;
	}
	public String getNonce_str() {
		return nonce_str;
	}
	public void setNonce_str(String nonce_str) {
		this.nonce_str = nonce_str;
	}
	public String getSign() {
		return sign;
	}
	public void setSign(String sign) {
		this.sign = sign;
	}
	public String getPrepay_id() {
		return prepay_id;
	}
	public void setPrepay_id(String prepay_id) {
		this.prepay_id = prepay_id;
	}
	public String getTrade_type() {
		return trade_type;
	}
	public void setTrade_type(String trade_type) {
		this.trade_type = trade_type;
	}
    
}

隨機數生成工具類

import java.util.Random;

/**
 * 隨機字串生成
 * @author zuoliangzhu
 *
 */
public class RandomStringGenerator {
	/**
     * 獲取一定長度的隨機字串
     * @param length 指定字串長度
     * @return 一定長度的字串
     */
    public static String getRandomStringByLength(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();
    }
}

簽名信息實體類


import com.thoughtworks.xstream.annotations.XStreamAlias;

/**
 * 簽名信息
 * @author zuoliangzhu
 *
 */
public class SignInfo {

	private String appId;//小程式ID	
	private String timeStamp;//時間戳	
	private String nonceStr;//隨機串	
	@XStreamAlias("package")
	private String repay_id;
	private String signType;//簽名方式
	
	public String getAppId() {
		return appId;
	}
	public void setAppId(String appId) {
		this.appId = appId;
	}
	public String getTimeStamp() {
		return timeStamp;
	}
	public void setTimeStamp(String timeStamp) {
		this.timeStamp = timeStamp;
	}
	public String getNonceStr() {
		return nonceStr;
	}
	public void setNonceStr(String nonceStr) {
		this.nonceStr = nonceStr;
	}
	public String getRepay_id() {
		return repay_id;
	}
	public void setRepay_id(String repay_id) {
		this.repay_id = repay_id;
	}
	public String getSignType() {
		return signType;
	}
	public void setSignType(String signType) {
		this.signType = signType;
	}
	
	
	
}

還有亂七八糟的配置

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.SignatureException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.commons.codec.digest.DigestUtils;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;

public class PayUtil {
    /**
     * 簽名字串
     * @param text 需要簽名的字串
     * @param key 金鑰
     * @param input_charset 編碼格式
     * @return 簽名結果
     */
    public static String sign(String text, String key, String input_charset) {
        text = text + "&key=" + key;
        return DigestUtils.md5Hex(getContentBytes(text, input_charset));
    }
    /**
     * 簽名字串
     *  @param text 需要簽名的字串
     * @param sign 簽名結果
     * @param key 金鑰
     * @param input_charset 編碼格式
     * @return 簽名結果
     */
    public static boolean verify(String text, String sign, String key, String input_charset) {
        text = text + key;
        String mysign = DigestUtils.md5Hex(getContentBytes(text, input_charset));
        if (mysign.equals(sign)) {
            return true;
        } else {
            return false;
        }
    }
    /**
     * @param content
     * @param charset
     * @return
     * @throws SignatureException
     * @throws UnsupportedEncodingException
     */
    public static byte[] getContentBytes(String content, String charset) {
        if (charset == null || "".equals(charset)) {
            return content.getBytes();
        }
        try {
            return content.getBytes(charset);
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException("MD5簽名過程中出現錯誤,指定的編碼集不對,您目前指定的編碼集是:" + charset);
        }
    }

    private static boolean isValidChar(char ch) {
        if ((ch >= '0' && ch <= '9') || (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z'))
            return true;
        if ((ch >= 0x4e00 && ch <= 0x7fff) || (ch >= 0x8000 && ch <= 0x952f))
            return true;// 簡體中文漢字編碼
        return false;
    }
    /**
     * 除去陣列中的空值和簽名引數
     * @param sArray 簽名引數組
     * @return 去掉空值與簽名引數後的新簽名引數組
     */
    public static Map<String, String> paraFilter(Map<String, String> sArray) {
        Map<String, String> result = new HashMap<String, String>();
        if (sArray == null || sArray.size() <= 0) {
            return result;
        }
        for (String key : sArray.keySet()) {
            String value = sArray.get(key);
            if (value == null || value.equals("") || key.equalsIgnoreCase("sign")
                    || key.equalsIgnoreCase("sign_type")) {
                continue;
            }
            result.put(key, value);
        }
        return result;
    }
    /**
     * 把陣列所有元素排序,並按照“引數=引數值”的模式用“&”字元拼接成字串
     * @param params 需要排序並參與字元拼接的引數組
     * @return 拼接後字串
     */
    public static String createLinkString(Map<String, String> params) {
        List<String> keys = new ArrayList<String>(params.keySet());
        Collections.sort(keys);
        String prestr = "";
        for (int i = 0; i < keys.size(); i++) {
            String key = keys.get(i);
            String value = params.get(key);
            if (i == keys.size() - 1) {// 拼接時,不包括最後一個&字元
                prestr = prestr + key + "=" + value;
            } else {
                prestr = prestr + key + "=" + value + "&";
            }
        }
        return prestr;
    }
    /**
     *
     * @param requestUrl 請求地址
     * @param requestMethod 請求方法
     * @param outputStr 引數
     */
    public static String httpRequest(String requestUrl,String requestMethod,String outputStr) {
        // 建立SSLContext
        StringBuffer buffer = null;
        try {
            URL url = new URL(requestUrl);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod(requestMethod);
            conn.setDoOutput(true);
            conn.setDoInput(true);
            conn.connect();
            //往伺服器端寫內容
            if (null != outputStr) {
                OutputStream os = conn.getOutputStream();
                os.write(outputStr.getBytes("utf-8"));
                os.close();
            }
            // 讀取伺服器端返回的內容
            InputStream is = conn.getInputStream();
            InputStreamReader isr = new InputStreamReader(is, "utf-8");
            BufferedReader br = new BufferedReader(isr);
            buffer = new StringBuffer();
            String line = null;
            while ((line = br.readLine()) != null) {
                buffer.append(line);
            }
            br.close();
        }catch(Exception e){
            e.printStackTrace();
        }
        return buffer.toString();
    }
    public static String urlEncodeUTF8(String source){
        String result=source;
        try {
            result=java.net.URLEncoder.encode(source, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return result;
    }
    /**
     * 解析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;
    }
    /**
     * 獲取子結點的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(getChildrenText(list));
                }
                sb.append(value);
                sb.append("</" + name + ">");
            }
        }

        return sb.toString();
    }
    public static InputStream String2Inputstream(String str) {
        return new ByteArrayInputStream(str.getBytes());
    }
}


好了,就這些了,應該沒漏了.. 抄了不少朋友的程式碼,大同小異