美文网首页iOS支付技术iOS CodingIos@IONIC
iOS开发-微信/支付宝/内购封装

iOS开发-微信/支付宝/内购封装

作者: 看我的大白眼 | 来源:发表于2017-03-25 16:14 被阅读219次
    • 一句话集成微信支付,支付宝支付以及内购
      可以在github上下载的工具类

    源码

    • 如何使用

    1.将工具类拖入工程
    2.AppDelegate代码如下:

    - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
    {
            
        return [[XNPayManager sharedPayManager] handleOpenURL:url];
    }
    
    
    // NOTE: 9.0以后使用新API接口
    - (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<NSString*, id> *)options
    {
        return [[XNPayManager sharedPayManager] handleOpenURL:url];
    }
    
    

    3.在使用的地方调起微信或者支付宝

    • 支付宝

    TIPS:拼装参数应在商户服务端完成,商户服务端直接将组装和签名后的请求串orderString传给客户端,客户端直接传给SDK发起请求。文档和Demo是为了示例效果在客户端实现。所以我们项目中应该按照次操作

      // params应该是由后台返回的
     [[XNPayManager sharedPayManager] ALIPayWithPayParams: params success:^(PayCode code) {
                        // 成功业务逻辑
                        DLog(@"成功");
      } failure:^(PayCode code) {
                        // 失败业务逻辑
                        DLog(@"失败");
     }]
    
    
    • 微信支付
      // dict应该是由后台返回的
      [[XNPayManager sharedPayManager] WXPayWithPayDict: dict success:^(PayCode code) {
                        // 成功业务逻辑
                        DLog(@"成功");
      } failure:^(PayCode code) {
                        // 失败业务逻辑
                        DLog(@"成功");
     }];
    
    
    • 内购
    //productId 产品唯一标示
            [[XNPayManager sharedPayManager] requestProductData: productId success:^(PayCode code) {
               // 成功业务逻辑
            } failure:^(PayCode code) {
                // 失败业务逻辑
            }];
    
    
    • 详细代码

    在封装的代码中没有使用通知,因为通知是全局的,然后在项目中出现了一下问题,比如:待支付订单->订单详情 这两个页面都有支付的操作.如果使用通知:这两个控制器支付成功\失败的业务逻辑就会冲突

    1.声明支付单例

    + (instancetype)sharedPayManager {
        static dispatch_once_t onceToken;
        static XNPayManager *instance;
        dispatch_once(&onceToken, ^{
            instance = [[XNPayManager alloc] init];
        });
        return instance;
    }
    

    2.自定义支付状态码,其实也可以不用定义

    /**
     支付状态码
     */
    typedef NS_ENUM(NSInteger, PayCode) {
        WXSUCESS            = 1001,   /**成功*/
        WXERROR             = 1002,   /**失败*/
        WXSCANCEL           = 1003,   /**取消*/
        
        ALIPAYSUCESS        = 1101,   /**支付宝支付成功*/
        ALIPAYERROR         = 1102,   /**支付宝支付错误*/
        ALIPAYCANCEL        = 1103,   /**支付宝支付取消*/
        
        APPSTOREPAYSUCESS   = 1201,   /**内购支付成功*/
        APPSTOREPAYERROR    = 1201,   /**内购支付成功*/
        APPSTOREPAYCANCEL   = 1201,   /**内购支付成功*/
    };
    

    3.微信支付

    /**
     微信支付
     
     @param dict 微信订单字典(全部由后台拼接好给iOS端)
     @param successBlock 成功的回调
     @param failBolck 失败的回调
     */
    - (void)WXPayWithPayDict:(NSDictionary *)dict
                     success:(void(^)(PayCode code)) successBlock
                     failure:(void(^)(PayCode code)) failBolck {
        self.successBlock = successBlock;
        self.failBolck = failBolck;
        NSString *strMsg = nil;
        //1. 判断是否安装微信
        if (![WXApi isWXAppInstalled]) {
            NSLog(@"您尚未安装\"微信App\",请先安装后再返回支付");
            strMsg = @"您尚未安装\"微信App\",请先安装后再返回支付";
            [self tipMessageAlert:nil message:strMsg];
            return;
        }
        
        //2.判断微信的版本是否支持最新Api
        if (![WXApi isWXAppInstalled]) {
            NSLog(@"您微信当前版本不支持此功能,请先升级微信应用");
            strMsg = @"您微信当前版本不支持此功能,请先升级微信应用";
            [self tipMessageAlert:nil message:strMsg];
            return;
        }
        
        if (!kDictIsEmpty(dict)) {
            
            //调起微信支付
            PayReq *req = [[PayReq alloc]init];
            req.openID = dict[@"appid"];
            req.partnerId = dict[@"partnerid"];
            req.prepayId = dict[@"prepayid"];
            req.nonceStr = dict[@"noncestr"];
            req.timeStamp = [dict[@"timestamp"] intValue];
            req.package = @"Sign=WXPay";
            req.sign = dict[@"sign"];
            [WXApi sendReq:req];
        }
    
    }
    
    #pragma mark - WXApiDelegate
    //支付结果回调
    /// - see [支付结果回调](https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=8_5)
    - (void)onResp:(BaseResp *)resp {
        
        NSString *strMsg = [NSString stringWithFormat:@"errcode:%d",resp.errCode];
        
        //回调中errCode值列表:
        // 0 成功 展示成功页面
        //-1 错误 可能的原因:签名错误、未注册APPID、项目设置APPID不正确、注册的APPID与设置的不匹配、其他异常等
        //-2 用户取消 无需处理。发生场景:用户不支付了,点击取消,返回APP
        
        if ([resp isKindOfClass:[PayResp class]]) {
            // 支付返回结果,实际支付结果需要去微信服务器端查询
            switch (resp.errCode) {
                case WXSuccess:{
                    strMsg = @"支付结果:成功!";
                    self.successBlock(WXSUCESS);
                    DLog(@"支付成功-PaySuccess,retcode = %d", resp.errCode);
                    break;
                }
                case WXErrCodeUserCancel:{
                    strMsg = @"支付结果:取消";
                    self.failBolck(WXSCANCEL);
                    DLog(@"支付取消-PayCancel,retcode = %d", resp.errCode);
                }
                default:{
                    strMsg = @"支付结果:失败";
                    self.failBolck(WXERROR);
                    DLog(@"错误,retcode = %d, retstr = %@", resp.errCode,resp.errStr);
                    break;
                }
            }
            [self tipMessageAlert:@"支付结果" message:strMsg];
        }
        
    }
    

    4.支付宝

    /**
     支付宝支付
     
     @param params 支付宝支付参数(全部由后台拼接好给iOS端)
     @param successBlock 成功的回调
     @param failBolck 失败的回调
     */
    - (void)ALIPayWithPayParams:(NSString *)params
                        success:(void(^)(PayCode code)) successBlock
                        failure:(void(^)(PayCode code)) failBolck {
        
        self.successBlock = successBlock;
        self.failBolck = failBolck;
        NSString *appScheme = @"appScheme";
        [[AlipaySDK defaultService] payOrder:params fromScheme:appScheme callback:^(NSDictionary *resultDic) {
            DLog(@"我这里是payVC%@",resultDic);
            DLog(@"%@",resultDic[@"memo"]);
            [self aliPayResult:resultDic];
        }];
    
    
    }
    
    #pragma mark - 支付宝支付结果处理
    
    - (void)aliPayResult:(NSDictionary *)resultDic {
        
        // 返回结果需要通过 resultStatus 以及 result 字段的值来综合判断并确定支付结果。 在 resultStatus=9000,并且 success="true"以及 sign="xxx"校验通过的情况下,证明支付成功。其它情况归为失败。较低安全级别的场合,也可以只通过检查 resultStatus 以及 success="true"来判定支付结果
        NSInteger resultStatus = [resultDic[@"resultStatus"] integerValue];
        
        if (resultDic && [resultDic objectForKey:@"resultStatus"]) {
            switch (resultStatus) {
                case 9000:
                    [self tipMessageAlert:@"支付结果" message:@"订单支付成功"];
                    self.successBlock(ALIPAYSUCESS);
                    break;
                case 8000:
                    [self tipMessageAlert:@"支付结果" message:@"正在处理中"];
                    self.failBolck(ALIPAYERROR);
                    break;
                case 4000:
                    [self tipMessageAlert:@"支付结果" message:@"订单支付失败,请稍后再试"];
                    self.failBolck(ALIPAYERROR);
                    break;
                case 6001:
                    [self tipMessageAlert:@"支付结果" message:@"已取消支付"];
                    self.failBolck(ALIPAYCANCEL);
                    break;
                case 6002:
                    [self tipMessageAlert:@"支付结果" message:@"网络连接错误,请稍后再试"];
                    self.failBolck(ALIPAYERROR);
                    break;
                default:
                    break;
            }
        }
        
    }
    
    

    5.内购

    /**
     内购
     
     @param productId productId
     @param successBlock 成功的回调
     @param failBolck 失败的回调
     */
    - (void)requestProductData:(NSString *)productId
                       success:(void(^)(PayCode code)) successBlock
                       failure:(void(^)(PayCode code)) failBolck {
    
        if ([SKPaymentQueue canMakePayments]) {
            self.successBlock = successBlock;
            self.failBolck = failBolck;
            // 最好设置上
            MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:kKeyWindow animated:YES];
            hud.labelText = @"正在购买,请勿离开...";
            hud.dimBackground = YES;
            self.productId = productId;
            NSArray *productArr = [[NSArray alloc]initWithObjects:productId, nil];
            
            NSSet *productSet = [NSSet setWithArray:productArr];
            
            SKProductsRequest *request = [[SKProductsRequest alloc]initWithProductIdentifiers:productSet];
            
            request.delegate = self;
            
            [request start];
            
        }else{
            DLog(@"不允许程序内付费");
        }
    
    }
    
    
    #pragma mark - SKProductsRequestDelegate
    // 收到产品返回信息
    - (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
        
        
        NSArray *productArr = response.products;
        
        if ([productArr count] == 0) {
            DLog(@"没有该商品");
            [MBProgressHUD hideAllHUDsForView:kKeyWindow animated:YES];
            return;
        }
        
        DLog(@"productId = %@",response.invalidProductIdentifiers);
        DLog(@"产品付费数量 = %zd",productArr.count);
        
        SKProduct *p = nil;
        
        for (SKProduct *pro in productArr) {
            DLog(@"description:%@",[pro description]);
            DLog(@"localizedTitle:%@",[pro localizedTitle]);
            DLog(@"localizedDescription:%@",[pro localizedDescription]);
            DLog(@"price:%@",[pro price]);
            DLog(@"productIdentifier:%@",[pro productIdentifier]);
            if ([pro.productIdentifier isEqualToString:self.productId]) {
                p = pro;
            }
        }
        
        SKPayment *payment = [SKPayment paymentWithProduct:p];
        
        //发送内购请求
        [[SKPaymentQueue defaultQueue] addPayment:payment];
        
    }
    
    #pragma mark - SKRequestDelegate
    
    - (void)requestDidFinish:(SKRequest *)request {
        
        DLog(@"获取产品成功");
    //    [MBProgressHUD hideHUDForView:kKeyWindow animated:YES];
        
        
    }
    - (void)request:(SKRequest *)request didFailWithError:(NSError *)error {
        
        [MBProgressHUD hideHUDForView:kKeyWindow animated:YES];
        
        MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:kKeyWindow animated:YES];
        hud.labelText = @"购买失败";
        hud.mode = MBProgressHUDModeText;
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            [MBProgressHUD hideHUDForView:kKeyWindow animated:YES];
        });
        
    }
    
    
    
    // 监听购买结果
    //SKPaymentTransactionObserver
    - (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray<SKPaymentTransaction *> *)transactions {
        
        
        for (SKPaymentTransaction *tran in transactions) {
            switch (tran.transactionState) {
                case SKPaymentTransactionStatePurchased: //交易完成
                    [self completeTransaction:tran];
                    // Remove the transaction from the payment queue.
                    [[SKPaymentQueue defaultQueue] finishTransaction: tran];
                    break;
                case SKPaymentTransactionStatePurchasing: //商品添加进列表
                    break;
                case SKPaymentTransactionStateRestored: //购买过
                    [self restoreTransaction:tran];
                    break;
                case SKPaymentTransactionStateFailed: //交易失败
                    [self failedTransaction:tran];
                    // Remove the transaction from the payment queue.
                    [[SKPaymentQueue defaultQueue] finishTransaction: tran];
                    break;
                    
                default:
                    break;
            }
        }
    }
    
    - (void)completeTransaction:(SKPaymentTransaction *)transaction {
        NSString * productIdentifier = transaction.payment.productIdentifier;
        DLog(@"productIdentifier Product id:%@", productIdentifier);
        NSString *transactionReceiptString= nil;
        
        //系统IOS7.0以上获取支付验证凭证的方式应该改变,切验证返回的数据结构也不一样了。
        
        // 验证凭据,获取到苹果返回的交易凭据
        // appStoreReceiptURL iOS7.0增加的,购买交易完成后,会将凭据存放在该地址
        NSURL *receiptUrl=[[NSBundle mainBundle] appStoreReceiptURL];
        NSData * receiptData = [NSData dataWithContentsOfURL:receiptUrl];
        
        transactionReceiptString = [receiptData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
        
        DLog(@"transactionReceiptString == %@",transactionReceiptString);
        
        if ([productIdentifier length] > 0) {
    
            self.hud.labelText = @"正在验证,请勿离开...";
    
             // 请求自己的服务器去验证用户购买结果
            // 往后台验证,只有服务器有反应就hide
            [MBProgressHUD hideAllHUDsForView:kKeyWindow animated:YES];
        }
    
    
    }
    - (void)failedTransaction:(SKPaymentTransaction *)transaction {
        if(transaction.error.code != SKErrorPaymentCancelled) {
            DLog(@"购买失败");
            self.failBolck(APPSTOREPAYCANCEL);
        } else {
            DLog(@"用户取消交易");
            self.failBolck(APPSTOREPAYCANCEL);
        }
        [MBProgressHUD hideHUDForView:kKeyWindow animated:YES];
        [SMGlobalMethod showViewCenter:kKeyWindow.center longMessage:@"购买失败"];
    }
    
    - (void)restoreTransaction:(SKPaymentTransaction *)transaction {
        // 对于已购商品,处理恢复购买的逻辑
        [[SKPaymentQueue defaultQueue] finishTransaction: transaction];
    }
    
    
    

    6.单例回调

    #pragma mark - 单例类回调
    - (BOOL)handleOpenURL:(NSURL *)url {
        
        if ([url.host isEqualToString:@"safepay"])
        {
            //跳转支付宝钱包进行支付,处理支付结果
            [[AlipaySDK defaultService] processOrderWithPaymentResult:url standbyCallback:^(NSDictionary *resultDic) {
                DLog(@"result = %@",resultDic);
                DLog(@"openURL : 支付宝回调 : result = %@",resultDic);
                [self aliPayResult:resultDic];
            }];
            // 授权跳转支付宝钱包进行支付,处理支付结果
            [[AlipaySDK defaultService] processAuth_V2Result:url standbyCallback:^(NSDictionary *resultDic) {
                NSLog(@"result = %@",resultDic);
                // 解析 auth code
                NSString *result = resultDic[@"result"];
                NSString *authCode = nil;
                if (result.length>0) {
                    NSArray *resultArr = [result componentsSeparatedByString:@"&"];
                    for (NSString *subResult in resultArr) {
                        if (subResult.length > 10 && [subResult hasPrefix:@"auth_code="]) {
                            authCode = [subResult substringFromIndex:10];
                            break;
                        }
                    }
                }
                DLog(@"授权结果 authCode = %@", authCode?:@"");
            }];
            
            return [url.host isEqualToString:@"safepay"];
        }
        else
        {
            return [WXApi handleOpenURL:url delegate:self];
        }
        
    }
    
    

    参考博客:

    iOS支付封装(微信/支付宝)

    相关文章

      网友评论

      • somesOne:我想知道的是,保存购买凭证,你不存入用户的uid、以及第一次请求的时候,自己的服务器返回的订单号吗?想问下,你成功之后删除了本地信息没?,如果存入钥匙串中保存、或者是偏好设置、如果丢单的信息多了,会覆盖吗 ?
        看我的大白眼:http://blog.csdn.net/goodeveningbaby/article/details/53372934
        看我的大白眼:@somesOne 会被覆盖了 这种情况避免不了! 目前我们是改变了策略,使用服务器来保存用户的相关订单信息! 掉单由于各种原因避免不了

      本文标题:iOS开发-微信/支付宝/内购封装

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