cordova-plugin-purchase icon indicating copy to clipboard operation
cordova-plugin-purchase copied to clipboard

(ios) On-device receipt validation/parsing

Open dpa99c opened this issue 3 years ago • 11 comments

Changes proposed in this pull request (specific to iOS):

  • Relates to #1170
  • Enables on-device validation/parsing of app store receipts using components from the RMStore library.
    • The decoded receipt payload is attached as a JSON structure to the app store receipt response alongside the base64-encoded binary version.
      • Pulls in the OpenSSL library via Cocoapods to perform the cryptographic decryption operation
    • Adds new setBundleDetails() API function to manually set bundle package name and version (rather than automatically trust the one in the app plist - see here)
  • Supports optional success/error callbacks when calling finish()

Reasoning:

  • Firstly, this is not a replacement for the recommended server-side validation/parsing of app store receipts such as provided by billing.fovea.cc.
    • If you are concerned about security and possibility of exploitation of your paid app features, then you should use server-side validation
  • However, in some circumstances where the in-app products are low-value or niche, server-side validation/parsing is overkill and client-side validation/parsing of the app store receipt within the app is sufficient and easier.
  • This PR adds the ability for the plugin to validate/parse app store receipts on the device; it's then the app developers choice whether to also carry out server-side validation/parsing.

TODO:

  • This PR currently doesn't expose the new functionality in the JS store API, it just adds the functionality to the Objective-C and JS layers of the Cordova plugin API.
  • Maybe an option could be added to as to whether to use the output of on-device receipt parsing to update the store DB?

To test this pull request with cordova:

# 1: Uninstall the plugin (if already installed)
cordova plugin rm cordova-plugin-purchase

# 2: Install from github
cordova plugin add "https://github.com/dpa99c/cordova-plugin-purchase.git#ios_local_receipt"

dpa99c avatar Apr 09 '21 15:04 dpa99c

@dpa99c I've read that OpenSSL adds several MB to the size of the app. Is this correct? If so, maybe the feature should be opt-in with an install-time flag?

j3k0 avatar Apr 16 '21 07:04 j3k0

@j3k0 yeah, the OpenSSL iOS/arm64 framework weighs in at around 20Mb. Though I'm not sure how we'd make it an optional addition at installation time since it's included via the <podspec> in config.xml - the Cordova plugin mechanism doesn't really support that. I guess we could do something similar to what I've done for the diagnostic plugin which is to run a node script on the node postinstall hook in order to comment in/out the relevant portion of config.xml. Any suggestions how we could do it more easily?

dpa99c avatar Apr 16 '21 07:04 dpa99c

Maybe the simplest solution is to maintain a separate branch? (cordova-purchase-plugin-local for example).

I wouldn't want my app to go from under 2MB (actual size of my test app on iOS) to 22MB for a feature I don't even need (when using a server-side validator or no validation at all). Maybe we can ask the cordova team if they have any other suggestions?

j3k0 avatar Apr 16 '21 07:04 j3k0

Another option is to try to use the built-in iOS crypto libraries to do the pkcs7 decoding. That way we wouldn't need to include OpenSSL at all. This gist is pretty old but contains an implementation for OSX so similar libraries to iOS. Maybe there's an up-to-date fork for iOS. The RMStore library I've used is quite convenient so it might be possible to swap out just the crypto operation and use the rest of the library. I'll look into it when I get a bit of time.

dpa99c avatar Apr 16 '21 08:04 dpa99c

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

stale[bot] avatar Apr 17 '22 05:04 stale[bot]

@j3k0 I've updated this PR branch onto your latest master branch and also made the inclusion of the iOS receipt validation functionality optional via a plugin variable:

By default, it will not be enabled and therefore the large OpenSSL library will not be pulled into the app build.

To enable the functionality, you must do so with a plugin variable at plugin installation time:

cordova plugin add cordova-plugin-purchase --variable LOCAL_RECEIPT_VALIDATION=true

Since some references are made to the local validation classes in the InAppPurchase class, when the functionality is disabled the plugin includes stubs of the local validation classes to keep the compiler happy.

Hopefully this should provide a way you will consider acceptable of make the functionality available to those who want it without adversely affecting other plugin users who don't want to use it. If you have time, please take a look at the changes in the PR and let me know your thoughts.

dpa99c avatar Oct 03 '22 11:10 dpa99c

@j3k0 I've updated the PR branch to align with latest (v13) of the plugin - any reason not to merge this now?

It's an opt-in feature so by default is disabled and the plugin will behave as it currently does, with the iOS receipt validation functionality stubbed out. It's only activated if the user installs the plugin with the specified plugin variable above. Therefore I can see no negative consequences for the existing implementation if this feature is merged.

dpa99c avatar Nov 20 '23 11:11 dpa99c

Hey @dpa99c - this is nice.

I think this should be a separate plugin (the way braintree support is added to this plugin by installing "cordova-plugin-purchase-braintre"). This way there's no need to hack into the plugin xml file, which I think might backfire on non-pure cordova platforms (capacitor, monaca, ...)

I'm busy finalizing another project, then I will look into this right after.

j3k0 avatar Nov 23 '23 15:11 j3k0

@j3k0 I've tweaked the npm postinstall script this PR introduces so it looks at package.json for the plugin variable to activate iOS local receipt verification, and ignores any error if config.xml does not exist.

This allows it to work in a Capacitor project (which I have tested - not sure about Monaca) by adding the following block to package.json (which Cordova CLI adds to a Cordova project):

{
  "cordova": {
    "plugins": {
      "cordova-plugin-purchase": {
        "LOCAL_RECEIPT_VALIDATION": "true"
      }
    }
  }
}

I looked into whether the dependency on OpenSSL could be eliminated by using the built-in iOS crypto libraries but unfortunately they don't support the PCKS7 operations required to decrypt the receipt.

As to publishing this as a different plugin, that would require keep it in sync with the main plugin which isn't something I'd have to for, but maybe someone else is willing to take on that maintenance?

dpa99c avatar Nov 23 '23 16:11 dpa99c

why not merge this? Apple Store deprecated their [verifyreceipt], it would be good option

JothikannanC avatar Dec 21 '23 17:12 JothikannanC

Hello

Do you have any working example code please ?

For Auto renewable subscription, one product only, i just need the expiry date or also the purchase data will be ok.

  • Restore purchases options

cordova plugin add "https://github.com/dpa99c/cordova-plugin-purchase.git#ios_local_receipt" --variable LOCAL_RECEIPT_VALIDATION=true

hooliapps avatar Jul 03 '24 17:07 hooliapps