質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
iOS

iOSとは、Apple製のスマートフォンであるiPhoneやタブレット端末のiPadに搭載しているオペレーションシステム(OS)です。その他にもiPod touch・Apple TVにも搭載されています。

PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

Xcode

Xcodeはソフトウェア開発のための、Appleの統合開発環境です。Mac OSXに付随するかたちで配布されています。

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

Q&A

解決済

1回答

7511閲覧

swift2 消耗型アプリ内課金 サーバー(PHP)側でレシート検証がうまくいかない

Ryo1012

総合スコア13

iOS

iOSとは、Apple製のスマートフォンであるiPhoneやタブレット端末のiPadに搭載しているオペレーションシステム(OS)です。その他にもiPod touch・Apple TVにも搭載されています。

PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

Xcode

Xcodeはソフトウェア開発のための、Appleの統合開発環境です。Mac OSXに付随するかたちで配布されています。

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

0グッド

0クリップ

投稿2016/08/19 02:42

編集2016/08/22 10:31

はじめまして、質問させていただきます。
現在iOS向けのアプリを制作中です。
その際に、消耗型のアプリ内課金の処理を実装する必要があり、なんとか書籍やネット上の情報を手掛かりにコードを組んでみたのですが、iOSとサーバーとのやりとりやレシートの検証のところでつまづいてしまったためどなたかご教授していただけたらと思います。

■環境
iOS側:Xcode7.3 swift2.2
サーバー側:PHP5.6.14

■問題点
1.iOS側でBase64にエンコードしたレシートをPHPにPOSTする部分は成功しているが、PHP側でiTunesConnectにJSON形式で送信すると、返ってきたステータスが21002(プロパティのデータが不正であるか、または欠落)になる。
2.ステータスが0(成功)の場合、その旨をiOS側にレスポンスしたが、その方法が分からない。

この2点のうまく方法をご教授いただきたいです。

■iOS側ソースコード(swift側)

// トランザクションの状態変更時に呼ばれる func paymentQueue(queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) { for trans:SKPaymentTransaction in transactions { // 購入処理中 if trans.transactionState == SKPaymentTransactionState.Purchasing { // インジゲーター表示 } // 購入成功 else if trans.transactionState == SKPaymentTransactionState.Purchased { // トランザクション完了 queue.finishTransaction(trans) // レシートの処理 let receiptURL = NSBundle.mainBundle().appStoreReceiptURL let receiptData = NSData(contentsOfURL: receiptURL!) // レシートの保存 let storage = NSUserDefaults.standardUserDefaults() var receipts = storage.arrayForKey("receipts") if (receiptData != nil) { if (receipts == nil) { storage.setObject([receiptData!], forKey: "receipts") } else { receipts!.append(receiptData!) storage.setObject(receipts, forKey: "receipts") } storage.synchronize() } // 独自サーバーに未送信レシートを送信 self.accessServer({ (success: Bool) in if success { // アイテム追加成功(アラート表示) self.showAlert(nil, text: "アイテムが追加されました") } }) } // 購入失敗(ユーザーキャンセル含む) else if trans.transactionState == SKPaymentTransactionState.Failed { // ユーザーキャンセル以外 if trans.error!.code == SKErrorCode.PaymentCancelled.rawValue { // 失敗(アラート表示) self.showAlert(nil, text: trans.error?.description) } // トランザクション完了 queue.finishTransaction(trans) } // その他のエラー else { // トランザクション完了 queue.finishTransaction(trans) } } } // トランザクションの終了時に呼ばれる func paymentQueue(queue: SKPaymentQueue, removedTransactions transactions: [SKPaymentTransaction]) { } // 独自サーバーに未送信レシートを送信 func accessServer(completion: (Bool) -> Void) { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), { let storage = NSUserDefaults.standardUserDefaults() var receipts = storage.arrayForKey("receipts") var result = false if (receipts != nil) { // 独自サーバーに未送信レシートを送信 for (var i = 0; i < receipts!.count; i+=1) { let receiptData = receipts![i] as! NSData // Base64にエンコード let base64 = receiptData.base64EncodedStringWithOptions(NSDataBase64EncodingOptions.Encoding64Charac LineLength) // 独自サーバーに未送信レシートを送信する self.sendReceipts(base64) } result = true // 通信成功時にストレージの削除 if (result) { storage.removeObjectForKey("receipts") storage.synchronize() } } // 結果を返す dispatch_async(dispatch_get_main_queue(), { completion(result) }) }) } // レシートをサーバーに送信 func sendReceipts(var receipts: String) { let server = "ドメイン名" + "receipts.php" let request = NSMutableURLRequest(URL: NSURL(string: server)!) request.HTTPMethod = "POST" receipts = "RECEIPTS=\(receipts)" // StringからNSDataにキャスト request.HTTPBody = receipts.dataUsingEncoding(NSUTF8StringEncoding) var response: NSURLResponse? // サーバへ送信 do { let resultData = try NSURLConnection.sendSynchronousRequest(request, returningResponse: &response) } catch { } }

■サーバー側ソースコード(PHP)

<?php // レシート受け取り $receipt = $_POST['RECEIPTS']; $postData = json_encode(array('receipt-data' => $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); } echo $response->status; // 21002と表示される // 検証成功の場合 if ($response->status == 0) { // 決済毎に transaction_id がユニークになるのでこれを控え、 // 同じレシートで複数回リクエストがあったら無視するように。 $transaction_id = $response->receipt->transaction_id; // 検証成功の旨をレスポンス // ここでiOS側に成功したことをレスポンスしたい } 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); curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-type: application/json; charset=UTF-8')); // HTTPヘッダー $response = json_decode(curl_exec($ch)); curl_close($ch); return $response; } ?>

以上、宜しくお願い致します。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

自己解決

解決致しました。

1.Base64にエンコードした後に不要な文字が混ざっているためそれを削除して、サーバー送信後に検証することによりうまくいきました。

2.検証がうまくいったことをPHP側でprint関数等で出力し、アプリ側の

let resultData = try NSURLConnection.sendSynchronousRequest(request, returningResponse: &response)

にてレスポンスを受け取ることにより解決致しました。

投稿2016/10/27 04:51

Ryo1012

総合スコア13

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問