Swiftで簡単に実装できるアプリ内課金

In-App Purchaseの処理を書くのは結構めんどくさい。できるだけメインの実装にコストを掛けて、サブ的な実装にはあまりコストを掛けたくないものです。
この記事ではSwiftを利用したアプリ内課金をできるだけコピペだけで実装できるように紹介します。

SwiftyStoreKitの準備

Xcodeへのインストール

タイトル通りIn-App Purchaseの実装にはSwitfyStorekitを使用します
あらかじめ、CocoaPodやCarthageを使ってXcodeインストールしてください。

参考 SwiftyStoreKitgithub.com

AppDelegateへの実装

AppDelegate

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
	// see notes below for the meaning of Atomic / Non-Atomic
	SwiftyStoreKit.completeTransactions(atomically: true) { purchases in
	    for purchase in purchases {
	        switch purchase.transaction.transactionState {
	        case .purchased, .restored:
	            if purchase.needsFinishTransaction {
	                // Deliver content from server, then:
	                SwiftyStoreKit.finishTransaction(purchase.transaction)
	            }
	            // Unlock content
	        case .failed, .purchasing, .deferred:
	            break // do nothing
	        }
	    }
	}
    return true
}

Swiftファイルへのimport

import

import SwiftyStoreKit

Swift実装

価格の取得

func

    func purchaseGetInfo(PRODUCT_ID:String) {
        SwiftyStoreKit.retrieveProductsInfo([PRODUCT_ID]) { result in
            if let product = result.retrievedProducts.first {
                //未購入の場合

            } else {
                //購入済みの場合

            }
        }
    }
呼び出し

purchaseGetInfo(PRODUCT_ID: "")
MEMO
呼び出し時にご自身のアプリ内課金の製品IDをPRODUCT_IDとして渡してください。

消費・非消費型課金

function

func purchase(PRODUCT_ID:String){
   SwiftyStoreKit.purchaseProduct(PRODUCT_ID, quantity: 1, atomically: true) { result in
       switch result {
       case .success(_):
           //購入成功
           //購入の検証
           self.verifyPurchase(PRODUCT_ID: PRODUCT_ID)
       case .error(_):
           //購入失敗
       }
    }
}
    
func verifyPurchase(PRODUCT_ID:String){
    let appleValidator = AppleReceiptValidator(service: .production, sharedSecret: SECRET_CODE)    
    SwiftyStoreKit.verifyReceipt(using: appleValidator) { result in
        switch result {
        case .success(let receipt):
            let purchaseResult = SwiftyStoreKit.verifyPurchase(productId: PRODUCT_ID, inReceipt: receipt)            
            switch purchaseResult {
            case .purchased:
                //リストアの成功
                
            case .notPurchased:
                //リストアの失敗
            }
        case .error:
            //エラー
            
        }
    }
}
MEMO
functionのSECRET_CODEにはご自身のアプリ内課金のApp用共有シークレットそして生成された共有シークレットを入力してください。
呼び出し

purchase(PRODUCT_ID: "")
MEMO
呼び出し時にご自身のアプリ内課金の製品IDをPRODUCT_IDとして渡してください。

非消費課金のリストア

メソッドを利用した方法

function

SwiftyStoreKit.restorePurchases(atomically: true) { results in
    if results.restoreFailedPurchases.count > 0 {
        //リストアに失敗
    }
    else if results.restoredPurchases.count > 0 {
        //リストアに成功
    }
    else {
        //リストアするものがない
    }
}

レシート検証を利用した方法

function

func restorePurchase(PRODUCT_ID:String) {
    SwiftyStoreKit.fetchReceipt(forceRefresh: true) { result in
        switch result {
        case .success(_):
            //レシートの取得
            self.verifyPurchase(PRODUCT_ID: PRODUCT_ID)
        case .error(_):
            //レシートの取得失敗
        }
    }
}

func verifyPurchase(PRODUCT_ID:String){
    let appleValidator = AppleReceiptValidator(service: .production, sharedSecret: SECRET_CODE)    
    SwiftyStoreKit.verifyReceipt(using: appleValidator) { result in
        switch result {
        case .success(let receipt):
            let purchaseResult = SwiftyStoreKit.verifyPurchase(productId: PRODUCT_ID, inReceipt: receipt)            
            switch purchaseResult {
            case .purchased:
                //リストアの成功
                
            case .notPurchased:
                //リストアの失敗
            }
        case .error:
            //エラー
            
        }
    }
}
MEMO
functionのSECRET_CODEにはご自身のアプリ内課金のApp用共有シークレットそして生成された共有シークレットを入力してください。
呼び出し

restorePurchase(PRODUCT_ID: "")
MEMO
呼び出し時にご自身のアプリ内課金の製品IDをPRODUCT_IDとして渡してください。

更新型課金

function

func purchase(PRODUCT_ID:String){
   SwiftyStoreKit.purchaseProduct(PRODUCT_ID, quantity: 1, atomically: true) { result in
       switch result {
       case .success(_):
           //購入成功
           //購入の検証
           self.verifyPurchase(PRODUCT_ID: PRODUCT_ID)
       case .error(_):
           //購入失敗
       }
    }
}

func verifyPurchase(PRODUCT_ID:String){
    let appleValidator = AppleReceiptValidator(service: .production, sharedSecret: SECRET_CODE)    
    SwiftyStoreKit.verifyReceipt(using: appleValidator) { result in
        switch result {
        case .success(let receipt):
            //自動更新
            let purchaseResult = SwiftyStoreKit.verifySubscription(
                    ofType: .autoRenewable,
                    productId: PRODUCT_ID,
                    inReceipt: receipt)

            //自動更新なし(30日間)
            //let purchaseResult = SwiftyStoreKit.verifySubscription(
                    ofType: .nonRenewing(validDuration: 3600 * 24 * 30),
                    productId: PRODUCT_ID,
                    inReceipt: receipt)

            switch purchaseResult {
            case .purchased:
                //リストアの成功
                
            case .notPurchased:
                //リストアの失敗
            }
        case .error:
            //エラー
            
        }
    }
}
MEMO
functionのSECRET_CODEにはご自身のアプリ内課金のApp用共有シークレットそして生成された共有シークレットを入力してください。
呼び出し

purchase(PRODUCT_ID: "")
MEMO
呼び出し時にご自身のアプリ内課金の製品IDをPRODUCT_IDとして渡してください。
実装方法について
上記のそれぞれの実装にはverifyPurchase,purchaseなど同じ部分が多く含まれています。
複数の実装をコピーペーストするとエラーが発生しますのでご注意ください。
また、更新型課金と非消費・消費型課金を同時にご使用になる際は、ifやswitch等で場合分けして、もしくはfuncの内部だけ取り出してご利用ください。

3 Comments

kabu kabuzo

SwitfyStoreKitの参考で大変助かっております。
一つ教えてほしいのですが
アプリ起動時に購入済かどうかを行う処理が分からなく どうしてるのでしょうか?

返信する
あたまの上のさる

ありがとうございます。

アプリ起動時に上に書いてある通り消費型の課金要素であれば「価格の取得」などをすれば購入済みかどうかのチェックはできるかと思います。(ただAppleのサーバとの通信が入るため多少の時間がかかってしまいます)

私の場合だと、UserDefaultsなどの永久保存できる方法で、課金されたときに課金済みかどうかの情報を保存する方法をとっています。

返信する
kabu kabuzo

ご返信ありがとうございます。
こちらは自動継続課金でやっているのですが、sandboxで課金完了後及びアプリ再起動し、purchaseGetInfoでなぜか未購入処理が走ってしまいます。

課金完了時にUserDefaultsに課金済を記録する方向で作ってみます。

返信する

コメントを残す

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください