Android微信支付功能集成【全攻略】
遵循:BY-SA
署名-相同方式共享 4.0协议
作者:谭东
时间:2016年10月28日
环境:Windows 7
Android版微信支付官方文档和Demo问题很多,官方也没有及时更新和细化开发集成文档。
这里分享我集成Android客户端微信支付的思路和部分代码。希望对大家有帮助。
遇到的问题无非以下几种:
1、提示签名不对;
2、打包签名后的APK无法调起微信支付客户端,直接返回回调页;
3、不支持中文的Body;
4、支付的钱倍数不对,因为微信的单位是分;
... ...
首先,本文针对的是最新版微信Android支付SDK3.1.1写的,大家可以放心使用。
首先,把WXPayEntryActivity.java复制到我们的包名下的.wxapi目录下,要一致。
WXPayEntryActivity.java官方Demo里有,我这里也复制一份我的,仅供参考。
public class WXPayEntryActivity extends BaseActivity implements IWXAPIEventHandler {private IWXAPI api;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);api = WXAPIFactory.createWXAPI(this, Conf.APP_ID);api.handleIntent(getIntent(), this);getSupportActionBar().setIcon(R.drawable.logo_gray);getSupportActionBar().setTitle("购买积分");getSupportActionBar().setDisplayHomeAsUpEnabled(true);Drawable dw = getResources().getDrawable(R.drawable.color_bg);getSupportActionBar().setBackgroundDrawable(dw);}@Overridepublic boolean onOptionsItemSelected(MenuItem item) {if (item.getItemId() == android.R.id.home) {this.finish();}return super.onOptionsItemSelected(item);}@Overrideprotected void onNewIntent(Intent intent) {super.onNewIntent(intent);setIntent(intent);api.handleIntent(intent, this);}@Overridepublic void onReq(BaseReq baseReq) {}@Overridepublic void onResp(BaseResp baseResp) {if (baseResp.getType() == ConstantsAPI.COMMAND_PAY_BY_WX) {Log.d("info", "onPayFinish,errCode=" + baseResp.errCode);if (baseResp.errCode == 0) {ToastUtil.showToast(this, "支付成功");Intent intent = new Intent("ACTION_PAY");sendBroadcast(intent);this.finish();} else if (baseResp.errCode == -1) {ToastUtil.showToast(this, "配置错误");this.finish();} else if (baseResp.errCode == -2) {ToastUtil.showToast(this, "用户取消");this.finish();}} else {ToastUtil.showToast(this, baseResp.errStr);}}
}
这里说一下Android微信支付流程,这里本地客户端都处理了所有的流程。
客户端把所有需要的信息拼接为XML后,统一下单,发送给微信服务器API请求,获取订单id,也就是prepayId。获取到这个prepayId后,我们再把必要的数据字段sign签名MD5后,调起微信支付客户端就可以了。比支付宝麻烦很多。
1、在程序的启动页,例如欢迎页就可以注册微信API的ID了。
IWXAPI msgApi = WXAPIFactory.createWXAPI(this, Conf.APP_ID);msgApi.registerApp(Conf.APP_ID);
2、接下来处理微信POST请求的必要参数的拼接和加密等处理。写PayActivity.java
统一下单获取prepayId的接口地址:https://api.mch.weixin.qq.com/pay/unifiedorder
微信文档地址:https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_1
把必填的参数处理下:
nonce_str:随机字符串,不长于32位。
out_trade_no:商户系统内部的订单号,32个字符内、可包含字母。
spbill_create_ip:用户端实际ip,16位,可写固定值。
notify_url:暂时写成固定的,http://www.weixin.qq.com/wxpay/pay.php。
最麻烦的,sign:32位签名,参照签名生成算法。
以上这些算法我都会写成工具类,供大家参考。
签名算法:
附上整个支付代码:
PayActivity.java里关于支付的代码:
private IWXAPI api;private String stringA;private String noneString;private String out_trade_no;private String sign;private String prepayId;private String ip;private String body = "商品-积分充值";private static final String order_url = "https://api.mch.weixin.qq.com/pay/unifiedorder";//POST请求统一下单接口地址private String notify_url = "http://www.weixin.qq.com/wxpay/pay.php";private String entity;//XML形式的post请求实体private void payWeixin(int money) {api = WXAPIFactory.createWXAPI(this, Conf.APP_ID);try {body = new String(body.getBytes(), "utf-8");} catch (UnsupportedEncodingException e) {e.printStackTrace();}noneString = Utils.createRandomString(true, 32);//32位随机字符串out_trade_no = Utils.createRandomString(true, 20) + Utils.timeStamp();//30位内部随机订单号ip = "123.123.123.123";stringA = "appid=" + Conf.APP_ID + "&body=" + body + "&mch_id=" + Conf.MCH_ID + "&nonce_str=" + noneString + "¬ify_url=" + notify_url + "&out_trade_no=" + out_trade_no + "&spbill_create_ip=" + ip + "&" +"total_fee=" + money + "&trade_type=APP";String stringSignTemp = stringA + "&key=" + Conf.KEY;sign = Utils.getMD5(stringSignTemp).toUpperCase(Locale.getDefault());entity = "" + Conf.APP_ID + " " + Conf.MCH_ID + " " + noneString + " " + sign +" " + body + "" + out_trade_no + " " + money +" " + ip + " " + notify_url + " APP ";try {entity = new String(entity.getBytes(), "ISO8859-1");//想要支持中文的Boby,那就要把XML转码为ISO8859-1即可} catch (UnsupportedEncodingException e) {e.printStackTrace();}new Thread(new Runnable() {@Overridepublic void run() {try {byte[] buf = Utils.httpPost(order_url, entity);Message message = new Message();message.what = 0;message.obj = buf;handler.sendMessage(message);} catch (Exception e) {Log.e("info", "异常:" + e.getMessage());}}}).start();}Handler handler = new Handler() {@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);switch (msg.what) {case 0:byte[] buf = (byte[]) msg.obj;if (buf != null && buf.length > 0) {String content = new String(buf);OrderResult orderResult = Tools.parseXml(new ByteArrayInputStream(content.getBytes()));if (!TextUtils.equals(orderResult.getReturnCode(), "SUCCESS")) {Toast.makeText(PayActivity.this, orderResult.getReturnMsg(), Toast.LENGTH_SHORT).show();return;}if (!TextUtils.equals(orderResult.getResultCode(), "SUCCESS")) {Toast.makeText(PayActivity.this, orderResult.getErrorDesc(), Toast.LENGTH_SHORT).show();return;}PayReq req = new PayReq();req.appId = Conf.APP_ID;req.partnerId = Conf.MCH_ID;req.prepayId = orderResult.getPrepayId();req.packageValue = "Sign=WXPay";req.nonceStr = noneString;String timeStamp = Utils.timeStamp();req.timeStamp = timeStamp;req.sign = Utils.getMD5("appid=" + Conf.APP_ID + "&noncestr=" + noneString + "&package=Sign=WXPay" +"&partnerid=" + Conf.MCH_ID + "&prepayid=" + orderResult.getPrepayId() + "×tamp=" + timeStamp + "&key=" + Conf.KEY).toUpperCase(Locale.getDefault());api.sendReq(req);} else {Log.d("PAY_GET", "服务器请求错误");Toast.makeText(PayActivity.this, "服务器请求错误", Toast.LENGTH_SHORT).show();}break;}}};
Utils工具类代码:
public class Utils {/*** 产生随机字符串** @param numberFlag 是否允许有字母* @param length 随机字符串长度* @return 随机字符串*/public static String createRandomString(boolean numberFlag, int length) {String retStr = "";String strTable = numberFlag ? "1234567890" : "1234567890abcdefghijkmnpqrstuvwxyz";int len = strTable.length();boolean bDone = true;do {retStr = "";int count = 0;for (int i = 0; i < length; i++) {double dblR = Math.random() * len;int intR = (int) Math.floor(dblR);char c = strTable.charAt(intR);if (('0' <= c) && (c <= '9')) {count++;}retStr += strTable.charAt(intR);}if (count >= 2) {bDone = false;}} while (bDone);return retStr;}/*** 获取MD5加密后的字符串** @param val 待加密字符串* @return 加密后字符串*/public static String getMD5(String val) {byte[] m = new byte[0];try {MessageDigest md5 = MessageDigest.getInstance("MD5");md5.update(val.getBytes());m = md5.digest();} catch (NoSuchAlgorithmException e) {e.printStackTrace();}return getString(m);}private static String getString(byte[] b) {StringBuffer sb = new StringBuffer();for (int i = 0; i < b.length; i++) {sb.append(b[i]);}return sb.toString();}/*** 获取10位长度的时间戳** @return 10位时间戳*/public static String timeStamp() {String time = String.valueOf(System.currentTimeMillis());return time.substring(0, 10);}public static byte[] httpGet(final String url) {if (url == null || url.length() == 0) {return null;}HttpClient httpClient = getNewHttpClient();HttpGet httpGet = new HttpGet(url);try {HttpResponse resp = httpClient.execute(httpGet);if (resp.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {return null;}return EntityUtils.toByteArray(resp.getEntity());} catch (Exception e) {e.printStackTrace();return null;}}public static byte[] httpPost(String url, String entity) {if (url == null || url.length() == 0) {return null;}HttpClient httpClient = getNewHttpClient();HttpPost httpPost = new HttpPost(url);try {httpPost.setEntity(new StringEntity(entity));httpPost.setHeader("Accept", "application/json");httpPost.setHeader("Content-type", "application/json");HttpResponse resp = httpClient.execute(httpPost);if (resp.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {return null;}return EntityUtils.toByteArray(resp.getEntity());} catch (Exception e) {e.printStackTrace();return null;}}private static HttpClient getNewHttpClient() {try {KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());trustStore.load(null, null);SSLSocketFactory sf = new SSLSocketFactoryEx(trustStore);sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);HttpParams params = new BasicHttpParams();HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);HttpProtocolParams.setContentCharset(params, HTTP.UTF_8);SchemeRegistry registry = new SchemeRegistry();registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));registry.register(new Scheme("https", sf, 443));ClientConnectionManager ccm = new ThreadSafeClientConnManager(params, registry);return new DefaultHttpClient(ccm, params);} catch (Exception e) {return new DefaultHttpClient();}}public static OrderResult parseXml(InputStream is) {XmlPullParser parser = Xml.newPullParser();OrderResult orderResult = null;try {parser.setInput(is, "UTF-8");int type = parser.getEventType();while (type != XmlPullParser.END_DOCUMENT) {switch (type) {case XmlPullParser.START_DOCUMENT:break;case XmlPullParser.START_TAG:if (parser.getName().equals("xml")) {orderResult = new OrderResult();} else if (parser.getName().equals("return_code")) {orderResult.setReturnCode(parser.nextText());} else if (parser.getName().equals("return_msg")) {orderResult.setReturnMsg(parser.nextText());} else if (parser.getName().equals("result_code")) {orderResult.setResultCode(parser.nextText());} else if (parser.getName().equals("err_code_des")) {orderResult.setErrorDesc(parser.nextText());} else if (parser.getName().equals("prepay_id")) {orderResult.setPrepayId(parser.nextText());} else if (parser.getName().equals("sign")) {orderResult.setSign(parser.nextText());}break;case XmlPullParser.END_TAG:break;}type = parser.next();}} catch (XmlPullParserException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}return orderResult;}
}
这样整个微信Android支付集成就完毕了。
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
