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

  • 一句话集成微信支付,支付宝支付以及内购
    可以在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支付封装(微信/支付宝)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,530评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,403评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,120评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,770评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,758评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,649评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,021评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,675评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,931评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,751评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,410评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,004评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,969评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,042评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,493评论 2 343

推荐阅读更多精彩内容