in-app Purchase (iOS内购)

in-app Purchase

准备工作

签订协议

做之前需要先签订协议, 需要去 iTunes Connect [协议、税务和银行业务]板块签订协议,所以之前需要将资料准备齐全。包括银行账户,联系人等。

创建商品

然后回到下图创建商品:
提前说一下需要的有 内购页面的截图 以供审核:
in-app Purchase (iOS内购)_第1张图片
in-app Purchase (iOS内购)_第2张图片
in-app Purchase (iOS内购)_第3张图片

最重要的就是商品的ID,将用于报告的唯一字母数字 ID。可以仿照bundleid的格式只将最后的详细描述改掉 com.projectName.detail

客户端处理

Manager

#import 
@interface LZGPurchaseManager : NSObject
@end

#import "LZGPurchaseManager.h"
#import 

@interface LZGPurchaseManager()<SKProductsRequestDelegate, SKPaymentTransactionObserver, SKRequestDelegate>
@property (nonatomic, strong) SKProductsRequest *proRequest;
@property (nonatomic, strong) SKProduct *currentProduct;
@end

@implementation LZGPurchaseManager

+ (instancetype)defaultPurchaseManager {
    static LZGPurchaseManager *manager;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        if (!manager) {
            manager = [[LZGPurchaseManager alloc] init];
            if ([SKPaymentQueue canMakePayments]) {
                [[SKPaymentQueue defaultQueue] addTransactionObserver:manager];
            } else {
                //show toast to tell user in-app purchase can not use.
            }
        }
    });
    return manager;
}

- (void)requestProductDataWithIdentifier:(NSString *)identifier {
    NSSet *productIdSet = [NSSet setWithObjects:identifier, nil];
    self.proRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:productIdSet];
    self.proRequest.delegate = self;
    [self.proRequest start];
}

- (void)dealloc {
    _proRequest.delegate = nil;
}

+ (void)cacheOrder:(AppleOrder *)order {}
+ (void)removeFinishedOrder:(AppleOrder *)order {}
+ (void)callBack:(id)info {}
+ (void)sendUnfinishedOrder {}


#pragma mark - ==========  SKProductsRequestDelegate  ==========
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
    if (response.products.count) {
        self.currentProduct = response.products.firstObject;
    }
}


#pragma mark - ==========  SKRequestDelegate  ==========
- (void)requestDidFinish:(SKRequest *)request {
    if (self.currentProduct) {
        [LZGPurchaseManager callBack:@"获取商品信息成功, 正在向App Store发送购买请求..."];
        SKPayment *payment = [SKPayment paymentWithProduct:self.currentProduct];
        [[SKPaymentQueue defaultQueue] addPayment:payment];
    } else {
        [LZGPurchaseManager callBack:@"获取商品信息失败,请重"];
    }
}

- (void)request:(SKRequest *)request didFailWithError:(NSError *)error {
    [LZGPurchaseManager callBack:error];
}


#pragma mark - ==========  client bussiness ==========
- (void)sendReception:(SKPaymentTransaction *)trans {
    AppleOrder *order = [[AppleOrder alloc] orderByTransaction:trans];
    //此处应该缓存所有的订单!!!然后就会有失败的订单,需要处理
    [LZGPurchaseManager cacheOrder:order];

    NSError *error;
    NetworkResult *result;
    //发送请求去服务器验证当前的order
    // send message to service with order:order ^(NSError *error, NetworkResult *result) {
    if (error) {
        //通知用户当前的订单从服务器没有查询到结果,订单失败
        [LZGPurchaseManager callBack:error];
    } else {
        if (result.isSucceed) {
            [LZGPurchaseManager removeFinishedOrder:order];
            [LZGPurchaseManager callBack:@"购买成功,刷新页面^&**($!@"];
        } else {
            [LZGPurchaseManager callBack:@"购买失败,联系客服"];
        }
        //===========  这里处理一些没有完成的但是缓存下来的订单 =============
        [LZGPurchaseManager sendUnfinishedOrder];
    }
}

#pragma mark - ==========  SKPaymentTransactionObserver  ==========
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions {
    if (!transactions.count) {
        return;
    }
    for (SKPaymentTransaction *trans in transactions) {
        switch (trans.transactionState) {
            case SKPaymentTransactionStatePurchasing:
                // 正在购买
                break;
            case SKPaymentTransactionStatePurchased:
                // 购买完成
                // Transaction is in queue, user has been charged.  Client should complete the transaction.
                [[SKPaymentQueue defaultQueue] finishTransaction:trans];
                //需要向服务器请求验证订单是否完成!
                [self sendReception:trans];
                break;
            case SKPaymentTransactionStateFailed:
                // 购买失败
                // Transaction was cancelled or failed before being added to the server queue.
                break;
            case SKPaymentTransactionStateRestored:
                // 购买修复
                // Transaction was restored from user's purchase history.  Client should complete the transaction.
                 [[SKPaymentQueue defaultQueue] finishTransaction:trans];
                break;
            case SKPaymentTransactionStateDeferred:
                // 延期购买
                break;
        }
    }
}

#pragma mark - ==========  暂不处理这些代理事件  ==========
// Sent when transactions are removed from the queue (via finishTransaction:).
- (void)paymentQueue:(SKPaymentQueue *)queue removedTransactions:(NSArray *)transactions {
}
// 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 {
}
// Sent when all transactions from the user's purchase history have successfully been added back to the queue.
- (void)paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue {
}
// Sent when the download state has changed.
- (void)paymentQueue:(SKPaymentQueue *)queue updatedDownloads:(NSArray *)downloads {
}

@end

Models

@class SKPaymentTransaction;
@interface AppleOrder : NSObject
@property (nonatomic, copy) NSString *base64Reception;              //AppStore交易回执
@property (nonatomic, copy) NSString *receptionTime;                //苹果交易完成时间
@property (nonatomic, copy) NSString *productIdentifier;            //苹果 商品 key
@property (nonatomic, copy) NSString *transactionIdentifier;        //苹果 交易 流水号
@property (nonatomic, getter=isVerifySuccess) BOOL verifySuccess;   //服务器验证状态
- (instancetype)orderByTransaction:(SKPaymentTransaction *)trans;
@end

@implementation AppleOrder
/* SKPaymentTransaction 属性
@property(nonatomic, readonly, nullable) NSError *error NS_AVAILABLE_IOS(3_0);
@property(nonatomic, readonly, nullable) SKPaymentTransaction *originalTransaction NS_AVAILABLE_IOS(3_0);
@property(nonatomic, readonly) SKPayment *payment NS_AVAILABLE_IOS(3_0);
@property(nonatomic, readonly) NSArray *downloads NS_AVAILABLE_IOS(6_0);
@property(nonatomic, readonly, nullable) NSDate *transactionDate NS_AVAILABLE_IOS(3_0);
@property(nonatomic, readonly, nullable) NSString *transactionIdentifier NS_AVAILABLE_IOS(3_0);
@property(nonatomic, readonly, nullable) NSData *transactionReceipt NS_DEPRECATED_IOS(3_0, 7_0, "Use -[NSBundle appStoreReceiptURL]");
@property(nonatomic, readonly) SKPaymentTransactionState transactionState NS_AVAILABLE_IOS(3_0);

*/
- (instancetype)orderByTransaction:(SKPaymentTransaction *)trans {
    AppleOrder *order = [[AppleOrder alloc] init];
    order.transactionIdentifier = trans.transactionIdentifier;
    order.verifySuccess = NO;
    NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
    [formatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
    order.receptionTime = [formatter stringFromDate:[NSDate date]];

    NSData *receiptData = nil;
    NSBundle *mainBundle = [NSBundle mainBundle];
    if ([mainBundle respondsToSelector:@selector(appStoreReceiptURL)]) {
        NSURL *receiptURL = [mainBundle appStoreReceiptURL];
        receiptData = [NSData dataWithContentsOfURL:receiptURL];
    }
    NSString *base64Reception = [receiptData base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength];
    order.base64Reception = [AppleOrder URLParameterEncodedString:base64Reception];

    SKPayment *payment = trans.payment;
    NSString *product = payment.productIdentifier;
    order.productIdentifier = product;
    return order;
}

+ (NSString *)URLParameterEncodedString:(NSString *)str {
    NSMutableString *beforeStr = [NSMutableString stringWithString:str];
    [beforeStr replaceOccurrencesOfString:@"\\" withString:@"\\\\" options:NSLiteralSearch range:NSMakeRange(0, beforeStr.length)];
    [beforeStr replaceOccurrencesOfString:@"\"" withString:@"\\\"" options:NSLiteralSearch range:NSMakeRange(0, beforeStr.length)];
    NSString *newString = CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (CFStringRef)beforeStr, NULL, CFSTR(":/?#[]@!$ &'()*+,;=\"<>%{}|\\^~`"), kCFStringEncodingUTF8));
    if (newString) {
        return newString;
    }
    return @"";
}
@end

你可能感兴趣的:(iOS-Skill,内购,iOS内购,iOS,in-app)