iOS内购代码(苹果支付ApplePay)

6 篇文章 1 订阅
4 篇文章 0 订阅

转载注明出处:https://www.jianshu.com/p/8c958e75f98f


刚刚做了内购, 记录一下
这里直接上代码, 至于写代码之前的一些设置工作参考以下文章:
http://www.jianshu.com/p/690a7c68664e
http://www.jianshu.com/p/86ac7d3b593a

需要注意的是:

  1. 只要工程配置了对应的证书, 就能请求商品信息, 不需要任何其他处理
  2. 沙盒测试填写的邮箱不能是已经绑定appleID的邮箱, 也不能是AppleID的救援邮箱, 其他的无所谓, 其实, 哪怕你填写的邮箱不存在也没有关系
//
//  IAPManager.m
//  SpeakEnglish
//
//  Created by Daniel on 16/6/8.
//  Copyright © 2016年 Daniel. All rights reserved.
//

#import "IAPManager.h"
#import <StoreKit/StoreKit.h>

@interface IAPManager ()<SKPaymentTransactionObserver, SKProductsRequestDelegate>
// 所有商品
@property (nonatomic, strong)NSArray *products;
@property (nonatomic, strong)SKProductsRequest *request;
@end

static IAPManager *manager = nil;

@implementation IAPManager

+ (instancetype)shareIAPManager {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        manager = [self new];
        [[SKPaymentQueue defaultQueue] addTransactionObserver:manager];
    });
    return manager;
}

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

// 请求可卖的商品
- (void)requestProducts
{
    if (![SKPaymentQueue canMakePayments]) {
        // 您的手机没有打开程序内付费购买
        return;
    }
    
    // 1.请求所有的商品ID
    NSString *productFilePath = [[NSBundle mainBundle] pathForResource:@"iapdemo.plist" ofType:nil];
    NSArray *products = [NSArray arrayWithContentsOfFile:productFilePath];
    
    // 2.获取所有的productid
     NSArray *productIds = [products valueForKeyPath:@"productId"];
    
    // 3.获取productid的set(集合中)
    NSSet *set = [NSSet setWithArray:productIds];
    
    // 4.向苹果发送请求,请求可卖商品
    _request = [[SKProductsRequest alloc] initWithProductIdentifiers:set];
    _request.delegate = self;
    [_request start];
}

/**
 *  当请求到可卖商品的结果会执行该方法
 *
 *  @param response response中存储了可卖商品的结果
 */
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response
{
     for (SKProduct *product in response.products) {

    // 用来保存价格
    NSMutableDictionary *priceDic = @{}.mutableCopy;
    // 货币单位
    NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
    [numberFormatter setFormatterBehavior:NSNumberFormatterBehavior10_4];
    [numberFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
    [numberFormatter setLocale:product.priceLocale];
    // 带有货币单位的价格
    NSString *formattedPrice = [numberFormatter stringFromNumber:product.price];
        [priceDic setObject:formattedPrice forKey:product.productIdentifier];

     NSLog(@"价格:%@", product.price);
     NSLog(@"标题:%@", product.localizedTitle);
     NSLog(@"秒速:%@", product.localizedDescription);
     NSLog(@"productid:%@", product.productIdentifier);
     }
     
    // 保存价格列表
    [[NSUserDefaults standardUserDefaults] setObject:priceDic forKey:@"priceDic"];
    [[NSUserDefaults standardUserDefaults] synchronize];
    
    // 1.存储所有的数据
    self.products = response.products;
    self.products = [self.products sortedArrayWithOptions:NSSortConcurrent usingComparator:^NSComparisonResult(SKProduct *obj1, SKProduct *obj2) {
        return [obj1.price compare:obj2.price];
    }];
}

#pragma mark - 购买商品
- (void)buyProduct:(SKProduct *)product
{
    // 1.创建票据
    SKPayment *payment = [SKPayment paymentWithProduct:product];
    WELog(@"productIdentifier----%@", payment.productIdentifier);
    
    // 2.将票据加入到交易队列中
    [[SKPaymentQueue defaultQueue] addPayment:payment];
}

#pragma mark - 实现观察者回调的方法
/**
 *  当交易队列中的交易状态发生改变的时候会执行该方法
 *
 *  @param transactions 数组中存放了所有的交易
 */
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
{
    /*
     SKPaymentTransactionStatePurchasing, 正在购买
     SKPaymentTransactionStatePurchased, 购买完成(销毁交易)
     SKPaymentTransactionStateFailed, 购买失败(销毁交易)
     SKPaymentTransactionStateRestored, 恢复购买(销毁交易)
     SKPaymentTransactionStateDeferred 最终状态未确定
     */
    for (SKPaymentTransaction *transaction in transactions) {
        switch (transaction.transactionState) {
            case SKPaymentTransactionStatePurchasing:
                WELog(@"用户正在购买");
                break;
                
            case SKPaymentTransactionStatePurchased:
                WELog(@"productIdentifier----->%@", transaction.payment.productIdentifier);
                [self buySuccessWithPaymentQueue:queue Transaction:transaction];
                break;
                
            case SKPaymentTransactionStateFailed:
                NSLog(@"购买失败");
                [queue finishTransaction:transaction];
                break;
                
            case SKPaymentTransactionStateRestored:
                NSLog(@"恢复购买");
                //TODO:向服务器请求补货,服务器补货完成后,客户端再完成交易单子
                //[queue finishTransaction:transaction];
                break;
                
            case SKPaymentTransactionStateDeferred:
                NSLog(@"最终状态未确定");
                break;
                
            default:
                break;
        }
    }
}

- (void)buySuccessWithPaymentQueue:(SKPaymentQueue *)queue Transaction:(SKPaymentTransaction *)transaction {
    
    AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
    NSDictionary *params = @{@"user_id":@"user_id",
                             // 获取商品
                             @"goods":[self goodsWithProductIdentifier:transaction.payment.productIdentifier]};
    
    [manager POST:@"url" parameters:params success:^(NSURLSessionDataTask *task, id responseObject) {
        
        if ([responseObject[@"code"] intValue] == 200) {
            
            // 防止丢单, 必须在服务器确定后从交易队列删除交易
            // 如果不从交易队列上删除交易, 下次调用addTransactionObserver:, 仍然会回调'updatedTransactions'方法, 以此处理丢单
            
            WELog(@"购买成功");
            [queue finishTransaction:transaction];
        }
        
    } failure:^(NSURLSessionDataTask *task, NSError *error) {
        
    }];
}

// 商品列表 也可以使用从苹果请求的数据, 具体细节自己视情况处理
// goods1 是商品的ID
- (NSString *)goodsWithProductIdentifier:(NSString *)productIdentifier {
    NSDictionary *goodsDic = [[NSUserDefaults standardUserDefaults] objectForKey:@"priceDic"];
    return goodsDic[productIdentifier];
}

// 恢复购买
- (void)restore
{
    [[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
}

- (void)paymentQueue:(SKPaymentQueue *)queue restoreCompletedTransactionsFailedWithError:(NSError *)error {
    // 恢复失败
    WELog(@"恢复失败");
}

// 取消请求商品信息
- (void)dealloc {
    [_request cancel];
}
@end

补充:

对于丢单的交易,在执行初始化[[SKpaymentQueue defaultQueue] addTransactionObserver: self] 的时候,如果有未完成的交易,会直接回调updatedTransactions,并且进入case SKPaymentTransationStateRestored,此时把这些未完成的交易告知服务器进行补货,补货完成再通知客户端,客户端再执行completeTransaction关闭单子

总结:
内购有三个可能出现的问题

  1. 支付成功后, 没来得及向服务器发送交易成功的数据就退出应用, 导致丢单. 这个问题貌似不需要本地化数据也已经没问题了, 除非再次回调updatedTransactions方法时已经拿不到票据了, 这样才有必要本地存储票据.
  2. 无法绑定交易和对应的用户. 因为applicationUsername的存在这已经不是问题了.
  3. 只用别人的手机进行购买, 没来得及向服务器发送交易成功的数据就退出应用, 导致丢单. 如果别人再也不打开这个应用甚至删掉了, 目前看来, 没有办法解决

参考资料:

  1. 苹果内购二次验证 PHP代码
    http://my.oschina.net/qianglong/blog/503861

  2. In-App Purchase Programming Guide
    https://developer.apple.com/library/prerelease/content/documentation/NetworkingInternet/Conceptual/StoreKitGuide/Introduction.html#//apple_ref/doc/uid/TP40008267

  3. iPhone In App Purchase购买完成时验证transactionReceipt
    http://www.cnblogs.com/eagley/archive/2011/06/15/2081577.html

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值