zoukankan      html  css  js  c++  java
  • iOS 微信支付流程详解

    背景

      自微信支付、支付宝支付入世以来,移动端的支付日渐火热。虚拟货币有取代实体货币的趋向(这句纯属扯淡,不用管),支付在app开发中是一项基本的功能,有必要去掌握。从难易程度上讲,不管是微信支付还是支付宝支付都是非常简单的,因为第三方的支付文档非常详细,而且他们内部的安全性也非常高。作为使用这些支付策略的我们,只需要掌握流程,能够实现正常支付的功能即可。为什么要写下这篇博文,原因有二。其一,微信支付流程中有坑,其二,以后忘记了可以拿出来看看。

    配置

      1.微信支付需要两个账号,财付通和微信开发者,注册完成后需要开通支付功能,这些流程就不用多说了。在所有申请成功后,我们要取出我们支付功能需要的key:

    appID、app密钥(微信发给你的邮件中有如何生成密钥的链接)、商户号。

      2.在Xcode中配置app ID,需要设置下白名单,在url中配置app ID,不清楚的童鞋可以百度一下。

    支付

      从此处开始,进入本次的主题,开始支付。支付的过程且分为四个步骤:

    第一步  获取订单号

      获取订单号的途径可以是客户端自己生成,也可以去服务器生成,不过一般都是服务器生成。假设拿到了订单号设为order_no。

    第二步  统一下单

      这个过程是非常关键的一步,也是坑常驻的一步。统一下单有的人做法是在服务器操作,有的在客户端,不管在哪下单都是有必要搞懂的。接下来看看统一下单必须要的参数列表:

    /*应用ID 微信开放平台审核通过的应用app ID*/
    @property (nonatomic, copy) NSString *appid;
    /*商户号 微信支付分配的商户号*/
    @property (nonatomic, copy) NSString *mch_id;
    /*随机字符串 随机字符串,不长于32位*/
    @property (nonatomic, copy) NSString *nonce_str;
    /*签名*/
    @property (nonatomic, copy) NSString *sign;
    /*商品描述 天天爱消除-游戏充值。*/
    @property (nonatomic, copy) NSString *body;
    /*商户订单号*/
    @property (nonatomic, copy) NSString *out_trade_no;
    /*总金额 订单总金额,单位为分*/
    @property (nonatomic, copy) NSString *total_fee;
    /*终端IP*/
    @property (nonatomic, copy) NSString *spbill_create_ip;
    /*通知地址*/
    @property (nonatomic, copy) NSString *notify_url;
    /*交易类型*/
    @property (nonatomic, copy) NSString *trade_type;

      其中,sign是其他所有参数按照key1=value1&key2=value2...的方式拼接,然后进行加密得到。参数拼接按照字母排序,举个例子,参数为appid=@"id",mch_id=@"mch"得到的字符串应该是:@"appid=id&mch_id=mch",然后对该字符串进行加密,如下述代码

    - (instancetype)initWithDicInfo:(NSDictionary *)infoDic{

        if (self = [super init]) {

            self.appid = WECHAT_SHARE_APPID;

            self.mch_id = WECHAT_MCH_ID;

            self.nonce_str = [AppMethod getRandomString];

            self.body = @"test";

            self.out_trade_no = [infoDic formateObjectForKey:@"order_no"];

            self.total_fee = [NSString stringWithFormat:@"%@", [infoDic formateObjectForKey:@"amount"]];

            self.spbill_create_ip = [AppMethod deviceIPAdress];

            self.notify_url = [NSString stringWithFormat:@"%@%@", BASE_URL, WECHAT_NOTI_URL];

            self.trade_type = @"APP";

            self.payDic = [NSMutableDictionary dictionary];

            [self.payDic setValue:self.appid forKey:@"appid"];

            [self.payDic setValue:self.mch_id forKey:@"mch_id"];

            [self.payDic setValue:self.nonce_str forKey:@"nonce_str"];

            [self.payDic setValue:self.body forKey:@"body"];

            [self.payDic setValue:self.out_trade_no forKey:@"out_trade_no"];

            [self.payDic setValue:self.total_fee forKey:@"total_fee"];

            [self.payDic setValue:self.spbill_create_ip forKey:@"spbill_create_ip"];

            [self.payDic setValue:self.notify_url forKey:@"notify_url"];

            [self.payDic setValue:self.trade_type forKey:@"trade_type"];

            self.sign = [self partnerSignOrder:self.payDic];

            [self.payDic setValue:self.sign forKey:@"sign"];

        }

        return self;

    }

    - (NSString *)partnerSignOrder:(NSDictionary*)paramDic{
        NSArray *keyArray = [paramDic allKeys];
        NSMutableArray *sortedKeyArray = [NSMutableArray arrayWithArray:keyArray];
        [sortedKeyArray sortUsingComparator:^NSComparisonResult(NSString* key1, NSString* key2) {
            return [key1 compare:key2];
        }];
        NSMutableString *paramString = [NSMutableString stringWithString:@""];
        // 拼接成 A=B&X=Y
        for (NSString *key in sortedKeyArray){
            if ([paramDic[key] length] != 0){
                [paramString appendFormat:@"&%@=%@", key, paramDic[key]];
            }
        }
        if ([paramString length] > 1){
            [paramString deleteCharactersInRange:NSMakeRange(0, 1)];    // remove first '&'
        }
        [paramString appendFormat:@"&key=%@", WeChatPARTNER_ID];//app密钥
        return [[AppMethod signString:paramString] uppercaseString];
    }
    
    AppMethod.m
    + (NSString *)signString:(NSString*)origString{
        const char *original_str = [origString UTF8String];
        unsigned char result[32];
        CC_MD5(original_str, (CC_LONG)strlen(original_str), result);//调用md5
        NSMutableString *hash = [NSMutableString string];
        for (int i = 0; i < 16; i++){
            [hash appendFormat:@"%02X", result[i]];
        }
        return hash;
    }

      这样,统一下单的参数已经准备好了,下面开始请求微信的下单接口:https://api.mch.weixin.qq.com/pay/unifiedorder,现在坑又来了。按照微信的下单说明,传给微信服务器的参数必须是XML格式的数据。如果你传过这种类型的自然好办,不过我猜大多数童鞋没有传过这种类型的数据,好在AF有提供方法,不然就等着哭吧。继续看代码

    + (void)postWechatPayWithUrl:(NSString *)url
                          params:(id)params
                      andSuccess:(requestSuccessResult)successBlock
                      andFailure:(requestFailureResult)failureBlock{
        NSString *string = [params XMLString];//这里需要导入XMLDictionary文件,里面有该方法
        AFHTTPSessionManager *session = [AFHTTPSessionManager manager];
        // 这里传入的XML字符串只是形似XML,但不是正确是XML格式,需要使用AF方法进行转义
        session.responseSerializer = [[AFHTTPResponseSerializer alloc] init];
        [session.requestSerializer setValue:@"text/xml; charset=utf-8" forHTTPHeaderField:@"Content-Type"];
        [session.requestSerializer setValue:url forHTTPHeaderField:@"SOAPAction"];
        [session.requestSerializer setQueryStringSerializationWithBlock:^NSString *(NSURLRequest *request, NSDictionary *parameters, NSError *__autoreleasing *error) {
            return string;
        }];
        
        [session POST:url parameters:params progress:^(NSProgress * _Nonnull uploadProgress) {
            
        } success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
            successBlock(responseObject);
        } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
            HPError *hpError = [HPError errorWithCode:error.code desc:error.description];
            failureBlock(hpError);
        }];
    }

      按照上述的步骤,到这里就可以正常下单了,如果微信服务器返回给你的数据中有"result_code" = SUCCESS; "return_code" = SUCCESS,说明下单成功,这个步骤也到此结束。

    第三步  调起微信客户端,并完成支付

      如果第二步正常下单,那么微信会返回给你预支付ID,这个ID在最后的支付中至关重要,下面的代码是比较统一的,大家都这么写。

    HPWechatProduct *product = [[HPWechatProduct alloc] initWithDic:result];
    PayReq *req              = [[PayReq alloc] init];
    req.partnerId            = product.partnerid;
    req.prepayId             = product.prepayid;
    req.nonceStr             = product.noncestr;
    req.timeStamp            = [product.timestamp intValue];
    req.package              = product.package;
    req.sign                 = product.sign;
    BOOL flag = [WXApi sendReq:req];
    
    HPWechatProduct.m
    - (instancetype)initWithDic:(NSDictionary *)dic{
        if (self = [super init]) {
            self.appid = WECHAT_SHARE_APPID;
            self.partnerid = mah_id;
            self.prepayid = [dic formateObjectForKey:@"prepay_id"];
            self.package = @"Sign=WXPay";
            self.noncestr = [AppMethod getRandomString];
            self.timestamp = [self getTime];
            NSMutableDictionary *dic = [NSMutableDictionary dictionary];
            [dic setValue:self.appid forKey:@"appid"];
            [dic setValue:self.partnerid forKey:@"partnerid"];
            [dic setValue:self.prepayid forKey:@"prepayid"];
            [dic setValue:self.package forKey:@"package"];
            [dic setValue:self.noncestr forKey:@"noncestr"];
            [dic setValue:self.timestamp forKey:@"timestamp"];
            self.sign = [self partnerSignOrder:dic];
        }
        return self;
    }
    - (NSString *)partnerSignOrder:(NSDictionary*)paramDic{
        NSArray *keyArray = [paramDic allKeys];
        NSMutableArray *sortedKeyArray = [NSMutableArray arrayWithArray:keyArray];
        [sortedKeyArray sortUsingComparator:^NSComparisonResult(NSString* key1, NSString* key2) {
            return [key1 compare:key2];
        }];
        NSMutableString *paramString = [NSMutableString stringWithString:@""];
        // 拼接成 A=B&X=Y
        for (NSString *key in sortedKeyArray){
            if ([paramDic[key] length] != 0){
                [paramString appendFormat:@"&%@=%@", key, paramDic[key]];
            }
        }
        if ([paramString length] > 1){
            [paramString deleteCharactersInRange:NSMakeRange(0, 1)];    // remove first '&'
        }
        [paramString appendFormat:@"&key=%@", WeChatPARTNER_ID];
        return [[AppMethod signString:paramString] uppercaseString];
    }
    
    - (NSString *)getTime{
        NSTimeInterval interval = [[NSDate date] timeIntervalSince1970];
        return [NSString stringWithFormat:@"%ld", (long)interval];
    }
    
    
    AppMethod.m
    + (NSString *)getRandomString
    {
        NSString *str = [NSString stringWithFormat:@"%s",genRandomString(32)];
        return str;
    }

      如果到了这一步,而且跑到了微信并完成了支付,那么微信会有一个回调,告诉你支付成功了。然而真的成功了嘛,请继续看第四步。

    第四步  去服务器查询是否支付成功

      即使微信告诉你支付成功了,你也不能相信,只有钱真正打到你们的账号里面了,才算支付成功。任何时候都不能以微信的回调的值判断支付是否成功(这是微信文档说的)。

  • 相关阅读:
    NetBeans IDE 6.8 发布候选版 1 已经可用!
    怎样的提问才是有效的提问
    Drop 简介
    NetBeans 时事通讯(刊号 # 83 Dec 03, 2009)
    体悟
    《程序员》杂志第8期笔记
    svmlight
    9158.com amazing
    别人玩女人,屌丝玩手机,我没得玩了orz
    光看菜谱成不了好厨师
  • 原文地址:https://www.cnblogs.com/lmfboke/p/7479705.html
Copyright © 2011-2022 走看看