bizz84 / SwiftyStoreKit

Lightweight In App Purchases Swift framework for iOS 8.0+, tvOS 9.0+ and macOS 10.10+ ⛺
MIT License
6.57k stars 795 forks source link

Implement Local Receipt Validation as recommended by Apple #101

Open bizz84 opened 8 years ago

bizz84 commented 8 years ago

As recommended by Apple, local receipt validation can be performed with a number of steps:

To validate the receipt, perform the following tests, in order:

  1. Locate the receipt. If no receipt is present, validation fails.

  2. Verify that the receipt is properly signed by Apple. If it is not signed by Apple, validation fails.

  3. Verify that the bundle identifier in the receipt matches a hard-coded constant containing the CFBundleIdentifier value you expect in the Info.plist file. If they do not match, validation fails.

  4. Verify that the version identifier string in the receipt matches a hard-coded constant containing the CFBundleShortVersionString value you expect in the Info.plist file. If they do not match, validation fails.

  5. Compute the hash of the GUID as described in Compute the Hash of the GUID. If the result does not match the hash in the receipt, validation fails.

If all of the tests pass, validation passes.

A discussion on how this has been implemented by RMStore is here: http://stackoverflow.com/questions/19943183/a-complete-solution-to-locally-validate-an-in-app-receipts-and-bundle-receipts-o

Also this series was recently published with some guidelines on how to implement local receipt validation. This links to the SwiftyLocalReceiptValidator project on GitHub, which you can already use independently of SwiftyStoreKit.

Ongoing discussion about how SwiftyStoreKit and SwiftyLocalReceiptValidator may fit together: https://github.com/andrewcbancroft/SwiftyLocalReceiptValidator/issues/1

Kymer commented 7 years ago

Have there been any developments in regards to this issue? I was planning on writing a gist / small library for doing local receipt validation myself. I might contribute if there is still a need for this.

bizz84 commented 7 years ago

I don't expect to have time to implement this unfortunately.

Recently we have introduced a ReceiptValidator protocol and an AppleReceiptValidator class to implement validation with the Apple server.

@Kymer If you are planning to write this yourself, I would advise to create a new class that conforms to the ReceiptValidator protocol. This way the new code can be used directly in SwiftyStoreKit without changing existing interfaces.

bizz84 commented 7 years ago

Closed linked issue as duplicate: #75

jabhiji commented 7 years ago

Hello, is there a way to clear all data associated with the in app purchase on a device? In particular, can we erase the "localReceiptData"? I see this is a read only property.

msamoylov commented 7 years ago

Yes, can you please make SwiftyStoreKit.localReceiptData swappable?

I'm experiencing issues with testing my receipt when running my macOS from Xcode because it doesn't have a receipt. I have to read Contents/_MASReceipt/receipt from the copy installed from the App Store.

I need the receipt to be able to read original_application_version.

Thanks!

stanmots commented 6 years ago

@hanming223 But the author said he has no time to implement local receipt validation.

JulianKahnert commented 5 years ago

Hey, first of all: thank you @bizz84 for maintaining this awesome project! It makes it so much easier to implement StoreKit functionality in any app. I just found the MerchantKit framework from @benjaminmayo who skipped the OpenSSL implementation by writing a seperate ASN1 parser in Swift. This might be a great starting point, when adding a LocalReceiptValidator ( which implements the ReceiptValidator protocol). I just wanted to save this thought for later, since I am quite busy in the next few weeks. But I definitely will have a look at it later on. 🙂

tikhop commented 5 years ago

Hi, A while ago I wrote a library to read and verify in app receipt locally. It has been decoding pkcs7 (and asn1 objects) into a sweet swift structure, validating hash and signature using OpenSSL. A week ago I decided to substitute openssl with own decoder and validator. So far, all functionality, but signature validation work well.

Here is the lib: https://github.com/tikhop/TPInAppReceipt

Personally, I use SwiftyStoreKit for every project where I need to make in app purchase and I use my lib to validate and read receipt. (thank you @bizz84 for simplifying our lives)

If it's really an issue, we can try to combine both projects.

P.

teologov commented 5 years ago

Hi @tikhop, is there a demo available using your TPInAppReceipt lib with SwiftyStoreKit?

nuthinking commented 5 years ago

@teologov they are kind of independent. That's how I check a subscription:

public static func isSubscribed() -> Bool {
        do {
            let receipt = try InAppReceipt.localReceipt()
            if let _ = receipt.activeAutoRenewableSubscriptionPurchases(ofProductIdentifier: subscriptionProductID, forDate: Date()){
                return true
            }
        } catch {
            print(error)
        }
        return false
    }

What's missing here is the logic to load from the Apple server if expired.

teologov commented 5 years ago

@nuthinking I tried to conform to ReceiptValidator protocol first, but it's not that convenient. So I just did the same way as you proposed. Thanks!

gerchicov-bp commented 5 years ago

@bizz84 it is not enhancement. It is a bug