Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support refreshing receipts #110

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

akshetpandey
Copy link
Contributor

Expose a method to refresh receipts. This is the recommended way by apple to restore purchases unless the purchases actually need the SKTransaction.

https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/StoreKitGuide/Chapters/Restoring.html

[self refreshReceipt:callback testExpired:NO testRevoked:NO];
}

RCT_EXPORT_METHOD(testRefreshExpiredReceipt:(RCTResponseSenderBlock)callback)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

instead of exposing three methods can you expose one with options object for expired and revoked?

something like :

refreshReceipt({ expired: true }, callback);

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Certainly

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Honestly I can't figure out how to do this without also requiring an empty dictionary to be passed in when neither are required, which is the most common case. I am not sure if this is the best interface in that case. I would be possible to do if we also had a js layer that abstracted the objc layer but as it stands, I don't know how to do this in a clean way.

@akshetpandey
Copy link
Contributor Author

Sorry have been on a vacation, will get back to my PRs soon.

@omeraplak
Copy link

@akshetpandey
Hello,
can i use this PR on production?

@akshetpandey
Copy link
Contributor Author

@omeraplak : We have been using this in production for a ~100k DAU app for about 6 months now so you should be safe to use it...

@omeraplak
Copy link

@akshetpandey thank you!

@kyangy
Copy link

kyangy commented May 10, 2018

@akshetpandey @omeraplak how are you using this in react native?

@akshetpandey
Copy link
Contributor Author

On our pay gate we expose a "Restore Purchase/Subscription" button... on pressing that we call this method, then get the new receipts if there is any and use that to restore anything a user may have purchased

@kyangy
Copy link

kyangy commented May 10, 2018

@akshetpandey How are you calling it? I am calling it like this

InAppUtils.refreshReceipt((error, result) => {})

but it prompted me to sign in twice and then I get hit with an error.

@akshetpandey
Copy link
Contributor Author

Yes that is what I am doing. You will need to be logged in to a valid app store account.

@akshetpandey
Copy link
Contributor Author

akshetpandey commented May 10, 2018

Straight from the codebase...

  async restoreSubscription (onProgress, onSuccess, onError) {
    try {
      await this._refreshReceipt()
      const { subscriptionPurchaseReceipts } = await this._getValidReceipts()
      this._logTransactions(subscriptionPurchaseReceipts)
      const latestSubscriptionReceipt = this._findMostRecentReceipt(subscriptionPurchaseReceipts)
      this._updateSubscriptionStatus(latestSubscriptionReceipt)
      onSuccess(latestSubscriptionReceipt.product_id)
      } else {
        throw new Error('no_valid_receipts')
      }
    } catch (error) {
      onError(error)
    }
  }

  _refreshReceipt () {
    return new Promise((resolve, reject) => {
      debugLog('IAP', 'Refreshing receipt...')
      InAppUtils.refreshReceipt((error, response) => {
        if (error) {
          debugLog('IAP', 'Error refreshing receipt', error)
          reject(error)
        } else if (response) {
          debugLog('IAP', 'Refreshed receipt')
          resolve()
        }
      })
    })
  }

  _getValidReceipts () {
    return new Promise((resolve, reject) => {
      debugLog('IAP', 'Checking receipts...')
      InAppUtils.receiptData(async (error, receiptData) => {
        if (error) {
          if (error === 'not_available') {
            debugLog('IAP', 'No receipt on device, this is fine')
            resolve({subscriptionPurchaseReceipts: null, consumablePurchaseReceipts: null})
          } else {
            throw error
          }
          reject(error)
        } else {
          debugLog('IAP', `Validating receipt data...`)
          try {
            const validationResponse = await validateReceipt(receiptData)
            const receipts = {
              subscriptionPurchaseReceipts: _.get(validationResponse, ['latest_receipt_info'], null),
              consumablePurchaseReceipts: _.get(validationResponse, ['receipt', 'in_app'], null)
            }
            resolve(receipts)
          } catch (error) {
            debugLog('IAP', 'Error in validating Receipt', error)
            reject(error)
          }
        }
      })
    })
  }

@kyangy
Copy link

kyangy commented May 10, 2018

@akshetpandey ok thanks will try something out!

@stefanwuest
Copy link

stefanwuest commented May 17, 2018

Hi @akshetpandey!
Will refreshReceipt enable me to give me receiptData after purchasing non-renewable subscriptions? At the moment I have the problem, that receiptData() returns not_available when I try to directly call it (not after purchaseProduct).

@akshetpandey
Copy link
Contributor Author

Yes, calling refreshReceipt will give you those receipts back

@djw27
Copy link

djw27 commented Aug 31, 2018

Is there a reason this hasn't been merged?

@akshetpandey
Copy link
Contributor Author

The requested changed were never implemented. If you would like, you can create a new PR with the requested changes and I can close this one.

@djw27
Copy link

djw27 commented Sep 4, 2018

@akshetpandey does #140 contain the requested changes?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

7 participants