热门标签 | HotTags
当前位置:  开发笔记 > 前端 > 正文

开发笔记:iOSApp内购Demo

/*注意事项:1.沙盒环境测试appStore内购流程的时候,请使用没越狱的设备。2.请务必使用真机来测试,一切以真机为准。3.

/*注意事项:

1.沙盒环境测试appStore内购流程的时候,请使用没越狱的设备。

2.请务必使用真机来测试,一切以真机为准。

3.项目的Bundle identifier需要与您申请AppID时填写的bundleID一致,不然会无法请求到商品信息。

4.如果是你自己的设备上已经绑定了自己的AppleID账号请先注销掉,否则你哭爹喊娘都不知道是怎么回事。

5.订单校验 苹果审核app时,仍然在沙盒环境下测试,所以需要先进行正式环境验证,如果发现是沙盒环境则转到沙盒验证。

识别沙盒环境订单方法:

1.根据字段 envirOnment= sandbox。

2.根据验证接口返回的状态码,如果status=21007,则表示当前为沙盒环境。

苹果反馈的状态码:

21000App Store无法读取你提供的JSON数据

21002 订单数据不符合格式

21003 订单无法被验证

21004 你提供的共享密钥和账户的共享密钥不一致

21005 订单服务器当前不可用

21006 订单是有效的,但订阅服务已经过期。当收到这个信息时,解码后的收据信息也包含在返回内容中

21007 订单信息是测试用(sandbox),但却被发送到产品环境中验证

21008 订单信息是产品环境中使用,但却被发送到测试环境中验证

 

*/

 

开发内购功能,首先需要一个开发者账号,在 App store connect -> 我的App -> 功能 中申请 如图:

技术图片

有四种类型,主要看项目的需求而决定(自动续期订阅这个最为麻烦,要签署协议,而已在App 启动是请求苹果服务器,是否存在自动续期订单)

技术图片

这个图是新增一项内购产品 

参考名称:介绍这个内购产品 

产品ID(主要用到的):支付的时候用到 

其他的都是在app store 中看到的介绍

 

首先引用 #import

封装一个内购功能,这个在app 中可能多个地方用到

+ (instancetype)shareIAPManager;
//添加内购产品
- (void)addPurchWithProductID:(NSString *)product_id completeHandle:(IAPCompletionHandleBlock)handle;

SKProductsRequestDelegate代理方法
交易结束后用到

- (void)completeTransaction:(SKPaymentTransaction *)transaction {
NSString * productIdentifier = transaction.payment.productIdentifier;
NSData *data = [productIdentifier dataUsingEncoding:NSUTF8StringEncoding];
NSString *receipt = [data base64EncodedStringWithOptions:0];
YMLog(@"%@",receipt);
if ([productIdentifier length] > 0) {
// 向自己的服务器验证购买凭证
NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
if (![[NSFileManager defaultManager] fileExistsAtPath:[receiptURL path]]) {
// 取 receipt 的时候要判空,如果文件不存在,就要从苹果服务器重新刷新下载 receipt 了
// SKReceiptRefreshRequest 刷新的时候,需要用户输入 Apple ID,同时需要网络状态良好
SKReceiptRefreshRequest *receiptRefreshRequest = [[SKReceiptRefreshRequest alloc] initWithReceiptProperties:nil];
receiptRefreshRequest.delegate = self;
[receiptRefreshRequest start];
return;
}
NSData *data = [NSData dataWithContentsOfURL:receiptURL];
/** 交易凭证*/
NSString *receipt_data = [data base64EncodedStringWithOptions:0];
/** 事务标识符(交易编号) 交易编号(必传:防止越狱下内购被破解,校验 in_app 参数)*/
NSString *transaction_id = transaction.transactionIdentifier;
NSString *goodID = transaction.payment.productIdentifier;
//这里缓存receipt_data,transaction_id 因为后端做校验的时候需要用到这两个字段
YMLog(@"%@",receipt_data);
YMLog(@"%@",transaction_id);
[self retquestApplePay:receipt_data transaction_id:transaction_id goodsID:goodID];
}
[self verifyPurchaseWithPaymentTransaction:transaction isTestServer:NO];
}

 


  • (void)completeTransaction:(SKPaymentTransaction *)transaction ;

 

中 获取

NSString * productIdentifier = transaction.payment.productIdentifier;

NSData *data = [productIdentifier
dataUsingEncoding:NSUTF8StringEncoding];

NSString *receipt = [data base64EncodedStringWithOptions:0];

NSString *receipt_data = [data base64EncodedStringWithOptions:0];

/** 事务标识符(交易编号) 交易编号(必传:防止越狱下内购被破解,校验 in_app 参数)*/

NSString *transaction_id = transaction.transactionIdentifier;

NSString *goodID = transaction.payment.productIdentifier;

得到的 transaction_id receipt_data goodID需要上传到app 服务器校验(具体看后端的需求)

//private 提交订单数据到app 服务器校验

- (void)retquestApplePay:(NSString *)receipt_data transaction_id:(NSString *)transaction_id goodsID:(NSString *)goodsId;

// 交易失败

- (void)failedTransaction:(SKPaymentTransaction *)transaction{
if (transaction.error.code != SKErrorPaymentCancelled) {
[self handleActionWithType:IAPPurchFailed data:nil];
}else{
[self handleActionWithType:IAPPurchCancel data:nil];
}
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}
#pragma mark -- 结束上次未完成的交易 防止串单
-(void)removeAllUncompleteTransactionBeforeStartNewTransaction{
NSArray* transactiOns= [SKPaymentQueue defaultQueue].transactions;
if (transactions.count > 0) {
//检测是否有未完成的交易
SKPaymentTransaction* transaction = [transactions firstObject];
if (transaction.transactiOnState== SKPaymentTransactionStatePurchased) {
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
return;
}
}
}

IAPDemo链接:Demo

我的掘金:https://juejin.im/post/5dca8b526fb9a04a8e3be109


推荐阅读
  • 深入探讨ASP.NET中的OAuth、JWT与OpenID Connect
    本文作为前文关于OAuth2.0和使用.NET实现OAuth身份验证的补充,详细阐述了OAuth与JWT及OpenID Connect之间的关系和差异,旨在提供更全面的理解。 ... [详细]
  • 本文旨在介绍在iOS平台进行直播技术开发前的准备工作,重点讲解AVFoundation框架的基本概念和使用方法。通过对AVFoundation的深入理解,开发者能够更好地掌握直播应用中的音视频处理技巧。 ... [详细]
  • 本文详细介绍了如何使用Python通过GET和POST方法发送HTTP请求,并接收HTTP响应的具体实现方法。包括示例代码和相关模块的功能说明。 ... [详细]
  • Android中解析XML文件的实践指南
    本文详细介绍了在Android应用开发中解析XML文件的方法,包括从本地文件和网络资源获取XML文件的不同途径,以及使用DOM、SAX和PULL三种解析方式的具体实现。 ... [详细]
  • ServletContext接口在Java Web开发中扮演着重要角色,它提供了一种方式来获取关于整个Web应用程序的信息。通过ServletContext,开发者可以访问初始化参数、共享数据以及应用资源。 ... [详细]
  • Eclipse 下 JavaFX 程序开发指南
    本文介绍了 JavaFX,这是一个用于创建富客户端应用程序的 Java 图形和媒体工具包,并详细说明了如何在 Eclipse 环境中配置和开发 JavaFX 应用。 ... [详细]
  • 本文详细介绍了如何在两台运行 Windows Server 2003 的计算机上配置两个 MySQL 实例以实现主从复制。每台计算机分别命名为 Master 和 Slave,确保系统分区及 MySQL 安装路径的正确配置。 ... [详细]
  • Git支持通过自定义钩子来扩展其功能,这些钩子根据触发条件的不同,可以分为客户端和服务器端两种类型。客户端钩子通常与本地操作相关联,如提交代码或合并分支;而服务器端钩子则与远程仓库的交互有关。 ... [详细]
  • 本文总结了WebSphere应用服务器出现宕机问题的解决方法,重点讨论了关键参数的调整,包括数据源连接池、线程池设置以及JVM堆大小等,旨在提升系统的稳定性和性能。 ... [详细]
  • 本文探讨了Java编程中MVC模式的优势与局限,以及如何利用Java开发一款基于鸟瞰视角的赛车游戏。 ... [详细]
  • 尽管PHP是一种强大且灵活的Web开发语言,但开发者在使用过程中常会陷入一些典型的陷阱。本文旨在列出PHP开发中最为常见的10种错误,并提供相应的预防建议。 ... [详细]
  • 本文介绍了MySQL数据库的安全权限管理思想及其制度流程,涵盖从项目开发、数据库更新到日常运维等多个方面的详细流程控制,旨在通过严格的流程管理和权限控制,有效预防数据安全隐患。 ... [详细]
  • 本文详细介绍了ASP.NET缓存的基本概念和使用方法,包括输出缓存、数据缓存及其高级特性,如缓存依赖、自定义缓存和缓存配置文件等。通过合理利用这些缓存技术,可以显著提升Web应用程序的性能。 ... [详细]
  • 本文介绍如何利用Scrapyd-Client工具简化Scrapy项目的部署流程。通过该工具,可以轻松将Scrapy项目打包并部署至Scrapyd服务,实现高效的分布式爬虫管理。 ... [详细]
  • 本文详细介绍如何结合Django框架和DRF(Django REST Framework)来设计一套有效的全局异常处理系统。这套系统不仅能够妥善处理DRF引发的异常,还能兼容Django自带的admin界面异常处理逻辑。 ... [详细]
author-avatar
魔者
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有