精华内容
下载资源
问答
  • ios内购返回html,iOS内购掉单问题处理方法
    2021-06-23 06:09:54

    iOS内购提供了两种模式,一种是单机(本地验证)模式,另一种是服务器端验证模式。

    单机验证模式:适用于单机应用,安全性低,数据易被篡改。

    服务器验证模式:应用服务器提交支付票据到苹果服务器验证,安全性较高。

    这里我画了一下我们iOS内购支付的时序图

    54b26410372c84961613bc91af6fe06b.png

    什么是掉单?

    用户选定商品支付完成后,服务器不能正确及时的获取支付状态,导致这笔已支付的订单未能发货。

    为什么会产生掉单?

    1. 手机网络情况复杂多变。

    2. 苹果服务器在境外连通性不确定。

    以上两个客观原因,我们无法改变,会导致如下情况:

    1. 用户支付完成之后,苹果服务器将支付票据返回给客户端,客户端发送票据到游戏服务器。(可能会断网,未能提交到游戏服务器)

    2. 游戏服务器拿到支付票据,请求苹果服务器票据验证。(游戏服务器连接苹果服务器超时,未能验证票据)。

    优化方案

    掉单的原因目前已经找到了,那么需要从客户端和服务器两个方面做优化,彻底解决掉iOS支付掉单问题。

    客户端需要做什么优化?

    客户端在拿到苹果支付票据后,一定要先将支付票据和用户账号做映射,标记为未验证,保存到本地数据库中,然后把票据提交到游戏服务器,在确保得到游戏服务器的反馈,在将本地数据库中该条记录删除,确保游戏服务器收到该票据。

    服务器端做什么优化?

    服务器接收到客户端的票据以及验证信息时,先将票据存储到数据库中,然后请求苹果服务器验证票据,如果因为连接苹果服务器超时或者其他网络情况,标记该票据为验证证状态,后续交给定时任务处理,确保能够正确验证票据结果。

    参考链接

    更多相关内容
  • 主要是Java对接iOS内购,回调验证相关的方法, 真机测试的时候,一定要退出原来的账号,才能用沙盒测试账号,二次验证,请注意区分宏, 测试用沙盒验证,App Store审核的时候也使用的是沙盒购买,所以验证购买凭证的...
  • iOS内购与apple pay

    2021-01-11 16:32:12
    首先得分清楚 内购和Apple Pay。 一、Apple Pay:是一种支付方式,跟支付宝、微信支付是类似的,这里就不详细介绍了。 二、内购:只要在iPhone App上购买的不是实物产品(也就是虚拟产品如qq币、鱼翅、电子书......) ...
  • Unity接入IOS内购demo 从Unity导出Xcode项目 恢复购买 配置商品和测试账号
  • iOS内购实现及测试CheckList.免费+应用内购买的模式已经被证明了是最有效的盈利模式,所以实现内购功能可能是很多开发者必做的工作和必备的技能了。但是鉴于内购这块坑不算少,另外因为sandbox测试所需要特定的配置...
  • 很久之前就想出一篇iOS内付费的教程,但是一查网上的教程实在太多了,有的写得真的蛮不错的,就心想算了,于是就保存在草稿箱了。至于为什么写完它呢!真是说来话长,最近公司有个项目经理跑来问我有关苹果内付费...
  • iOS内购功能demo

    2017-07-19 17:13:08
    iOS内购功能简易实现
  • Unity使用IAP接入IOS内购工程,接入过程和介绍可查看博文:https://czhenya.blog.csdn.net/article/details/120173348
  • iOS内购笔记

    2016-11-30 16:17:00
    iOS内购
  • app一直没有接入iOS内购充值,随着业务支付功能越来越多,ios内购充值就提到日程上来了。那么,ios内购充值怎么做呢?其实iOS内购充值是通过客户端接入iOS的IAP模块(In-AppPurchase)后,由客户端发起充值,然后再把...

    app一直没有接入iOS内购充值,随着业务支付功能越来越多,ios内购充值就提到日程上来了。那么,ios内购充值怎么做呢?

    其实iOS内购充值是通过客户端接入iOS的IAP模块(In-AppPurchase)后,由客户端发起充值,然后再把充值数据(receipt)发给服务端,最后由服务端远程调用AppStore服务器验证。

    具体的流程如图:

    服务端连接AppStore验单

    验单的过程是,服务端发起HTTP Post请求,将以下字段的数据以json格式请求 AppStore 服务器,解析返回数据来验证。

    字段:receipt-data

    来源:ios端内置的生成base64编码的token。

    AppStore 服务器有两个,对应测试环境(沙盒测试)和正式环境:

    测试环境: https://sandbox.itunes.apple.com/verifyReceipt

    正式环境: https://buy.itunes.apple.com/verifyReceipt

    // $verification_uri = 'https://buy.itunes.apple.com/verifyReceipt';

    $verification_uri = 'https://sandbox.itunes.apple.com/verifyReceipt';

    $post_data = array(

    'receipt-data'=>$receipt_data // 此处的值 是ios客户端生成的验签token

    );

    $ch = curl_init($verification_uri);

    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

    curl_setopt($ch, CURLOPT_POST, true);

    curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($post_data));

    $response = curl_exec($ch);

    $errno    = curl_errno($ch);

    $errmsg   = curl_error($ch);

    curl_close($ch);

    if ($errno != 0) {

    throw new Exception($errmsg, $errno);

    }

    $data = json_decode($response, 1);

    服务端验证返回数据

    iOS发起票据验证请求后,通过处理AppStore返回数据来验单。下面举两个示例,同时说明不同iOS版本的返回数据不同,服务端要做好区别。

    1、iOS7及以上获取的票据返回数据:{

    receipt =  {

    "adam_id" = 0,

    "app_item_id" = 0,

    "application_version" = 1,

    "bundle_id" = "com.test",

    "download_id" = 0,

    "in_app" = {

    {

    "is_trial_period" = false,

    "original_purchase_date" = "2017-01-01 01:01:01 Etc/GMT",

    "original_purchase_date_ms" = 1483203661000,

    "original_purchase_date_pst" = "2017-01-01 01:01:01 America/Los_Angeles",

    "original_transaction_id" = 1000000000000001,

    "product_id" = "com.test.10",

    "purchase_date" = "2017-01-01 01:01:01 Etc/GMT",

    "purchase_date_ms" = 1483203661000,

    "purchase_date_pst" = "2017-01-01 01:01:01 America/Los_Angeles",

    "transaction_id" = 1000000000000001

    },

    //......

    },

    "receipt_type" = ProductionSandbox,

    "request_date" = "2017-01-01 01:01:01 Etc/GMT",

    "request_date_ms" = 1483203661000,

    "request_date_pst" = "2017-01-01 01:01:01 America/Los_Angeles",

    "version_external_identifier" = 0,

    },

    status = 0

    }

    2、iOS7以下获取的票据返回数据(不包括iOS7):{

    receipt = {

    "bid" = "com.test",

    "bvrs" = 1,

    "item_id" = 573837050,

    "original_purchase_date" = "2017-01-01 01:01:01 Etc/GMT",

    "original_purchase_date_ms" = 1483203661000,

    "original_purchase_date_pst" = "2017-01-01 01:01:01 America/Los_Angeles",

    "original_transaction_id" = 1000000000000001,

    "product_id" = "com.test.10",

    "purchase_date" = "2017-01-01 01:01:01 Etc/GMT",

    "purchase_date_ms" = 1483203661000,

    "purchase_date_pst" = "2017-01-01 01:01:01 America/Los_Angeles",

    "transaction_id" = 1000000000000001

    },

    status = 0

    }

    验证订单是否成功,关键看这几个数据:

    1、status为 0 表示成功;其他都为失败,表示失败原因

    2、根据 receipt.in_app 字段判断iOS版本,验证方法也不同

    iOS7及以上:有in_app字段,验证 receipt.bundle_id 是否为你 App 的 bundle id,根据 in_app 处理充值的每一笔订单, 根据 product_id 判断用户充值了哪个档位,同时取出 transaction_id

    iOS7以下:没有in_app字段,验证 receipt.bid 是否为你 App 的 bundle id,根据 product_id 判断用户充值了哪个档位,同时取出 transaction_id

    3、根据 transaction_id 对比数据库历史订单判断是否已处理过,没有则认为本次充值是有效的。

    iOS充值坑点: in_app 究竟是什么

    receipt.in_app 是请求AppStore验单后返回的数据,前面有提及,为用户的充值订单数据。

    有两个问题要注意:

    1、iOS内购充值时,客户端充值后从iOS得到的票据 receipt_data 不是针对本次充值的,而是相当于给一个授权 token, 获取用户 appleid 账号在本 App 中所有未关闭的充值记录,包括刚刚发起的充值。

    2、根据这个票据查到的充值数据(receipt.in_app) ,除了最近发起的充值,还包括了非消耗品型,订阅型的充值数据。其中,最近发起的充值,不只是刚刚发起的充值,还可能是最近的几笔充值。特别是沙盒测试,还可能拿到已经确认关闭了的充值订单

    所以,取到充值数据,不是取 receipt.in_app 中的第一个数据、或最后一个,而是在客户端完成充值后,将AppStore回调给到的 transaction_id 拿来做匹配。

    iOS充值坑点:App审核不通过

    苹果审核App时,是在沙盒环境下测试。所以,当App提交苹果审核时,服务端需换成沙盒环境,否则就无法通过苹果审核。通常游戏开发商都会搞一个审核服来给苹果审核,这样,审核服用沙盒环境,正式服用正式环境。

    但对于很多App应用开发商来说,专门搞一个服务器显然增加了不少成本。其实还是有办法处理的,方法如下:

    根据验单返回的 status 字段:

    当 status = 21007 时,把请求地址换成沙盒测试地址,再次请求验单。

    展开全文
  • iOS内购-防越狱破解刷单

    千次阅读 2020-12-22 15:08:49
    最近我们公司丢单率上涨,尤其是10月份比9月份来说丢单率翻了3倍,和一些同行交流了一下,发现他们也是丢单量增加,初步推断可能是苹果iOS12的原因,某些情况下会有用户内购成功后,却返回的是订单失败,错误类型为...

    ---------------------------2018.10.16更新---------------------------

    最近我们公司丢单率上涨,尤其是10月份比9月份来说丢单率翻了3倍,和一些同行交流了一下,发现他们也是丢单量增加,初步推断可能是苹果iOS12的原因,某些情况下会有用户内购成功后,却返回的是订单失败,错误类型为SKErrorUnknown。目前客户端好像没办法去解决。如果有小伙伴和我一样也遇到过相同的问题话,请私信我下,我们都多互相交流一下。

    ---------------------------2018.10.16更新---------------------------

    ---------------------------以下为正文---------------------------

    iOS内购开发大家一定不陌生,网上类似的文章能搜出千八百篇。大部分都是围绕着如何实现?如何防止漏单丢单说明的。很少有提及到越狱的,即使偶尔有一两篇说越狱,也是简单的三言两语说 为了安全,我们直接屏蔽了越狱手机的内购功能。巴拉巴拉... 以前我也是这么想的,直到上个周末发现我们的内购被破解了...才有了这篇文章。本篇文章就是来讲述越狱下的内购如何防止被破解。

    首先我们先简单理一下整个内购的核心流程:

    1. 客户端发起支付订单

    2. 客户端监听购买结果

    3. 苹果回调订单购买成功时,客户端把苹果给的receipt_data和一些订单信息上报给服务器

    4. 后台服务器拿receipt_data向苹果服务器校验

    5. 苹果服务器向返回status结果,含义如下,其中为0时表示成功。

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

    • 21002 收据数据不符合格式

    • 21003 收据无法被验证

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

    • 21005 收据服务器当前不可用

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

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

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

    6.服务器发现订单校验成功后,会把这笔订单存起来,receipt_data用MD5值映射下,保存到数据库,防止同一笔订单,多次发放内购商品。

    以上应该是主流的校验流程。当然客户端其中会插一些丢单漏单的逻辑校验,因为那些跟本篇文章无关,所以不在此展开。

    从上面的流程可以看出,整个内购的核心其实就是receipt_data。苹果回调给客户端,客户端上报给服务器,服务器拿到后去向苹果服务器校验,苹果服务器再返回给我们服务器订单结果。其实严格来说,整个流程是没问题的。整个的漏洞是在最后一步上,【苹果服务器再返回给我们服务器订单结果】。receipt_data在越狱环境下是可以被插件伪造的,后台向苹果验证时,居然还能验证通过。是的,你没看错,苹果这里有个贼鸡儿坑的地方。这是最坑最坑的地方,伪造的receipt_data苹果校验也返回支付成功

    如何解决?我们先来看下越狱订单和正常订单对比

    越狱订单receipt_data向苹果服务器校验后如下:

    {
        "status": 0, 
        "environment": "Production", 
        "receipt": {
            "receipt_type": "Production", 
            "adam_id": 1377028992, 
            "app_item_id": 1377028992, 
            "bundle_id": "*******【敏感信息不给看】*******", 
            "application_version": "3", 
            "download_id": 80042231041057, 
            "version_external_identifier": 827853261, 
            "receipt_creation_date": "2018-07-23 07:30:45 Etc/GMT", 
            "receipt_creation_date_ms": "1532331045000", 
            "receipt_creation_date_pst": "2018-07-23 00:30:45 America/Los_Angeles", 
            "request_date": "2018-07-23 07:33:54 Etc/GMT", 
            "request_date_ms": "1532331234485", 
            "request_date_pst": "2018-07-23 00:33:54 America/Los_Angeles", 
            "original_purchase_date": "2018-07-01 12:16:21 Etc/GMT", 
            "original_purchase_date_ms": "1530447381000", 
            "original_purchase_date_pst": "2018-07-01 05:16:21 America/Los_Angeles", 
            "original_application_version": "3", 
            "in_app": [ ]
        }
    }
    复制代码

    正常订单receipt_data向苹果服务器校验后如下:

    {
       {
        "status": 0, 
        "environment": "Production", 
        "receipt": {
            "receipt_type": "Production", 
            "adam_id": 1377028992, 
            "app_item_id": 1377028992, 
            "bundle_id": "*******【敏感信息不给看】*******", 
            "application_version": "3", 
            "download_id": 36042096097927, 
            "version_external_identifier": 827703432, 
            "receipt_creation_date": "2018-07-10 13:54:27 Etc/GMT", 
            "receipt_creation_date_ms": "1531230867000", 
            "receipt_creation_date_pst": "2018-07-10 06:54:27 America/Los_Angeles", 
            "request_date": "2018-07-23 08:03:27 Etc/GMT", 
            "request_date_ms": "1532333007664", 
            "request_date_pst": "2018-07-23 01:03:27 America/Los_Angeles", 
            "original_purchase_date": "2018-06-13 06:52:13 Etc/GMT", 
            "original_purchase_date_ms": "1528872733000", 
            "original_purchase_date_pst": "2018-06-12 23:52:13 America/Los_Angeles", 
            "original_application_version": "5", 
            "in_app": [
                {
                    "quantity": "1", 
                    "product_id": "*******【敏感信息不给看】*******", 
                    "transaction_id": "160000477610856", 
                    "original_transaction_id": "160000477610856", 
                    "purchase_date": "2018-07-10 13:54:27 Etc/GMT", 
                    "purchase_date_ms": "1531230867000", 
                    "purchase_date_pst": "2018-07-10 06:54:27 America/Los_Angeles", 
                    "original_purchase_date": "2018-07-10 13:54:27 Etc/GMT", 
                    "original_purchase_date_ms": "1531230867000", 
                    "original_purchase_date_pst": "2018-07-10 06:54:27 America/Los_Angeles", 
                    "is_trial_period": "false"
                }
            ]
        }
    }
    复制代码

    看完两笔订单的对比我相信大家可以清楚的知道,越狱订单虽然状态返回是成功的,但是in_app这个参数是空的。大概查了一下。iOS7以下是没有这个in_app参数的,iOS7以上是有的。因为现在App基本支持的起步都是iOS8 iOS9了,iOS7可以不用管了。但这里还有一个问题,就是in_app这个字段并不总是只返回一个,有可能会返回多个,比如下面的这种订单。

    正常订单receipt_data校验后  in_app多个元素时:

    {
        "status":0,
        "environment":"Sandbox",
        "receipt":{
            "receipt_type":"ProductionSandbox",
            "adam_id":0,
            "app_item_id":0,
            "bundle_id":"*******【敏感信息不给看】*******",
            "application_version":"1",
            "download_id":0,
            "version_external_identifier":0,
            "receipt_creation_date":"2018-07-24 04:28:24 Etc/GMT",
            "receipt_creation_date_ms":"1532406504000",
            "receipt_creation_date_pst":"2018-07-23 21:28:24 America/Los_Angeles",
            "request_date":"2018-07-24 04:30:06 Etc/GMT",
            "request_date_ms":"1532406606695",
            "request_date_pst":"2018-07-23 21:30:06 America/Los_Angeles",
            "original_purchase_date":"2013-08-01 07:00:00 Etc/GMT",
            "original_purchase_date_ms":"1375340400000",
            "original_purchase_date_pst":"2013-08-01 00:00:00 America/Los_Angeles",
            "original_application_version":"1.0",
            "in_app":[
                {
                    "quantity":"1",
                    "product_id":"*******【敏感信息不给看】*******",
                    "transaction_id":"1000000398911598",
                    "original_transaction_id":"1000000398911598",
                    "purchase_date":"2018-05-16 03:26:12 Etc/GMT",
                    "purchase_date_ms":"1526441172000",
                    "purchase_date_pst":"2018-05-15 20:26:12 America/Los_Angeles",
                    "original_purchase_date":"2018-05-16 03:26:12 Etc/GMT",
                    "original_purchase_date_ms":"1526441172000",
                    "original_purchase_date_pst":"2018-05-15 20:26:12 America/Los_Angeles",
                    "is_trial_period":"false"
                },
                {
                    "quantity":"1",
                    "product_id":"*******【敏感信息不给看】*******",
                    "transaction_id":"1000000398911640",
                    "original_transaction_id":"1000000398911640",
                    "purchase_date":"2018-05-16 03:26:37 Etc/GMT",
                    "purchase_date_ms":"1526441197000",
                    "purchase_date_pst":"2018-05-15 20:26:37 America/Los_Angeles",
                    "original_purchase_date":"2018-05-16 03:26:37 Etc/GMT",
                    "original_purchase_date_ms":"1526441197000",
                    "original_purchase_date_pst":"2018-05-15 20:26:37 America/Los_Angeles",
                    "is_trial_period":"false"
                },
                {
                    "quantity":"1",
                    "product_id":"*******【敏感信息不给看】*******",
                    "transaction_id":"1000000398911784",
                    "original_transaction_id":"1000000398911784",
                    "purchase_date":"2018-05-16 03:26:50 Etc/GMT",
                    "purchase_date_ms":"1526441210000",
                    "purchase_date_pst":"2018-05-15 20:26:50 America/Los_Angeles",
                    "original_purchase_date":"2018-05-16 03:26:50 Etc/GMT",
                    "original_purchase_date_ms":"1526441210000",
                    "original_purchase_date_pst":"2018-05-15 20:26:50 America/Los_Angeles",
                    "is_trial_period":"false"
                },
                {
                    "quantity":"1",
                    "product_id":"*******【敏感信息不给看】*******",
                    "transaction_id":"1000000398911801",
                    "original_transaction_id":"1000000398911801",
                    "purchase_date":"2018-05-16 03:27:22 Etc/GMT",
                    "purchase_date_ms":"1526441242000",
                    "purchase_date_pst":"2018-05-15 20:27:22 America/Los_Angeles",
                    "original_purchase_date":"2018-05-16 03:27:22 Etc/GMT",
                    "original_purchase_date_ms":"1526441242000",
                    "original_purchase_date_pst":"2018-05-15 20:27:22 America/Los_Angeles",
                    "is_trial_period":"false"
                },
                {
                    "quantity":"1",
                    "product_id":"*******【敏感信息不给看】*******",
                    "transaction_id":"1000000399060767",
                    "original_transaction_id":"1000000399060767",
                    "purchase_date":"2018-05-16 11:10:45 Etc/GMT",
                    "purchase_date_ms":"1526469045000",
                    "purchase_date_pst":"2018-05-16 04:10:45 America/Los_Angeles",
                    "original_purchase_date":"2018-05-16 11:10:45 Etc/GMT",
                    "original_purchase_date_ms":"1526469045000",
                    "original_purchase_date_pst":"2018-05-16 04:10:45 America/Los_Angeles",
                    "is_trial_period":"false"
                },
                {
                    "quantity":"1",
                    "product_id":"*******【敏感信息不给看】*******",
                    "transaction_id":"1000000399061778",
                    "original_transaction_id":"1000000399061778",
                    "purchase_date":"2018-05-16 11:14:52 Etc/GMT",
                    "purchase_date_ms":"1526469292000",
                    "purchase_date_pst":"2018-05-16 04:14:52 America/Los_Angeles",
                    "original_purchase_date":"2018-05-16 11:14:52 Etc/GMT",
                    "original_purchase_date_ms":"1526469292000",
                    "original_purchase_date_pst":"2018-05-16 04:14:52 America/Los_Angeles",
                    "is_trial_period":"false"
                },
                ...
            ]
        }
    }
    复制代码

    综上,整个服务器那边校验逻辑应该是这样的。

    首先客户端必须要给服务器传的三个参数:receipt_data, product_id ,transaction_id

    //该方法为监听内购交易结果的回调
    - (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray<SKPaymentTransaction *> *)transactions
    transactions 为一个数组 遍历就可以得到 SKPaymentTransaction 对象的元素transaction。然后从transaction里可以取到以下这两个个参数,product_id,transaction_id。另外从沙盒里取到票据信息receipt_data 
    我们先看怎么取到以上的三个参数
    //获取receipt_data
    NSData *data = [NSData dataWithContentsOfFile:[[[NSBundle mainBundle] appStoreReceiptURL] path]];
    NSString * receipt_data = [data base64EncodedStringWithOptions:0];
    //获取product_id
    NSString *product_id = transaction.payment.productIdentifier;
    //获取transaction_id
    NSString * transaction_id = transaction.transactionIdentifier;
    复制代码

    这是我们必须要传给服务器的三个字段。以上三个字段需要做好空值校验,避免崩溃。

    下面我们来解释一下,为什么要给服务器传这三个参数。

    • receipt_data:这个不解释了 大家都懂 不传的话 服务器根本没法校验

    • product_id:这个也不用解释 内购产品编号 你不传的话 服务器不知道你买的哪个订单

    • transaction_id:这个是交易编号,是必须要传的。因为你要是防止越狱下内购被破解就必须要校验in_app这个参数。而这个参数的数组元素有可能为多个,你必须得找到一个唯一标示,才可以区分订单到底是那一笔。

    所以服务器那边逻辑就很清晰了。

    1. 首先判断订单状态是不是成功。

    2. 如果订单状态成功在判断in_app这个字段有没有,没有直接就返回失败了。如果存在的话,遍历整个数组,通过客户端给的transaction_id 来比较,取到相同的订单时,对比一下bundle_id ,product_id 是不是正确的。

    3. 如果以上校验都正确再去查询一下数据库里这笔订单是不是存在,如果存在也是返回失败,避免重复分发内购商品。如果都成功了,再把这笔订单充值进去,给用户分发内购商品。

    注意:一定要告诉后台,不论校验是否成功,只要客户端给服务器传了receipt_data等参数就一定要保存到数据库里。【下面会解释为什么】

    以上的校验步骤,可以有效的防止内购破解,下面内容是我看苹果官方能文档的关于in_app这个参数说明和解释下为啥服务器必须要保存每一个不同的receipt_data。

    苹果IAP官方文档

    苹果文档上介绍in_app参数内容截图

    In the JSON file, the value of this key is an array containing all in-app purchase receipts based on the in-app purchase transactions present in the input base-64 receipt-data. For receipts containing auto-renewable subscriptions, check the value of the latest_receipt_info key to get the status of the most recent renewal.

    大概意思是说:

    在这个JSON文件中,这个键的值是一个数组,该数组包含基于base-64后的所有内购收据。如果你的内购类型是自动更新订阅,那么请通过检查latest_receipt_info键的值,来确定最近更新的状态。

    很有意思的是,苹果还特别标明了这么一句话:

    Note: An empty array is a valid receipt.

    也就是说这个in_app参数可能为空,如果为空的话,也需要把这笔交易认为是有效的交易。这是苹果建议的操作。当然我们肯定不能这么干,这个参数是必须必须要校验的,不然越狱环境下,分分钟就把你内购破解了。我去校验了很多正常用户的内购订单,没发现一个in_app参数是为空的。但为了保险,还是让后台把所有前端传的receipt_data等参数不管成功失败都保存下来,万一哪个用户因此投诉充值不到账,我们有据可查。

    下面两段话

    The in-app purchase receipt for a consumable product is added to the receipt when the purchase is made. It is kept in the receipt until your app finishes that transaction. After that point, it is removed from the receipt the next time the receipt is updated - for example, when the user makes another purchase or if your app explicitly refreshes the receipt.

    The in-app purchase receipt for a non-consumable product, auto-renewable subscription, non-renewing subscription, or free subscription remains in the receipt indefinitely.

    大概意思是说:

    每当有一笔交易发起的时候,in_app里就会添加收据的一些信息。这些信息会一直保存直到你结束这笔交易。在此之后,下次更新收据时会将其从收据中删除 - 例如,当用户再次购买时,或者您的应用明确刷新收据时。

    非消耗型项目,自动续期订阅,非续期订阅或免费订阅的应用内购买收据将无限期保留在收据中。

    这一点也解释了说,为什么in_app这个数组有时候会有多个元素。

    以下内容和本篇文章无关,下面只是简单分享下我了解到的越狱,以及我身边关于越狱的一些事情和对这件事情的反思。

    关于越狱的一些事情:

    现在越狱已经支持iOS11.3了,我的安卓同事前阵子用自己的iPhone X 越狱了。装了下插件【付费的 并且需要美国区App Store账号】他是我们产品的重度用户,很多小号。但偏偏产品因为某些原因不让做退出登录,切换账号的功能。所以一般用户如果想切换账号,只能卸载重装。但他就不一样了,他骚的飞起,在越狱机上装的插件,他可以改我的所有代码。我看了一眼。整个项目的类,方法,返回值,入参,在那个插件下一览无遗。他找到了我保存用户信息的方法,因为是公司自己人,各种业务流程,他都懂。篡改了一些我的方法,返回参数。比如一些判断bool的方法,应该返回NO,直接篡改成YES。还一些其他的东西。当然后台本身对安全的逻辑校验控制的比较弱,只在一些关键接口【支付 送礼等】做了控制。

    抖音的国际板Tik Tok 如果你在国内切换成美区的App Store账号,并用vpn下载后,你会发现接口刷不到数据,看不到国外的小姐姐。我同事篡改了抖音的方法,就可以拉到数据了。具体方法。[PS:日本人发的抖音感觉都傻乎乎的,上面的小姐姐比国内的差远了。水平【化妆 滤镜 美颜 拍摄角度】明显不如国内的666]

    类名:CTCarrier 方法:- (id) isoCountryCode 该方法是系统方法 拿来获取电话服务商的iOS国家编码。改成日本就是拿日本数据,改成香港就是香港数据

    修改支付宝步数

    类名:APStepInfo 方法:- (long long) numberofSteps 返回值就是步数,你随便改成几万步都可以。

    修改支付宝朋友小红点

    类名:MPBadgeView 方法:- (void) drawBadgeRedPoint 

    另外,还有各种被玩坏的逆向微信功能。

    是的,就是这么简单。在越狱环境下。我们写的App犹如一个被剥光衣服的小姑娘,只要越狱+V【和】P【谐】N+一些付费插件,谁都可以过来上几下。我的安卓同事不会写任何iOS的代码,不懂汇编,不懂砸壳,不懂啥iOS逆向,但他懂只要我付费几美元买个插件,一个个App就是一个个脱光衣服的姑娘。是的,现在破解的门槛真的很低,你花点钱站在巨人的肩膀上就可以为所欲为。

    另外上面的几个例子,不知道大家发现了没。方法名,类名基本上都是见名知意,这也正是我们iOS的规范所在。严格的命名方式,让人一目了然,也让破解者一目了然。我大概看了,国内大厂App。抖音,支付宝,微信等等这些,没有一家做混淆的。方法名都挺规范的。这时候,我又想起了之前和同事开玩笑说的话,“以后再也特么不歧视命名-(void)a -(void)b -(void)c,label1 label2 label3的人了,人家自带混淆”。这里,我也很奇怪,虽然我知道其实就算你做了混淆对攻击者来说也是没卵用,但好歹之前是裸体小姑娘,现在是穿衣服的小姑娘了,总是会提高一些门槛把。细思了一下,大概是真的因为这玩意工作量大,容易引起很多潜在bug,收益又很低【攻击者无法是秒破变成天破而已,慢慢试总能试出来】,性价比极低,所以才不做的把。

    另外,可能也有朋友会说,既然越狱机下这么多搞事情的,我能不能写个方法直接禁掉越狱机子啊。反正越狱用户也不会多,就不要了。答案是否定的,网上的一些代码,你去搜iOS判断是否越狱,iOS越狱检测等等。出来的文章基本都是复制粘贴,没什么价值的,里面的代码古老且旧,尤其是检测越狱时方法用bool值返回的那种,在一些防越狱检测的插件下,更是被秒破的。有价值的资料比我想象中的要更少。如果有朋友能有一些最新防越狱插件检测的方法,请不吝分享下。

    下面在举几个大家做内购经常遇到的一些问题,和一些容易混淆的点。

    Q1:内购和Apple Pay的区别?

    A1:内购是内购,Apple Pay是Apple Pay。我不知道有多少人第一次接触时,会把这俩概念混淆掉,这里你可以简单这么理解,虚拟的物品就是用内购,实际的物品就是用Apple Pay。Apple Pay是一种支付方式,你可以类比为支付宝,微信那种。但人家只支持实际物品,如果你东西是虚拟的话,你却集成Apple Pay上架是要被拒绝的哦~当然反过来,实际物品你却集成内购上架,也是一样被拒。对于大部分的国内开发者而言,你很少会遇到需要集成Apple Pay的App的。能用支付宝/微信的场景还要求支持Apple Pay的产品毕竟是少数。

    Q2:内购项目的类型区别?

    A2:首先内购项目分为以下4种,消耗型项目,非消耗型项目,自动续期订阅,非续期订阅。我们来一个个介绍。

    消耗型项目:只可使用一次的产品,使用之后即失效,必须再次购买。就是大家最广为所知的虚拟币,比如直播平台斗鱼的鱼翅,熊猫的竹子,哔哩哔哩的B币等,这个概念大家应该很好理解,不过多解释了。

    非消耗型项目:只需购买一次,不会过期或随着使用而减少的产品。这个一般是游戏那里用的多,一般是付费解锁关卡的场景,用户买过一次,卸载重装或者同一个Apple id但换App账号时,也要能保证用户重新获得该内购商品。所以App内部需要额外去实现恢复购买的逻辑。

    自动续期订阅:允许用户在固定时间段内购买动态内容的产品。除非用户选择取消,否则此类订阅会自动续期。iTunces上给的示例是:每月订阅提供流媒体服务的 App。对比我们熟悉的,网易云音乐的内购商品-连续包月黑胶VIP,就是此类型。一般来说,没啥必要不要选这一种,如果是VIP的那种场景推荐下面非续期订阅类型去做。自动续期订阅的坑非常多,比另外几种内购类型都要复制。

    非续期订阅:一般来说VIP可以用这种方法来做订阅,我们公司项目的VIP购买就是这种方式。他的实现方式你可以完全照搬消耗性项目,不用做什么额外处理,也不用去管返回的订阅日期什么的东西,就是以服务器那边为准。服务器的日期开始,服务器的日期结束。既简单又保险,不需要额外的做什么处理。

    Q3:VIP一定要用内购做吗?

    A3:其实判断你们公司的App到底需不需要用内购,很简单,就是看跟实际物品有没有关系。如果你的VIP功能是类似饿了么这种,点外卖可以打折/多领红包 那么就不需要用内购,上架的时候说清楚就行了。如果你的VIP功能是虚拟的,比如头像更炫酷,尊贵的VIP身份标示,独特的入场动画等等虚拟相关的,比如QQ会员,就必须要用内购去做。需要说明的是,那种是VIP才能和某某用户聊天的场景,是VIP才能得到App里某某用户的服务【语音,视频】时,这一类的场景苹果一样认为是虚拟的,一样要用内购去做。

    Q4:VIP内购一定是非续期/自动续期订阅吗?我可不可以用虚拟币购买VIP呢?

    A4:这个问题我自己经历过。我的答案是你也可以用虚拟币购买VIP的这种方式,但如果被拒绝,你只能老老实实的按前种方式去做。如果你们的App既有虚拟币又有VIP,产品希望你VIP是直接用虚拟币去购买,这样整个流程都很方便。那么你一定要记住。千万不要在1.0版本这么做,这是血泪教训。1.0版本会抓的很严很严,同时虚拟币+VIP功能,百分百苹果会要求你VIP要用续期订阅去实现。最保险的做法呢,1.0版本不要做任何内购,迭代几个小版本后,加入虚拟币内购,在迭代几个小版本,加入VIP直接用虚拟币购买的功能,这是最最保险的做法。记住:1.0的审核力度是真的很严,能先不做内购就不要做内购,老板或许不懂,1.0版本什么都想要,但往往因为内购,会让你们的产品反复被拒。这一块如果大家感兴趣,可以看看,我的1.0版本就是加了内购,反复被拒5次。血泪教训

    Q5:我们老板心疼那百分之30的手续费,我能不能不用内购啊,有没办法绕过内购?

    A5:办法是有的。但是有风险。我16年做过绕开内购的方法。思路很简单,就是App里集成支付宝/微信/内购这种功能,后台做控制开关,审核时,开关打开,给审核人员看内购功能,审核通过后,开关关闭,给正常用户是用支付宝/微信功能。这个方法,我17年的时候听到很多群友说不行了,你在上架审核时,苹果会扫描你的包,检测到第三方支付sdk时,会拒绝掉。后来又有群友说可以用H5的方式实现支付功能。另外可能会有别的绕开苹果审核的实现方式,如果有哪位朋友知道,不妨留言告知。但不管是哪种方式,都是有风险的,苹果对内购一直抓的很严,如果让它知道你们在钱的方面上欺骗过他,后果还是很严重的。iOS上的用户付费率还是很不错的,付费意愿基本上可以是安卓用户的十几倍。所以如果你们的产品真的有前景,并且想长久做下去,还是奉劝不要做欺骗苹果的事情了。

    Q6:网上有好多讲丢单的博客,看的是一脸懵逼,有的看懂后,在看下一篇又不懂了,感觉都好复杂。

    A6:我在刚接触内购的时候也是这样,我觉得有些博客讲的真有点过了,它为了考虑一些用户的极端操作,多出来很多逻辑处理,导致博客异常的复杂,我记得有博客讲必须要把receipt_data等信息存到keychain里,因为用户有可能卸载App,如果你只存到NSUserDefaults里,那样就丢单了。 ......那么有没有这种情况呢?我觉得是肯定有的。但我们来算算几率,首先他内购成功,在向服务器调接口的时候,他手机突然没电了/断网了/程序崩溃了/网络差等的久他自己杀死进程了 巴拉巴拉。 然后他在下一次手机恢复正常的时候,果断卸载掉App,重新去App Store上下载安装,进入App后,发现内购没到账。

    网上博客还爱用那种切换账号的场景举例,A内购成功了,但用户各种骚操作后,自己换到B账号,然后服务器那边把商品发到B账号上了,等等。

    这些情况都是存在的,因为苹果的内购机制问题,你是不能百分百保证不丢单的,不要把丢单情况看的那么严重,逻辑写的那么复杂。你看看所有大厂的App上都会写充值遇到问题,点我联系客服 巴拉巴拉。关于丢单,我的做法是这样的,在苹果内购成功的回调里,NSUserDefaults存每一笔支付成功的订单,如果服务器校验成功,就把本地存的这笔订单删除。如果没收到服务器的响应,就一直保留。然后每次App启动就会去把本地存的丢单信息扔向服务器校验,校验成功删除,校验失败不管。这里还是看开发时间,当时我写内购功能的时候,预算时间就两天不到,所以写的飞快,就简单的用这个办法去防止丢单,目前来看,没有发现过一笔真正用户充钱但商品没到账的例子。如果大家开发时间充足,可以慢慢去弥补极端操作漏洞。

    Q7:内购为什么会有这么多坑啊?看网上好多博客都在说,我自己做微信/支付宝的时候,没感觉有这么多坑啊

    A7:苹果的内购坑主要有以下几点

    applicationUsername该字段可能为nil 导致客户端没办法用这个参数给服务器透传订单编号,来形成一个交易订单号的绑定。

    校验订单流程是必须服务器主动去询问苹果服务器,而支付宝/微信 却是他们的服务器会在用户支付成功时主动给我们服务器回调。正是这个原因,让iOS开发者饱受折磨,大部分的丢单漏单都是苹果的这个设计造成的。苹果不会主动回调给我们服务器,也就意味着我们服务器需要主动去苹果那里询问这笔订单,到底成没成功。但服务器询问的时机,又是客户端告诉服务器的。这就鸡儿坑了,一些情况下,用户在付费成功后,突然断网了/崩溃了/出现意外了等等,客户端没办法告诉服务器,这就出现了,用户钱成功了,内购商品却没到账。所以网上才会有这么多篇讲防止丢单的博客。

    越狱下,插件也能破解掉苹果内购,然后校验状态status还返回成功。也就是本篇博客开头讲的那种情况。这一点真的是无力吐槽,亏你特么回调给我的receipt_data那么一大长串,有卵用?

    苹果的订单机制。苹果为了保护用户隐私,你是看不到一条条流水明细的。你看到的只有

    这种。

    每一种内购类型的总收入,或者总销量。导致对账查询的时候加了不少麻烦。

    苹果的退款机制。这个比上面一点更坑,iOS用户,内购了某商品,你可以在完全用完了后,联系苹果客服,说我误操作了巴拉巴拉或者说感觉这个商品不值那么多被开发者欺骗了巴拉巴拉,快给我退款,客服就会温柔的告诉你,不要急,她会帮你处理,1-2个工作日把,你就会发现你的钱就退回来了。没记错的话,一段时间内,一个Apple Id可以申请1-2次。但不能多,多了的话就会被苹果拒绝。而这一切,开发者这边是完全不知情的。你不知道哪个用户退款了,你知道的只是一个图,类似下面的这种。

    用户消费了你的内购商品,公司却收不到钱,很多公司的内购服务都是要成本的。如果这种用户一旦多起来,坏账率会飙升,公司就会被活活的拖垮。一个好的项目也就凉凉掉。淘宝上关于iOS内购退款专门有一个超级庞大的黑色产业链。从弄账号到专门联系苹果客服再到道具销赃变现,各司其职,一环套一环,每个环节人都赚的盆满钵满。苦的都是公司,因为苹果没有任何损失,他也不会补偿你公司1毛钱,一切损失都是公司自己承担。没记错的话,15-16年,很多很多游戏公司都是因为这个被活活拖垮的。幸运的是,这种恶意退款一般都是针对游戏公司,因为游戏道具可以快速变现。像正常的App甚少碰到,因为他退款了也没毛用,没法及时变现。毕竟他们可不稀罕跟你们的女用户1v1视频聊天。

    作者:羽化归来

    链接:https://www.jianshu.com/p/5cf686e92924

     

    展开全文
  • 内有微信app支付和ios内购支付的demo,语言为php,如果不足欢迎提建议,谢谢,希望能帮助大家。
  • iOS内购之二次验证

    2021-01-15 17:22:56
    开篇:关于iOS内购整体流程网上能找到很多。我抽丝剥茧,着重说一下二次验证及收据回传的数据问题。二次验证关于二次验证,其实有两种做法,第一种是在app端验证,第二种也是安全防盗的一种,在服务端进行验证。具体...

    开篇:关于iOS内购整体流程网上能找到很多。我抽丝剥茧,着重说一下二次验证及收据回传的数据问题。

    二次验证

    关于二次验证,其实有两种做法,第一种是在app端验证,第二种也是安全防盗的一种,在服务端进行验证。

    具体区别不一一表述,可以查看下面的链接。

    iOS二次验证两种做法的区别

    我要着重说的是二次验证的实际做法和收到的数据是什么。

    一、二次验证具体如何验证

    // 从沙盒中获取交易凭证(收据)

    NSURL *receiptUrl = [[NSBundle mainBundle] appStoreReceiptURL];

    NSData *receiptData = [NSData dataWithContentsOfURL:receiptUrl];

    // 转化为base64字符串

    NSString *receiptString = [receiptData base64EncodedStringWithOptions:0];

    为了方便拿到数据,我们在app端上进行验证,注意⚠️实际开发中都是在服务端进行验证的。

    // 拼接请求数据

    NSString *bodyString = [NSString stringWithFormat:@"{\"receipt-data\" : \"%@\"}", receiptString];

    NSData *bodyData = [bodyString dataUsingEncoding:NSUTF8StringEncoding];

    // 创建请求到苹果官方进行购买验证

    NSURL *url = [NSURL URLWithString:verifyReceipt_url];

    NSMutableURLRequest *requestM = [NSMutableURLRequest requestWithURL:url];

    requestM.HTTPBody = bodyData;

    requestM.HTTPMethod = @"POST";

    // 创建连接并发送同步请求

    NSError *error = nil;

    NSData *responseData = [NSURLConnection sendSynchronousRequest:requestM returningResponse:nil error:&error];

    if (error) {

    NSLog(@"验证购买过程中发生错误,错误信息:%@",error.localizedDescription);

    return;

    }

    NSDictionary *dic = [NSJSONSerialization JSONObjectWithData:responseData options:NSJSONReadingAllowFragments error:nil];

    二、收据回传得到的数据分析

    上文得到的dic就是我们通过收据得到的所有数据

    在这里有个坑,我相信有很多同学都踩过。

    就是你支付成功了,但是在这个数组里却找不到你想要的数据。

    我们可以看下面一张图,然后再分析分析。

    935f8eb69f03?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

    内购类型.png

    可以看到有四种类型,但是具体到我们项目中我们该如何选择和操作呢。

    1.消耗型:比如我们游戏里的金币,6元600个金币,你使用完了就失效了。比如现在各大视频app推出的电影购买,6元买完一张影片之后,你如果想买下一张影片就必须再次购买。

    2.非消耗型:比如某app推出永久会员,购买一次终身使用,就是非消耗型。再比如游戏过关时app的某关,你一直过不去,花钱购买过去了,等你下次再打开app时,他这关默认是已经通过的。

    3.自动续订型:具体可以看爱奇艺视频开通会员(腾讯app也类似),里面就有一个自动续订会员。也就是到期了,它会自动从你的苹果账号上扣钱。这个慎点慎点,他们这样搞很坑钱。

    4.非自动续订型:这个就是1个月优酷会员啦,或者3个月季度会员啥的。有期限,到期时也不会自动扣钱。一般的都是选择这个,不然像爱奇艺的自动续订,用户不知情时,会被骂死,哈哈

    说了这么多,来看看订单数据长啥样

    {

    product_id = "lalalalahahaha",//商品的标识,和产品定,随便写但是不能重复

    quantity = "1",//购买商品的数量

    transaction_id = "1000000357637984",//交易的标识

    purchase_date_ms = "1512613065000",//购买时间毫秒

    original_purchase_date_pst = "2017-12-06 18:17:45 America/Los_Angeles",//购买时间,太平洋标准时间

    purchase_date_pst = "2017-12-06 18:17:45 America/Los_Angeles",//太平洋标准时间

    original_purchase_date_ms = "1512613065000",//毫秒

    is_trial_period = "false",

    original_purchase_date = "2017-12-07 02:17:45 Etc/GMT",//原始购买时间

    original_transaction_id = "1000000357637984",//原始交易ID

    purchase_date = "2017-12-07 02:17:45 Etc/GMT"//购买时间

    },

    我们得到的字典里如果有多条数据,就会有一个数组in_app。

    如果只有一条数据,或者没有数据,就没有in_app。

    in_app里的数据格式就如上文代码所书。

    所有的订单信息都在in_app里。所以我们要找到最新成功的那条数据,就需要在这里面去找。

    注:

    1.消耗性的和其他的有区别,消耗性的下一次购买的,会覆盖上一次购买的,所以只有一条消耗性的订单信息,当然也是最新的。

    2.其他三种每次购买,都会生成新的订单信息,不会覆盖原来的,所以你购买多少次,上面就会显示多少条。

    三、内购二次验证实际开发中具体做法

    1.我们把收据信息传给服务器

    2.服务器自行处理所有的订单信息,返回一个成功与失败的状态给我们。所有的操作,后台都已经记录在案了,我们不再需要管。我们只需要知道,此次购买是否成功就行了。

    结语:内购其实很简单,只是有时候不经意就会入坑,在此特意记录下困惑的地方,帮助自己深刻记忆,也希望帮助到其他遇到该问题的童鞋。

    展开全文
  • IOS内购验证 (Java版)

    2021-07-16 10:39:34
    IOS内购验证 (Java版) 此处给各位贴出apple官方文档 apple收据文档 apple收据responseBody字段释义 package xxxxx; /** * @description 苹果验证返回结果状态码枚举 * @Author xc * @Date 2021/3/12 18:23 **...
  • /*** @Author woann * @param Request $request* @return \Illuminate\Http\JsonResponse* @des ios内购支付回调*/public function iosNotify(Request $request){$this->validate($request,['order_id' =>...
  • iOS内购的实现

    2021-09-06 18:10:04
    第一步,去开发者网站配置税务账号 第二步,去开发者网站更新响应APP的价格设置 第三步,在Appdelegate开启补单通知 第四步:在Xcode开启App的内购权限 第五步:去掉手机的代理去购买
  • java集成ios内购\与ios退款通知处理

    千次阅读 2021-09-01 18:19:31
    使用ios内购,需在项目数据库建立虚拟币相关表(虚拟币余额表、充值面额表、充值订单表等)上代码 苹果IAP内购验证工具类 IosVerifyUtil import javax.net.ssl.*; import java.io.BufferedOutputStream; import ...
  • iOS内购三:Receipt

    千次阅读 2020-09-10 11:55:20
    iOS内购三:Receipt 可参考: Validating Receipts Locally 需验证receipt,可以在本地验证,也可以在服务端验证 本地验证,涉及到security和加密,比较复杂 而服务端验证相对而言简单些 收到transaction后,将...
  • iOS 内购遇到的坑

    2020-12-21 01:19:26
    您以购买过此APP内购项目,此项目将免费恢复您以购买过此APP内购项目,此项目将免费恢复.PNG原因:当使用内购购买过商品后没有把这个交易事件关,所以当我们再次去购买商品后就会调用以前已经购买成功的交易事件去...
  • iOS内购二:购买和恢复

    千次阅读 2020-09-09 11:28:41
    iOS内购二:购买和恢复 购买 构建一个SKPayment对象,传递SKProduct。SKPayment被创建后,就会将其加入到SKPaymentQueue队列中 然后用户会授权,payment是异步,所以需要一个observer,apple建议observer添加都的...
  • iOS内购原因审核被拒

    2021-03-03 14:37:25
    【uniapp】打包成iOS内购原因审核被拒,自己在沙箱环境测试正常支付,审核人员不行;被拒原因如下图,求助200-500元感谢。 <p><img alt="" height="848" src=...
  • iOS内购--java后台

    千次阅读 2019-02-15 09:49:15
    最近公司iOS发布了新版本,被拒,原因就是没有添加内购,并被严重警告,为此,不得已要加上iOS内购功能,以下就是我为了iOS内购所写的后台代码,首先看下支付的时序图吧: 简单说下,时序图的意思吧: 第一...
  • iOS 内购 大致步骤

    2021-02-28 18:53:17
    需要创建内购项目 核心代码(伪代码)导入头文件#import//开始充值请求 rechargeid是自己设置产品id-(void)startRechargeWithRechargeId:(NSString *)rechargeId {SKProductsRequest *queryRequest = [...
  • IOS 内购价格文档

    2020-09-09 14:18:44
    Afghanistan(USD) China(CNY) 价格 收入 价格 收入 等级1 0.99 0.7 6 4.12 等级2 1.99 1.4 12 8.23 等级3 2.99 2.1 ... 20.5...
  • iOS原生内购代码, 支持消耗型内购,一次性内购和订阅, 原生ObjectC编写封装,支持最新iOS 13系统!
  • 一、内购项目的创建 游戏内购项目就跟商品一样,需要在appstoreconnect后台对应用下创建APP内购买项目 app内购项目分为四种 消耗型项目–这类内购项目是指游戏内一次性购买的道具,元宝等 非消耗型项目 – 指购买后...
  • Q1:内购和Apple Pay的区别? A1:内购内购,Apple Pay是Apple Pay。我不知道有多少人第一次接触时,会把这俩概念混淆掉,这里你可以简单这么理解,虚拟的物品就是用内购,实际的物品就是用Apple Pay。Apple Pay是...
  • iOS 内购SwiftyStoreKit

    千次阅读 2021-12-17 14:29:29
    最近又重新写了很久之前写的内购,该项目中没有订阅,而另一个项目中包含了订阅和消费型的购买。重新整理了一下,项目中用的是SwiftyStoreKit。 我们先来看一下内购类型: 由于项目中有购买虚拟币和订阅,这里就...
  • iOS 内购最新讲解

    2020-12-19 11:15:45
    总说内购的内容协议、税务和银行业务 信息填写内购商品的添加添加沙盒测试账号内购代码的具体实现内购的注意事项二.协议、税务和银行业务 信息填写2.1、协议、税务和银行业务 信息填写 的入口协议、税务和银行业务 ...
  • iOS-iOS内购流程(手把手图文教程)

    千次阅读 2020-01-05 21:54:55
    上家公司做了一个APP,可以充值虚拟金币,但是如果是虚拟道具,就必须使用苹果内购,不然审核过不了,而且很黑,三七分;当然,你如果购买真是东西,比如淘宝、京东等等就不需要了!这里我就来说说苹果内购流程,...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 27,068
精华内容 10,827
关键字:

ios内购

友情链接: 案例.rar