feat: enable explicit IAP restoration (#21461)
This commit is contained in:
		
					parent
					
						
							
								53957d47d6
							
						
					
				
			
			
				commit
				
					
						dba8a0caa8
					
				
			
		
					 7 changed files with 43 additions and 23 deletions
				
			
		| 
						 | 
					@ -40,11 +40,17 @@ Retrieves the product descriptions.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### `inAppPurchase.canMakePayments()`
 | 
					### `inAppPurchase.canMakePayments()`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Returns `Boolean`, whether a user can make a payment.
 | 
					Returns `Boolean` - whether a user can make a payment.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### `inAppPurchase.restoreCompletedTransactions()`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Restores finished transactions. This method can be called either to install purchases on additional devices, or to restore purchases for an application that the user deleted and reinstalled.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[The payment queue](https://developer.apple.com/documentation/storekit/skpaymentqueue?language=objc) delivers a new transaction for each previously completed transaction that can be restored. Each transaction includes a copy of the original transaction.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### `inAppPurchase.getReceiptURL()`
 | 
					### `inAppPurchase.getReceiptURL()`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Returns `String`, the path to the receipt.
 | 
					Returns `String` - the path to the receipt.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
### `inAppPurchase.finishAllTransactions()`
 | 
					### `inAppPurchase.finishAllTransactions()`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -85,6 +85,8 @@ void InAppPurchase::BuildPrototype(v8::Isolate* isolate,
 | 
				
			||||||
  prototype->SetClassName(gin::StringToV8(isolate, "InAppPurchase"));
 | 
					  prototype->SetClassName(gin::StringToV8(isolate, "InAppPurchase"));
 | 
				
			||||||
  gin_helper::ObjectTemplateBuilder(isolate, prototype->PrototypeTemplate())
 | 
					  gin_helper::ObjectTemplateBuilder(isolate, prototype->PrototypeTemplate())
 | 
				
			||||||
      .SetMethod("canMakePayments", &in_app_purchase::CanMakePayments)
 | 
					      .SetMethod("canMakePayments", &in_app_purchase::CanMakePayments)
 | 
				
			||||||
 | 
					      .SetMethod("restoreCompletedTransactions",
 | 
				
			||||||
 | 
					                 &in_app_purchase::RestoreCompletedTransactions)
 | 
				
			||||||
      .SetMethod("getReceiptURL", &in_app_purchase::GetReceiptURL)
 | 
					      .SetMethod("getReceiptURL", &in_app_purchase::GetReceiptURL)
 | 
				
			||||||
      .SetMethod("purchaseProduct", &InAppPurchase::PurchaseProduct)
 | 
					      .SetMethod("purchaseProduct", &InAppPurchase::PurchaseProduct)
 | 
				
			||||||
      .SetMethod("finishAllTransactions",
 | 
					      .SetMethod("finishAllTransactions",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -17,13 +17,15 @@ typedef base::OnceCallback<void(bool isProductValid)> InAppPurchaseCallback;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// --------------------------- Functions ---------------------------
 | 
					// --------------------------- Functions ---------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool CanMakePayments(void);
 | 
					bool CanMakePayments();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void FinishAllTransactions(void);
 | 
					void RestoreCompletedTransactions();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void FinishAllTransactions();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void FinishTransactionByDate(const std::string& date);
 | 
					void FinishTransactionByDate(const std::string& date);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
std::string GetReceiptURL(void);
 | 
					std::string GetReceiptURL();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void PurchaseProduct(const std::string& productID,
 | 
					void PurchaseProduct(const std::string& productID,
 | 
				
			||||||
                     int quantity,
 | 
					                     int quantity,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -141,6 +141,10 @@ bool CanMakePayments() {
 | 
				
			||||||
  return [SKPaymentQueue canMakePayments];
 | 
					  return [SKPaymentQueue canMakePayments];
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void RestoreCompletedTransactions() {
 | 
				
			||||||
 | 
					  [[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void FinishAllTransactions() {
 | 
					void FinishAllTransactions() {
 | 
				
			||||||
  for (SKPaymentTransaction* transaction in SKPaymentQueue.defaultQueue
 | 
					  for (SKPaymentTransaction* transaction in SKPaymentQueue.defaultQueue
 | 
				
			||||||
           .transactions) {
 | 
					           .transactions) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -51,7 +51,7 @@ class TransactionObserver {
 | 
				
			||||||
      const std::vector<Transaction>& transactions) = 0;
 | 
					      const std::vector<Transaction>& transactions) = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 private:
 | 
					 private:
 | 
				
			||||||
  InAppTransactionObserver* obeserver_;
 | 
					  InAppTransactionObserver* observer_;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  base::WeakPtrFactory<TransactionObserver> weak_ptr_factory_;
 | 
					  base::WeakPtrFactory<TransactionObserver> weak_ptr_factory_;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -183,14 +183,14 @@ Transaction::Transaction(const Transaction&) = default;
 | 
				
			||||||
Transaction::~Transaction() = default;
 | 
					Transaction::~Transaction() = default;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
TransactionObserver::TransactionObserver() : weak_ptr_factory_(this) {
 | 
					TransactionObserver::TransactionObserver() : weak_ptr_factory_(this) {
 | 
				
			||||||
  obeserver_ = [[InAppTransactionObserver alloc]
 | 
					  observer_ = [[InAppTransactionObserver alloc]
 | 
				
			||||||
      initWithCallback:base::BindRepeating(
 | 
					      initWithCallback:base::BindRepeating(
 | 
				
			||||||
                           &TransactionObserver::OnTransactionsUpdated,
 | 
					                           &TransactionObserver::OnTransactionsUpdated,
 | 
				
			||||||
                           weak_ptr_factory_.GetWeakPtr())];
 | 
					                           weak_ptr_factory_.GetWeakPtr())];
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
TransactionObserver::~TransactionObserver() {
 | 
					TransactionObserver::~TransactionObserver() {
 | 
				
			||||||
  [obeserver_ release];
 | 
					  [observer_ release];
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}  // namespace in_app_purchase
 | 
					}  // namespace in_app_purchase
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,9 +6,14 @@ describe('inAppPurchase module', function () {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  this.timeout(3 * 60 * 1000)
 | 
					  this.timeout(3 * 60 * 1000)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  it('canMakePayments() does not throw', () => {
 | 
					  it('canMakePayments() returns a boolean', () => {
 | 
				
			||||||
 | 
					    const canMakePayments = inAppPurchase.canMakePayments()
 | 
				
			||||||
 | 
					    expect(canMakePayments).to.be.a('boolean')
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  it('restoreCompletedTransactions() does not throw', () => {
 | 
				
			||||||
    expect(() => {
 | 
					    expect(() => {
 | 
				
			||||||
      inAppPurchase.canMakePayments()
 | 
					      inAppPurchase.restoreCompletedTransactions()
 | 
				
			||||||
    }).to.not.throw()
 | 
					    }).to.not.throw()
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -29,22 +34,23 @@ describe('inAppPurchase module', function () {
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // The following three tests are disabled because they hit Apple servers, and
 | 
					  // The following three tests are disabled because they hit Apple servers, and
 | 
				
			||||||
  // Apple started blocking requests from AWS IPs (we think), so they fail on
 | 
					  // Apple started blocking requests from AWS IPs (we think), so they fail on CI.
 | 
				
			||||||
  // CI.
 | 
					 | 
				
			||||||
  // TODO: find a way to mock out the server requests so we can test these APIs
 | 
					  // TODO: find a way to mock out the server requests so we can test these APIs
 | 
				
			||||||
  // without relying on a remote service.
 | 
					  // without relying on a remote service.
 | 
				
			||||||
  xit('purchaseProduct() fails when buying invalid product', async () => {
 | 
					  xdescribe('handles product purchases', () => {
 | 
				
			||||||
    const success = await inAppPurchase.purchaseProduct('non-exist', 1)
 | 
					    it('purchaseProduct() fails when buying invalid product', async () => {
 | 
				
			||||||
    expect(success).to.be.false('failed to purchase non-existent product')
 | 
					      const success = await inAppPurchase.purchaseProduct('non-exist', 1)
 | 
				
			||||||
  })
 | 
					      expect(success).to.be.false('failed to purchase non-existent product')
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  xit('purchaseProduct() accepts optional arguments', async () => {
 | 
					    it('purchaseProduct() accepts optional arguments', async () => {
 | 
				
			||||||
    const success = await inAppPurchase.purchaseProduct('non-exist')
 | 
					      const success = await inAppPurchase.purchaseProduct('non-exist')
 | 
				
			||||||
    expect(success).to.be.false('failed to purchase non-existent product')
 | 
					      expect(success).to.be.false('failed to purchase non-existent product')
 | 
				
			||||||
  })
 | 
					    })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  xit('getProducts() returns an empty list when getting invalid product', async () => {
 | 
					    it('getProducts() returns an empty list when getting invalid product', async () => {
 | 
				
			||||||
    const products = await inAppPurchase.getProducts(['non-exist'])
 | 
					      const products = await inAppPurchase.getProducts(['non-exist'])
 | 
				
			||||||
    expect(products).to.be.an('array').of.length(0)
 | 
					      expect(products).to.be.an('array').of.length(0)
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue