Android微信支付集成流程及其常見錯誤

[支付寶集成傳送門:http://blog.csdn.net/hx7013/article/details/61476320]

一、你需要關心的東西

1.申請與認證

2.上傳密鑰(商戶KEY),包名和簽名的修改

3.服務端集成

4.安卓集成

本篇就全部寫了吧,讓我們一步步來。

二、申請與認證

1.提交您的開發者資料

微信開放平臺: https://open.weixin.qq.com/
進入後申請賬戶 -> 創建移動應用 -> 開發者認證[300 RMB] -> 申請微信支付,基本上無坑點,公司資料齊全可以很順利可以通過。

2.獲得商戶平臺帳號

認證時你需要登陸 微信支付商戶平臺:https://pay.weixin.qq.com
提交騰訊打給你公司對公賬戶上的審覈款,登陸微信商戶平臺的賬戶密碼在開發者賬戶認證通過或會通過郵件發送到申請時填寫的郵箱,不能自主註冊。

三、上傳密鑰、配置包名、配置應用簽名

Tips:本是很簡單的功能,爲何要單獨列一個來說呢?
因爲網上的其它教程基本上是老的提交方法了,當初在這也迷糊密鑰在何處生成或提交。所以單獨列出來圖文說明。

1.配置商戶KEY

登陸微信支付商戶平臺:https://pay.weixin.qq.com,用你獲得的賬戶密碼進行登陸。
然後點擊 賬戶中心 -> API安全 -> 設置API密鑰
微信支付集成
微信支付集成

Tips:密鑰使用的是32位大小寫+數字的密串,推薦自動生成。
百度[隨機密碼]很多的,注意只勾選大小寫+數字,長度爲32既可。
例如:bXubGehLcukfQc2OyP76qPs84LBuq8oO
當然你也可以自己填寫,但是個人感覺隨機密碼相對更加安全。至少社工不能破掉。

2.設置包名

登陸微信開放平臺: https://open.weixin.qq.com/
點擊 管理中心 -> 選擇應用點擊查看 -> 在最下面有[開發信息] -> 點擊修改填入包名。

當初審覈時提交的是豌豆莢的App鏈接,所以包名和簽名已自動填入。
如沒有自動填入或錯誤,可以自行修改。

3.設置簽名

Tips:在翻閱其它文檔時說修改簽名後需要審覈,但是在測試中發現並不需要審覈,估計是以前微信支付剛上線時有該限制。所以現在可以放心修改爲Debug簽名直接調試,免得先發布,在adb install...

同樣和上一步設置包名一樣,先登陸微信開發者平臺,而後在 管理中心 -> 選擇應用點擊查看 -> 在最下面有[開發信息] -> 點擊修改。

正式發佈的簽名和Debug簽名可以通過幾種方法獲取:http://blog.csdn.net/hx7013/article/details/61671633

四、服務端的集成

服務端集成重點只說簽名Sign的生成,其它都是傻瓜式集成,這點我們自己集成時也遇到了坑,所以單獨說一下。
當初在集成服務端的時候調用的是微信提供的開發包,但不知爲何在Android上調用後,總是彈不出支付的頁面,調用時就閃退回來直接反 -1 錯誤(不得不吐槽一下,-1有那麼多情況,就不能學支付寶彈個詳細點的錯誤碼嗎?)耽擱半天,終於解決。

首先來看看微信提供的這個接口調試工具:https://pay.weixin.qq.com/wiki/tools/signverify/ (貌似IE打不開)

先來張圖看看:
微信支付集成

Tips:不用糾結我爲何沒打碼,因爲都是虛假參數。但是簽名的過程一樣,大家能看懂既可。

首先進去後把接口方式從【提交被掃支付】改爲【自定義】,然後填入appid、partnerid、prepayid、package、noncestr、timestamp和對應的值。在該調試工具頁面,記住我強調是該調試頁面提交這些參的順序是不強求的,但是如果你是自己服務端簽名的時候就一定得按順序提交,這個順序馬上會說到。

填入值後點擊生成簽名,會發現下面有#1、#2、#3、#4四個流程,其實這個流程就是簽名的過程。
認證看後會發現,其實就是參數排序連接,然後再連接商戶Key,最後取MD5。

重點來了,也許你會糾結爲何參數正確的,但是Sign簽名一直不對呢?其實瞭解MD5的就應該知道,MD5是摘要算法,只知道通過文本去取Hash值。所以appid=123456&partnerid=123456 和 partnerid=123456&appid=123456計算出來的值一定是不同的。這也是坑之一,簽名一定得按順序組合後再取MD5值。

這個順序就是微信接口調試工具中#2步驟中所提示的,從上圖中我們可以看到我提交的是appid、partnerid、prepayid、package、noncestr、timestamp,但是微信給我組合成了appid、noncestr、package、partnerid、prepayid、timestamp、key。

appid=wx666888abc8defg88&noncestr=VCG4w95nS5Ku7mCi&package=Sign=WXPay&partnerid=1668866888&prepayid=wx20170312211500a8y53pgb0klvyh2rmoyu&timestamp=1489324509&key=UWMOIg1qJdHLZfEzAbm91BOBJG4oyC5C

而這個順序就是簽名通常出錯的地方,自己在服務端生成簽名時一不小心順序出錯,雖然參數正確,但是由於取出來的MD5是和微信那邊取出的MD5是不同的,所以固然簽名錯誤。
所以後面服務器生成簽名的最簡單解決方法就是硬接+取MD5

string signStr = "appid="+valAppid+"&noncestr="+valNoncestr+"&package=Sign=WXPay&partnerid="+valPartnerid+"&prepayid="+valPrepayid+"&timestamp="+valTimestamp+"&key="+valKEY
//c#的取MD5方法,其它語言自行Google
string signMd5 = FormsAuthentication.HashPasswordForStoringInConfigFile(signStr , "MD5") //signMd5即爲最終簽名

獲得Sign後Android提交調用既可。

五、Android的集成

1.Manifest的聲明

<!-- 這個也可以不設置,只是習慣跟着官方的Demo了,順便填寫了一下appid,也就是data android:scheme="wx666888abc8defg88" -->
 <application
   android:name="com.wx.wxTest.xApplication"
   android:allowBackup="true"
   android:icon="@drawable/app_icon"
   android:label="@string/app_name"
   android:screenOrientation="portrait"
   android:theme="@style/AppTheme" >
      <activity
        android:name="com.wx.wxTest.Activity.Welcome"
        android:screenOrientation="portrait"
        android:theme="@style/Welcome" >
          <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
          </intent-filter>
          <intent-filter>
            <action android:name="android.intent.action.VIEW" />
              <category android:name="android.intent.category.DEFAULT" />
            <data android:scheme="wx666888abc8defg88" />
          </intent-filter>
      </activity>

<!-- 該activity是申明給微信回調時通知的Activity -->
      <activity
        android:name="com.wx.wxTest.wxapi.WXPayEntryActivity"
        android:exported="true"
        android:launchMode="singleTop"
        android:screenOrientation="portrait" >
      </activity>

又一個坑點來了,我們先來看看官方的文檔。
微信支付集成

什麼叫“在net.sourceforge.simcpux.wxapi包路徑中實現WXPayEntryActivity類(包名或類名不一致會造成無法回調)“我就傻不拉唧的新建了一個包,名爲net.sourceforge.simcpux.wxapi,再新建了一個Activity名爲WXPayEntryActivity。後面果斷調不起…後面嘗試 自己的包名(也就是)Manifest中的package=”com.wx.wxTest”,組合爲com.wx.wxTest.wxapi.WXPayEntryActivity就可以回調了。微信你不能說清楚是自己的包名+wxapi.WXPayEntryActivity嗎?來個必須和你說的一致!

2.調用類Test_WxActivity

package com.wx.wxTest.activity;

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

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.widget.Toast;

public class Test_WxActivity extends Activity {
    private IWXAPI msgApi;
    private PayReq request;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // 可以在Application中註冊,也可以在這裏註冊
        // 如果在當前註冊即爲
        // msgApi = WXAPIFactory.createWXAPI(this, null);
        // msgApi.registerApp("wx666888abc8defg88");

        // 如果在Application註冊即爲,如果在當前註冊請註釋掉該代碼
        msgApi = WXAPIFactory.createWXAPI(this, "wx666888abc8defg88");

        new Thread(wxPay).start();
    }

    Handler mainHandler = new Handler() {
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
            case 100:

                Boolean mSendReq = msgApi.sendReq(request);
                Toast.makeText(Test_WxActivity.this, "調起微信sendReq=>" + mSendReq, Toast.LENGTH_SHORT).show();

                break;
            default:
                break;
            }

        };
    };

    Runnable wxPay = new Runnable() {

        @Override
        public void run() {
            // 這些值應該從服務器獲取,這裏只是爲了演示
            request = new PayReq();

            request.appId = "wx666888abc8defg88";
            request.partnerId = "1668866888";
            request.prepayId = "wx20170312211500a8y53pgb0klvyh2rmoyu";
            request.nonceStr = "VCG4w95nS5Ku7mCi";
            request.timeStamp = "1489324509";
            request.packageValue = "Sign=WXPay";
            request.sign = "19890233F9E17CC54E0B6285032AE419";

            mainHandler.sendEmptyMessage(100);
        }
    };

}

3.回調類WXPayEntryActivity

package com.wx.wxTest.wxapi;

import com.jhx.hyxs.R;
import com.tencent.mm.opensdk.constants.ConstantsAPI;
import com.tencent.mm.opensdk.modelbase.BaseReq;
import com.tencent.mm.opensdk.modelbase.BaseResp;
import com.tencent.mm.opensdk.openapi.IWXAPI;
import com.tencent.mm.opensdk.openapi.IWXAPIEventHandler;
import com.tencent.mm.opensdk.openapi.WXAPIFactory;

import android.app.Activity;
import android.content.Intent;
import android.graphics.Color;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

public class WXPayEntryActivity extends Activity implements IWXAPIEventHandler {

    private IWXAPI api;

    private ImageView ivImg;
    private TextView tvText, tvSub;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_wxentry);

        // 通過WXAPIFactory工廠,獲取IWXAPI的實例
        api = WXAPIFactory.createWXAPI(this, "wx666888abc8defg88", false);

        // 注意:
        // 第三方開發者如果使用透明界面來實現WXEntryActivity,需要判斷handleIntent的返回值,如果返回值爲false,則說明入參不合法未被SDK處理,應finish當前透明界面,避免外部通過傳遞非法參數的Intent導致停留在透明界面,引起用戶的疑惑
        try {
            api.handleIntent(getIntent(), this);
        } catch (Exception e) {
            e.printStackTrace();
        }

        initView();
    }

    private void initView() {
        ((TextView) findViewById(R.id.head_title)).setText("支付結果");
        ((ImageView) findViewById(R.id.head_back)).setVisibility(View.INVISIBLE);

        ivImg = (ImageView) findViewById(R.id.wxentry_img);
        tvText = (TextView) findViewById(R.id.wxentry_text);
        tvSub = (TextView) findViewById(R.id.wxentry_sub);

        tvSub.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                finish();
            }
        });

    }

    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);

        setIntent(intent);
        api.handleIntent(intent, this);
    }

    @Override
    public void onResp(BaseResp resp) {
        if (resp.getType() == ConstantsAPI.COMMAND_PAY_BY_WX) {
            if (resp.errCode == 0) {
                Toast.makeText(this, "支付成功", Toast.LENGTH_LONG).show();
                ivImg.setImageResource(R.drawable.pay_success);
                tvText.setText("支付成功");
                tvText.setTextColor(Color.parseColor("#39BC32"));
            } else {
                Toast.makeText(this, "支付失敗! (" + resp.errCode + ")", Toast.LENGTH_LONG).show();
                ivImg.setImageResource(R.drawable.pay_error);
                tvText.setText("支付失敗");
                tvText.setTextColor(Color.parseColor("#FFA800"));
            }
        }
    }

    @Override
    public void onReq(BaseReq arg0) {
        // TODO Auto-generated method stub

    }
}

六、常見集成時遇到的坑和解決的方法

1. 微信回調errCode = -1

官方文檔:https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=8_5
各種填坑後發現,這個可以相信官方文檔,雖然模糊不清,但是說的的確大部分可能也都爲“簽名錯誤、未註冊APPID、項目設置APPID不正確、註冊的APPID與設置的不匹配”,再加一條包名錯誤(雖然可能性很低)
解決方法:
1. 檢查在開發者平臺設置的應用簽名是否和當前APP簽名一致(Debug簽名或發佈版簽名)
2. 是否調用了註冊微信的代碼

final IWXAPI msgApi = WXAPIFactory.createWXAPI(this, null);
msgApi.registerApp("wx666888abc8defg88");

2.簽名錯誤

一定得按照appid、noncestr、package、partnerid、prepayid、timestamp、key的順序組合後取MD5。
例如:

appid=wx666888abc8defg88&noncestr=VCG4w95nS5Ku7mCi&package=Sign=WXPay&partnerid=1668866888&prepayid=wx20170312211500a8y53pgb0klvyh2rmoyu&timestamp=1489324509&key=UWMOIg1qJdHLZfEzAbm91BOBJG4oyC5C

3.回調錯誤

這個錯誤其實爲微信的坑,是的,我就是那麼直白!
解決方法:
別聽信微信說的net.sourceforge.simcpux.wxapi包路徑中實現WXPayEntryActivity類(包名或類名不一致會造成無法回調)。
而是使用你的包名+wxapi.WXPayEntryActivity,例如com.wx.wxTest.wxapi.WXPayEntryActivity

4.簽名

說一個不是錯誤但一定得注意的地方,簽名過程一定得在服務端完成!若你在客戶端(如:Android)上完成時,你的商戶KEY泄露可能很大(畢竟反調試Android並不是什麼難事),而商戶KEY的泄露將導致你的整個支付系統崩塌。

七、總結

微信支付的坑比支付寶支付的坑要多,大多數是因爲官方的文檔模糊不清,但是若按照我剛說的配置,基本沒有問題。如果問題歡迎留言,定知無不言。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章