Android微信支付詳解與Demo

2018年4月5日補充

注意:
1.Android Studio 3.0
2.gradle 3.0
3.Android 8.0


最近公司弄Ionic框架,項目中需要微信支付,無奈,把我調(diào)過去弄,期間也是幾近崩潰,好在皇天不負有心人揣苏,在看別人的文檔,終于是在項目中集成了微信支付件舵,下面作為一個小白的我卸察,想要把我的經(jīng)驗分享給大家,希望對大家有所幫助铅祸。

先給一個可用的demo吧(運行前先看txt文件)
https://download.csdn.net/download/qq_35229022/10356669

這個demo是基于eclipse開發(fā)的坑质,博主也在Android Studio開發(fā)過微信支付合武,原理都是一樣的,大家把這個demo弄懂了涡扼,在AS上面也是一樣的稼跳。

(溫馨提示:大家下載下來可能會出錯,也有可能不會吃沪。下面給出出錯的解決方法:1.進入項目中的WeIXinPay->Build Path->configure build path汤善,移除那個報錯的jar包。 2.會出現(xiàn)資源找不到的情況票彪,這是因為你沒有v7包萎津,下載一個v7包,或者把出錯的地方都刪除抹镊,只是一個主題,刪除了看起來不好看而已荤傲,當然垮耳,你也可以用你有的主題。 還有一個問題需要提出來遂黍,就是你可能按照里面的text操作的仍然調(diào)不起客戶端终佛,有可能是你沒有安裝微信客戶端,因為我沒有做判斷雾家。這個demo不會出現(xiàn)只能成功支付一次的情況铃彰,博主親測有效。出現(xiàn)只能支付一次只能說明你的簽名沒有對應(yīng))

1.去微信開放平臺申請微信支付服務(wù)芯咧,綁定自己的應(yīng)用這里具體不多講牙捉,但是一定要申請完成,將會得到是三個參數(shù)

 //appid 微信分配的公眾賬號ID
  public static final String APP_ID = "";

  //商戶號 微信分配的公眾賬號ID
   public static final String MCH_ID = "";

 //  API密鑰敬飒,在商戶平臺設(shè)置
  public static final  String API_KEY= "";

坑點提示:在微信開發(fā)平臺設(shè)置包名和簽名邪铲。這里的包名一定要和你自己的包名一樣,就是manifest中的package,簽名一定要和你用官方app生成的一樣(https://open.weixin.qq.com/zh_CN/htmledition/res/dev/download/sdk/Gen_Signature_Android.apk)无拗。
微信會根據(jù)你的填寫的包名带到,然后對你的keystore進行一種算法,生成你的簽名英染。包名和簽名一定要和微信開放平臺的相同揽惹。不過這里需要注意的是,如果你發(fā)布的正式版本四康,需要用官方app重新生成簽名搪搏,然后在開放平臺重新設(shè)置sign,因為測試版本的keystore與正式版的keystore不一樣闪金∧饺拢總之,就是你用的keystore生成的sign要和微信開放平臺的時刻保持一致。

2.準備工作做好了喝检,接下來就是開發(fā)了嗅辣,先下載微信的jar包,導入挠说。
微信支付分為三個步驟
1.生成prepayId

   @Override
    protected Map<String, String> doInBackground(String... params) {
        // TODO Auto-generated method stub
        String url=String.format(params[0]);
        String entity=getProductArgs();
        Log.e("Simon",">>>>"+entity);
        byte[] buf=Util.httpPost(url, entity);
        String content = new String(buf);
        Log.e("orion", "----"+content);
        Map<String,String> xml=decodeXml(content);

        return xml;
    }

2.生成簽名參數(shù)

   private void genPayReq() {

    req.appId = Constants.APP_ID;
    req.partnerId = Constants.MCH_ID;
    if (resultunifiedorder!=null) {
        req.prepayId = resultunifiedorder.get("prepay_id");
        req.packageValue = "prepay_id="+resultunifiedorder.get("prepay_id");
    }
    else {
        Toast.makeText(MainActivity.this, "prepayid為空", Toast.LENGTH_SHORT).show();
    }
    req.nonceStr = getNonceStr();
    req.timeStamp = String.valueOf(genTimeStamp());


    List<NameValuePair> signParams = new LinkedList<NameValuePair>();
    signParams.add(new BasicNameValuePair("appid", req.appId));
    signParams.add(new BasicNameValuePair("noncestr", req.nonceStr));
    signParams.add(new BasicNameValuePair("package", req.packageValue));
    signParams.add(new BasicNameValuePair("partnerid", req.partnerId));
    signParams.add(new BasicNameValuePair("prepayid", req.prepayId));
    signParams.add(new BasicNameValuePair("timestamp", req.timeStamp));

    req.sign = genAppSign(signParams);

    sb.append("sign\n"+req.sign+"\n\n");

    textView.setText(sb.toString());

    Log.e("Simon", "----"+signParams.toString());

}

3.調(diào)起支付澡谭。

 /*
 * 調(diào)起微信支付
 */
private void sendPayReq() {


    msgApi.registerApp(Constants.APP_ID);
    msgApi.sendReq(req);
    Log.i(">>>>>", req.partnerId);
}

下面給出完整代碼 :

  package com.alpha.live;

  import java.io.StringReader;
  import java.util.HashMap;
  import java.util.LinkedList;
  import java.util.List;
  import java.util.Map;
  import java.util.Random;

  import org.apache.http.NameValuePair;
  import org.apache.http.message.BasicNameValuePair;
  import org.xmlpull.v1.XmlPullParser;

  import com.tencent.mm.sdk.modelpay.PayReq;
  import com.tencent.mm.sdk.openapi.IWXAPI;
  import com.tencent.mm.sdk.openapi.WXAPIFactory;

  import android.app.Activity;
  import android.app.AlertDialog;
  import android.app.ProgressDialog;
  import android.os.AsyncTask;
  import android.os.Bundle;
  import android.util.Log;
  import android.util.Xml;
  import android.view.View;
  import android.view.View.OnClickListener;
  import android.widget.Button;
  import android.widget.TextView;
  import android.widget.Toast;
  /**
  * Created by Simon on 2016/12/2.
  */
  public class MainActivity extends Activity implements OnClickListener {
private Button submitButton;
private Button confirmButton;
private TextView textView;
private StringBuffer sb;
private Map<String,String> resultunifiedorder;
private PayReq req;
private final IWXAPI msgApi = WXAPIFactory.createWXAPI(this, null);
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    submitButton=(Button) findViewById(R.id.bt_submit_order);
    confirmButton=(Button) findViewById(R.id.bt_corfirm);
    textView=(TextView) findViewById(R.id.tv_prepay_id);
    submitButton.setOnClickListener(this);
    confirmButton.setOnClickListener(this);
    sb=new StringBuffer();
    req=new PayReq();
}
@Override
public void onClick(View v) {
    // TODO Auto-generated method stub
    switch (v.getId()) {
    case R.id.bt_submit_order:
        String urlString="https://api.mch.weixin.qq.com/pay/unifiedorder";
         PrePayIdAsyncTask prePayIdAsyncTask=new PrePayIdAsyncTask();
         prePayIdAsyncTask.execute(urlString);      //生成prepayId

    break;
    case R.id.bt_corfirm:
        genPayReq();//生成簽名參數(shù)
        sendPayReq();//調(diào)起支付
    break;
    default:
        break;
    }
}
/*
 * 調(diào)起微信支付
 */
private void sendPayReq() {


    msgApi.registerApp(Constants.APP_ID);
    msgApi.sendReq(req);
    Log.i(">>>>>", req.partnerId);
}
private long genTimeStamp() {
    return System.currentTimeMillis() / 1000;
}
private void genPayReq() {

    req.appId = Constants.APP_ID;
    req.partnerId = Constants.MCH_ID;
    if (resultunifiedorder!=null) {
        req.prepayId = resultunifiedorder.get("prepay_id");
        req.packageValue = "prepay_id="+resultunifiedorder.get("prepay_id");
    }
    else {
        Toast.makeText(MainActivity.this, "prepayid為空", Toast.LENGTH_SHORT).show();
    }
    req.nonceStr = getNonceStr();
    req.timeStamp = String.valueOf(genTimeStamp());


    List<NameValuePair> signParams = new LinkedList<NameValuePair>();
    signParams.add(new BasicNameValuePair("appid", req.appId));
    signParams.add(new BasicNameValuePair("noncestr", req.nonceStr));
    signParams.add(new BasicNameValuePair("package", req.packageValue));
    signParams.add(new BasicNameValuePair("partnerid", req.partnerId));
    signParams.add(new BasicNameValuePair("prepayid", req.prepayId));
    signParams.add(new BasicNameValuePair("timestamp", req.timeStamp));

    req.sign = genAppSign(signParams);

    sb.append("sign\n"+req.sign+"\n\n");

    textView.setText(sb.toString());

    Log.e("Simon", "----"+signParams.toString());

}
private String genAppSign(List<NameValuePair> params) {
    StringBuilder sb = new StringBuilder();

    for (int i = 0; i < params.size(); i++) {
        sb.append(params.get(i).getName());
        sb.append('=');
        sb.append(params.get(i).getValue());
        sb.append('&');
    }
    sb.append("key=");
    sb.append(Constants.API_KEY);

    this.sb.append("sign str\n"+sb.toString()+"\n\n");
    String appSign = MD5.getMessageDigest(sb.toString().getBytes());
    Log.e("Simon","----"+appSign);
    return appSign;
}
private class PrePayIdAsyncTask extends AsyncTask<String,Void, Map<String, String>>
{
    private ProgressDialog dialog;
    @Override
    protected void onPreExecute() {
        // TODO Auto-generated method stub
        super.onPreExecute();
        dialog = ProgressDialog.show(MainActivity.this, "提示", "正在提交訂單");

    }
    @Override
    protected Map<String, String> doInBackground(String... params) {
        // TODO Auto-generated method stub
        String url=String.format(params[0]);
        String entity=getProductArgs();
        Log.e("Simon",">>>>"+entity);
        byte[] buf=Util.httpPost(url, entity);
        String content = new String(buf);
        Log.e("orion", "----"+content);
        Map<String,String> xml=decodeXml(content);

        return xml;
    }

    @Override
    protected void onPostExecute(Map<String, String> result) {
        // TODO Auto-generated method stub
        super.onPostExecute(result);
        if (dialog != null) {
            dialog.dismiss();
        }
        sb.append("prepay_id\n"+result.get("prepay_id")+"\n\n");
        textView.setText(sb.toString());
        resultunifiedorder=result;
    }
}

public 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) {
        Log.e("Simon","----"+e.toString());
    }
    return null;

}
private String getProductArgs() {
    // TODO Auto-generated method stub
    StringBuffer xml=new StringBuffer();
    try {
        String nonceStr=getNonceStr();
        xml.append("<xml>");
        List<NameValuePair> packageParams=new LinkedList<NameValuePair>();
        packageParams.add(new BasicNameValuePair("appid",Constants.APP_ID));
        packageParams.add(new BasicNameValuePair("body", "APP pay test"));
        packageParams.add(new BasicNameValuePair("mch_id", Constants.MCH_ID));
        packageParams.add(new BasicNameValuePair("nonce_str", nonceStr));
        packageParams.add(new BasicNameValuePair("notify_url", "https://www.baidu.com"));//寫你們的回調(diào)地址
        packageParams.add(new BasicNameValuePair("out_trade_no",genOutTradNo()));
        packageParams.add(new BasicNameValuePair("total_fee", "1"));
        packageParams.add(new BasicNameValuePair("trade_type", "APP"));

        String sign=getPackageSign(packageParams);
        packageParams.add(new BasicNameValuePair("sign", sign));
        String xmlString=toXml(packageParams);
        return xmlString;
    } catch (Exception e) {
        // TODO: handle exception
        return null;
    }
}
//生成訂單號,測試用,在客戶端生成
private String genOutTradNo() {
    Random random = new Random();
//      return "dasgfsdg1234"; //訂單號寫死的話只能支付一次损俭,第二次不能生成訂單
    return MD5.getMessageDigest(String.valueOf(random.nextInt(10000)).getBytes());
}
//生成隨機號蛙奖,防重發(fā)
    private String getNonceStr() {
    // TODO Auto-generated method stub
    Random random=new Random();

    return MD5.getMessageDigest(String.valueOf(random.nextInt(10000)).getBytes());
}
/**
 生成簽名
 */

private String getPackageSign(List<NameValuePair> params) {
    StringBuilder sb = new StringBuilder();

    for (int i = 0; i < params.size(); i++) {
        sb.append(params.get(i).getName());
        sb.append('=');
        sb.append(params.get(i).getValue());
        sb.append('&');
    }
    sb.append("key=");
    sb.append(Constants.API_KEY);


    String packageSign = MD5.getMessageDigest(sb.toString().getBytes()).toUpperCase();
    Log.e("Simon",">>>>"+packageSign);
    return packageSign;
}
/*
 * 轉(zhuǎn)換成xml
 */
private String toXml(List<NameValuePair> params) {
    StringBuilder sb = new StringBuilder();
    sb.append("<xml>");
    for (int i = 0; i < params.size(); i++) {
        sb.append("<"+params.get(i).getName()+">");


        sb.append(params.get(i).getValue());
        sb.append("</"+params.get(i).getName()+">");
    }
    sb.append("</xml>");

    Log.e("Simon",">>>>"+sb.toString());
    return sb.toString();
}

}  

接下來就是有個支付結(jié)果的頁面代碼。是微信官方提供的一個類杆兵。你要在manifest注冊這個類雁仲。這里需要注意的是,這個類必須放在wxapi包下琐脏,你自己新建一個包即可攒砖。

為了大家可以直接運行這個demo,我的微信加簽都是在本地執(zhí)行的日裙,獲取prepayid和加簽都應(yīng)該在服務(wù)端完成吹艇,還有最終的支付返回結(jié)果也是以服務(wù)端的為準。

下面給出運行結(jié)果圖

微信支付

image.png

大家下載demo然后把參數(shù)換了昂拂,弄下keystore受神,包名,簽名格侯。應(yīng)該就可以用了鼻听。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市联四,隨后出現(xiàn)的幾起案子精算,更是在濱河造成了極大的恐慌,老刑警劉巖碎连,帶你破解...
    沈念sama閱讀 212,718評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件灰羽,死亡現(xiàn)場離奇詭異,居然都是意外死亡鱼辙,警方通過查閱死者的電腦和手機廉嚼,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,683評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來倒戏,“玉大人怠噪,你說我怎么就攤上這事《捧危” “怎么了傍念?”我有些...
    開封第一講書人閱讀 158,207評論 0 348
  • 文/不壞的土叔 我叫張陵矫夷,是天一觀的道長。 經(jīng)常有香客問我憋槐,道長双藕,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,755評論 1 284
  • 正文 為了忘掉前任阳仔,我火速辦了婚禮忧陪,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘近范。我一直安慰自己嘶摊,他們只是感情好,可當我...
    茶點故事閱讀 65,862評論 6 386
  • 文/花漫 我一把揭開白布评矩。 她就那樣靜靜地躺著叶堆,像睡著了一般。 火紅的嫁衣襯著肌膚如雪斥杜。 梳的紋絲不亂的頭發(fā)上虱颗,一...
    開封第一講書人閱讀 50,050評論 1 291
  • 那天,我揣著相機與錄音果录,去河邊找鬼。 笑死咐熙,一個胖子當著我的面吹牛弱恒,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播棋恼,決...
    沈念sama閱讀 39,136評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼返弹,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了爪飘?” 一聲冷哼從身側(cè)響起义起,我...
    開封第一講書人閱讀 37,882評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎师崎,沒想到半個月后默终,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,330評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡犁罩,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,651評論 2 327
  • 正文 我和宋清朗相戀三年齐蔽,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片床估。...
    茶點故事閱讀 38,789評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡含滴,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出丐巫,到底是詐尸還是另有隱情谈况,我是刑警寧澤勺美,帶...
    沈念sama閱讀 34,477評論 4 333
  • 正文 年R本政府宣布,位于F島的核電站碑韵,受9級特大地震影響赡茸,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜泼诱,卻給世界環(huán)境...
    茶點故事閱讀 40,135評論 3 317
  • 文/蒙蒙 一坛掠、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧治筒,春花似錦屉栓、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,864評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至堤框,卻和暖如春域滥,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背蜈抓。 一陣腳步聲響...
    開封第一講書人閱讀 32,099評論 1 267
  • 我被黑心中介騙來泰國打工启绰, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人沟使。 一個月前我還...
    沈念sama閱讀 46,598評論 2 362
  • 正文 我出身青樓委可,卻偏偏與公主長得像,于是被迫代替她去往敵國和親腊嗡。 傳聞我的和親對象是個殘疾皇子着倾,可洞房花燭夜當晚...
    茶點故事閱讀 43,697評論 2 351

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