微信支付趟坑记

    最近因为项目需求做了支付,暂时集成了支付宝支付和微信的支付,涉及到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、配置证书,

微信支付趟坑记_第1张图片

    建议在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张图片

    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、配置这个,不然报错;

微信支付趟坑记_第3张图片

 

        b、URL Types配置,RUL Schemes配置为APPID;

微信支付趟坑记_第4张图片

 

到此,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

 

 

最后,如有问题欢迎指正和咨询。

 

你可能感兴趣的:(移动开发)