zoukankan      html  css  js  c++  java
  • iOS-关于微信支付

    突然发现的一篇文章,这位博主介绍的还是挺详细的,给大家分享一下

    不懂的也可以咨询我qq:564702640

    1、申请接入

    详见 微信支付申请接入 。 
    创建应用+审核通过,你将得到:APP_ID、APP_SECRET、APP_KEY、PARTNER_ID 。那就可以开始实现支付功能的接入。

    2、业务流程

    不管是客户端还是后台开发者,微信支付开发者文档里面这张交互时序图,都有必要看看。其实很多开发者,当然也包括我,在接入第三方sdk时,一般都是从其官方demo入手,快速了解其api。结果在这里就掉坑里了(后续细讲)。因为微信支付sdk,不像其他第三方的sdk,只需客户端导入实现即可,还需要本地服务器配合。这里就涉及了:

    • 本地服务器与微信支付系统的交互
    • 客户端与本地服务器的交互

    从交互时序图可以发现,不存在客户端与微信支付系统的交互!第二步对支付的流程有一个大概的了解。

    3、客户端具体开发步骤

    1、项目配置

    打开项目–TARGETS – Info – URL types (或者 info.plist 的 URL types)设置你的APP_ID 。

    2、注册APPID

    在项目中引入微信支付的SDK、lib库、头文件后,在AppDelegate文件中:

    • AppDelegate.h
    #import <UIKit/UIKit.h>#import "payRequsestHandler.h"#import "WXApi.h"@interfaceAppDelegate : UIResponder < UIApplicationDelegate,WXApiDelegate>@property (strong, nonatomic) UIWindow *window;
    @end
    • AppDelegate.m
    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
        ...
        //向微信注册
        BOOL isok = [WXApi registerApp:WEIXIN_APP_ID];
        if (isok) {
            DLog(@"注册微信成功");
        }else{
            DLog(@"注册微信失败");
        }
        ...
     }
    
     ... - (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url
    {
        return  [WXApi handleOpenURL:url delegate:self];
    }
    
    - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
    {
        return  [WXApi handleOpenURL:url delegate:self];
    }
    ...
    /* WXApiDelegate代理方法
    ** onReq是微信终端向第三方程序发起请求,要求第三方程序响应。第三方程序响应完后必须调用sendRsp返回。在调用sendRsp返回时,会切回到微信终端程序界面。
    */
    -(void) onResp:(BaseResp*)resp
    {
        if([resp isKindOfClass:[PayResp class]]){
            //支付返回结果,实际支付结果需要去微信服务器端查询
             [[NSNotificationCenter defaultCenter]postNotificationName:NOTIFICATION_WEIXIN_PAY_BACK object:[NSNumber numberWithInt:resp.errCode]];
        }
    }
    @end

    3、创建支付

    当在app端点击去支付,app server 会先生成订单,再调用微信支付提高的 统一下单API 生成预支付单,即prepay_id,然后进行二次签名,返回给客户端一个参数列表(其包含appId,partnerId,prepayId,nonceStr,timeStamp,package。注意:package的值格式为Sign=WXPay)。app收到返回的参数列表后,就可以创建支付:

    #pragma mark weixin pay//创建微信支付
    - (void)weixinPay
    {
    //_orderId为订单号,作为参数传递给app server
        XQDataHelper *helper = [[XQDataHelper alloc]init];
        [helper createPayWithOrderId:_orderId sucessed:^(id resultObj) {
            NSDictionary *dict = resultObj;
            //获取到实际调起微信支付的参数后,在app端调起支付if(dict){
                NSMutableString *stamp = dict[@"timestamp"];
                //调起微信支付
                PayReq* req   = [[PayReq alloc] init];
                req.openID    = dict [@"appid"];
                req.partnerId = dict [@"partnerid"];
                req.prepayId  = dict [@"prepayid"];
                req.nonceStr  = dict [@"noncestr"];
                req.timeStamp = stamp.intValue;
                req.package   = dict [@"package"];
                req.sign      = dict [@"sign"];
    
                BOOL bs = [WXApi isWXAppSupportApi];
                if (bs) {
                    BOOL isok = [WXApi sendReq:req];
                    if (isok) {
                        DLog(@"调用微信支付成功");
                    }else{
                        DLog(@"调用微信支付失败");
                    }
                }
                else{
                    DLog(@"微信版本过低,不支持支付");
                }
            }
        } failed:^(NSString *strError) {
        }];
    }
    
    #pragma mark weixinpay 回掉//当收到微信支付回掉通知时
    -(void) weixinPayBack:(NSNotification*)notif{
        int code = [notif.object intValue];
        switch (code) {
            case WXSuccess:{
                DLog(@"支付成功");
            }
                break;
            case WXErrCodeUserCancel:{
                DLog(@"支付取消");
            }
                break;
            default:{
               DLog(@"支付失败");
            }
                break;
        }
    }

    4、客户端实现2次签名

    以上就是成功创建支付的实现方法,那么在创建支付时,客户端能否实现2次签名,来生成参数,创建支付呢?答案是肯定的。 
    (好吧,如果app server没有理解支付的交互逻辑,而在创建支付时,只返回给你一个预支付号,让你来实现生成其他需要的参数时,你就可以这么干!) 
    但是,要清楚,支付签名放在客户端是很不安全的,所以,不建议放在客户端来实现。 
    创建支付,首先得创建一个PayReq的对象,其属性有:

    @property (nonatomic, retain) NSString* openID;
    /** 商家向财付通申请的商家id */@property (nonatomic, retain) NSString *partnerId;
    /** 预支付订单 */@property (nonatomic, retain) NSString *prepayId;
    /** 随机串,防重发 */@property (nonatomic, retain) NSString *nonceStr;
    /** 时间戳,防重发 */@property (nonatomic, assign) UInt32 timeStamp;
    /** 商家根据财付通文档填写的数据和签名 */@property (nonatomic, retain) NSString *package;
    /** 商家根据微信开放平台文档对数据做的签名 */@property (nonatomic, retain) NSString *sign;
    

    从微信支付开放文档-调起支付接口 也可以对请求参数有所认识,其中openID即appid,故已有的参数:openID、partnerId、prepayId。那么主要是生成nonceStr、timeStamp、package和sign。其中package又为固定值Sign=WXPay,所以就只需实现nonceStr、timeStamp、sign的生成。

    而在官方文档及Demo中,生成方法都有提及:

    #pragma mark - 生成各种参数
    // 获取时间戳
    - (NSString *)genTimeStamp
    {
        return [NSString stringWithFormat:@"%.0f", [[NSDate date] timeIntervalSince1970]];
    }
    
    /**
     *  获取32位内的随机串noncestr, 防重发
     *
     *  注意:商户系统内部的订单号,32个字符内、可包含字母,确保在商户系统唯一
     */
    - (NSString *)genNonceStr
    {
        return [WXUtil md5:[NSString stringWithFormat:@"%d", arc4random() % 10000]];
    }

    sign的生成规则稍显复杂,相见官方签名算法文档

    // 根据参数生成签名
    - (NSString *)genSign:(NSDictionary *)dict
    {
        NSMutableString *contentString  =[NSMutableString string];
        NSArray *keys = [dict allKeys];
        //按字母顺序排序NSArray *sortedArray = [keys sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
            return [obj1 compare:obj2 options:NSNumericSearch];
        }];
        //拼接字符串for (NSString *categoryId in sortedArray) {
            if (   ![[dict objectForKey:categoryId] isEqualToString:@""]
                && ![categoryId isEqualToString:@"sign"]
                && ![categoryId isEqualToString:@"key"]
                )
            {
                [contentString appendFormat:@"%@=%@&", categoryId, [dict objectForKey:categoryId]];
            }
        }
        //添加key字段
        [contentString appendFormat:@"key=%@", APP_KEY];//即创建应用时生成的//得到MD5 sign签名NSString *md5Sign =[WXUtil md5:contentString];
        return md5Sign;
    }

    需要的参数都生成了,那么创建支付如下:

     - (void)weixinPay
    {
        //_orderId为订单号,作为参数传递给app server,而app server只给你返回了预支付号时
        XQDataHelper *helper = [[XQDataHelper alloc]init];
        [helper createPayWithOrderId:_orderId sucessed:^(id resultObj) {
            NSString *repayId = resultObj;
            if(repayId){
                NSString *timeStamp = [self genTimeStamp];
                //调起微信支付
                PayReq* req   = [[PayReq alloc] init];
                req.openID    = APP_ID;
                req.partnerId = PARTNER_ID;
                req.prepayId  = repayId;//
                req.nonceStr  = [self genNonceStr];
                req.timeStamp = [timeStamp intValue];
                req.package   = @"Sign=WXPay";
    
                // 构造参数列表NSMutableDictionary *params = [NSMutableDictionary dictionary];
                [params setObject:APP_ID forKey:@"appid"];
                [params setObject:APP_KEY forKey:@"appkey"];
                [params setObject:req.nonceStr forKey:@"noncestr"];
                [params setObject:req.package forKey:@"package"];
                [params setObject:req.partnerId forKey:@"partnerid"];
                [params setObject:req.prepayId forKey:@"prepayid"];
                [params setObject:timeStamp forKey:@"timestamp"];
                req.sign = [self genSign:params];
    
                BOOL bs = [WXApi isWXAppSupportApi];
                if (bs) {
                    BOOL isok = [WXApi sendReq:req];
                    if (isok) {
                        DLog(@"调用微信支付成功");
                    }else{
                        DLog(@"调用微信支付失败");
                    }
                }
                else{
                    DLog(@"微信版本过低,不支持支付");
                }
            }
        } failed:^(NSString *strError) {
        }]; 
    }

    5、在过程中遇到的问题

    1、点击支付不跳转到微信app

    • 问题:点击支付,发现app没有跳转到微信,更没有执行回掉,结果发现调用微信支付返回失败
    BOOL isok = [WXApi sendReq:req];
    if (isok) {
         DLog(@"调用微信支付成功");
    }else{
         DLog(@"调用微信支付失败");
    }
    • 原因:因为项目里面还用到umeng分享sdk,其中也集成了libWeChatSDK.a等文件,将其删除,重置Search Paths。(我是将其都彻底删除,然后又重新添加最新的libWeChatSDK.a等文件,这样Search Paths自动设置好了)

    2、跳转到微信后又很快跳转回app,并且报错支付失败

    • 问题:跳转到微信后又很快跳转回app,并且回掉时log错误为:
    retcode = -1, retstr = (null)
    • 原因:参数签名错误,即sign生成出错,可能是某个参数传递错误或者不匹配。若2次签名为服务器实现,那么就找app server探讨吧,若客户端生成,就一步一步检查本地参数生成代码吧。

    题外话: 
    若项目中用到umeng分享来实现微信的分享的话,请注意,umeng为了实现分享后从其他app跳转回来,肯定在AppDelegate.m中实现了如下:

    - (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url{
        return  [UMSocialSnsService handleOpenURL:url];
    }
    - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation{
        return [UMSocialSnsService handleOpenURL:url];
    }

    当然,也是实现了-(void) onResp:(BaseResp*)resp 方法,只是封装了,你看不到而已,此时调用支付,你会发现,根本不会执行你实现的-(void) onResp:(BaseResp*)resp 方法。因为被umeng给实现啦! 
    所以应该区别开来:

    - (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url{
        return  [UMSocialSnsService handleOpenURL:url];
    }
    - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation{
        NSString *urlStr = [NSString stringWithFormat:@"%@",url];
        if ([sourceApplication isEqualToString:@"com.tencent.xin"] && [urlStr containsString:@"pay"]) {
            return [WXApi handleOpenURL:url delegate:self];
        }
        return [UMSocialSnsService handleOpenURL:url];
    }

    这样,微信支付则能正常回调。 

  • 相关阅读:
    如何使用gitbash 把你的代码托管到github
    发送邮件错误常见错误码
    使用snipworks/php-smtp发送邮件
    微信支付demo
    Linux——ps命令
    数组对象互转
    变量名下划线和驼峰互转
    对象数组转换
    curl请求
    百度地图接口的使用
  • 原文地址:https://www.cnblogs.com/yjg2014/p/5026614.html
Copyright © 2011-2022 走看看