Latest 4.2.0
Homepage https://github.com/crashoverride777/swifty-receipt-validator
License MIT
Platforms ios 10.3, requires ARC
Authors

Swifty Receipt Validator

Swift 5.0
[Platform]()
CocoaPods Compatible

A swift helper to handle app store receipt validation.

Your own server

The recommned way by Apple is to use your own server to validate app store receipts.
However for obvious reason this is a hassle for alot of people like me, because I dont have a webserver and dont understand languages like PHP to make it work.

In those cases where you dont want to use your own server you can communcate directly with Apples server.
Doing this is apparently not very secure and therefore you should use your own server when verifying receipts

Nevertheless its still better than not doing any validation at all. I will eventually try to update this helper to include guidlines/sample code to make it work with your own server. My knowledge about server code is very basic at the moment.

https://www.raywenderlich.com/23266/in-app-purchases-in-ios-6-tutorial-consumables-and-receipt-validation

Before you go live

  • Test, Test, Test

Please test this properly, including production mode which will use apples production server URL. Use xcode release mode to test this to make sure everything is working. This is not something you want take lightly, triple check purchases are working when your app is in release mode.

Requirements

  • iOS 10.3+
  • Swift 5.0+

Installation

CocoaPods is a dependency manager for Cocoa projects. Simply install the pod by adding the following line to your pod file

pod 'SwiftyReceiptValidator'

There is now an app which makes handling pods much easier

Altenatively you can drag the swift file(s) manually into your project.

Usage

  • Add the import statement to your swift file(s) when you installed via cocoa pods
import SwiftyReceiptValidator
  • In your class with your in app purchase code create a reference to SwiftyReceiptValidator
class SomeClass {
    var receiptValidator: SwiftyReceiptValidator!

    init() {
        // Standard config communicates with apples server directly, which is not recommended
        // add your own custom config if required
        receiptValidator = SwiftyReceiptValidator(configuration: .standard)
    }
}

Validate purchases

  • Go to the following delegate method which you must implement for in app purchases
extension SomeClass: SKPaymentTransactionObserver {

    func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
        transactions.forEach {
            switch $0.transactionState {
                case .purchased:
                ...
                case .restored:
                ...
            }
        }
    }
}

and modify the .purchased and .restored enum cases to look like this

case .purchased:
        // Transaction is in queue, user has been charged.  Client should complete the transaction.
        let productId = transaction.payment.productIdentifier

        receiptValidator.validate(.purchase(productId: productId),
                                  sharedSecret: nil,
                                  excludeOldTransactions: false) { result in
            switch result {

           case .success(let response):
                 defer {
                    // Complete the transaction only after validation was successful
                    // if validation error e.g due to internet, the transaction will stay in pending state
                    // and than can/will be resumed on next app launch
                    queue.finishTransaction(transaction)
                }

               print("Receipt validation was successfull with receipt response (response)")
                // Unlock products and/or do additional checks

            case .failure(let error, let code):
                print("Receipt validation failed with code (code), error (error.localizedDescription)")    
                // Inform user of error, maybe try validation again.
            }
        }

case .restored:
    // Transaction was restored from user's purchase history.  Client should complete the transaction.
    guard let productId = transaction.originalTransaction?.payment.productIdentifier else {
        queue.finishTransaction(transaction)
        return
    }

    receiptValidator.validate(.purchase(productId: productId),
                              sharedSecret: nil,
                              excludeOldTransactions: false) { result in
        switch result {

        case .success(let response):
            defer {
                // Complete the transaction only after validation was successful
                // if validation error e.g due to internet, the transaction will stay in pending state
                // and than can/will be resumed on next app launch
                queue.finishTransaction(transaction)
            }

            print("Receipt validation was successfull with receipt response (response)")
            // Unlock products and/or do additional checks

       case .failure(let error, let code):
            print("Receipt validation failed with code (code), error (error.localizedDescription)")  
            // Inform user of error, maybe try validation again.
        }
    }              

In this example sharedSecret is set to nil because I am only validating regular in app purchases. To validate an auto renewable subscriptions you can enter your shared secret that you have set up in itunes.

Validate subscriptions

  • To validate your subscriptions (e.g on app launch), select .subscription as the validation method. This will search for all subscription receipts and check if there is at least 1 thats not expired.
receiptValidator.validate(.subscription, 
                          sharedSecret: "enter your secret or set to nil",
                          excludeOldTransactions: true) { result in
    switch result {
    case .success(let response):
        print("Receipt validation was successfull with receipt response (response)")
        // Unlock subscription features and/or do additional checks first
    case .failure(let error, let code):
        switch error {
        case .noValidSubscription:
            // no active subscription found, update your cache/app etc
        default:
            break // do nothing e.g internet error or other errors
        }
    }
}

Just fetch receipt

  • To only fetch the verified receipt, select .none as the validation method.
receiptValidator.validate(.none, 
                          sharedSecret: "enter your secret or set to nil", 
                          excludeOldTransactions: false) { result in
    switch result {
    case .success(let response):
        print("Receipt response (response)")
        // Do additional checks etc
    case .failure(let error, let code):
        // Handle error e.g no internet
    }
}

StoreKit Alert Controllers

One thing I do not know about receipt validation is if there is a way to stop the default StoreKit alert controller to show. When you get to the purchase code and to the .purchased switch statement, storeKit automatically shows an AlertController ("Thank you, purchase was succesfull"). This however is the point where receipt validation is actually starting so it takes another few seconds for the products to unlock. I guess this must be normal, although it would be nicer to show that alert once receipt validation is finished.

Final Note

As per apples guidlines you should always first connect to apples production servers and than fall back on apples sandbox servers if needed. So keep this in mind when testing in sandbox mode, validation will take a bit longer due to this.

I will try to update this in the future if I have a better grasp of what is needed for your own server.

Latest podspec

{
    "name": "SwiftyReceiptValidator",
    "version": "4.2.0",
    "license": "MIT",
    "summary": "A swift helper for in app purchase receipt validation.",
    "homepage": "https://github.com/crashoverride777/swifty-receipt-validator",
    "social_media_url": "http://twitter.com/overrideiactive",
    "authors": {
        "DominikRingler": "[email protected]"
    },
    "requires_arc": true,
    "platforms": {
        "ios": "10.3"
    },
    "source": {
        "git": "https://github.com/crashoverride777/swifty-receipt-validator.git",
        "tag": "4.2.0"
    },
    "source_files": "SwiftyReceiptValidator/**/*.{swift}"
}

Pin It on Pinterest

Share This