トップ «前の日記(2012-05-20) 最新 次の日記(2012-05-27)» 編集

Cocoa練習帳

iOS/iPhone/iPad/watchOS/tvOS/MacOSX/Android プログラミング, Objective-C, Cocoa, Swiftなど

2012|01|02|03|04|05|06|07|08|09|10|11|12|
2013|01|02|03|04|05|06|07|08|09|10|11|12|
2014|01|02|03|04|05|06|07|08|09|10|11|12|
2015|01|02|03|04|05|06|07|08|09|10|11|12|
2016|01|02|03|04|05|06|07|08|09|10|11|12|
2017|01|02|03|04|05|06|07|08|09|10|11|12|
2018|01|02|03|04|05|06|07|08|09|10|11|12|
2019|01|02|03|04|05|06|07|08|09|10|11|12|
2020|01|02|03|04|05|06|07|08|09|10|11|12|
2021|01|02|03|04|05|06|

2012-05-26 [iOS]アプリ内課金(レシートの確認)

アプリケーションは購入情報を得ても、外部のサーバが購入情報によって何らかの対応を行う場合は、それが正しい物である事を確認できないといけない。ということで、Storeレシートの確認に挑戦する。

外部サーバとの通信が必要ない場合。例えば、購入されると、アプリケーション内の機能制限フラグを落として、隠し機能を有効にする場合は、前回までの方法で対応できる。サーバから、購入に対応するデータを取得して、アプリケーションで利用できるようにする場合、外部のサーバへの通知が本当に正しいのか気にする必要があるようだ。そこで、購入が成功するとStoreKitからレシートを渡されるので、されを外部サーバに渡し、外部サーバがSoteにレシートの内容が正しい事を確認するという仕組みのようだ。

サンプルでは、購入成功を受けたメソッドで、簡易的にアプリケーションからStoreにレシートの内容を確認している。

まず初めに、サンプル・コードはBASE64にエンコードするコードだ。簡易なもので、テストも不十分なのであしからず。

#define BASE64PAD @"="
 
static const char   base64Alphabet[64] = {
    'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
    'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
    'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
    'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
    'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
    'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
    'w', 'x', 'y', 'z', '0', '1', '2', '3',
    '4', '5', '6', '7', '8', '9', '+', '/'
};
 
- (NSString *)stringEncodedWithBase64:(NSData *)data
{
    if (! data) return nil;
    
    NSUInteger      dataLen = data.length;
    unsigned char   *dataBytes = (unsigned char *)[data bytes];
    NSMutableString *str = [[NSMutableString alloc] init];
    
    NSUInteger  dataIndex = 0;
    while (dataIndex < dataLen) {
        char    d[3] = {0, 0, 0};
        d[0] = dataBytes[dataIndex];
        if ((dataIndex + 1) < dataLen)
            d[1] = dataBytes[dataIndex + 1];
        if ((dataIndex + 2) < dataLen)
            d[2] = dataBytes[dataIndex + 2];
        NSUInteger  bit6 = 0;
        char    s[5];
        
        bit6 = (d[0] >> 2) & 0x3F;
        s[0] = base64Alphabet[bit6];
        
        bit6 = ((d[1] >> 4) & 0x0F) | ((d[0] << 4) & 0x3F);
        s[1] = base64Alphabet[bit6];
        
        bit6 = ((d[2] >> 6) & 0x03) | ((d[1] << 2) & 0x3F);
        s[2] = base64Alphabet[bit6];
        
        bit6 = d[2] & 0x3F;
        s[3] = base64Alphabet[bit6];
        
        s[4] = '\0';
        
        [str appendString:[NSString stringWithCString:s encoding:NSASCIIStringEncoding]];
        
        dataIndex += 3;
    }
    if (dataIndex < dataLen) {
        NSRange aRange = NSMakeRange(dataLen + 2, (dataIndex - dataLen));
        [str replaceCharactersInRange:(NSRange)aRange withString:BASE64PAD];
    }
    
    NSUInteger  padNum = [str length] % 4;
    for (NSUInteger i = 0; i < padNum; i++) {
        [str appendString:BASE64PAD];
    }
    
    return str;
}

自分自身が分かりやすい事を優先したコードなので、ちょっと、恥ずかしい。

次はレシートを確認するコードだ。

- (void)completeTransaction:(SKPaymentTransaction *)transaction
{
    /* NSURL *url = [NSURL URLWithString:@"https://buy.itunes.apple.com/verifyReceipt"]; */
    NSURL *url = [NSURL URLWithString:@"https://sandbox.itunes.apple.com/verifyReceipt"];
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
    [request setHTTPMethod:@"POST"];
    NSString *json = [NSString stringWithFormat:@"{\"receipt-data\" :\"%@\"}",
                      [self stringEncodedWithBase64:transaction.transactionReceipt]];
    [request setHTTPBody:[json dataUsingEncoding:NSUTF8StringEncoding]];
    NSURLResponse *response;
    NSError *error;
    NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
    NSLog(@"%@", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
    
    [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}

通信先のURLは"https://buy.itunes.apple.com/verifyReceipt"が正しいのだが、テストなのでサンドボックス環境の"https://sandbox.itunes.apple.com/verifyReceipt"を設定している。

transaction.transactionReceiptがレシートで、これを外部のサーバに私て、外部サーバが上記のようなコードを例えば、PythonやRuby、PHPで記述して、レシートを確認するという事だ。

以下が結果。

{
"receipt":{
    ...
"status":0}

正しいということか。

_ 関連情報

In App Purchase プログラミングガイド
Technical Note TN2259
Adding In-App Purchase to your iOS and Mac Applications
失敗しないiOS In-App Purechaseプログラミング
『A Day In The Life』参考にさせていただきました。分かりやすく、具体的な説明、ありがとうございます。

トップ «前の日記(2012-05-20) 最新 次の日記(2012-05-27)» 編集