Android支付——微信支付

最近項目中需要支付功能撞牢,一個是支付寶率碾,一個是微信叔营。由于之前都沒有接觸過屋彪,所以在網(wǎng)上看了許多文章與文檔,逐步了解了這兩個支付绒尊,項目中配置完成后畜挥,也順便記錄一下配置的環(huán)節(jié),以便以后查看使用婴谱。
說實在的蟹但,支付寶的開發(fā)文檔寫的比微信的支付文檔 好太多!谭羔!(不吹不黑)华糖。

一、參考的文章

1.Android微信支付爬坑 (http://blog.csdn.net/ywl5320/article/details/50856922#reply
這篇文章是由App客戶端完全同微信服務器進行交互瘟裸,自家的服務器不參與客叉。官方不提倡,但是為了更好地明白原理话告,還是很好的
2.Android App支付系列(http://blog.csdn.net/xiong_it/article/details/51685033
這篇文章是由自家的服務器同微信的服務器交互兼搏,生成與支付訂單,返回給App客戶端的沙郭,這樣一來佛呻,比較安全,通知客戶端所需要做的事情也少了病线。
3.微信支付官方文檔(https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_1
自己看吓著。。剛開始看的時候一頭霧水送挑,需要慢慢有耐心的看绑莺。。让虐。
4.微信支付官方demo(https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=11_1
集成微信支付的時候需要用到其中的類和方法紊撕。

二、支付流程(附上官方圖)

微信支付流程

根據(jù)我的理解可以簡單的分為以下幾步
1.App客戶端將簡單的訂單信息發(fā)送給自己的服務器(訂單信息跟后臺商量赡突,需要傳哪些參數(shù))对扶;
2.App服務端調(diào)用統(tǒng)一下單Api同微信服務端進行交互区赵,微信服務端生成與支付訂單(帶有prepay_id的訂單信息)返回給App服務端。
3.服務端將預支付訂單進行加簽處理后返回給App客戶端浪南,由App客戶端進行支付笼才。
4.App客戶端收到加簽后的預支付訂單后,調(diào)用微信SDK進行支付络凿,支付結果在WxPayEntryActivity中顯示骡送。

三、支付中遇到的問題

1.提交訂單時顯示簽名錯誤絮记,原因可能是下單中的參數(shù)(body)中含有中文摔踱,需要進行iso8859-1編碼。
2.微信服務端返回prepay_id后怨愤,進行支付時返回簽名錯誤派敷,原因可能是沒有進行注冊,wXapi.registerApp(WxConstans.APP_ID);
3.能夠進行支付了撰洗,但是支付結果一直顯示-1篮愉,支付失敗,原因可能是
①App打包時必須用你申請支付時的簽名文件
②我將微信的緩存清空了差导,可以進行一次支付试躏,然后就不行了。難道要每次支付都要清空一次緩存么设褐。颠蕴。這樣肯定不行,包名也要與申請支付時的包名一致络断。
4.官方demo中用到了HttpClient這個類裁替,然而在AS中已經(jīng)不適用這個類了,需要在module的build.gradle中添加
useLibrary 'org.apache.http.legacy'方可使用HttpClient貌笨。

開始進行支付了H跖小!锥惋!

我將所有的工作全部放在了客戶端完成昌腰,講道理加簽什么的應該放在服務端的!0虻)
1.支付準備

  • APP_ID (App_id申請時候給的)
  • MCH_ID (商戶號)
  • API_KEY (API秘鑰)
  • APP_notify_url (異步通知服務器地址遭商,跟自家服務端商量)
  • 微信的jar包(官方demo中有)
  • dom4j-full.jar (解析xml使用的jar包)

2.構造支付實體類

private String appid; //appid
private String body; //商品描述
private String mch_id; //商戶ID
private String nonce_str; //隨機字符串32位
private String notify_url; //微信通知后臺支付結果url
private String out_trade_no; //我們自己的訂單號,由自家服務端返回的唯一訂單號
private String spbill_create_ip; //客戶端IP  (我感覺不寫也可以捅伤。劫流。)
private int total_fee; //總的支付金額(單位是分!!l艋恪)
private String trade_type; //因為是移動應用 所以是APP(固定值)
private String sign; //以上所有參數(shù)的MD5簽名

3.注冊微信H猿印!?珊堋JΑ!N铱佟N尽!2送亍瓣窄!
在你需要進行支付的Activiity中注冊微信,一般在onCreate方法中

private IWXAPI wXapi = WXAPIFactory.createWXAPI(this, null);
wXapi.registerApp(WxConstans.APP_ID);

4.構造訂單的實體類

entity.setAppid(WxConstans.APP_ID);
entity.setBody("test");
entity.setMch_id(WxConstans.MCH_ID);
entity.setNonce_str(getNonceStr());//單獨一個方法獲取32位隨機數(shù)
entity.setNotify_url(WxConstans.APP_notify_url);
entity.setOut_trade_no(getOutTradNo());//單獨方法獲取唯一訂單號
entity.setTotal_fee(1);//1分錢
entity.setTrade_type("APP");//固定值
entity.setSpbill_create_ip("192.168.179.2");

//生成隨機號尘惧,防重發(fā)
private String getNonceStr() { 
    Random random = new Random();  
    return MD5.getMessageDigest(String.valueOf(random.nextInt(10000)).getBytes());
    }

//生成訂單號
private String getOutTradNo() { 
    Random random = new Random();  
    return MD5.getMessageDigest(String.valueOf(random.nextInt(10000)).getBytes());

}
5.參考下單API開始拼單

 //構造商品參數(shù)集合,因為需要排序康栈,所以用到了SortedMap
SortedMap<Object, Object> parameters = new TreeMap<Object, Object>();
parameters.put("appid", entity.getAppid());
parameters.put("body", entity.getBody());
parameters.put("mch_id", entity.getMch_id());
parameters.put("nonce_str", entity.getNonce_str());
parameters.put("notify_url", entity.getNotify_url());
parameters.put("out_trade_no", entity.getOut_trade_no());
parameters.put("total_fee", entity.getTotal_fee());
parameters.put("trade_type", entity.getTrade_type());
parameters.put("spbill_create_ip", entity.getSpbill_create_ip());
//將訂單信息簽名后再傳入
parameters.put("sign", WxUtils.createSign("UTF-8", parameters, WxConstans.API_KEY));

/** * 微信支付簽名算法sign 
    * @param characterEncoding 簽名編碼(UTF-8) 
    * @param parameters 要簽名的參數(shù)的集合
    * @param key 商戶自己設置的key
*/
 public static String createSign(String characterEncoding, SortedMap<Object,Object> parameters, String key){
    StringBuffer sb = new StringBuffer();
    Set es = parameters.entrySet();//所有參與傳參的參數(shù)按照accsii排序(升序)
    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=" + key);
    System.out.println(sb.toString());
    String sign = WxMd5.MD5Encode(sb.toString(), characterEncoding).toUpperCase();
    System.out.println(sign); 
    return sign;}

public class WxMd5 {
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("utf-8")));
    } 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"};
}

6.將訂單信息拼成xml格式
由于微信服務器只接收xml格式的數(shù)據(jù),需要將訂單信息拼成xml格式

//3.因為統(tǒng)一下單接口需要以xml格式post發(fā)送給微信喷橙,所以我們先拼接xml格式的參數(shù):
StringBuilder xmlBuilder = new StringBuilder();
xmlBuilder.append("<xml>");
Set es = parameters.entrySet();//所有參與傳參的參數(shù)按照accsii排序(升序)
Iterator it = es.iterator();while (it.hasNext()) {
Map.Entry entry = (Map.Entry) it.next();
String k = (String) entry.getKey();
Object v1 = entry.getValue();
xmlBuilder.append("<").append(k).append(">");
xmlBuilder.append(v1);
xmlBuilder.append("</").append(k).append(">");}
xmlBuilder.append("</xml>");
System.out.println(xmlBuilder.toString());
Log.d("tag", "拼裝的xml信息" + xmlBuilder.toString());
try {
//異步線程獲取微信服務器返回的信息
new GetPrepayId(new String(xmlBuilder.toString().getBytes(), "ISO8859-1")).execute();//這一步非常重要,不這樣轉換編碼的話登舞,傳遞中文就會報“簽名錯誤”贰逾,這是很多人都會遇到的錯誤。} 
catch (Exception e) {
e.printStackTrace();}

//異步線程請求統(tǒng)一下單接口:
public class GetPrepayId extends AsyncTask {
String str;
public GetPrepayId(String str) {
    this.str = str;
}
@Override
protected void onPreExecute() {
    super.onPreExecute();
}
@Override
protected Object doInBackground(Object[] params) {
    //微信給的下單接口
    String url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
    //Util類為微信demo中自帶的一個類菠秒,可以復制出來疙剑,自行使用
    byte[] buf = Util.httpPost(url, str);
    String content = new String(buf);
    Log.d("tag", "微信服務器返回的xml為: " + content);
    //將xml轉為map
    Map<String, String> map = xmlToMap(content);
    Log.d("tag", "xml轉為map: " + map.toString());
    String nonceStr = getNonceStr();
    //獲取時間戳
    String timeStamp = String.valueOf(getTimeStamp());
    //開始準備付款
    PayReq request = new PayReq();
    request.appId = WxConstans.APP_ID;
    request.partnerId = WxConstans.MCH_ID;
    request.prepayId = map.get("prepay_id");
    request.packageValue = "Sign=WXPay";
    request.nonceStr = nonceStr;
    request.timeStamp = timeStamp;
    //再簽名
    SortedMap<Object, Object> parameters = new TreeMap<Object, Object>();
    parameters.put("appid", request.appId);
    parameters.put("partnerid",request.partnerId);
    parameters.put("prepayid", request.prepayId);
    parameters.put("package", request.packageValue);
    parameters.put("noncestr", request.nonceStr);
    parameters.put("timestamp", request.timeStamp);

    request.sign = WxUtils.createSign("UTF-8", parameters, WxConstans.API_KEY);
    Log.d("tag", "request.sign" + request.sign);
    //真開始支付了,支付結果在WXPayEntryActivity中顯示
    wXapi.sendReq(request);
    return content;
}}

xml轉map践叠,需要用到dom4j-full.jar 包

//將xml轉為map言缤,相信都能看懂,不做注釋禁灼。管挟。。
public Map<String, String> xmlToMap(String xmlstr) {
Map<String, String> map = new HashMap<>();
try {
    SAXReader reader = new SAXReader();
    InputStream ins = new ByteArrayInputStream(xmlstr.getBytes("UTF-8"));
    Document doc = reader.read(ins);
    Element root = doc.getRootElement();
    List<Element> list = root.elements();
    for (Element e : list) {
        map.put(e.getName(), e.getText());
    }
    ins.close();
} catch (Exception e) {
    e.printStackTrace();
}
return map;}

獲取時間戳

public static long getTimeStamp() {    return System.currentTimeMillis() / 1000;}

需要注意
WXPayEntryActivity必須在當前包名.wxapi包下弄捕。
xml中聲明

<activity
android:name=".wxapi.WXPayEntryActivity"
android:exported="true"
android:launchMode="singleTop"/>

基本完事Fⅰ!J匚健穿铆!

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市斋荞,隨后出現(xiàn)的幾起案子荞雏,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,430評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件凤优,死亡現(xiàn)場離奇詭異羡疗,居然都是意外死亡,警方通過查閱死者的電腦和手機别洪,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,406評論 3 398
  • 文/潘曉璐 我一進店門叨恨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人挖垛,你說我怎么就攤上這事痒钝。” “怎么了痢毒?”我有些...
    開封第一講書人閱讀 167,834評論 0 360
  • 文/不壞的土叔 我叫張陵送矩,是天一觀的道長。 經(jīng)常有香客問我哪替,道長栋荸,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,543評論 1 296
  • 正文 為了忘掉前任凭舶,我火速辦了婚禮晌块,結果婚禮上,老公的妹妹穿的比我還像新娘帅霜。我一直安慰自己匆背,他們只是感情好,可當我...
    茶點故事閱讀 68,547評論 6 397
  • 文/花漫 我一把揭開白布身冀。 她就那樣靜靜地躺著钝尸,像睡著了一般。 火紅的嫁衣襯著肌膚如雪搂根。 梳的紋絲不亂的頭發(fā)上珍促,一...
    開封第一講書人閱讀 52,196評論 1 308
  • 那天,我揣著相機與錄音剩愧,去河邊找鬼猪叙。 笑死,一個胖子當著我的面吹牛隙咸,可吹牛的內(nèi)容都是我干的沐悦。 我是一名探鬼主播,決...
    沈念sama閱讀 40,776評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼五督,長吁一口氣:“原來是場噩夢啊……” “哼藏否!你這毒婦竟也來了?” 一聲冷哼從身側響起充包,我...
    開封第一講書人閱讀 39,671評論 0 276
  • 序言:老撾萬榮一對情侶失蹤副签,失蹤者是張志新(化名)和其女友劉穎遥椿,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體淆储,經(jīng)...
    沈念sama閱讀 46,221評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡冠场,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,303評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了本砰。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片碴裙。...
    茶點故事閱讀 40,444評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖点额,靈堂內(nèi)的尸體忽然破棺而出舔株,到底是詐尸還是另有隱情,我是刑警寧澤还棱,帶...
    沈念sama閱讀 36,134評論 5 350
  • 正文 年R本政府宣布载慈,位于F島的核電站,受9級特大地震影響珍手,放射性物質發(fā)生泄漏办铡。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,810評論 3 333
  • 文/蒙蒙 一琳要、第九天 我趴在偏房一處隱蔽的房頂上張望寡具。 院中可真熱鬧,春花似錦焙蹭、人聲如沸晒杈。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,285評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至帖努,卻和暖如春撰豺,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背拼余。 一陣腳步聲響...
    開封第一講書人閱讀 33,399評論 1 272
  • 我被黑心中介騙來泰國打工污桦, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人匙监。 一個月前我還...
    沈念sama閱讀 48,837評論 3 376
  • 正文 我出身青樓凡橱,卻偏偏與公主長得像,于是被迫代替她去往敵國和親亭姥。 傳聞我的和親對象是個殘疾皇子稼钩,可洞房花燭夜當晚...
    茶點故事閱讀 45,455評論 2 359

推薦閱讀更多精彩內(nèi)容