nirasan's tech blog

趣味や仕事の覚え書きです。Linux, Perl, PHP, Ruby, Javascript, Android, Cocos2d-x, Unity などに興味があります。

Unity での iOS アプリ内課金結果をサーバーサイドで検証する

はじめに

  • Unity で iOS のアプリ内課金をした際に、サーバーサイドでの課金内容チェックをしたメモ。
  • Unity でのアプリ内課金には prime31 の iOS StoreKit In App Purchase Plugin を使用。
  • サーバーサイドは PHP を使用。

Unity 側

  • prime31 の iOS StoreKit In App Purchase Plugin を購入してインポート
  • Asset の Plugins/StoreKit/demo/StoreKitEventListener.cs の課金成功時コールバックメソッドに、サーバーへのレシート検証リクエストのコードを追加。
    void purchaseSuccessfulEvent( StoreKitTransaction transaction )
    {
        Debug.Log( "purchaseSuccessfulEvent: " + transaction );
        
        // エンコード済みのレシートデータの取得
        string receipt = transaction.base64EncodedTransactionReceipt;

        // ペイロードの作成
        WWWForm form = new WWWForm ();
        form.AddField ("receipt", receipt);
        
        // レシートを検証するサーバーのURL
        string url = "http://myserver";
        
        // 検証リクエストの送信と、成功時のコールバック処理の定義
        StartCoroutine(this.DoWWW(new WWW(url, form), (www) => {
            Debug.Log("-------- Callback Success: " + www.text);
        }));
    }
    
    private delegate void WWWCallback (WWW www);
    private IEnumerator DoWWW (WWW www, WWWCallback callback) {

        yield return www;

        bool error = false;
        if (!string.IsNullOrEmpty (www.error) || string.IsNullOrEmpty (www.text)) {
            error = true;
        }

        if (!error && callback != null)
            callback (www);
    }

サーバー側

<?php

$postData = json_encode(
    array('receipt-data' => $_POST["receipt"])
);

// 本番環境
$response = post("https://buy.itunes.apple.com/verifyReceipt", $postData);

// 本番環境にテスト環境のレシートを問い合わせてしまった場合、テスト環境に問い合わせなおす。
// 実行時に本番かテストかを判定できないのでこのように対応する。
// http://www.aguuu.com/archives/2012/12/in-app-purchase-annotation/
if ($response->status == 21007) {
    $response = post("https://sandbox.itunes.apple.com/verifyReceipt", $postData);
}

var_dump($response);
/*
stdClass::__set_state(array(
    'receipt' =>
        stdClass::__set_state(array(
            'original_purchase_date_pst' => '2014-05-17 17:48:39 America/Los_Angeles',
            'purchase_date_ms' => '1400374119583',
            'unique_identifier' => 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
            'original_transaction_id' => '1000000111096489',
            'bvrs' => '1.0',
            'transaction_id' => '1000000111096489',
            'quantity' => '1',
            'unique_vendor_identifier' => 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX',
            'item_id' => 'MY_ITEM_ID',
            'product_id' => 'MY_PRODUCT_ID',
            'purchase_date' => '2014-05-18 00:48:39 Etc/GMT',
            'original_purchase_date' => '2014-05-18 00:48:39 Etc/GMT',
            'purchase_date_pst' => '2014-05-17 17:48:39 America/Los_Angeles',
            'bid' => 'MY_BID',
            'original_purchase_date_ms' => '1400374119583',
        )),
    'status' => 0,
))
*/

// 検証成功の場合
if ($response->status == 0) {
    // 決済毎に transaction_id がユニークになるのでこれを控え、
    // 同じレシートで複数回リクエストがあったら無視するように。
    $transaction_id = $response->receipt->transaction_id;
    // 検証成功の旨をレスポンス
}

private function post($endpoint_url, $postData)
{
    $ch = curl_init($endpoint_url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
    $response = json_decode(curl_exec($ch));
    curl_close($ch);
    return $response;
}