参考:
微信支付API:
https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=8_1
支付宝API:
https://doc.open.alipay.com/docs/doc.htm?spm=a219a.7629140.0.0.q4Kj5i&treeId=193&articleId=105051&docType=1
以下分篇介绍微信支付和支付宝支付,其中服务端负责生成订单及签名,及接受支付异步通知。客户端负责使用服务端传来的订单信息调用支付接口,及根据SDK同步返回的支付结果展示结果页。
使用前准备
SDK及Demo下载
https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1419319167&token=&lang=zh_CN
Jar包的使用(3.1.1版本)
图 2
在项目中按照如上方法使用微信支付jar包
libammsdk.jar 微信支付主jar包
获取AppID
http://kf.qq.com/faq/120911VrYVrA150906F3qqY3.html
图 3
注册微信开放平台并认证开发者资质,认证成功后点击创建应用,填写相关资料
图 4
图 5
其中 中文的应用名称和应用简介必须填写,app图标必须准备28*28、108*108尺寸的
https://open.weixin.qq.com/zh_CN/htmledition/res/dev/download/sdk/Gen_Signature_Android.apk(用于下载获取应用签名工具)
图 6
将输入框中输入应用包名,点击按钮获取应用签名,将绿色字符串填写至 图5 中的签名处
填写完成后提交审验,审核完成后在管理中心找到刚刚申请的应用
图 7
点击查看就可以看到appid了
申请接入微信支付
在开放平台的管理中心找到所需接入支付能力的app,点击查看
点击微信支付一栏的申请开通,跳转至申请界面。
在申请接入支付的时候,请准备好app demo,以及含图文的文档说明,对公账户,参考下面的地址填写表格信息。
http://kf.qq.com/faq/120911VrYVrA150910QzeqEz.html
等待七个工作日左右,微信支付审批通过后会给你的预留邮箱发送微信商户号和密码。(此账号同时也需提供给服务器开发人员)
如图所示的就申请成功了
页面底部有如下信息需确保与app信息符合,否则后续无法操作成功。
如有出入,点击修改更改至和app信息一致即可,在打包正式签名版本的时候需要更改,签名会变化。
AndroidManifest配置
权限配置:
广播配置:
Activity配置:
此activity路径名称必须一致,不然无法收到微信的返回码
此外,在需要使用微信支付的Activity中需加入如下配置(如不配置可能造成返回码一直为 -1 )
混淆配置:
-keep class com.tencent.mm.sdk.** {
*;
}
微信支付使用
微信支付拉起
操作类封装:
public class WeChatPay {
private Activity activity;
private String appid;
private String partnerId;
private String prepayId;
private String nonceStr;
private String timeStamp;
private String packageValue;
private String sign;
private MaterialDialog materialDialog;
/*
* 构造函数
*/
public WeChatPay(Activity activity, GetDepositResponse.OrderBean orderBean, MaterialDialog materialDialog) {
this.activity = activity;
this.appid = orderBean.getAppid();
this.partnerId = orderBean.getPartnerid();
this.prepayId = orderBean.getPrepayid();
this.nonceStr = orderBean.getNoncestr();
this.timeStamp = orderBean.getTimestamp();
this.packageValue = orderBean.getPackage();
this.sign = orderBean.getSign();
this.materialDialog = materialDialog;
}
// 支付调用的代码
public void pay() {
IWXAPI mWxApi = WXAPIFactory.createWXAPI(activity, appid, true);
boolean sIsWXAppInstalledAndSupported = mWxApi.isWXAppInstalled() && mWxApi.isWXAppSupportAPI();
if (!sIsWXAppInstalledAndSupported) {
materialDialog.dismiss();
materialDialog = new MaterialDialog.Builder(activity)
.content("系统检测设备尚未安装微信客户端")
.title("提示")
.positiveText("下载微信")
.onPositive(new MaterialDialog.SingleButtonCallback() {
@Override
public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) {
Uri uri = Uri.parse("http://weixin.qq.com/cgi-bin/readtemplate?uin=&stype=&promote=&fr=www.baidu.com&lang=zh_CN&ADTAG=&check=false&t=weixin_download_method&sys=android&loc=weixin,android,web,0");
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
activity.startActivity(intent);
}
})
.negativeText("更换支付方式")
.build();
materialDialog.show();
} else {
mWxApi.registerApp(appid);
if (mWxApi != null) {
PayReq req = new PayReq();
try {
req.appId = appid;
req.partnerId = partnerId;
req.prepayId = prepayId;
req.nonceStr = nonceStr;
req.timeStamp = timeStamp;
req.packageValue = packageValue;
req.sign = sign;
} catch (Exception e) {
e.printStackTrace();
}
mWxApi.sendReq(req);
Log.d("WeChatPay", "发起微信支付申请");
}
}
}
}
使用说明:
将微信支付所需用的数据都封装到WeChatPay这个操作类里,其中activity是用来创建微信支付时所必须的,orderbean是微信支付所需要的参数的bean类,materialdialog是用来弹出检测是否安装微信客户端的提示。
调用支付的api前,首先得向微信注册本app,代码如下:
IWXAPI mWxApi = WXAPIFactory.createWXAPI(activity, appid, true);
mWxApi.registerApp(appid);
服务器生成支付订单,先调用统一下单API生成预付单,获取到prepay_id后将参数再次签名传输给APP发起支付。以下是调起微信支付的关键代码:
if (mWxApi != null) {
PayReq req = new PayReq();
try {
req.appId = appid;
req.partnerId = partnerId;
req.prepayId = prepayId;
req.nonceStr = nonceStr;
req.timeStamp = timeStamp;
req.packageValue = packageValue;
req.sign = sign;
} catch (Exception e) {
e.printStackTrace();
}
mWxApi.sendReq(req);
Log.d("WeChatPay", "发起微信支付申请");
}
订单信息的字段说明可以参照下表:
这些字段都是app服务器端拉取的,app端无需关心具体的生成操作。
支付错误码处理
错误码接收Activity:
public class WXPayEntryActivity extends BaseActivity implements IWXAPIEventHandler {
private static final String TAG = "WXPayEntryActivity";
private IWXAPI mWxapi;
private String appid = "appid";
private static WXpaycallback wXpaycallback;
//微信支付结果码
private static final int PAY_OK = 0;//交易成功
private static final int PAY_ERR = -1;//交易失败
private static final int PAY_CANCLE = -2;//交易取消
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
String str = AppConfigUtils.getInstanse(this).getAppid();
if (!str.isEmpty() && !appid.equals(str)) {
appid = str;
}
mWxapi = WXAPIFactory.createWXAPI(this, appid);
mWxapi.handleIntent(getIntent(), this);
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
setIntent(intent);
mWxapi.handleIntent(intent, this);
}
@Override
public void onReq(BaseReq req) {
}
@Override
public void onResp(BaseResp resp) {
Log.d(TAG, "onPayFinish, errCode = " + resp.errCode);
if (resp.getType() == ConstantsAPI.COMMAND_PAY_BY_WX) {
if(resp.errCode == PAY_OK){
this.finish();
wXpaycallback.dialogdoOK();
}else if (resp.errCode == PAY_ERR){
this.finish();
wXpaycallback.dialogdoERR();
}else if(resp.errCode == PAY_CANCLE){
this.finish();
wXpaycallback.dialogdoCANCLE();
}
}
}
//回调接口
public interface WXpaycallback {
public void dialogdoOK();
public void dialogdoERR();
public void dialogdoCANCLE();
}
public void setWXpaycallback(WXpaycallback wXpaycallback) {
this.wXpaycallback = wXpaycallback;
}
}
使用说明:
特别需要注意的就是,微信支付的返回码只能用微信指定的wxapi包下面的WXPayEntryActivity来接收(包名或类名不一致会造成无法回调)。
错误码的回调操作全在onResp()方法中,由于微信支付的返回码只可在指定页面接收,这个页面又无法成为我们的App界面,所以此处代码我做了回调操作,以便app界面处理UI动作。
可以看到我的代码中,在处理错误码的时候,做操作回调之前都会finish()这个activity,这是因为在支付时,WXPayEntryActivity会以透明的形式启动,所以你可能需要在支付完成得到支付响应码后手动finish该activity,不然你自己写的支付页面的activity需要返回两次才能退出,因为第一次按下back键实际是finish掉WXPayEntryActivity,第二次才是你自己的activity。
如果没有什么特殊操作了,错误码的接受页面就这样写不要动了。另外在支付时,微信尚未登录,得不到任何回调,如果调起支付时有弹窗进度框,并且你想在得到响应码后让它消失是行不通的,需要在跳出的onStop中处理进度条,不然未登录并且用户取消了支付时进度条无法消失。
Errcode参数表:
外部支付调用
调用代码:
WeChatPay weChatPay = new WeChatPay(DepositActivity.this, depositResponse.getData(), materialDialog);
WXPayEntryActivity wxPayEntryActivity = new WXPayEntryActivity();
wxPayEntryActivity.setWXpaycallback(new WXPayEntryActivity.WXpaycallback() {
@Override
public void dialogdoOK() {
new Timer().schedule(new TimerTask() {
public void run() {
httpOrderResultRequest(accessToken, orderinfoId);
}
}, 1000);
}
@Override
public void dialogdoERR() {
materialDialog.dismiss();
ToastUtils.showToast(DepositActivity.this, "支付失败");
}
@Override
public void dialogdoCANCLE() {
materialDialog.dismiss();
ToastUtils.showToast(DepositActivity.this, "支付取消");
}
});
weChatPay.pay();
使用说明:
首先用WeChatPay实例化一个对象,传入当前的activity,从服务器得到的订单参数的实体对象,dialog实例对象,完成这些步骤后就可以通过pay()方法拉取微信支付了,当然,我们还得通过刚刚在WXPayEntryActivity里设置的回调方法来实现我们自己app界面在完成支付或者取消或者失败时候的操作。dialogdoOK()是用来处理支付成功的,dialogdoERR()是用来处理支付发生错误或者失败的情况的,dialogdoCANCLE()是用来处理支付取消的情况的。
使用前准备
SDK及Demo下载
https://doc.open.alipay.com/docs/doc.htm?spm=a219a.7629140.0.0.zROl1g&treeId=59&articleId=103563&docType=1
Jar包的使用(20160516版本)
在项目中按照如上方法使用支付宝支付jar包
alipaySdk-20160516.jar 支付宝支付主jar包
商户签约审核
首先得注册商户账号,打开以下商户网页点击注册,注册认证完商户号之后才可以进行后续操作。
https://b.alipay.com/newIndex.htm
商户在 http://b.alipay.com 里进行产品签约;
商户登录http://qy.alipay.com ,可在"签约订单"中查看审核进度。
PID和秘钥管理(拉通demo所需步骤)
支付宝提供商户接口产品时,会自主提供一个保障商户接入安全的一组信息及其对应的配置平台,这组信息就是密钥。由商户密钥与支付宝密钥交换后与支付宝商户标识(如partnerID、APPID等)绑定。
https://openhome.alipay.com/platform/keyManage.htm?keyType=partner
在"合作伙伴密钥管理"下(根据不同的产品选择对应的入口),点击"RSA加密"后的"添加密钥",把自己的公钥复制进去。
点击"确认上传",提示:上传成功,说明已经成功上传
说明: 如果需要修改公钥,只需要把新的公钥复制进去,点击"修改"即可!
https://doc.open.alipay.com/docs/doc.htm?spm=a219a.7629140.0.0.7YdLFa&treeId=58&articleId=103242&docType=1 生成RSA私钥的工具可以在此下载。
上述的所有的参数配置完成了需要交给app服务器端开发人员,用以生成支付宝订单信息。
AndroidManifest配置
权限配置:
Activity配置:
android:name="com.alipay.sdk.app.H5PayActivity"
android:configChanges="orientation|keyboardHidden|navigation"
android:exported="false"
android:screenOrientation="behind" >
android:name="com.alipay.sdk.auth.AuthActivity"
android:configChanges="orientation|keyboardHidden|navigation"
android:exported="false"
android:screenOrientation="behind" >
混淆配置:
-libraryjars libs/alipaySDK-20160516.jar
-keep class com.alipay.android.app.IAlixPay{*;}
-keep class com.alipay.android.app.IAlixPay$Stub{*;}
-keep class com.alipay.android.app.IRemoteServiceCallback{*;}
-keep class com.alipay.android.app.IRemoteServiceCallback$Stub{*;}
-keep class com.alipay.sdk.app.PayTask{ public *;}
-keep class com.alipay.sdk.app.AuthTask{ public *;}
支付宝支付使用
支付宝支付拉起
操作类封装:
public class AliPay {
/*支付宝支付结果码*/
private static final String PAY_OK = "9000";// 支付成功
private static final String PAY_WAIT_CONFIRM = "8000";// 交易待确认
private static final String PAY_NET_ERR = "6002";// 网络出错
private static final String PAY_CANCLE = "6001";// 交易取消
private static final String PAY_FAILED = "4000";// 交易失败
private Activity activity;
Alipaycallback alipaycallback;
private SoftReference activitySoftReference;
private static final int SDK_PAY_FLAG = 1;
private String orderInfo;
public AliPay(Activity activity, String orderInfo) {
activitySoftReference = new SoftReference(activity);
this.activity = activitySoftReference.get();
this.orderInfo = orderInfo;
}
@SuppressLint("HandlerLeak")
private Handler mHandler = new Handler() {
@SuppressWarnings("unused")
public void handleMessage(Message msg) {
switch (msg.what) {
case SDK_PAY_FLAG: {
AliPayResult payResult = new AliPayResult((String) msg.obj);
String resultInfo = payResult.getResult();// 同步返回需要验证的信息
String resultStatus = payResult.getResultStatus();
if (alipaycallback != null) {
if (resultStatus.equals(PAY_OK)) {
alipaycallback.dialogOK();
} else if (resultStatus.equals(PAY_CANCLE)) {
alipaycallback.dialogCANCLE();
} else if (resultStatus.equals(PAY_NET_ERR)) {
alipaycallback.dialogERR();
} else if (resultStatus.equals(PAY_WAIT_CONFIRM)) {
ToastUtils.showToast(activity, "支付结果确认中");
} else if (resultStatus.equals(PAY_FAILED)) {
alipaycallback.dialogERR();
} else {
alipaycallback.dialogERR();
}
}
break;
}
default:
break;
}
}
};
/**
* call alipay sdk pay. 调用SDK支付
*/
public void pay() {
if (!ValidatorUtils.isEmpty(orderInfo)) {
final String payInfo = orderInfo;
Runnable payRunnable = new Runnable() {
@Override
public void run() {
// 构造PayTask 对象
PayTask alipay = new PayTask(activity);
// 调用支付接口,获取支付结果
String result = alipay.pay(payInfo, true);
Message msg = new Message();
msg.what = SDK_PAY_FLAG;
msg.obj = result;
mHandler.sendMessage(msg);
}
};
// 必须异步调用
Thread payThread = new Thread(payRunnable);
payThread.start();
} else {
ToastUtils.showToast(activity, "支付信息有误");
}
}
//回调接口
public interface Alipaycallback {
public void dialogOK();
public void dialogERR();
public void dialogCANCLE();
}
public void setAlipaycallback(Alipaycallback alipaycallback) {
this.alipaycallback = alipaycallback;
}
}
使用说明:
将支付宝支付所需用的数据都封装到AliPay这个操作类里,其中activity是用来创建支付宝支付时所必须的,orderInfo是支付宝支付所需要的参数的订单信息。支付宝拉起的时候,会进行检测(sdk功能),如果本机安装了支付宝客户端则会拉起客户端,如果本机尚未安装客户端,则直接拉起支付宝h5界面进行支付。
服务器生成支付订单,通过加密算法生成订单信息后传输给APP发起支付。以下是调起支付宝支付的关键代码:
new Thread() {
@Override
public void run() {
super.run();
PayTask payTask = new PayTask(mActivity);
String result = payTask.pay(signInfo, true);
Message message = mHandler.obtainMessage();
message.what = PAY_RESULT;
message.obj = result;
mHandler.sendMessage(message);
}
}.start();
订单详情的参考字段说明:(app端无须设置,知道字段含义即可)
https://doc.open.alipay.com/docs/doc.htm?spm=a219a.7629140.0.0.TGh8fB&treeId=59&articleId=103663&docType=1#
所有的生成订单的步骤全部放在服务器端,app端只需将金额发送给服务器,服务器生成相应的订单后再返回给app拉起支付宝支付。
支付错误码处理
错误码接收类:
public class AliPayResult {
private String resultStatus;
private String result;
private String memo;
public AliPayResult(String rawResult) {
if (TextUtils.isEmpty(rawResult))
return;
String[] resultParams = rawResult.split(";");
for (String resultParam : resultParams) {
if (resultParam.startsWith("resultStatus")) {
resultStatus = gatValue(resultParam, "resultStatus");
}
if (resultParam.startsWith("result")) {
result = gatValue(resultParam, "result");
}
if (resultParam.startsWith("memo")) {
memo = gatValue(resultParam, "memo");
}
}
}
@Override
public String toString() {
return "resultStatus={" + resultStatus + "};memo={" + memo
+ "};result={" + result + "}";
}
private String gatValue(String content, String key) {
String prefix = key + "={";
return content.substring(content.indexOf(prefix) + prefix.length(),
content.lastIndexOf("}"));
}
/**
* @return the resultStatus
*/
public String getResultStatus() {
return resultStatus;
}
/**
* @return the memo
*/
public String getMemo() {
return memo;
}
/**
* @return the result
*/
public String getResult() {
return result;
}
}
使用说明:
AliPayResult这个类主要是用来获取支付宝客户端返回的错误码的,此类为官方demo里编写的处理类,我们直接调用加以获取错误码,默认认为他是sdk里封装好的类,了解其功能即可。
支付订单的处理结果,app端可以通过消息机制获取到,具体的可以通过以下代码获取
AliPayResult payResult = new AliPayResult((String) msg.obj);
String resultStatus = payResult.getResultStatus();
客户端的返回码说明可以参照下图:
外部支付调用
调用代码:
AliPay aliPay = new AliPay(DepositActivity.this, depositResponse.getData().getOrderInfo().toString());
aliPay.setAlipaycallback(new AliPay.Alipaycallback() {
@Override
public void dialogOK() {
new Timer().schedule(new TimerTask() {
public void run() {
httpOrderResultRequest(accessToken, orderinfoId);
}
}, 1000);
}
@Override
public void dialogERR() {
materialDialog.dismiss();
ToastUtils.showToast(DepositActivity.this, "充值失败");
}
@Override
public void dialogCANCLE() {
materialDialog.dismiss();
ToastUtils.showToast(DepositActivity.this, "充值取消");
}
});
aliPay.pay();
使用说明:
首先用AliPay实例化一个对象,传入当前的activity,从服务器得到的订单信息,完成这些步骤后就可以通过pay()方法拉取支付宝支付了,当然,我们还得通过刚刚在AliPay里设置的回调方法来实现我们自己app界面在完成支付或者取消或者失败时候的操作。dialogdoOK()是用来处理支付成功的,dialogdoERR()是用来处理支付发生错误或者失败的情况的,dialogdoCANCLE()是用来处理支付取消的情况的。
在开发app端的支付接入的时候, app需要和服务器端协同开发这个支付接入,app的所需数据都得先在demo中跑通确保数据是可使用的,然后再交送给服务器端的开发人员,否则后期拉不起支付的时候很难定位问题所在。
本次开发中同时接入了微信支付和支付宝支付,所以我在开发过程中就对两者的接入过程有了一个比较。值得一提的是支付宝的支付,他的SDK封装的很好,包括检测本地是否有支付宝客户端以确定用什么方式拉起支付宝支付,开发人员无需自己检测。同时,支付宝的产品签约过程比微信支付的申请快多了,虽然官网上两家公司标注的都是七个工作日内完成审核,但是支付宝基本上隔天就审核完成,而微信不到第七个工作日都不会给你审核。还有微信支付申请的过程中所需提供的资料非常多,这还单单只是申请的指定应用的支付接入功能,如果说你的开发者账号下还有一个app需要接入微信支付,那么恭喜你,这边的申请流程还得再来一遍。相比之下,支付宝支付的申请就好了不知道多少了,它的支付能力的申请都是和商户号绑定的,无论创建多少应用你都只需申请一次支付能力,非常的人性化。
支付宝接入的时候,基本没有坑,app端只要代码写对就能拉通,当然了商户平台里参数需要配置正确,这些配置最好还是服务器端开发人员来做,不然有了订单还是拉不起支付的。
接下来我要细说一下微信支付开发过程中的坑了,首先,app的微信支付的错误码返回还得必须用它自己的那套activity来接收,包名或者类名不对,就获取不到错误码了。在开发过程中,你可能遇到的最多的问题就是errcode为-1的问题。相信你只要按照我上述的步骤进行配置,这应该不算问题。官方的开发文档里写的非常简洁,好多坑都是文档中没写出相应的配置所导致的,所以参考意义还不如某些博客上的教程。另外微信服务器端一旦订单信息拉取失败后需要重置秘钥时,这个时候本地拉起的时候就会一直报-1的错误码,通常遇到这种情况,你可以等上半个小时左右,虽然更改在服务器端生效了,但是本机微信这边还没生效,得有一段响应时间,过了这段时间后,app就可以成功拉起微信支付了。最后需要强调的一点就是,在我们开发过程中,通常都是svn进行代码管理的,团队成员中某个人要是用一样的代码打出来的包是不能拉起微信支付的,这个时候微信会报errcode为-1的错误,这是因为每个机子打出来的包的签名都是不一样的,这和微信开放平台上申请支付的那个应用的签名对不上号了,微信支付自然就调不起来了。当然在出正式版本的时候都会有一套自定的签名,这样就可以解决不同机子打出来的包不能成功拉起支付的问题了。忠告一点就是,保持开放平台上的参数和本地包的参数一致,不然就是一个个磨人的微信支付坑。
写到这里,差不多完结了,最后预祝各位同仁成功接入支付。