最近因为项目需求做了支付,暂时集成了支付宝支付和微信的支付,涉及到PC端Web网页支付,和手机APP支付。支付宝的集成相对容易一些,文档齐全,描述规范,也便于理解,在此就不再累述;但是微信支付真的就是呵呵哒了,文档及其简单,有等于没有,沙箱测试环境问题非常多,且支付过程难以理解,趟了无数坑之后,终于搞定,因此记录一下,希望能帮到有需要的人。
注:内容比较粗,需要有一点点基础,至少是研究过微信支付,有所了解的朋友。
【只说微信APP支付】
先简单讲几个需要注意的地方:
0、额外说一句,目前很多APP都是集成了H5,支付宝和微信也是支持手机APP中通过H5页面直接唤起支付宝/微信支付的,但是官方都不太建议这样做,APP端还是建议集成SDK,在APP里调用支付,而非H5页面直接调用。
1、PC端web支付和app支付,需要申请两套APPID;PC端采用二维码支付,APP调用微信APP支付;需要在微信平台申请两套APPID,一套是公众号的,一套的APP的,对应两个APPID、两个商户号、两个密钥(总之就是两个独立的,网页二维码支付用公众号的,APP支付用APP的一套);
2、申请APP支付时,Android的需要配置签名信息的,而IOS的不需要;因为这两个发布平台的机制不同,Android的APP在各大市场发布,是需要做签名的。就是说,把你的正式的Android的APP装到手机上,然后下载一个工具(微信文档中有下载在地址),读取你这个APP的签名信息,配置到微信支付平台上,否则无法唤起微信支付。(关于这一点,文档略有说明,但是我觉得不够清晰,所以强调一下)
3、关于支付过程的问题,简单说一下,第一次访问微信服务器,发起预付订单,提交所有支付单据的数据,拿到预付单号;第二次调用微信SDK,根据预付单号(当然了还有其他的信息,如商户号等等),发起支付;参考官方文档来就行,关键是,APP要拿到预付单号,然后才能调用SDK唤起微信支付;
4、关于加签(或者说是加密)的问题,多说一点,我们的数据都是通过网络请求的方式提交到支付平台的服务器,在这个过程中,数据可能会被拦截、篡改,为了解决这个问题,就引用的数据加密。简单来说,我要提交数据到服务器,比如【name=zhangshan,action=delete,key=858486】(可能是url、可能是post等请求;JSON、XML等各种结构),服务器怎么知道数据提交的过程没有被篡改呢?
a、支付宝的方式:自己生成一对公私钥;私钥自行保存,不可泄露,公钥上传到支付宝服务器;提交数据的时候,以url为例:xx.do?name=zhangshan&action=delete&key=858486&sign=【加密后内容】;被加密内容就是url后面的参数,使用你的私钥,进行加密;比如你传的action=delete被篡改为action=save,支付宝接收到的数据会根据公钥解密,会发现请求数据与解密后数据不匹配,认为数据被篡改,拒绝请求。
b、微信的方式:我没有仔细研究,只是根据加密后的内容(短且定长,怀疑不是数据加密后内容,只是数据指纹),以及只有一个密码来看,可能是把请求的数据通过密码进行计算,得出一个类似于数据身份ID(或者数据指纹)类的玩意。比如你要提交a,密码是b,那么数据指纹就是ab;提交之后,微信把你提交的数据通过密码进行计算,得出数据指纹,然后比对你提交时的数据指纹,验证数据是否被篡改;(类似于MD5,不同的数据,MD5值一定不同。另外指纹的说法不一定准确,我对加密了解也不多,如果说错了,望指正)
c、微信支付,预付订单提交数据要加签,调用SDK支付也要加签,也就是说有两次加签,且加签的内容是本次提交的数据。也就是说预付订单的sign和支付时的sign内容是不同的,切记。
5、关于回调:Android的回调,activity的名称不能改、不能改、不能改,package有要求、有要求、有要求。IOS也有几个重要的地方需要配置,我会一一说明;
6、关于沙箱测试:极其坑爹,但是为了能上线,必须做,且比如通过才行。个人建议,开发及测试时,直接拿正式环境撸,支付改成1分钱,100次也才1块。微信的测试要求,纯粹为了过上线的审核而做,对于项目的开发和测试,一点帮助都没有,反而起副作用,能让你怀疑人生。
a、沙箱环境,APPID不变,商户号不变,通过微信提供的接口,申请一个沙箱密码(正式环境有正式环境的密码),加密的密码换成沙箱密码,然后所有请求路径切换一下,就切换到测试环境了;
b、APP端无法唤醒微信,无法唤醒微信,无法唤醒微信,这不重要,debug看看调用微信接口返回数据是否正确即可;
c、APP端回调结果永远是-1,永远是-1,永远是-1,这也不重要,你通过微信绑定商户号看测试的结果就行了;
d、强烈建议,微信的测试用例通过之后,就切回正式环境开发和测试吧;
e、另外说一句,微信所谓的沙箱测试,根本不需要到APP通过发起SDK唤醒微信支付这一步,换句话说,我的APP不集成SDK,一样能通过测试。只需要我们的服务端能发起预付订单的调用,能够正确被微信支付回调,然后按照测试用例操作即可。
【Android支付】
1、集成SDK,在gradle文件中增加:
dependencies {
//wx支付SDK
compile 'com.tencent.mm.opensdk:wechat-sdk-android-with-mta:+'}
2、配置证书,
建议在gradle文件中配置,放在defaultConfig下方即可,这样方便debug:
signingConfigs {
debug {
storeFile file('../test.jks')//签名文件路径
storePassword "123456"
keyAlias "app"
keyPassword "123456" //签名密码
println("====== signingConfigs.debug ======")
}
release {
storeFile file('../test.jks')//签名文件路径
storePassword "123456"
keyAlias "app"
keyPassword "123456" //签名密码
println("====== signingConfigs.release ======")
}
}
配置的值,和在IDE中配置的一样。
3、发起支付:
在调用支付的activity中:
a、初始化,在onCrate中配置:
mIWXAPI = WXAPIFactory.createWXAPI(this, "appid");
mIWXAPI.handleIntent(getIntent(), this);
b、调用支付,下面的信息,均从我们的服务器获取;业务过程就不再叙述。
mIWXAPI.registerApp(orderInfo.get("appId"));
PayReq request = new PayReq();
request.appId = orderInfo.get("appId");//应用ID
request.partnerId = orderInfo.get("partnerId");//商户号
request.prepayId = orderInfo.get("prepayId");//预支付交易会话ID
request.packageValue = orderInfo.get("packageValue");//扩展字段
request.nonceStr = orderInfo.get("nonceStr");//随机字符串
request.timeStamp = orderInfo.get("timeStamp");//时间戳
request.sign = orderInfo.get("sign");//签名
mIWXAPI.sendReq(request);
回调的activity:
a、类名必须是WXPayEntryActivity,通过工具去生成activity(包含class和xml文件),实现IWXAPIEventHandler接口;
c、package,必须是包名.wxapi下。例如你的包名是cn.cause.myapp,那么回调的activity必须在cn.cause.myapp.wxapi下。
public class WXPayEntryActivity extends Activity implements IWXAPIEventHandler {
private final String TAG = this.getClass().getName();
private IWXAPI api;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_wxpay_entry);
api = WXAPIFactory.createWXAPI(this, "appid");
api.handleIntent(getIntent(), this);
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
setIntent(intent);
api.handleIntent(intent, this);
}
@Override
public void onReq(BaseReq baseReq) {
Log.i(TAG, "onReq: " + baseReq.getType());
}
@Override
public void onResp(BaseResp resp) {
if (resp.getType() == ConstantsAPI.COMMAND_PAY_BY_WX) {
Log.i(TAG, "onPayFinish,errCode=" + resp.errCode);
if (0 == resp.errCode) {
} else if (-1 == resp.errCode) {
} else if (-2 == resp.errCode) {
}
}
}
@Override
public void onPointerCaptureChanged(boolean hasCapture) {
Log.i(TAG, "onPointerCaptureChanged: " + hasCapture);
}
}
4、AndroidManifest.xml文件配置:
5、到此,Android的支付已经完成。
【IOS支付】
因为是先做的Android的支付,所以坑差不多都趟完了,IOS相对而言比较顺利,但是也有几个坑。
1、集成SDK:
这个没什么好说的,参考文档即可。
2、初始化,要在AppDelegate中:
[WXApi registerApp:@"appid"];
3、发起支付:
//调起微信支付
NSString *wx_appId = [paramsDictionary objectForKey:@"wx_appId"];
NSString *wx_partnerid = [paramsDictionary objectForKey:@"wx_partnerid"];
NSString *wx_prepayid = [paramsDictionary objectForKey:@"wx_prepayid"];
NSString *wx_package = [paramsDictionary objectForKey:@"wx_package"];
NSString *wx_noncestr = [paramsDictionary objectForKey:@"wx_noncestr"];
NSString *wx_timestamp = [paramsDictionary objectForKey:@"wx_timestamp"];
NSString *wx_sign = [paramsDictionary objectForKey:@"wx_sign"];
PayReq* wxreq = [[PayReq alloc] init];
wxreq.openID = wx_appId; // 微信的appid
wxreq.partnerId = wx_partnerid;
wxreq.prepayId = wx_prepayid;
wxreq.nonceStr = wx_noncestr;
wxreq.timeStamp = [wx_timestamp intValue];
wxreq.package = wx_package;
wxreq.sign = wx_sign;
[WXApi sendReq:wxreq];
4、回调,回调也需要写到AppDelegate中,
#pragma mark 微信回调方法
- (void)onResp:(BaseResp *)resp
{
NSString * strMsg = [NSString stringWithFormat:@"errorCode: %d",resp.errCode];
NSLog(@"strMsg: %@",strMsg);
NSString * errStr = [NSString stringWithFormat:@"errStr: %@",resp.errStr];
NSLog(@"errStr: %@",errStr);
NSString * strTitle;
//判断是微信消息的回调 --> 是支付回调回来的还是消息回调回来的.
if ([resp isKindOfClass:[SendMessageToWXResp class]])
{
strTitle = [NSString stringWithFormat:@"发送媒体消息的结果"];
}
NSString * wxPayResult;
//判断是否是微信支付回调 (注意是PayResp 而不是PayReq)
if ([resp isKindOfClass:[PayResp class]])
{
//支付返回的结果, 实际支付结果需要去微信服务器端查询
strTitle = [NSString stringWithFormat:@"支付结果"];
switch (resp.errCode)
{
case WXSuccess:
{
strMsg = @"支付结果:";
NSLog(@"支付成功: %d",resp.errCode);
wxPayResult = @"success";
break;
}
case WXErrCodeUserCancel:
{
strMsg = @"用户取消了支付";
NSLog(@"用户取消支付: %d",resp.errCode);
wxPayResult = @"cancel";
break;
}
default:
{
strMsg = [NSString stringWithFormat:@"支付失败! code: %d errorStr: %@",resp.errCode,resp.errStr];
NSLog(@":支付失败: code: %d str: %@",resp.errCode,resp.errStr);
wxPayResult = @"faile";
break;
}
}
//发出通知 从微信回调回来之后,发一个通知,让请求支付的页面接收消息,并且展示出来,或者进行一些自定义的展示或者跳转
NSNotification * notification = [NSNotification notificationWithName:@"WXPay" object:wxPayResult];
[[NSNotificationCenter defaultCenter] postNotification:notification];
}
}
5、配置
a、配置这个,不然报错;
b、URL Types配置,RUL Schemes配置为APPID;
到此,IOS的支付也完成了。
最后附上几个参考的文章:
https://www.cnblogs.com/mancong/p/5422192.html
https://blog.csdn.net/deft_mkjing/article/details/52701608
https://www.jianshu.com/p/c3b63c4bd6e6
https://www.jianshu.com/p/162ece335b31
最后,如有问题欢迎指正和咨询。