做之前需要先签订协议, 需要去 iTunes Connect [协议、税务和银行业务]板块签订协议,所以之前需要将资料准备齐全。包括银行账户,联系人等。
然后回到下图创建商品:
提前说一下需要的有 内购页面的截图 以供审核:
最重要的就是商品的ID,将用于报告的唯一字母数字 ID。可以仿照bundleid的格式只将最后的详细描述改掉 com.projectName.detail
#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
@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