• iPhone In App Purchase购买完成时验证transactionReceipt


      最近正在做一个iphone游戏内购买的项目,所以了解了一些In App Purchase相关的技术。

      根据Apple官方文档,In App Purchase(IAP)有两种模型:内建模型(Built-in Model)和服务器模型(Server Model)。由于我做的项目需要用自己的服务器管理虚拟货币,因此自然就选择了服务器模型。

      由于IAP的整个流程比较复杂,一篇博客的篇幅无法完全介绍清楚,所以我将用几篇博客的篇幅来做介绍。在这篇博客里就仅仅介绍IAP购买完成后如何通过将客户端收到的transactionReceipt发送到我自己的服务器(我用的Google App Engine,Python环境),再由我的服务器验证用户的购买行为是否已完成。

      如果想要全面了解IAP流程的,请移步至apple文档:http://developer.apple.com/library/ios/#documentation/NetworkingInternet/Conceptual/StoreKitGuide/Introduction/Introduction.html

      以下是验证receipt的详细流程:

      IAP在购买流程中,会给每一次购买行为创建一个SKPaymentTransaction,这个transaction会记录用户购买行为的状态,如正在购买(SKPaymentTransactionStatePurchasing),已购买(SKPaymentTransactionStatePurchased),购买失败(SKPaymentTransactionStateFailed)等。而当transaction状态是 SKPaymentTransactionStatePurchased的时候,客户端就能得到一个transaction.transactionReceipt。我的目的就是要从客户端发送这个receipt,然后服务器收到receipt后,通过POST方式发送receipt到app store,app store会验证receipt并返回验证结果,服务器收到结果后再判断验证是否成功。

      第一步:发送receipt

      首先在SKPaymentTransaction的状态改变的时候,会调用一个delegate:- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions;。我可以通过transactions数组得到里面的每一个transaction。再判断它的transactionState,如果是等于SKPaymentTransactionStatePurchased,就调用另外一个函数:completeTransaction,这个函数的具体实现:

    - (void)completeTransaction:(SKPaymentTransaction *)transaction {
    // truely purchase IAP product for user
    NSString* jsonObjectString = [self encode:(uint8_t *)transaction.transactionReceipt.bytes
    length:transaction.transactionReceipt.length];
    NSString
    * varStr = [[NSString alloc] initWithFormat:
    @"your_url?receipt=%@",
    jsonObjectString];
    VMRequest
    * request = [[VMRequest alloc] initWithVariableStr:varStr
    requestType:PURCHASED_IAP_PRODUCT
    delegate:self];
    [[VMRequestQueue sharedQueue] addRequest:request
    toQueue:[VMRequestQueue sharedQueue].requestSetInfoQueue];
    [mIAPRequestDict setObject:request forKey:transaction.transactionIdentifier];
    [varStr release];
    [request release];
    }

      这个函数首先是执行了一段encode操作。这段操作是对transactionReceipt做了一次base64的编码(根据apple文档的要求)。编码的代码是我从网上找到的,代码如下:

    - (NSString *)encode:(const uint8_t *)input length:(NSInteger)length {
    static char table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";

    NSMutableData
    *data = [NSMutableData dataWithLength:((length + 2) / 3) * 4];
    uint8_t
    *output = (uint8_t *)data.mutableBytes;

    for (NSInteger i = 0; i < length; i += 3) {
    NSInteger value
    = 0;
    for (NSInteger j = i; j < (i + 3); j++) {
    value
    <<= 8;

    if (j < length) {
    value
    |= (0xFF & input[j]);
    }
    }

    NSInteger index
    = (i / 3) * 4;
    output[index
    + 0] = table[(value >> 18) & 0x3F];
    output[index
    + 1] = table[(value >> 12) & 0x3F];
    output[index
    + 2] = (i + 1) < length ? table[(value >> 6) & 0x3F] : '=';
    output[index
    + 3] = (i + 2) < length ? table[(value >> 0) & 0x3F] : '=';
    }

    return [[[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding] autorelease];
    }
    原文链接:http://stackoverflow.com/questions/1298998/verify-receipt-for-in-app-purchase

      encode完成后,就开始将编码过的receipt发送到我的服务器。这一段很简单,只要先创建一段url格式的字符串,再创建url,然后在初始化request的时候添加这个url,最后创建connection就行了。其中VMRequest是我写的一个继承自NSMutableURLRequest的类,这里可以直接用NSMutableURLRequest代替。需要注意的是这里的request最好使用POST方式,因为GET方式很可能因为发送的receipt数据量过大而导致数据丢失。VMRequestQueue是VMRequest的队列,这个队列会自动按顺序执行队列中的request,这部分可以改成直接创建NSURLConnection。总之,只要创建好需要访问的URL,再用POST方式发送到自己的服务器,这第一步就完成了。

      第二步:向app store验证

      这一步中所做的事全部是在服务器端完成。我的服务器使用的是Google App Engine(GAE)的Python环境。

      首先,服务器收到receipt。因为这段receipt是已经经过编码的,所以不需要再度编码,唯一要做的就是生成一个json格式的数据,发送到app store并获取返回的数据。具体代码如下:

    import httplib
     # have to import simplejson because google app engine uses python 2.5. When it upgrades, can import json directly
    import simplejson as json

    jsonStr
    = json.dumps({"receipt-data": receipt})
    #connect = httplib.HTTPSConnection("buy.itunes.apple.com")
    #
    sandbox
    connect = httplib.HTTPSConnection("sandbox.itunes.apple.com")
    headers
    = {"Content-type": "application/json"}
    connect.request(
    "POST", "/verifyReceipt", jsonStr)
    result
    = connect.getresponse()
    data
    = result.read()
    connect.close()
    decodedJson
    = json.loads(data)
    status
    = decodedJson[u'status']
    if status == 0:
    return decodedJson
    else:
    return False

      simplejson是python2.5支持的一个json库,使用simplejson的原因是GAE只支持到python2.5。如果GAE版本更新到2.6及以上了,那就可以直接import json。

      json对象的格式有点类似于一个dictionary,一个key对应于一个value。

      json.dumps是将一个json对象序列化成一段字符串,相对的json.loads就是将一段字符串反序列化成一个json对象。

      httplib是一个python自带的库,用来做http连接。根据apple文档,代码中的request类型必须是POST,这一点需要注意。

      最后,在读取json对象的数据的时候,必须要在key的前面加“u”。

      结果:服务器收到解码的receipt

      服务器收到的解码的receipt格式是:

      {u'status': < status>, u'receipt': {u'product_id': u'<product_id>', u'original_transaction_id': u'< original_transaction_id>', u'bid': u'<bid>', u'original_purchase_date': u'< original_purchase_date>', u'bvrs': u'< bvrs>', u'purchase_date': u'< purchase_date>', u'item_id': u'<item_id>', u'transaction_id': u'< transaction_id>', u'quantity': u'< quantity>'}}

      这其中,status就表示receipt是否通过验证;receipt后又是一个json对象,里面product_id就是用户购买的IAP产品的id。

    最后一步,判断status是否等于0。如果等于0,就执行用户购买后应该实现的操作,最终再将结果反映到客户端上。
  • 相关阅读:
    ECSHOP获取当前分类所在顶级分类信息
    二级域名会不会分散主域名权重
    ECSHOP增加模板页的方法
    ECSHOP之transport.js/run() error:'process_request' 未定义
    AIR任务栏图标的闪烁
    googlemap数据采集器(三)
    Flex中List自己定义itemrenderer渲染问题的解决
    GIS理论(墨卡托投影、地理坐标系、地面分辨率、地图比例尺、Bing Maps Tile System)
    android下歌曲名称乱码的解决办法
    史上最强劲的android模拟器命令详解
  • 原文地址:https://www.cnblogs.com/eagley/p/2081577.html
Copyright © 2020-2023  润新知