微信接入支付流程跟支付宝差不多,这里介绍一下接入流程以及注意事项。
接入流程:
1.1、到微信开放平台添加移动应用,申请权限
到微信开放平台注册开发者账号,并添加应用,申请支付权限,等待审核,需要提前做,审核需要时间,一般在7个工作日内。
获取的参数配置:
1.2、后台设置
这里应用跟应用签名和包名挂钩,也就是说一个移动应用对应一个APPID,表明了配置参数的不可公用性。另外要注意调试的时候签名问题,可以先设置为debug签名,等调试成功换成正式签名。一般调试问题就是签名问题。
签名工具下载地址:
https://open.weixin.qq.com/zh_CN/htmledition/res/dev/download/sdk/Gen_Signature_Android.apk
1.3、注册吊起支付
// 通过WXAPIFactory工厂,获取IWXAPI的实例
IWXAPI api = WXAPIFactory.createWXAPI(this, Constants.APP_ID, false);
// 将该app注册到微信
api.registerApp(Constants.APP_ID);
商户服务器生成支付订单,先调用统一下单API(详见第7节)生成预付单,获取到prepay_id后将参数再次签名传输给APP发起支付,由服务器端进行操作,简化了客户端的复杂度跟安全性。
订单参数:
这里是服务器端生成预支付订单并加签名返回。
调用方式:
api = WXAPIFactory.createWXAPI(this, "wxb4ba3c02aa476ea1");
Button appayBtn = (Button) findViewById(R.id.appay_btn);
appayBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//订单接口
String url = "http://wxpay.weixin.qq.com/pub_v2/app/app_pay.php?plat=android";
Button payBtn = (Button) findViewById(R.id.appay_btn);
payBtn.setEnabled(false);
Toast.makeText(PayActivity.this, "获取订单中...", Toast.LENGTH_SHORT).show();
try{
byte[] buf = Util.httpGet(url);
if (buf != null && buf.length > 0) {
String content = new String(buf);
Log.e("get server pay params:",content);
JSONObject json = new JSONObject(content);
if(null != json && !json.has("retcode") ){
PayReq req = new PayReq();
//req.appId = "wxf8b4f85f3a794e77";
// 测试用appId
//订单参数
req.appId = json.getString("appid");
req.partnerId = json.getString("partnerid");
req.prepayId = json.getString("prepayid");
req.nonceStr = json.getString("noncestr");
req.timeStamp = json.getString("timestamp");
req.packageValue = json.getString("package");
req.sign = json.getString("sign");
req.extData = "app data"; // optional
Toast.makeText(PayActivity.this, "正常调起支付", Toast.LENGTH_SHORT).show();
// 在支付之前,如果应用没有注册到微信,应该先调用IWXMsg.registerApp将应用注册到微信
api.sendReq(req);
}else{
Log.d("PAY_GET", "返回错误"+json.getString("retmsg"));
Toast.makeText(PayActivity.this, "返回错误"+json.getString("retmsg"), Toast.LENGTH_SHORT).show();
}
}else{
Log.d("PAY_GET", "服务器请求错误");
Toast.makeText(PayActivity.this, "服务器请求错误", Toast.LENGTH_SHORT).show();
}
}catch(Exception e){
Log.e("PAY_GET", "异常:"+e.getMessage());
Toast.makeText(PayActivity.this, "异常:"+e.getMessage(), Toast.LENGTH_SHORT).show();
}
payBtn.setEnabled(true);
}
});
1.4、支付结果回调
在包名.wxapi包路径中实现WXPayEntryActivity类(包名或类名不一致会造成无法回调),在WXPayEntryActivity类中实现onResp函数,支付完成后,微信APP会返回到商户APP并回调onResp函数,开发者需要在该函数中接收通知,判断返回错误码,如果支付成功则去后台查询支付结果再展示用户实际支付结果。注意一定不能以客户端返回作为用户支付的结果,应以服务器端的接收的支付通知或查询API返回的结果为准。
清单文件:
<activity
android:name=".wxapi.WXPayEntryActivity"
android:exported="true"
android:launchMode="singleTop" />
Activity设置:
package net.sourceforge.simcpux.wxapi;
import net.sourceforge.simcpux.Constants;
import net.sourceforge.simcpux.R;
import com.tencent.mm.sdk.constants.ConstantsAPI;
import com.tencent.mm.sdk.modelbase.BaseReq;
import com.tencent.mm.sdk.modelbase.BaseResp;
import com.tencent.mm.sdk.openapi.IWXAPI;
import com.tencent.mm.sdk.openapi.IWXAPIEventHandler;
import com.tencent.mm.sdk.openapi.WXAPIFactory;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
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);
setContentView(R.layout.pay_result);
api = WXAPIFactory.createWXAPI(this, Constants.APP_ID);
api.handleIntent(getIntent(), this);
}
@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) {
Log.d(TAG, "errCode = " + resp.errCode);
if (resp.getType() == ConstantsAPI.COMMAND_PAY_BY_WX) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.app_tip);
builder.setMessage(getString(R.string.pay_result_callback_msg, String.valueOf(resp.errCode)));
builder.show();
}
}
}
errCode:
0 支付成功
-1 支付出错,可能的原因:签名错误、未注册APPID、项目设置APPID不正确、注册的APPID与设置的不匹配、其他异常等。
-2 用户取消支付
这样一个完整的流程就走完了,下面来说一下老版本关于在客户端加签的流程。因为现在要求加签过程都扔到服务器端进行操作。
旧版本的操作总结:
商户系统和微信支付系统主要交互说明:
步骤1:用户在商户APP中选择商品,提交订单,选择微信支付。
步骤2:商户后台收到用户支付单,调用微信支付统一下单接口。
步骤3:统一下单接口返回正常的prepay_id,再按签名规范重新生成签名后,将数据传输给APP。参与签名的字段名为appId,partnerId,prepayId,nonceStr,timeStamp,package。注意:package的值格式为Sign=WXPay
步骤4:商户APP调起微信支付。
步骤5:商户后台接收支付通知。
步骤6:商户后台查询支付结果。
这里主要讲一下步骤2,步骤3,这两步现在是放在服务器端进行的操作,以前旧版本很多都是放在客户端进行的。
步骤2:
需要调用微信的下单接口,那么需要了解微信的接口规则。
标注红都是应该注意的地方。
签名算法:
private String genProductArgs(BaseOrder order) {
StringBuffer xml = new StringBuffer();
try {
//生成随机数算法
String nonceStr = genNonceStr();
//第一步:对参数按照key=value的格式,并按照参数名ASCII字典序排序如下:
xml.append("</xml>");
List<NameValuePair> packageParams = new LinkedList<NameValuePair>();
packageParams.add(new BasicNameValuePair("appid", Constants.APP_ID));
packageParams.add(new BasicNameValuePair("body", order.getTitle()));
packageParams.add(new BasicNameValuePair("mch_id", Constants.MCH_ID));
packageParams.add(new BasicNameValuePair("nonce_str", nonceStr));
packageParams.add(new BasicNameValuePair("notify_url", order.getNotifyUrl()));
packageParams.add(new BasicNameValuePair("out_trade_no", order.getOrderNo()));
packageParams.add(new BasicNameValuePair("spbill_create_ip", "127.0.0.1"));
packageParams.add(new BasicNameValuePair("total_fee", order.getWXTotalFee() + ""));
packageParams.add(new BasicNameValuePair("trade_type", "APP"));
//第二步:拼接API密钥:
String sign = genPackageSign(packageParams);
packageParams.add(new BasicNameValuePair("sign", sign));
//转为成xml格式
String xmlstring = toXml(packageParams);
//有汉字时需要用ISO8859-1编码,没有时用utf-8
return new String(xmlstring.toString().getBytes(), "ISO8859-1");
} catch (Exception e) {
Log.e(TAG, "genProductArgs fail, ex = " + e.getMessage());
return null;
}
}
//转为成xml格式
private String toXml(List<NameValuePair> params) {
StringBuilder sb = new StringBuilder();
sb.append("<xml>");
for (int i = 0; i < params.size(); i++) {
sb.append("<" + params.get(i).getName() + ">");
sb.append(params.get(i).getValue());
sb.append("</" + params.get(i).getName() + ">");
}
sb.append("</xml>");
Log.e(TAG, sb.toString());
return sb.toString();
}
//生成随机数算法
private String genNonceStr() {
Random random = new Random();
return MD5.getMessageDigest(String.valueOf(random.nextInt(10000)).getBytes());
}
具体参考:
https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=9_1
步骤三:
private void genPayReq() {
req = new PayReq();
req.appId = Constants.APP_ID;
req.partnerId = Constants.MCH_ID;
req.prepayId = resultunifiedorder.get("prepay_id");
req.packageValue = "prepay_id=" + resultunifiedorder.get("prepay_id");
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");
}
private long genTimeStamp() {
return System.currentTimeMillis() / 1000;
}
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());
Log.e(TAG, appSign);
return appSign;
}
签名比较麻烦啊,建议还是按照新版的来吧,省事,安全!