iOSAPP アプリ内課金について 忘備録
参考サイト アプリ内課金の実装方法 - TERAKOYA
setを宣言
配列、で構造体
- 要素が重複しない。
- 順番が保証されない
まさにアイテム課金用の配列と言ってもいい。
private var productManagers: Set<ProductManager> = Set() //setで宣言したものに追加していく。 productManagers.insert(productManager) //SKProductは商品 クロージャー型の型を指定している 必ずクロージャーを利用する private var completionForProductidentifiers : (([SKProduct]?, NSError?) -> ())?
クラスfunc はインスタンス化しなくても呼び出せる。static funcのようなもの オーバーライドもできる
class ProductManager:NSObject,SKProductsRequestDelegate{ //SKProduct商品 クロージャーの型をしてい 必ずクロージャーを利用する private var completionForProductidentifiers : (([SKProduct]?, NSError?) -> ())? class func productsWithProductIdentifiers(productIdentifiers: [String]!, completion:(([SKProduct]?, NSError)-> () )?){ let productManager = ProductManager() //クラスのインスタンスから、productsWithProductIdentifiers関数を呼び出している。 //クラスfuncなので、インスタンス化しなくても呼び出せる。 ここで初めてインスタンスができている 帰ってくるのは商品。 productManager.completionForProductidentifiers = completion as? (([SKProduct]?, NSError?) -> ()) //SKProductsRequest appストアに上げた情報を取得する let productRequest = SKProductsRequest(productIdentifiers: Set(productIdentifiers)) productRequest.delegate = productManager productRequest.start() //productManagersはskproduct requestの非同期の処理を 複数抱え込むもの。 productManagers.insert(productManager) }
@optional - (void)requestDidFinish:(SKRequest *)request NS_AVAILABLE(10_7, 3_0);
SKRequestは型 第一のキーワード引数と内部引数は兼ねているので requestは内部引数
NS_AVAILABLE(10_7, 3_0); はマクロ 利用できるバージョンが書いてある。
PurchaseManager
/// シングルトン private let purchaseManagerSharedManager = PurchaseManager() /// シングルトン 外側からsharedManagerを通して呼ばれる class func sharedManager() -> PurchaseManager{ return purchaseManagerSharedManager; }
//未処理のトランザクションがあればそれを利用 OS側がもつことが多い //バックグラウンドで、SKPaymentQueueを持ってくれている パスワードを入力して下さいと何回も出るときは //SKPaymentQueueが溜まっているということ let transactions = SKPaymentQueue.default().transactions if transactions.count > 0 { for transaction in transactions { if transaction.transactionState != .purchased { continue } //すでに購入したものを買うと、買いましたよと言ってくれる 間違えて2回押しても、もう購入しましたよと教える // 非消耗型 機能開放するなど 別のコンテンツを買うのなら非消耗型 //コンテンツ配信は非消耗型、200円とか値段がついて入れば //通貨は消耗型 通貨で買わせればいい //アプリ内の武器などは消耗型 if transaction.payment.productIdentifier == product.productIdentifier { if let window = UIApplication.shared.delegate?.window { let ac = UIAlertController(title: nil, message: "\(product.localizedTitle)は購入処理が中断されていました。\nこのまま無料でダウンロードできます。", preferredStyle: .alert) let action = UIAlertAction(title: "続行", style: UIAlertAction.Style.default, handler: {[weak self] (action : UIAlertAction!) -> Void in if let weakSelf = self { weakSelf.productIdentifier = product.productIdentifier weakSelf.completeTransaction(transaction) } }) ac.addAction(action) window!.rootViewController?.present(ac, animated: true, completion: nil) return } } } //課金処理開始 未処理のトランザクションがない場合 普通の課金処理 let payment = SKMutablePayment(product: product) SKPaymentQueue.default().add(payment) self.productIdentifier = product.productIdentifier }
1.Viewcontrollerで商品陳列する 2.どんな商品があるかは、ProductManagerクラスが管理 3.購入は、PurchaseManager startwithproductが呼ばれる
ProductManagerはどれがいくらか書いているだけ 販売停止にしたりするので、分けておいたほうがいい。 値段とかも変更できるので。
課金で難しいのは、サーバーの実装が絡む時
課金しましたよをアプリ内で保持するのは簡単。 サーバーに買いましたよというAPIを投げたときに、 アップルに検証する。 レシートの検証をする。
アプリ内にしておいたほうがいい。 リクエストの改ざんは簡単にでききる けんしょうるための仕組みが大変
サーバーで課金情報を保持するか?
このユーザーはこのアイテムを買いましたー>危険です。
アプリの外に出さない。通信しないほうがいい。 userdefaultに暗号化して保持しておく。 userdefaultはあまりセキュアでないので、暗号化して持っておく。
OSまたぐと、サーバーで課金情報持たないといけない
ゲームのアイテム等、消耗型はosで保持できないのでむつかしい。