解決微信支付失敗-1的坑

1. 基本使用

最近公司需要用到微信支付,后臺說已經搞好了所有的簽名,好吧,我直接調用微信的接口就好了:

  • 接入依賴
//微信支付
implementation 'com.tencent.mm.opensdk:wechat-sdk-android-with-mta:+'
  • 使用:
//微信支付調起來
  IWXAPI api =WXAPIFactory.createWXAPI(getContext(),null);
  api.registerApp(HttpsContract.Wx_Pay_App_Id);
  PayReq req = new PayReq();//PayReq就是訂單信息對象
  //給req對象賦值
  req.appId = HttpsContract.Wx_Pay_App_Id;//APP-ID
  req.partnerId = wxPayBean.payWxBean.mchId;//    商戶號
  req.prepayId = wxPayBean.payWxBean.prepayId;//  預付款ID
  req.nonceStr = wxPayBean.payWxBean.nonceStr;//隨機數
  req.timeStamp = wxPayBean.payWxBean.timestamp;//時間戳
  req.packageValue = wxPayBean.payWxBean.signType;//固定值Sign=WXPay
  req.sign = wxPayBean.payWxBean.paySign;//簽名
  api.sendReq(req);//將訂單信息對象發送給微信服務器,即發送支付請求

上面其中,除了 HttpsContract.Wx_Pay_App_Id 外,其他所有的都是服務器接口返回給我的,好了,確實挺簡單的。

  • 接收微信支付的結果返回,WXPayEntryActivity ,名稱不能錯,一定要這個,寫死,而且要放在:<你的包名>.wxapi.下!
public class WXPayEntryActivity extends BaseActivity implements IWXAPIEventHandler {
    private IWXAPI api;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_wxpay_entry);
        api = WXAPIFactory.createWXAPI(this, HttpsContract.Wx_Pay_App_Id);
        api.handleIntent(getIntent(), this);
    }

    @Override
    public void onReq(BaseReq baseReq) {
    }

    @Override
    public void onResp(BaseResp baseResp) {
        if (baseResp.getType() == ConstantsAPI.COMMAND_PAY_BY_WX) {
            Log.i("WXPayEntryActivity",
                    "onPayFinish, errCode = " + baseResp.errCode
                            + ", errStr: " + baseResp.errStr
                            + ", openId: " + baseResp.openId
                            + ", transaction: " + baseResp.transaction);
            int errCord = baseResp.errCode;
            if (errCord == 0) {
                toast("支付成功!");
            } else {
                toast("支付失敗");
            }
            finish();
        }
    }

    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        setIntent(intent);
        api.handleIntent(intent, this);
    }
}
  • AndroidManifest文件中注冊這個Activity:
 <!--微信支付 -->
<activity
    android:name=".wxapi.WXPayEntryActivity"
    android:exported="true"
    android:launchMode="singleTop">
    <intent-filter>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <data android:scheme="${WX_Pay_AppId}" />
    </intent-filter>
</activity>

2. 調起失敗,-1

說實話,這個問題真的很惡心,微信開放平臺提供的文檔在關于-1這個問題的描述(可能的原因:簽名錯誤、未注冊APPID、項目設置APPID不正確、注冊的APPID與設置的不匹配、其他異常等)。一開始我看到這個說明的時候,我的內心是崩潰的,這說了跟沒說有什么區別。

3. 排查

還是一步一步來仔細排查吧!

APP-ID不對

APP-ID是指這個,一般不會有人寫錯吧?(不好意思我是二班的)

Snipaste_2019-04-01_18-38-07.png

要仔細檢查!有很多地方用到了這個IP:初始化 IWXAPI 用到了,分別在調起支付的時候,和接收支付回包的時候,還有,在 manifest 中聲明 activity 的時候用到。

簽名、包名問題

簽名在后臺配置的,位置在這里:

Snipaste_2019-04-01_18-41-07.png

不管你是 debug / release 都可以,只要是簽名和后臺一樣就可以了,我開發的時候,是把 debug 和 release 的簽名都用同一個,debug 的時候就可以調起來了。還有這里的包名也要仔細核對!

使用微信分享接口來驗證上述的信息是否有誤

因為一般申請了支付的,都有申請了微信的分享接口,而分享接口,也是需要后臺配置好簽名,包名,所有我們可以先調用一下微信分享(不需要額外引用包):

final IWXAPI api =WXAPIFactory.createWXAPI(getContext(),null);
api.registerApp(HttpsContract.Wx_Pay_App_Id);
WXWebpageObject webpage = new WXWebpageObject();
webpage.webpageUrl = "http://www.baidu.com";
WXMediaMessage msg = new WXMediaMessage(webpage);
msg.title = "這里填寫標題";
msg.description = "這里填寫內容";
//這里替換一張自己工程里的圖片資源
Bitmap thumb = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher);
msg.setThumbImage(thumb);
SendMessageToWX.Req req = new SendMessageToWX.Req();
req.transaction = String.valueOf(System.currentTimeMillis());
req.message = msg;
req.scene = SendMessageToWX.Req.WXSceneSession;
api.sendReq(req);

如果你在這里都調不起來分享,那就好好仔細檢查信息吧!

服務器返回的簽名不對

當時我就是被這里給坑了。首先,要先清楚大致的調用邏輯:

  • 1)客戶端向自己服務器調用下單接口
  • 2)下單接口由自己服務器去調用微信服務器,注意,這里會返回一個簽名!有人直接把這個簽名返回給我了,擦,不對的哦!客戶端需要一個二次簽名!
  • 3)在自己服務器中做二次簽名!關于怎么簽名,你讓后臺小哥去百度吧
  • 4)或者,服務器不做簽名,也好,我們客戶端自己做!

如果你是服務器中做二次簽名,我們在這里就要開始懷疑服務器的二次簽名有誤,所有我們在代碼中先,自己來寫簽名!(我們不相信別人,只相信自己的代碼!),簽名類我都幫你寫好了:

public class WXPayHelper {

    public IWXAPI api;
    private PayReq req;

    private String appId;
    private String prePayId;
    private String partNerId;
    private String partNerSecret;//商戶密鑰

    public WXPayHelper(Context context, String appId, String prePayId, String partNerId, String partNerSecret) {
        this.appId = appId;
        this.prePayId = prePayId;
        this.partNerId = partNerId;
        this.partNerSecret = partNerSecret;
        api = WXAPIFactory.createWXAPI(context, this.appId,false);
    }

    /**
     * 向微信服務器發起的支付請求
     */
    public void pay() {
        req = new PayReq();
        req.appId = appId;//APP-ID
        req.partnerId = partNerId;//    商戶號
        req.prepayId = prePayId;//  預付款ID
        req.nonceStr = String.valueOf(System.nanoTime());                   //隨機數
        req.timeStamp = String.valueOf(System.currentTimeMillis()*0.001);   //時間戳
        req.packageValue = "Sign=WXPay";//固定值Sign=WXPay
        req.sign = getSign();//簽名
        api.registerApp(BuildConfig.WX_Pay_AppId);
        api.sendReq(req);
    }

    @NonNull
    private String getSign() {
        Map<String, String> map = new HashMap<>();
        map.put("appid", req.appId);
        map.put("partnerid", req.partnerId);
        map.put("prepayid", req.prepayId);
        map.put("package", req.packageValue);
        map.put("noncestr", req.nonceStr);
        map.put("timestamp", req.timeStamp);

        ArrayList<String> sortList = new ArrayList<>();
        sortList.add("appid");
        sortList.add("partnerid");
        sortList.add("prepayid");
        sortList.add("package");
        sortList.add("noncestr");
        sortList.add("timestamp");
        Collections.sort(sortList);

        StringBuilder md5 = new StringBuilder();
        int size = sortList.size();
        for (int k = 0; k < size; k++) {
            if (k == 0) {
                md5.append(sortList.get(k)).append("=").append(map.get(sortList.get(k)));
            } else {
                md5.append("&").append(sortList.get(k)).append("=").append(map.get(sortList.get(k)));
            }
        }
        String stringSignTemp = md5+"&key="+partNerSecret;

        return Md5(stringSignTemp).toUpperCase();
    }

    private String Md5(String s) {
        char[] hexDigits = new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
        try {
            byte[] e = s.getBytes(StandardCharsets.UTF_8);
            MessageDigest mdTemp = MessageDigest.getInstance("MD5");
            mdTemp.update(e);
            byte[] md = mdTemp.digest();
            int j = md.length;
            char[] str = new char[j * 2];
            int k = 0;
            for (byte byte0 : md) {
                str[k++] = hexDigits[byte0 >>> 4 & 15];
                str[k++] = hexDigits[byte0 & 15];
            }
            return new String(str);
        } catch (Exception var10) {
            return null;
        }
    }
}

直接使用:

WXPayHelper wxPayHelper = new WXPayHelper(getContext(),
        wxPayBean.payWxBean.appId,
        wxPayBean.payWxBean.prepayId,
        wxPayBean.payWxBean.mchId,
        "這里是商戶號的密鑰!");
wxPayHelper.pay();

如果,我們自己簽名可以調起來,那么就是服務器的二次簽名有問題,你可以拿刀問候一下后臺小哥了。如果不是,那就好好檢查信息!

在這里我們看到自己簽名會多用到一個商戶號密鑰,這很重要的,所以一般是放在服務器中。

參考微信支付官方Demo?

你參考我的還比較好。

4. 統一APP-ID

APP-ID在很多地方都用到了,AndroidManifest中,Java代碼中,gradle 中,那么怎么統一起來呢?也許可以這樣:

  • AndroidManifest,${WX_Pay_AppId} ,在Gradle中使用占坑的形式聲明
 <!--微信支付 -->
<activity
    android:name=".wxapi.WXPayEntryActivity"
    android:exported="true"
    android:launchMode="singleTop">
    <intent-filter>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <data android:scheme="${WX_Pay_AppId}" />
    </intent-filter>
</activity>
  • Gradle:
apply plugin: 'com.android.application'

def WX_Pay_AppId = "wx12192u012091"

android {
    //...
    defaultConfig {
        //...
        manifestPlaceholders = [
                //微信支付APPID
                WX_Pay_AppId : WX_Pay_AppId,
        ]
        //...
    }
    //...
    buildTypes {
        debug {
            //...
            buildConfigField "String", "WX_Pay_AppId", "\"$WX_Pay_AppId\""
            //...
        }
        release {
            //...
            buildConfigField "String", "WX_Pay_AppId", "\"$WX_Pay_AppId\""
            //...
        }
    }
}

在最上方聲明好WX-APP-ID,然后,繼續聲明好 manifestPlaceholders ,映射到 Manifest 文件,最后,在 buildTypes 中,聲明:buildConfigField ,做好映射到JAVA代碼,在Java代碼就可以:String Wx_Pay_App_Id = BuildConfig.WX_Pay_AppId; 獲取到。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容