最近项目里面需要支付功能,boss一致决定用微信支付,所以在网上查了很多资料,说的不全,完了就找以前的同事指教。算是成功集成上去了。在这里做个总结记录。
1、在APP上集成微信支付,首先当然是当官网上去注册并获取到支付功能。这些不涉及到开发,官网上说的很详细,这里就不多做文章。获取到这些能力了就为开发提供了条件了。开发中会用到的就是平台给的APPID、APPsercet、以及商户平台上设置的APP_key。
2、具备了支付能力等前提条件之后,就是开发过程了。代码里面怎么才能吊起支付了,参照官网上的DEMO自己也做了一些总结和各方大神的指教。分为了下面几个步奏。
(1)、首先当然是将sdk配置进工程环境中,官网中下载Android端SDK,解压后将libmmsdk.jar导入工程,然后将DEMO中Constant.java(这里是参照官方demo的样式写的,当然也可以采用其他方式)、MD5.java、Util.java放入工程(我这里用到了这些),这些先决条件有了之后就可以下一步写代码操作了。
(2)、生成订单信息
生成订单信息采用了如下方法生成,生成订单信息需要签名文件,所以里面包含了生成签名。微信要求所有请求采用XML参数形式,所有生产订单信息之后又需要转换成xml。订单信息需要的请求参数可以到官网上去对照,这里只加入了一些必要的参数。
生成订单信息方法:
-
-
private String genProductArgs() {
-
StringBuffer xml = new StringBuffer();
-
try {
-
String nonceStr = genNonceStr();
-
-
xml.append("</xml>");
-
List<NameValuePair> packageParams = new LinkedList<NameValuePair>();
-
-
packageParams.add(new BasicNameValuePair("appid", Constants.APP_ID));
-
-
packageParams.add(new BasicNameValuePair("body", "单价:" + singlePrice + " x " + payment_num.getText().toString() + "份"));
-
-
packageParams.add(new BasicNameValuePair("mch_id", Constants.MCH_ID));
-
-
packageParams.add(new BasicNameValuePair("nonce_str", nonceStr));
-
-
packageParams.add(new BasicNameValuePair("notify_url","http://www.weixin.qq.com/wxpay/pay.php")); //通知地址
-
-
packageParams.add(new BasicNameValuePair("out_trade_no",getTrade()));
-
-
packageParams.add(new BasicNameValuePair("spbill_create_ip",getLocalHostIp()));
-
-
-
double price = Double.parseDouble(singlePrice) * 100 * n;
-
int priceInt = (int) price;
-
packageParams.add(new BasicNameValuePair("total_fee", priceInt+""));
-
-
packageParams.add(new BasicNameValuePair("trade_type", "APP"));
-
-
String sign = genAppSign(packageParams);
-
packageParams.add(new BasicNameValuePair("sign", sign));
-
-
String xmlstring = parseNodeToXML(packageParams);
-
-
return xmlstring;
-
} catch (Exception e) {
-
e.printStackTrace();
-
return null;
-
}
-
}
构造这个xml请求参数采用的是httpclient生成的,所以引入了某些包。也可以采用其他方式生成支付订单,只要最后的形式与官网中的形式相同即可。
里面涉及到某些参数的生成,这里列出的是我们项目里面的业务逻辑,当然不同项目可定是不同的。
-
-
private String getTrade(){
-
long nowTime = System.currentTimeMillis();
-
SimpleDateFormat format = new SimpleDateFormat("yyMMddHHmmss");
-
return format.format(new Date(nowTime));
-
}
-
-
-
-
-
StringBuilder sb = new StringBuilder();
-
private String genAppSign(List<NameValuePair> params) {
-
StringBuilder sb = new StringBuilder();
-
for (int i = 0; i < params.size(); i++) {
-
sb.append(params.get(i).getName());
-
sb.append(‘=‘);
-
sb.append(params.get(i).getValue());
-
sb.append(‘&‘);
-
}
-
sb.append("key=");
-
sb.append(Constants.API_KEY);
-
this.sb.append("sign str\n" + sb.toString() + "\n\n");
-
String appSign = MD5.getMessageDigest(sb.toString().getBytes()).toUpperCase();
-
return appSign;
-
}
-
-
-
private String genNonceStr() {
-
Random random = new Random();
-
return MD5.getMessageDigest(String.valueOf(random.nextInt(10000)).getBytes());
-
}
-
-
-
-
-
-
-
public String parseNodeToXML(List<NameValuePair> treeNodes) {
-
StringBuffer xmlnodes = new StringBuffer();
-
if (treeNodes != null && treeNodes.size() > 0) {
-
xmlnodes.append("<xml>");
-
for (int i = 0; i < treeNodes.size(); i++) {
-
NameValuePair node = treeNodes.get(i);
-
xmlnodes.append("<"+node.getName()+">").append(node.getValue()).append("</"+node.getName()+">");
-
}
-
xmlnodes.append("</xml>");
-
}
-
-
String xml = xmlnodes.toString();
-
try {
-
xml = new String(xml.toString().getBytes(), "ISO8859-1");
-
return xml;
-
} catch (UnsupportedEncodingException e) {
-
e.printStackTrace();
-
return null;
-
}
-
}
-
-
-
-
public String getLocalHostIp() {
-
String ipaddress = "";
-
try {
-
Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces();
-
-
while (en.hasMoreElements()) {
-
NetworkInterface nif = en.nextElement();
-
Enumeration<InetAddress> inet = nif.getInetAddresses();
-
-
while (inet.hasMoreElements()) {
-
InetAddress ip = inet.nextElement();
-
if (!ip.isLoopbackAddress() && InetAddressUtils.isIPv4Address(ip.getHostAddress())) {
-
return ip.getHostAddress();
-
}
-
}
-
}
-
}
-
catch (SocketException e) {
-
Log.e("feige", "获取本地ip地址失败");
-
e.printStackTrace();
-
}
-
return ipaddress;
-
}
(3)、访问微信后台指定接口,获取perpay_id。
可以说前面的都是为了获取这个perpay_id做准备的,官网上给出的指定接口是“https://api.mch.weixin.qq.com/pay/unifiedorder” 请求采用官网demo中util提供的请求方式
这里采用异步处理方式,当请求指定接口得到perapy_id之后直接吊起支付的方式。
-
//调用支付获取id
-
public void gotoWechat() {
-
new AsyncTask() {
-
@Override
-
protected Object doInBackground(Object[] objects) { //获取Prepay_id
-
String url = String.format("https://api.mch.weixin.qq.com/pay/unifiedorder");
-
String entity = genProductArgs(); //获取订单信息
-
byte[] buf = Util.httpPost(url, entity);
-
String content = new String(buf); //请求成功返回的信息
-
//Log.e("orion", content);
-
try {
-
xmlParseTest(content); //解析返回的信息
-
} catch (IOException e) {
-
e.printStackTrace();
-
} catch (XmlPullParserException e) {
-
e.printStackTrace();
-
}
-
return null;
-
}
-
-
@Override
-
protected void onPostExecute(Object o) {
-
super.onPostExecute(o);
-
wechatPay();
-
}
-
}.execute();
-
}
请求成功返回的数据当然也是xml格式的,需要解析并从中取到perpay_id(返回的结果不止perpay_id,包括其他信息,个人感觉吊起支付只需要perpay_id就行了)。这里也是参考demo中的方式将获取到的信息通过xml解析到
WeixinParentId对象当中。
-
-
-
-
-
- WeixinParentId book = null;
- public void xmlParseTest(String str) throws IOException, XmlPullParserException {
- XmlPullParser pullParser = Xml.newPullParser();
-
- ByteArrayInputStream is = new ByteArrayInputStream(str.getBytes("UTF-8"));
- ArrayList<WeixinParentId> books = null ;
-
- pullParser.setInput(is, "UTF-8");
- int type = pullParser.getEventType();
- while (type != pullParser.END_DOCUMENT) {
- switch(type){
- case XmlPullParser.START_DOCUMENT:
- books = new ArrayList<WeixinParentId>();
- break;
- case XmlPullParser.START_TAG:
- if (pullParser.getName().equals("xml")) {
- book = new WeixinParentId();
- }else if (pullParser.getName().equals("return_msg")) {
- type = pullParser.next();
- book.setReturn_msg(pullParser.getText());
- }else if (pullParser.getName().equals("appid")) {
- type = pullParser.next();
- book.setAppid(pullParser.getText());
- }else if (pullParser.getName().equals("prepay_id")) {
- type = pullParser.next();
- book.setPrepay_id(pullParser.getText());
- }
-
- break;
- case XmlPullParser.END_TAG:
- if (pullParser.getName().equals("book")) {
- books.add(book);
- book = null;
- }
- break;
- }
- type = pullParser.next();
-
- }
-
- }
成功执行这不之后,book对象中perpay_id已经被赋值。只需在异步中取出使用即可。
-
- protected void wechatPay() {
- PayReq req = new PayReq();
- req.appId = Constants.APP_ID;
- req.partnerId = Constants.MCH_ID;
- req.prepayId = book.getPrepay_id();
- req.packageValue = "Sign=WXPay";
- req.nonceStr = genNonceStr();
- req.timeStamp = String.valueOf(genTimeStamp());
-
- List<NameValuePair> signParams = new LinkedList<NameValuePair>();
- signParams.add(new BasicNameValuePair("appid", req.appId));
- signParams.add(new BasicNameValuePair("noncestr", req.nonceStr));
- signParams.add(new BasicNameValuePair("package", req.packageValue));
- signParams.add(new BasicNameValuePair("partnerid", req.partnerId));
- signParams.add(new BasicNameValuePair("prepayid", req.prepayId));
- signParams.add(new BasicNameValuePair("timestamp", req.timeStamp));
- req.sign = genAppSign(signParams);
- sb.append("sign\n" + req.sign + "\n\n");
-
-
- api.sendReq(req);
-
-
- }
-
-
- private long genTimeStamp() {
- return System.currentTimeMillis() / 1000;
- }
把前面的异步操作方法赋给一个按钮点击事件,如果所有步奏正确,就可以进入支付界面了,如下图:
点击确支付当然就是输入密码什么的操作了,支付成功后有一个反馈信息如下图:
点击完成,当然是回到APP咯,这里就是微信提供的一个回调了。也就是官网上强调的wxapi包下的
WXPayEntryActivity里的Onresp()方法中做回调处理,该包必须在项目工程包目录下才能回调成功。下面贴出的是示例工程结构
在回调中弹出了对话框提示用户支付成功并处理其他逻辑。
-
public class WXPayEntryActivity extends Activity implements IWXAPIEventHandler {
-
-
private static final String TAG = "MicroMsg.SDKSample.WXPayEntryActivity";
-
-
private IWXAPI api;
-
-
-
@Override public void onCreate(Bundle savedInstanceState) {
-
super.onCreate(savedInstanceState);
-
-
-
api = WXAPIFactory.createWXAPI(this, Constants.APP_ID);
-
api.handleIntent(getIntent(), this);
-
api.registerApp(Constants.APP_ID);
-
}
-
-
-
@Override
-
protected void onNewIntent(Intent intent) {
-
super.onNewIntent(intent);
-
setIntent(intent);
-
api.handleIntent(intent, this);
-
}
-
-
-
@Override
-
public void onReq(BaseReq req) {
-
-
}
-
-
-
@Override
-
public void onResp(BaseResp resp) {
-
int errCode = resp.errCode;
-
-
if (errCode == 0) {
-
-
-
-
-
-
-
-
new AlertDialog.Builder(this).setMessage("支付成功").setPositiveButton("确定", new DialogInterface.OnClickListener() {
-
@Override
-
public void onClick(DialogInterface dialog, int which) {
-
dialog.dismiss();
-
finish();
-
PaymentActivity.instance.finish();
-
-
Intent intent = new Intent(WXPayEntryActivity.this, PuzzGameActivity.class);
-
intent.putExtra("ISPLAY",true);
-
startActivity(intent);
-
-
}
-
}).setTitle("提示").create().show();
-
-
Toast.makeText(this,"点击确定按钮开始参与拼图游戏活动",Toast.LENGTH_LONG).show();
-
-
}
-
else if (errCode == -1) {
-
-
new AlertDialog.Builder(this).setMessage("支付出错").setPositiveButton("确定", new DialogInterface.OnClickListener() {
-
@Override
-
public void onClick(DialogInterface dialog, int which) {
-
dialog.dismiss();
-
finish();
-
}
-
}).setTitle("提示").create().show();
-
finish();
-
}
-
else if (errCode == -2) {
-
-
finish();
-
}
-
}
-
-
-
}
这样所有步骤就几乎完全了,结合官方示例和文档,应该可以快速的在项目中加入支付功能了。
当然,这里所涉及到的步骤全是在app客户端进行的,官网上建议将获取签名等操作放在服务后台进行,应该是为了安全吧,也就是客服端将订单信息传给服务端。服务端返回吊起支付必要的信息
(包括perpay_id、商户id 、签名等),然后由客户端吊起微信支付的。
android_app开发微信支付集成
原文:http://blog.csdn.net/yft_android/article/details/51567563