Skip to content

Commit

Permalink
Sync purchases improvements (#403)
Browse files Browse the repository at this point in the history
* renamed restoreTransactionsProgrammatically -> syncPurchases

* updated syncPurchases so that it passes the value of allowSharingAppStoreAccount for isRestore.
small cleanup.
  • Loading branch information
aboedo committed Nov 19, 2020
1 parent 44b1e25 commit 822279f
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 35 deletions.
9 changes: 4 additions & 5 deletions Purchases/Public/RCPurchases.h
Expand Up @@ -291,7 +291,7 @@ NS_SWIFT_NAME(purchasePackage(_:_:));
the same `appUserId` used to purchase originally.
@note This may force your users to enter the App Store password so should only be performed on request of the user.
Typically with a button in settings or near your purchase UI. Use restoreTransactionsProgrammaticallyWithCompletionBlock
Typically with a button in settings or near your purchase UI. Use syncPurchasesWithCompletionBlock
if you need to restore transactions programmatically.
*/
- (void)restoreTransactionsWithCompletionBlock:(nullable RCReceivePurchaserInfoBlock)completion
Expand All @@ -302,14 +302,13 @@ NS_SWIFT_NAME(restoreTransactions(_:));
If the receipt is being used by an existing user, the current `appUserID` will be aliased together with the `appUserID` of the existing user.
Going forward, either `appUserID` will be able to reference the same user.
You shouldn't use this method if you have your own account system. In that case "restoration" is provided by your app passing
the same `appUserId` used to purchase originally.
@warning This function should only be called if you're not calling any purchase method.
@note This method will not trigger a login prompt from App Store. However, if the receipt currently on the device does not contain subscriptions,
but the user has made subscription purchases, this method won't be able to restore them. Use restoreTransactionsWithCompletionBlock to cover those cases.
*/
- (void)restoreTransactionsProgrammaticallyWithCompletionBlock:(nullable RCReceivePurchaserInfoBlock)completion
NS_SWIFT_NAME(restoreTransactionsProgrammatically(_:));
- (void)syncPurchasesWithCompletionBlock:(nullable RCReceivePurchaserInfoBlock)completion
NS_SWIFT_NAME(syncPurchases(_:));


/**
Expand Down
17 changes: 11 additions & 6 deletions Purchases/Public/RCPurchases.m
Expand Up @@ -574,16 +574,21 @@ - (void) purchaseProduct:(SKProduct *)product
[self.storeKitWrapper addPayment:[payment copy]];
}

- (void)restoreTransactionsProgrammaticallyWithCompletionBlock:(nullable RCReceivePurchaserInfoBlock)completion {
[self restoreTransactionsWithReceiptRefreshPolicy:RCReceiptRefreshPolicyNever completionBlock:completion];
- (void)syncPurchasesWithCompletionBlock:(nullable RCReceivePurchaserInfoBlock)completion {
[self syncPurchasesWithReceiptRefreshPolicy:RCReceiptRefreshPolicyNever
isRestore:self.allowSharingAppStoreAccount
completion:completion];
}

- (void)restoreTransactionsWithCompletionBlock:(nullable RCReceivePurchaserInfoBlock)completion {
[self restoreTransactionsWithReceiptRefreshPolicy:RCReceiptRefreshPolicyAlways completionBlock:completion];
[self syncPurchasesWithReceiptRefreshPolicy:RCReceiptRefreshPolicyAlways
isRestore:YES
completion:completion];
}

- (void)restoreTransactionsWithReceiptRefreshPolicy:(RCReceiptRefreshPolicy)refreshPolicy
completionBlock:(nullable RCReceivePurchaserInfoBlock)completion {
- (void)syncPurchasesWithReceiptRefreshPolicy:(RCReceiptRefreshPolicy)refreshPolicy
isRestore:(BOOL)isRestore
completion:(nullable RCReceivePurchaserInfoBlock)completion {
if (!self.allowSharingAppStoreAccount) {
RCDebugLog(@"allowSharingAppStoreAccount is set to false and restoreTransactions has been called. "
"Are you sure you want to do this?");
Expand Down Expand Up @@ -611,7 +616,7 @@ - (void)restoreTransactionsWithReceiptRefreshPolicy:(RCReceiptRefreshPolicy)refr
RCSubscriberAttributeDict subscriberAttributes = self.unsyncedAttributesByKey;
[self.backend postReceiptData:data
appUserID:self.appUserID
isRestore:YES
isRestore:isRestore
productInfo:nil
presentedOfferingIdentifier:nil
observerMode:!self.finishTransactions
Expand Down
59 changes: 35 additions & 24 deletions PurchasesTests/Purchasing/PurchasesTests.swift
Expand Up @@ -1062,13 +1062,13 @@ class PurchasesTests: XCTestCase {
expect(receivedError).toEventuallyNot(beNil())
}

func testRestoringPurchasesProgrammaticallyPostsTheReceipt() {
func testSyncPurchasesPostsTheReceipt() {
setupPurchases()
purchases!.restoreTransactionsProgrammatically()
purchases!.syncPurchases()
expect(self.backend.postReceiptDataCalled).to(beTrue())
}

func testRestoringPurchasesProgrammaticallyDoesntPostIfReceiptEmptyAndPurchaserInfoLoaded() {
func testSyncPurchasesDoesntPostIfReceiptEmptyAndPurchaserInfoLoaded() {
let info = Purchases.PurchaserInfo(data: [
"subscriber": [
"subscriptions": [:],
Expand All @@ -1085,21 +1085,21 @@ class PurchasesTests: XCTestCase {
mockReceiptParser.stubbedReceiptHasTransactionsResult = false

setupPurchases()
purchases!.restoreTransactionsProgrammatically()
purchases!.syncPurchases()

expect(self.backend.postReceiptDataCalled) == false
}

func testRestoringPurchasesProgrammaticallyPostsIfReceiptEmptyAndPurchaserInfoNotLoaded() {
func testSyncPurchasesPostsIfReceiptEmptyAndPurchaserInfoNotLoaded() {
mockReceiptParser.stubbedReceiptHasTransactionsResult = false

setupPurchases()
purchases!.restoreTransactionsProgrammatically()
purchases!.syncPurchases()

expect(self.backend.postReceiptDataCalled) == true
}

func testRestoringPurchasesProgrammaticallyPostsIfReceiptHasTransactionsAndPurchaserInfoLoaded() {
func testSyncPurchasesPostsIfReceiptHasTransactionsAndPurchaserInfoLoaded() {
let info = Purchases.PurchaserInfo(data: [
"subscriber": [
"subscriptions": [:],
Expand All @@ -1116,67 +1116,78 @@ class PurchasesTests: XCTestCase {
mockReceiptParser.stubbedReceiptHasTransactionsResult = true

setupPurchases()
purchases!.restoreTransactionsProgrammatically()
purchases!.syncPurchases()

expect(self.backend.postReceiptDataCalled) == true
}

func testRestoringPurchasesProgrammaticallyPostsIfReceiptHasTransactionsAndPurchaserInfoNotLoaded() {
func testSyncPurchasesPostsIfReceiptHasTransactionsAndPurchaserInfoNotLoaded() {
mockReceiptParser.stubbedReceiptHasTransactionsResult = true

setupPurchases()
purchases!.restoreTransactionsProgrammatically()
purchases!.syncPurchases()

expect(self.backend.postReceiptDataCalled) == true
}

func testRestoringPurchasesProgrammaticallyDoesntRefreshTheReceiptIfNotEmpty() {
func testSyncPurchasesDoesntRefreshTheReceiptIfNotEmpty() {
setupPurchases()
self.receiptFetcher.shouldReturnReceipt = true
purchases!.restoreTransactionsProgrammatically()
purchases!.syncPurchases()

expect(self.receiptFetcher.receiptDataTimesCalled) == 1
expect(self.requestFetcher.refreshReceiptCalled) == false
}

func testRestoringPurchasesProgrammaticallyDoesntRefreshTheReceiptIfEmpty() {
func testSyncPurchasesDoesntRefreshTheReceiptIfEmpty() {
setupPurchases()
self.receiptFetcher.shouldReturnReceipt = false
purchases!.restoreTransactionsProgrammatically()
purchases!.syncPurchases()

expect(self.receiptFetcher.receiptDataTimesCalled) == 1
expect(self.requestFetcher.refreshReceiptCalled) == false
}

func testRestoringPurchasesProgrammaticallySetsIsRestore() {
func testSyncPurchasesPassesIsRestoreAsAllowSharingAppStoreAccount() {
setupPurchases()
purchases!.restoreTransactionsProgrammatically(nil)
expect(self.backend.postedIsRestore!).to(beTrue())

purchases.allowSharingAppStoreAccount = false
purchases!.syncPurchases()
expect(self.backend.postedIsRestore!) == false

purchases.allowSharingAppStoreAccount = true
purchases!.syncPurchases()
expect(self.backend.postedIsRestore!) == true
}

func testRestoringPurchasesProgrammaticallySetsIsRestoreForAnon() {
func testSyncPurchasesSetsIsRestoreForAnon() {
setupAnonPurchases()
purchases!.restoreTransactionsProgrammatically(nil)

expect(self.backend.postedIsRestore!).to(beTrue())
purchases.allowSharingAppStoreAccount = false
purchases!.syncPurchases()
expect(self.backend.postedIsRestore!) == false

purchases.allowSharingAppStoreAccount = true
purchases!.syncPurchases()
expect(self.backend.postedIsRestore!) == true
}

func testRestoringPurchasesProgrammaticallyCallsSuccessDelegateMethod() {
func testSyncPurchasesCallsSuccessDelegateMethod() {
setupPurchases()

let purchaserInfo = Purchases.PurchaserInfo()
self.backend.postReceiptPurchaserInfo = purchaserInfo

var receivedPurchaserInfo: Purchases.PurchaserInfo?

purchases!.restoreTransactionsProgrammatically { (info, error) in
purchases!.syncPurchases { (info, error) in
receivedPurchaserInfo = info
}

expect(receivedPurchaserInfo).toEventually(be(purchaserInfo))
}

func testRestoringPurchasesProgrammaticallyPassesErrorOnFailure() {
func testSyncPurchasesPassesErrorOnFailure() {
setupPurchases()

let errorCode = Purchases.RevenueCatBackendErrorCode.invalidAPIKey.rawValue as NSNumber
Expand All @@ -1189,7 +1200,7 @@ class PurchasesTests: XCTestCase {

var receivedError: Error?

purchases!.restoreTransactionsProgrammatically { (_, newError) in
purchases!.syncPurchases { (_, newError) in
receivedError = newError
}

Expand Down

0 comments on commit 822279f

Please sign in to comment.