Browse Source

feat: enable explicit IAP restoration (#21461)

Shelley Vohr 5 years ago
parent
commit
dba8a0caa8

+ 8 - 2
docs/api/in-app-purchase.md

@@ -40,11 +40,17 @@ Retrieves the product descriptions.
 
 ### `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()`
 
-Returns `String`, the path to the receipt.
+Returns `String` - the path to the receipt.
 
 ### `inAppPurchase.finishAllTransactions()`
 

+ 2 - 0
shell/browser/api/atom_api_in_app_purchase.cc

@@ -85,6 +85,8 @@ void InAppPurchase::BuildPrototype(v8::Isolate* isolate,
   prototype->SetClassName(gin::StringToV8(isolate, "InAppPurchase"));
   gin_helper::ObjectTemplateBuilder(isolate, prototype->PrototypeTemplate())
       .SetMethod("canMakePayments", &in_app_purchase::CanMakePayments)
+      .SetMethod("restoreCompletedTransactions",
+                 &in_app_purchase::RestoreCompletedTransactions)
       .SetMethod("getReceiptURL", &in_app_purchase::GetReceiptURL)
       .SetMethod("purchaseProduct", &InAppPurchase::PurchaseProduct)
       .SetMethod("finishAllTransactions",

+ 5 - 3
shell/browser/mac/in_app_purchase.h

@@ -17,13 +17,15 @@ typedef base::OnceCallback<void(bool isProductValid)> InAppPurchaseCallback;
 
 // --------------------------- Functions ---------------------------
 
-bool CanMakePayments(void);
+bool CanMakePayments();
 
-void FinishAllTransactions(void);
+void RestoreCompletedTransactions();
+
+void FinishAllTransactions();
 
 void FinishTransactionByDate(const std::string& date);
 
-std::string GetReceiptURL(void);
+std::string GetReceiptURL();
 
 void PurchaseProduct(const std::string& productID,
                      int quantity,

+ 4 - 0
shell/browser/mac/in_app_purchase.mm

@@ -141,6 +141,10 @@ bool CanMakePayments() {
   return [SKPaymentQueue canMakePayments];
 }
 
+void RestoreCompletedTransactions() {
+  [[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
+}
+
 void FinishAllTransactions() {
   for (SKPaymentTransaction* transaction in SKPaymentQueue.defaultQueue
            .transactions) {

+ 1 - 1
shell/browser/mac/in_app_purchase_observer.h

@@ -51,7 +51,7 @@ class TransactionObserver {
       const std::vector<Transaction>& transactions) = 0;
 
  private:
-  InAppTransactionObserver* obeserver_;
+  InAppTransactionObserver* observer_;
 
   base::WeakPtrFactory<TransactionObserver> weak_ptr_factory_;
 

+ 2 - 2
shell/browser/mac/in_app_purchase_observer.mm

@@ -183,14 +183,14 @@ Transaction::Transaction(const Transaction&) = default;
 Transaction::~Transaction() = default;
 
 TransactionObserver::TransactionObserver() : weak_ptr_factory_(this) {
-  obeserver_ = [[InAppTransactionObserver alloc]
+  observer_ = [[InAppTransactionObserver alloc]
       initWithCallback:base::BindRepeating(
                            &TransactionObserver::OnTransactionsUpdated,
                            weak_ptr_factory_.GetWeakPtr())];
 }
 
 TransactionObserver::~TransactionObserver() {
-  [obeserver_ release];
+  [observer_ release];
 }
 
 }  // namespace in_app_purchase

+ 23 - 17
spec-main/api-in-app-purchase-spec.ts

@@ -6,9 +6,14 @@ describe('inAppPurchase module', function () {
 
   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(() => {
-      inAppPurchase.canMakePayments()
+      inAppPurchase.restoreCompletedTransactions()
     }).to.not.throw()
   })
 
@@ -29,22 +34,23 @@ describe('inAppPurchase module', function () {
   })
 
   // 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
-  // CI.
+  // Apple started blocking requests from AWS IPs (we think), so they fail on CI.
   // TODO: find a way to mock out the server requests so we can test these APIs
   // without relying on a remote service.
-  xit('purchaseProduct() fails when buying invalid product', async () => {
-    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 () => {
-    const success = await inAppPurchase.purchaseProduct('non-exist')
-    expect(success).to.be.false('failed to purchase non-existent product')
-  })
-
-  xit('getProducts() returns an empty list when getting invalid product', async () => {
-    const products = await inAppPurchase.getProducts(['non-exist'])
-    expect(products).to.be.an('array').of.length(0)
+  xdescribe('handles product purchases', () => {
+    it('purchaseProduct() fails when buying invalid product', async () => {
+      const success = await inAppPurchase.purchaseProduct('non-exist', 1)
+      expect(success).to.be.false('failed to purchase non-existent product')
+    })
+
+    it('purchaseProduct() accepts optional arguments', async () => {
+      const success = await inAppPurchase.purchaseProduct('non-exist')
+      expect(success).to.be.false('failed to purchase non-existent product')
+    })
+
+    it('getProducts() returns an empty list when getting invalid product', async () => {
+      const products = await inAppPurchase.getProducts(['non-exist'])
+      expect(products).to.be.an('array').of.length(0)
+    })
   })
 })