转载注明出处: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;
@endstatic 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.请求所有的商品IDNSString *productFilePath = [[NSBundle mainBundle] pathForResource:@"iapdemo.plist" ofType:nil];NSArray *products = [NSArray arrayWithContentsOfFile:productFilePath];// 2.获取所有的productidNSArray *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

iOS内购代码(苹果支付ApplePay)相关推荐

  1. 【支付】Cocos2d-x IOS内购(IAP支付)

    [说明] 游戏开发中,集成支付是一个重要的环节,AppStore审核指南规定,App内虚拟物品必须使用IAP支付.这篇文章主要记录我在集成IAP的过程,我参考了泰然网的一篇文章,对其进行了封装和扩展, ...

  2. Springboot 对接苹果内购代码

    Springboot 对接苹果内购代码 苹果内购和微信.支付宝支付流程有所不同,微信和支付宝都是通过各自的统一下单接口,拿到客户端所要的参数,之后返回给客户端,客户端支付完成进行回调并进行业务操作,而 ...

  3. Unity iOS内购

    前言:最近项目需要切换到iOS平台做一些提交审核和支付对接相关的工作,上一篇刚分享了最新的iOS10提交审核的一些坑,这篇分享一些内购相关的流程. Unity iOS内购 思路: Unity调用iOS ...

  4. 【iOS内购支付】Uniapp拉起苹果内购支付注意事项、实现步骤以及踩过的坑(手把手教程)

    前言 Hello!又是很长时间没有写博客了,因为最近又开始从事新项目,也是第一次接触关于uniapp开发原生IOS应用的项目,在这里做一些关于我在项目中使用苹果内购支付所实现的方式以及要注意的事项,希 ...

  5. iOS 内购IAP(In-App Purchases)代码实现(上)

    iOS 内购IAP(In-App Purchases)代码实现(上) iOS 内购,也叫内支付,是在iOS应用内部,向苹果服务器发起购买请求的过程.我们在这边来讲一讲代码的实现过程.还有,在做内购的时 ...

  6. iOS——内购支付详解后台操作(一)

    一.思维导图 二.重点总结: 1.获取内购列表(从App内读取或从自己服务器读取) 2.App Store请求可用的内购列表 3.向用户展示内购列表 4.用户选择了内购列表,再发个购买请求,收到购买完 ...

  7. .NET C# IOS内购支付功能

    /// <summary>/// IOS内购支付/// </summary>public class ApplePay{/// <summary>/// 苹果验证支 ...

  8. iOS内购(IAP,In App Purchases-在APP内部支付),设置及使用

    项目中使用到了中间货币(金币)的形式来进行功能使用,模式是使用RMB换成-金币比如:(1RMB = 10金币),所以会集成第三方的支付平台,使用了微信和支付宝的第三方平台过后,发现审核失败,被苹果拒绝 ...

  9. ios内购二次验证安全性问题_苹果IOS内购二次验证返回state为21002的坑

    项目是三四年前的老项目,之前有IOS内购二次验证的接口,貌似很久都没用了,然而最近IOS的妹子说接口用不了,让我看看啥问题.接口流程时很简单的,就是前端IOS在购买成功之后,接收到receipt后进行 ...

最新文章

  1. 如何对正在运行的进程,进行heap profile
  2. centos6 安装 mantisbt-1.2.8 —— (3)Linux系统下yum源配置(Centos 6)
  3. Android在屏幕任意位置显示对话框
  4. Webstorm2017安装以及破解和汉化
  5. Segment公司--整合数据进行分析
  6. Java FilePermission getActions()方法与示例
  7. matlab数据导出excel,求教怎么将matlab的数据导出到excel
  8. EDA技术实用教程 | 复习十 | 移位寄存器的行为级描述
  9. 分享一份适合练手的软件测试实战项目
  10. 去掉GaussView 3.07启动警告窗口的完整解决方案
  11. STM32 USB主机通信连接中断过程
  12. C语言——判断一个数字是否为质数(素数)!
  13. Foundation框架之字符串和日期
  14. 【MySQL连接】MySQLdb安装与使用
  15. 好好说话之IO_FILE利用(1):利用_IO_2_1_stdout泄露libc
  16. 用 Python 给程序加个进度条,让你的程序看起来更炫酷
  17. Windows10升级21H1黑屏解决办法
  18. Tinkpad T480设置U盘启动
  19. 张正友棋盘法定标--Matlab【Camera Calibration Toolbox】
  20. Redis主从同步(复制)

热门文章

  1. php页面怎么转换成静态页面,php如何生成静态页面
  2. 解析URL得到IP地址
  3. 【经典教程】怎样能把SWF反编译为fla?
  4. QT下载网址(国内)
  5. Delphi FireMonkey 振动手机
  6. Windows下UDP编程
  7. 时域,频域和空域的概念
  8. 2011税率改革 3500起征 个人所得税计算
  9. redis实战总结,go语言实现
  10. 5G NR - RACH学习笔记2 - 上行同步/Timing Advance