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是指這個,一般不會有人寫錯吧?(不好意思我是二班的)
要仔細檢查!有很多地方用到了這個IP:初始化 IWXAPI 用到了,分別在調起支付的時候,和接收支付回包的時候,還有,在 manifest 中聲明 activity 的時候用到。
簽名、包名問題
簽名在后臺配置的,位置在這里:
不管你是 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;
獲取到。