微信支付开发

作者: 千煌89 | 来源:发表于2015-11-25 17:23 被阅读1272次

    微信支付的demo写的乱七八糟的,各种参数名以及参数封装混乱,好不容易理清之后,写下这篇文章帮助以后要做微信支付的小伙伴少走弯路。

    业务流程

    业务流程业务流程

    看完这张流程图可得知,我们iOS端要做的事情无非就是

    1. 向服务端请求预支付,获得prepayid以及noncestr
    2. 把参数拼起来签名
    3. 发起支付请求
    4. 处理支付结果

    接入指南

    相信早期做过微信分享之类的小伙伴已经很熟悉了,我不再赘述,请看iOS接入指南

    开发步骤

    1.向服务端请求预支付,获得prepayid以及noncestr
    /**
     *  微信预支付
     *
     *  @param uid   用户id
     *  @param sid   学校id
     *  @param num   数量
     *  @param cid   商品id
     *  @param ip    客户端ip
     *  @param block 回调
     *
     *  @return task
     */
    + (NSURLSessionDataTask *)getPrepayWithUid:(long)uid
                                           sid:(NSInteger)sid
                                           num:(NSInteger)num
                                           cid:(long)cid
                                            ip:(NSString *)ip
                                         block:(void (^)(NSString *prepay_id,NSString *nonce_str, NSError *error))block
    {
        NSMutableDictionary *parameters = [[NSMutableDictionary alloc] init];
        [parameters setObject:@(uid) forKey:@"uid"];
        [parameters setObject:@(sid) forKey:@"sid"];
        [parameters setObject:@(num) forKey:@"num"];
        [parameters setObject:@(cid) forKey:@"cid"];
        [parameters setObject:ip forKey:@"ip"];
        
        NSString *url = @"接口名";
        
        return [[ZXApiClient sharedClient] POST:url parameters:parameters success:^(NSURLSessionDataTask *task, id JSON) {
            NSString *prepay_id = [[JSON objectForKey:@"prepay_id"] stringValue];
            NSString *nonce_str = [[JSON objectForKey:@"nonce_str"] stringValue];
            !block?:block(prepay_id,nonce_str,nil);
        } failure:^(NSURLSessionDataTask *__unused task, NSError *error) {
            !block?:block(nil,nil,error);
        }];
    }
    

    以上接口只是一个示例,除了ip这个参数,其他都根据自己需求来。事实上ip这个参数也并没有什么卵用,因为我看大家都是传的都是192.168.1.1

    2.把参数拼起来签名

    这里我把参数封装成一个类,用来处理各种参数拼接的方法

    ZXWeixinSignParams.h

    //微信开发平台应用id
    #define Weixin_Appid @""
    //财付通商户号
    #define Weixin_MCH_ID @""
    //商户号对应的密钥
    #define Weixin_PARTNER_ID @""
    
    @interface ZXWeixinSignParams : NSObject
    
    @property (nonatomic , copy) NSString *appid;
    @property (nonatomic , copy) NSString *noncestr;
    @property (nonatomic , copy) NSString *package;
    @property (nonatomic , copy) NSString *partnerid;
    @property (nonatomic , copy) NSString *timestamp;
    @property (nonatomic , copy) NSString *prepayid;
    
    - (instancetype)initWithPrepayid:(NSString *)prepayid noncestr:(NSString *)noncestr;
    - (NSString *)sign;
    @end
    

    ZXWeixinSignParams.m

    @implementation ZXWeixinSignParams
    - (instancetype)initWithPrepayid:(NSString *)prepayid noncestr:(NSString *)noncestr
    {
        self = [super init];
        if (self) {
            self.prepayid = prepayid;
            self.noncestr = noncestr;
        }
        return self;
    }
    
    - (NSString *)appid
    {
        if (!_appid) {
            _appid = Weixin_Appid;
        }
        return _appid;
    }
    
    - (NSString *)timestamp
    {
        if (!_timestamp) {
            time_t now;
            time(&now);
            _timestamp = [NSString stringWithFormat:@"%ld", now];
        }
        return _timestamp;
    }
    
    - (NSString *)package
    {
        if (!_package) {
            _package = @"Sign=WXPay";
        }
        return _package;
    }
    
    - (NSString *)partnerid
    {
        if (!_partnerid) {
            _partnerid = Weixin_MCH_ID;
        }
        return _partnerid;
    }
    
    - (NSString *)sign
    {
        NSDictionary *dic = [self keyValues];
        return [self createMd5Sign:[dic mutableCopy]];
    }
    
    - (NSString*)createMd5Sign:(NSMutableDictionary *)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=%@", Weixin_PARTNER_ID];
        //得到MD5 sign签名
        NSString *md5Sign = [[contentString md5] uppercaseString];
        
        return md5Sign;
    }
    @end
    

    - (NSString *)sign方法就是拼接参数签名了。其中NSDictionary *dic = [self keyValues];这部分我使用了MJExtension很方便的字典模型转换,还有NSString *md5Sign = [[contentString md5] uppercaseString];这部分使用了我自己写的md5,demo里的可以直接拿来用。

    3.发起支付请求
    - (void)sendPrepay
    {
          MBProgressHUD *hud = [MBProgressHUD showWaiting:@"" toView:self.view];
          [ZXMessageBill getPrepayWithUid:GLOBAL_UID sid:[ZXUtils sharedInstance].currentSchool.sid num:self.num cid:self.messageCommodity.cid ip:@"196.168.1.1" block:^(NSString *prepay_id, NSString *nonce_str, NSError *error) {
                        self.prepay_id = prepay_id;
                        self.nonce_str = nonce_str;
                        if (self.prepay_id && self.nonce_str) {
                            [hud hide:YES];
                            [self weixinpay];
                        } else {
                            [hud turnToError:@"订单提交失败,请重试"];
                        }
                    }];
    }
    
    - (void)weixinpay
    {
        ZXWeixinSignParams *signParams = [[ZXWeixinSignParams alloc] initWithPrepayid:self.prepay_id noncestr:self.nonce_str];
        
        //调起微信支付
        PayReq *req = [[PayReq alloc] init];
        req.openID = signParams.appid;
        req.partnerId = signParams.partnerid;
        req.prepayId = signParams.prepayid;
        req.nonceStr = signParams.noncestr;
        req.timeStamp = signParams.timestamp.intValue;
        req.package = signParams.package;
        req.sign = [signParams sign];
        
        [WXApi sendReq:req];
    }
    

    这样看起来是不是比demo里清爽的多呢?

    4. 处理支付结果
    
    - (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url
    {
         if ([url.absoluteString hasPrefix:Weixin_Appid]) {
            return [WXApi handleOpenURL:url delegate:self];
        } 
        return YES;
    }
    
    
    - (BOOL)application:(UIApplication *)application
                openURL:(NSURL *)url
      sourceApplication:(NSString *)sourceApplication
             annotation:(id)annotation {
         if ([url.absoluteString hasPrefix:Weixin_Appid]) {
            return [WXApi handleOpenURL:url delegate:self];
        } 
        
        return YES;
    }
    
    - (void)onResp:(BaseResp*)resp
    {
        if([resp isKindOfClass:[SendMessageToWXResp class]])
        {
            SendMessageToWXResp *smresp = (SendMessageToWXResp *)resp;
            if (smresp.errCode == 0) {
                [MBProgressHUD showSuccess:@"分享成功" toView:nil];
                [ZXUmengHelper logShare];
            }
        } else if([resp isKindOfClass:[PayResp class]]){
            //支付返回结果,实际支付结果需要去微信服务器端查询
            
            switch (resp.errCode) {
                case WXSuccess:
                {
                    [MBProgressHUD showSuccess:@"支付成功" toView:nil];
                    [[NSNotificationCenter defaultCenter] postNotificationName:weixnpaySuccessNotification object:nil];
                }
                    NSLog(@"支付成功-PaySuccess,retcode = %d", resp.errCode);
                    break;
                    
                default:
                    [MBProgressHUD showText:[NSString stringWithFormat:@"支付结果:失败!retcode = %d, retstr = %@", resp.errCode,resp.errStr] toView:nil];
                    NSLog(@"错误,retcode = %d, retstr = %@", resp.errCode,resp.errStr);
                    break;
            }
        }
    }
    

    onResp里处理支付结果,成功了发个通知给viewController,失败了就弹个提示。

    常见问题


    如果你跳转到微信里之只弹出了一个确定按钮,那么这时候你需要检查几个方面

    1. appid,mch_id,partner_id是否设置正确。mch_id对应的key其实是partnerid,partner_id对应的key其实是key,微信在这里挖了个大坑,相信踩过的同学不在少数。
    2. timestamp 应为10位
    3. 签名需要排序,并用md5加密,例子里使用了全大写,小写没试过不知道会不会出问题
    4. 如果集成了友盟,并且友盟含有微信分享,先注册友盟,再注册微信

    相关文章

      网友评论

      • maniacRadish:可能是测试帐号的原因吧
      • maniacRadish:url schemes 已经配置了,用的是demo里的测试帐号,这个函数在微信分享的时候会回调,但是在微信支付的时候不回调,并且我写在了appdelegate里的,也遵守了协议
        千煌89:@maniacRadish 微信支付是需要申请的呀,你key拿到了没有,能支付成功吗,是成功了不走回调吗?
      • maniacRadish:我在 - (void)onResp:(BaseResp*)resp 打断点 ,发现这个函数不执行,是什么问题,不知道楼主遇到过没有,求楼主解答
        千煌89:@maniacRadish 这是写在appdelegate里的,还有就是要配置url schemes等等的东西,才会收的到回调

      本文标题:微信支付开发

      本文链接:https://www.haomeiwen.com/subject/bvtdhttx.html