内购


#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

//typedef NS_ENUM(NSUInteger, APTYPE) {
//    AP_WECHAT,
//    AP_ALIPAY,
//};


//@protocol APDelegate <NSObject>
//-(void)APSuccess:(APTYPE)type;
//-(void)APFail:(APTYPE)type;
//@end


static NSString * const USERDEFAULT_KEY = @"transaction";

typedef enum : NSUInteger {
    VTPaymentTransactionStateNoPaymentPermission,//没有Payment权限
    VTPaymentTransactionStateAddPaymentFailed,//addPayment失败
    VTPaymentTransactionStatePurchasing,//正在购买
    VTPaymentTransactionStatePurchased,//购买完成(销毁交易)
    VTPaymentTransactionStateFailed,//购买失败(销毁交易)
    VTPaymentTransactionStateCancel,//用户取消
    VTPaymentTransactionStateRestored,//恢复购买(销毁交易)
    VTPaymentTransactionStateDeferred,//最终状态未确定
} VTPaymentTransactionState;

@class SKPaymentTransaction;

@protocol VTInAppPurchasingDelegate <NSObject>

@required
- (BOOL)isProductIdentifierAvailable:(NSString*)productIdentifier;
@optional
- (void)updatedTransactions:(VTPaymentTransactionState)state;
//购买成功
- (void)buySuccess;
//购买失败
- (void)buyFailed:(NSString*)errorInfo;

@end



@interface APManager : NSObject

+(instancetype) shared;

@property(nonatomic,weak) id<VTInAppPurchasingDelegate> delegate;
- (void)identifyCanMakePayments:(NSArray*)requestArray;
//-(void)handleOpenURL:(NSURL *)url;
//-(void)sendOrder:(APTYPE)type Order:(NSDictionary *)order;

@end

NS_ASSUME_NONNULL_END
#import "APManager.h"
#import <StoreKit/StoreKit.h>
//#import "WXApi.h"
//#import <AlipaySDK/AlipaySDK.h>

//#define WECHAT @"pay"
//#define WECHATSCHEME @""
//#define ALIPAY @"safepay"
//#define ALIPAYSCHEME @"alipay"
//#define WX_AppID @""


#define ITMS_VERIFY_RECEIPT_URL   @"https://sandbox.itunes.apple.com/verifyReceipt"
//@"https://buy.itunes.apple.com/verifyReceipt"

#define isServiceVerify 0

@interface APManager ()<SKPaymentTransactionObserver,SKProductsRequestDelegate/*WXApiDelegate*/>
@property (nonatomic, strong) SKProductsRequest *request;
@end

@implementation APManager


+(instancetype) shared{
    static APManager *_singleInstance;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _singleInstance = [[APManager alloc]init];
    });
    
    return _singleInstance;
}


-(instancetype)init{
    if (self = [super init]) {
        
         [[SKPaymentQueue defaultQueue] addTransactionObserver:self];
    //微信支付
//        [WXApi registerApp:WX_AppID];
    }
    return self;
}


- (void)identifyCanMakePayments:(NSArray*)requestArray{
    if (requestArray.count == 0) {
        if (self.delegate && [self.delegate respondsToSelector:@selector(updatedTransactions:)]) {
            [self.delegate updatedTransactions:VTPaymentTransactionStateAddPaymentFailed];
        }
        return;
    }
    
    if ([SKPaymentQueue canMakePayments]) {
        [self releaseRequest];
        self.request = [[SKProductsRequest alloc]initWithProductIdentifiers:[NSSet setWithArray:requestArray]];
        self.request.delegate = self;
        [self.request start];
    }else{
        if (self.delegate && [self.delegate respondsToSelector:@selector(updatedTransactions:)]) {
            [self.delegate updatedTransactions:VTPaymentTransactionStateNoPaymentPermission];
        }
    }
    
}




- (void)releaseRequest{
    if(_request) {
        [_request cancel];
        _request.delegate=nil;
        _request=nil;
    }
}

-(void)dealloc{
    [self releaseRequest];
    [[SKPaymentQueue defaultQueue] removeTransactionObserver:self];
}


#pragma mark ------------ SKProductsRequestDelegate

- (void)productsRequest:(SKProductsRequest*)request didReceiveResponse:(SKProductsResponse*)response{
    NSArray * myProducts = response.products;
    for(SKProduct*product in myProducts){
        NSLog(@"SKProduct 描述信息%@", [product description]);
        NSLog(@"产品标题 %@", product.localizedTitle);
        NSLog(@"产品描述信息: %@" , product.localizedDescription);
        NSLog(@"价格: %@", product.price);
        NSLog(@"Product id: %@" , product.productIdentifier);
        
        
        
        if (self.delegate && [self.delegate respondsToSelector:@selector(isProductIdentifierAvailable:)]) {
            if ([self.delegate isProductIdentifierAvailable:product.productIdentifier]) {
                SKPayment *payment = [SKPayment paymentWithProduct:product];
                [[SKPaymentQueue defaultQueue] addPayment:payment];
                return;
            }
        }
    }
    
    if (self.delegate && [self.delegate respondsToSelector:@selector(updatedTransactions:)]) {
        [self.delegate updatedTransactions:VTPaymentTransactionStateAddPaymentFailed];
    }
   
}

- (void)request:(SKRequest *)request didFailWithError:(NSError *)error{
    NSLog(@"-------弹出错误信息----------");
    
}

#pragma mark -------- SKPaymentTransactionObserver


-(void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray<SKPaymentTransaction *> *)transactions{
    
    for (SKPaymentTransaction *transaction in transactions) {
        VTPaymentTransactionState state;
        switch (transaction.transactionState) {
            case SKPaymentTransactionStatePurchasing:
                
                state = VTPaymentTransactionStatePurchasing;
                
                break;
            case SKPaymentTransactionStatePurchased:
                
                //防止漏单
//                [[NSUserDefaults standardUserDefaults] setObject:transaction forKey:USERDEFAULT_KEY];
//                [[NSUserDefaults standardUserDefaults] synchronize];
                
                state = VTPaymentTransactionStatePurchased;
                if (isServiceVerify) {
                    [self completeTransaction:transaction];
                }else{
                    [self verifyPurchase:transaction];
                }
                
                break;
            case SKPaymentTransactionStateFailed:
            {
                if (transaction.error.code!= SKErrorPaymentCancelled) {
                    state = VTPaymentTransactionStateFailed;
                }else{
                    state = VTPaymentTransactionStateCancel;
                }
                [self finshTransaction:transaction];
            }
                break;
            case SKPaymentTransactionStateRestored:
                state = VTPaymentTransactionStateRestored;
                [self finshTransaction:transaction];
                break;
            case SKPaymentTransactionStateDeferred:
                state = VTPaymentTransactionStateDeferred;
                break;
            default:
                break;
        }
        
        
        if (self.delegate && [self.delegate respondsToSelector:@selector(updatedTransactions:)]) {
            [self.delegate updatedTransactions:state];
        }
    }
}

// Sent when an error is encountered while adding transactions from the user's purchase history back to the queue.
- (void)paymentQueue:(SKPaymentQueue*)queue restoreCompletedTransactionsFailedWithError:(NSError*)error
{
    NSLog(@"restoreCompletedTransactionsFailedWithError");
}
// Sent when all transactions from the user's purchase history have successfully been added back to the queue.
- (void)paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue*)queue
{
    NSLog(@"paymentQueueRestoreCompletedTransactionsFinished");
}
// Sent when the download state has changed.
- (void)paymentQueue:(SKPaymentQueue*)queue updatedDownloads:(NSArray *)downloads
{
    NSLog(@"updatedDownloads");
}


- (void)paymentQueue:(SKPaymentQueue*)queue removedTransactions:(NSArray *)transactions
{
    NSLog(@"---removedTransactions");
}


#pragma mark ------ private methods

- (void)completeTransaction:(SKPaymentTransaction*)transaction{
    //服务器校验
    
}

- (void)verifyPurchase:(SKPaymentTransaction*)transaction{
    NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
    NSData*receiptData = [NSData dataWithContentsOfURL:receiptURL];
    NSString *encodeStr = [receiptData base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithLineFeed];
    NSURL *url = [NSURL URLWithString:ITMS_VERIFY_RECEIPT_URL];
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:10.0f];
    
    NSString *payload = [NSString stringWithFormat:@"{\"receipt-data\" : \"%@\"}", encodeStr];
    NSData *payloadData = [payload dataUsingEncoding:NSUTF8StringEncoding];
    [request setHTTPBody:payloadData];
    [request setHTTPMethod:@"POST"];
    NSURLResponse*response = nil;
    // 此请求返回的是一个json结果  将数据反序列化为数据字典
    NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:nil];
    if(data ==nil) {
        return;
    }
    NSDictionary *jsonResponse = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:nil];
    if(jsonResponse !=nil) {
        if([[jsonResponse objectForKey:@"status"]intValue] ==0)
            {
                //通常需要校验:bid,product_id,purchase_date,status
                if (self.delegate && [self.delegate respondsToSelector:@selector(buySuccess)]) {
                    [self.delegate buySuccess];
                }
            }
        else
            {
                if (self.delegate && [self.delegate respondsToSelector:@selector(buyFailed:)]) {
                    [self.delegate buyFailed:@""];
                }
                //验证失败,检查你的机器是否越狱
            }
        
        [[NSUserDefaults standardUserDefaults] removeObjectForKey:USERDEFAULT_KEY];
        
    }else{
        if (self.delegate && [self.delegate respondsToSelector:@selector(buyFailed:)]) {
            [self.delegate buyFailed:@""];
        }
    }
    
    
    
    //结束交易
    [self finshTransaction:transaction];
    
}

- (void)finshTransaction:(SKPaymentTransaction*)transaction
{
    //结束交易
    [[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}


//-(void)handleOpenURL:(NSURL *)url{
//    if ([url.host isEqualToString:WECHAT]) {
//        [WXApi handleOpenURL:url delegate:self];
//    }else if ([url.host isEqualToString:ALIPAY]){
//        // 支付跳转支付宝钱包进行支付,处理支付结果
//        [[AlipaySDK defaultService] processOrderWithPaymentResult:url standbyCallback:^(NSDictionary *resultDic) {
//            NSLog(@"result = %@",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;
////                    }
////                }
////            }
////            NSLog(@"授权结果 authCode = %@", authCode?:@"");
////        }];
//    }
//}
//
//-(void)sendOrder:(APTYPE)type Order:(NSDictionary *)order{
//    switch (type) {
//        case AP_ALIPAY:
//        {
//            NSString *orderString = [order objectForKey:@"orderString"];
//            [[AlipaySDK defaultService] payOrder:orderString fromScheme:ALIPAYSCHEME callback:^(NSDictionary *resultDic) {
//                NSLog(@"reslut = %@",resultDic);
//            }];
//        }
//            break;
//        case AP_WECHAT:
//        {
//            PayReq *request = [[PayReq alloc] init];
//            request.openID = [order objectForKey:@"appid"];
//            request.partnerId = [order objectForKey:@"partnerid"];
//            request.prepayId= [order objectForKey:@"prepayid"];
//            request.package = [order objectForKey:@"package"];
//            request.nonceStr= [order objectForKey:@"noncestr"];
//            request.timeStamp = [[order objectForKey:@"timestamp"] intValue];
//            request.sign = [order objectForKey:@"sign"];
//            [WXApi sendReq:request];
//        }
//            break;
//        default:
//            break;
//    }
//}


//#pragma mark ----WXApiDelegate-----

//- (void)onResp:(BaseResp *)resp{
//    if ([resp isKindOfClass:[PayResp class]]) {
//        PayResp*response=(PayResp*)resp;  // 微信终端返回给第三方的关于支付结果的结构体
//        switch (response.errCode) {
//            case WXSuccess:
//            {// 支付成功,向后台发送消息
//                NSLog(@"支付成功");
//            }
//                break;
//            case WXErrCodeCommon:
//            { //签名错误、未注册APPID、项目设置APPID不正确、注册的APPID与设置的不匹配、其他异常等
//                NSLog(@"支付失败");
//            }
//                break;
//            case WXErrCodeUserCancel:
//            { //用户点击取消并返回
//                NSLog(@"取消支付");
//            }
//                break;
//            case WXErrCodeSentFail:
//            { //发送失败
//                NSLog(@"发送失败");
//            }
//                break;
//            case WXErrCodeUnsupport:
//            { //微信不支持
//                NSLog(@"微信不支持");
//            }
//                break;
//            case WXErrCodeAuthDeny:
//            { //授权失败
//                NSLog(@"授权失败");
//            }
//                break;
//            default:
//                break;
//        }
//    }
//}

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

推荐阅读更多精彩内容