事情是这样的我接了个单子就是写微信登陆和支付的
一开始我接单子的时候想之前都写过qq登录微信也应该差不多结果我错了首先遇到的坑有
sdk的配置他是把sdk放到了maven远程仓库然后直接compile但是我as抽风好了许久最终下载了官方的demo找到了jar直接copy到工程文件夹里了
我边写边吐槽写文档的开发者乱七八糟后来我看着官方demo改的终于成功了。于是就有了这篇文章
登录的class名称必须是WXEntryActivity并且继承activity实现IWXAPIEventHandler接口否则无法回调
然后剩下的直接上图了
1.png
2.png
3.png
4.png

微信登录和qq登录不同他是先把回调返回来的code进行发送请求获取access_token但access_token有效期只有两小时
我这里登录只需要用户的openid和nickname所以我没有获取有效期一个月的access_token详细见官网
这里也没有api判断是否登录全部得自己写所以写这个微信登陆的时候很恼火别看代码那么简单实现写起来真的是各种坑这是最坑的地方不仅class要WXEntryActivity还要在wxapi包下所以要新建一个wxapi放class不然也是没用滴这些官网也没没说明甚至连调用回调也没说都是百度捣鼓出来的几张图片就能完成

第一个坑:
回调不了
这里和上文章的登录回调有一点不同
也是要在wxapi包下名字必须是WXPayEntryActivity
配置清单的时候记得加上android:exported="true"否则会导致无法启动回调的activity

第二个坑订单会话id
获取订单会话id的时候需要传入很多花里胡哨的参数甚至支付的客户端IP都要提交其实没必要这么麻烦的
只要提交这几个参数即可
appid,mch_id,nonce_str,body,out_trade_no,total_fee,notify_url,trade_type,sign
分别是申请的微信开放平台的id,商户号,随机字符串32位,名称,订单号随机生成32位同一个订单号只能支付一次,金额单位不是元是分注意了,支付回调接口php写好可以防止app被本地破解,类型默认APP,签名使用算法进行签名

第三个坑算法签名
官网把集合也扯出来了虽然我也会,但有想过一些人吗?我还是直接百度了发现那么简单的又被复杂化了

1
2
3
4
5
6
7
8
9
10
private static String generateSign(SortedMap<String, String> parameters, String key) {
Set<Map.Entry<String, String>> iterator = parameters.entrySet();
StringBuilder stringA = new StringBuilder();
for (Map.Entry<String, String> map : iterator) {
stringA.append(map.getKey() + "=" + map.getValue());
stringA.append("&");
}
stringA.append("key=" + key);
return md5(stringA.toString().getBytes());
}

上面写好了传入的参数是一个sortmap为什么不是hashmap因为需要对参数的key进行ascii码排序所以用的SortMap,第二个是自己后台设置的key32位

搞好了签名还没完他需要传入的是xml内容不是http的表单内容
转xml我也帮你们写好了

1
2
3
4
5
6
7
8
9
10
11
12
13
private String toXml(SortedMap<String, String> params) {
StringBuilder sb = new StringBuilder();
sb.append("<xml>");
Set<Map.Entry<String, String>> entrySet = params.entrySet();
for (Map.Entry<String, String> map :
entrySet) {
sb.append("<" + map.getKey() + ">");
sb.append(map.getValue());
sb.append("</" + map.getKey() + ">");
}
sb.append("</xml>");
return sb.toString();
}

传入一个SortMap
到这里还没结束需要发包https://api.mch.weixin.qq.com/pay/unifiedorder提交刚刚生成的xml内容
返回来一个xml还需要解析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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) {
xml.put(nodeName, parser.nextText());
}
break;
case XmlPullParser.END_TAG:
break;
}
event = parser.next();
}
return xml;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}

获取prepay_id把多余的内容<![CDATA[,]]>替换成空才是最终的prepay_id
你以为获取到prepay_id就完美了吗不,发送支付请求的时候需要传
appid,noncestr,package,partnerid,prepayid,timestamp
分别是分别是申请的微信开放平台的id,随机字符串这里注意了这里的随机字符串要和第一次订单会话id生成的随机字符串一样,Sign=WXPay固定,商户号,会话id,10位时间戳

然后基本上就这些坑了把完整代码贴在下面

Payment

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package com.yarluong.tb.xm;
import android.app.Activity;
import android.os.Bundle;
import android.util.Xml;
import android.widget.Button;

import com.tencent.mm.opensdk.modelpay.PayReq;
import com.tencent.mm.opensdk.openapi.IWXAPI;
import com.tencent.mm.opensdk.openapi.WXAPIFactory;
import com.yarluong.tb.R;
import com.yarluong.tb.wxapi.utils.Http;

import org.xmlpull.v1.XmlPullParser;

import java.io.StringReader;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.DecimalFormat;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;

public class Payment extends Activity {
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
private Button btn_pay;
private IWXAPI iwxapi;
private final String APP_ID = "自己的id";
private final String MCH_ID = "自己的商户id";
private final String KEY = "自己设置的key用于获取签名";

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.xm_payment);
this.iwxapi = WXAPIFactory.createWXAPI(this, APP_ID);
this.iwxapi.registerApp(this.APP_ID);
this.btn_pay = findViewById(R.id.btn_pay);

this.btn_pay.setOnClickListener((e) -> {
pay(0.01);
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
}

private void pay(double rmb) {
new Thread(() -> {
String randomString = generateRandomString(32);
String respone = Http.httpPost("https://api.mch.weixin.qq.com/pay/unifiedorder", getData(rmb,randomString));
String prepayid = decodeXml(respone).get("prepay_id").replace("<![CDATA[", "").replace("]]>", "");
String timeStamp = ((int) (System.currentTimeMillis() / 1000)) + "";
PayReq request = new PayReq();
request.appId = this.APP_ID;
request.partnerId = this.MCH_ID;
request.prepayId = prepayid;
request.packageValue = "Sign=WXPay";
request.nonceStr = randomString;
request.timeStamp = timeStamp;

SortedMap<String, String> stringSortedMap = new TreeMap<>();
stringSortedMap.put("appid", this.APP_ID);
stringSortedMap.put("noncestr", randomString);
stringSortedMap.put("package", "Sign=WXPay");
stringSortedMap.put("partnerid", this.MCH_ID);
stringSortedMap.put("prepayid", prepayid);
stringSortedMap.put("timestamp", timeStamp);
request.sign = generateSign(stringSortedMap, this.KEY);
iwxapi.sendReq(request);

}).start();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
private String toXml(SortedMap<String, String> params) {
StringBuilder sb = new StringBuilder();
sb.append("<xml>");
Set<Map.Entry<String, String>> entrySet = params.entrySet();
for (Map.Entry<String, String> map :
entrySet) {
sb.append("<" + map.getKey() + ">");
sb.append(map.getValue());
sb.append("</" + map.getKey() + ">");
}
sb.append("</xml>");
return sb.toString();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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) {
xml.put(nodeName, parser.nextText());
}
break;
case XmlPullParser.END_TAG:
break;
}
event = parser.next();
}
return xml;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
private String getData(double rmb,String nonce) {
SortedMap<String, String> stringSortedMap = new TreeMap<>();
stringSortedMap.put("appid", this.APP_ID);
stringSortedMap.put("mch_id", this.MCH_ID);
stringSortedMap.put("nonce_str", nonce);
stringSortedMap.put("sign_type", "MD5");
stringSortedMap.put("body", "xxx-会员购买");
stringSortedMap.put("out_trade_no", md5(generateRandomString(32).getBytes()).toLowerCase());
stringSortedMap.put("total_fee", changeY2F(rmb) + "");
stringSortedMap.put("notify_url", "http://www.weixin.qq.com/wxpay/pay.php");
stringSortedMap.put("trade_type", "APP");
stringSortedMap.put("sign", generateSign(stringSortedMap, this.KEY));
return toXml(stringSortedMap);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public static int changeY2F(double price) {
DecimalFormat df = new DecimalFormat("#.00");
price = Double.valueOf(df.format(price));
int money = (int) (price * 100);
return money;
}

private String generateRandomString(int len) {
String str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
Random random = new Random();
StringBuffer sb = new StringBuffer();
for (int i = 0; i < len; i++) {
int number = random.nextInt(str.length());
sb.append(str.charAt(number));
}
return sb.toString();
}

private static String generateSign(SortedMap<String, String> parameters, String key) {
Set<Map.Entry<String, String>> iterator = parameters.entrySet();
StringBuilder stringA = new StringBuilder();
for (Map.Entry<String, String> map : iterator) {
stringA.append(map.getKey() + "=" + map.getValue());
stringA.append("&");
}
stringA.append("key=" + key);
return md5(stringA.toString().getBytes());
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
private static String md5(byte[] src) {
try {
MessageDigest messageDigest = MessageDigest.getInstance("MD5");
messageDigest.update(src);
byte[] byt = messageDigest.digest();
return bytesToHex(byt).toUpperCase();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return null;
}

public static String bytesToHex(byte[] src) {
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < src.length; i++) {
stringBuilder.append(String.format("%02x", src[i] & 0xff));
}
return stringBuilder.toString();
}

}

WXPayEntryActivity

1
2
3
4
5
6
7
8
9
10
11
12
13
package com.yarluong.tb.wxapi;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.widget.Toast;

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;

public class WXPayEntryActivity extends Activity implements IWXAPIEventHandler {
1
2
3
4
5
6
7
8
9
private IWXAPI iwxapi;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
iwxapi = WXAPIFactory.createWXAPI(this, "自己的id");
iwxapi.registerApp("自己的id");
iwxapi.handleIntent(getIntent(), this);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Override
public void onReq(BaseReq baseReq) {

}

@Override
public void onResp(BaseResp baseResp) {
Log.d("WXPayEntryActivity", "onPayFinish, errCode = " + baseResp.errCode);
switch (baseResp.errCode) {
case BaseResp.ErrCode.ERR_OK:
Toast.makeText(this,"支付成功",Toast.LENGTH_LONG).show();
this.finish();
break;
//用户取消支付
case BaseResp.ErrCode.ERR_USER_CANCEL:
Toast.makeText(this,"支付失败",Toast.LENGTH_LONG).show();
this.finish();
break;

}
}

}

Androidmanifest.xml

1
2
3
4
<activity
android:name=".wxapi.WXPayEntryActivity"
android:exported="true"
android:theme="@android:style/Theme.Translucent.NoTitleBar" />