Browse Source

Turn InAppPurchase into an EventEmitter

Cheng Zhao 7 years ago
parent
commit
ac6f895f64

+ 75 - 30
atom/browser/api/atom_api_in_app_purchase.cc

@@ -15,47 +15,92 @@
 
 namespace mate {
 
-v8::Local<v8::Value> Converter<in_app_purchase::Payment>::ToV8(
-    v8::Isolate* isolate,
-    const in_app_purchase::Payment& payment) {
-  mate::Dictionary dict = mate::Dictionary::CreateEmpty(isolate);
-  dict.SetHidden("simple", true);
-  dict.Set("productIdentifier", payment.productIdentifier);
-  dict.Set("quantity", payment.quantity);
-  return dict.GetHandle();
+template <>
+struct Converter<in_app_purchase::Payment> {
+  static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
+                                   const in_app_purchase::Payment& payment) {
+    mate::Dictionary dict = mate::Dictionary::CreateEmpty(isolate);
+    dict.SetHidden("simple", true);
+    dict.Set("productIdentifier", payment.productIdentifier);
+    dict.Set("quantity", payment.quantity);
+    return dict.GetHandle();
+  }
+};
+
+template <>
+struct Converter<in_app_purchase::Transaction> {
+  static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
+                                   const in_app_purchase::Transaction& val) {
+    mate::Dictionary dict = mate::Dictionary::CreateEmpty(isolate);
+    dict.SetHidden("simple", true);
+    dict.Set("transactionIdentifier", val.transactionIdentifier);
+    dict.Set("transactionDate", val.transactionDate);
+    dict.Set("originalTransactionIdentifier",
+             val.originalTransactionIdentifier);
+    dict.Set("transactionState", val.transactionState);
+    dict.Set("errorCode", val.errorCode);
+    dict.Set("errorMessage", val.errorMessage);
+    return dict.GetHandle();
+  }
+};
+
+}  // namespace mate
+
+namespace atom {
+
+namespace api {
+
+// static
+mate::Handle<InAppPurchase> InAppPurchase::Create(v8::Isolate* isolate) {
+  return mate::CreateHandle(isolate, new InAppPurchase(isolate));
 }
 
-v8::Local<v8::Value> Converter<in_app_purchase::Transaction>::ToV8(
-    v8::Isolate* isolate,
-    const in_app_purchase::Transaction& transaction) {
-  mate::Dictionary dict = mate::Dictionary::CreateEmpty(isolate);
-  dict.SetHidden("simple", true);
-  dict.Set("transactionIdentifier", transaction.transactionIdentifier);
-  dict.Set("transactionDate", transaction.transactionDate);
-  dict.Set("originalTransactionIdentifier",
-           transaction.originalTransactionIdentifier);
-  dict.Set("transactionState", transaction.transactionState);
-
-  dict.Set("errorCode", transaction.errorCode);
-  dict.Set("errorMessage", transaction.errorMessage);
-
-  return dict.GetHandle();
+// static
+void InAppPurchase::BuildPrototype(v8::Isolate* isolate,
+                                   v8::Local<v8::FunctionTemplate> prototype) {
+  prototype->SetClassName(mate::StringToV8(isolate, "InAppPurchase"));
+  mate::ObjectTemplateBuilder(isolate, prototype->PrototypeTemplate())
+      .SetMethod("canMakePayments", &in_app_purchase::CanMakePayments)
+      .SetMethod("getReceiptURL", &in_app_purchase::GetReceiptURL)
+      .SetMethod("purchaseProduct", &InAppPurchase::PurchaseProduct)
+      .SetMethod("addTransactionListener",
+                 &in_app_purchase::AddTransactionObserver);
+}
+
+InAppPurchase::InAppPurchase(v8::Isolate* isolate) {
+  Init(isolate);
+}
+
+InAppPurchase::~InAppPurchase() {
+}
+
+void InAppPurchase::PurchaseProduct(const std::string& product_id,
+                                    mate::Arguments* args) {
+  int quantity = 1;
+  in_app_purchase::InAppPurchaseCallback callback;
+  args->GetNext(&quantity);
+  args->GetNext(&callback);
+  in_app_purchase::PurchaseProduct(product_id, quantity, callback);
 }
-}  // namespace mate
+
+}  // namespace api
+
+}  // namespace atom
 
 namespace {
 
+using atom::api::InAppPurchase;
+
 void Initialize(v8::Local<v8::Object> exports,
                 v8::Local<v8::Value> unused,
                 v8::Local<v8::Context> context,
                 void* priv) {
-  mate::Dictionary dict(context->GetIsolate(), exports);
 #if defined(OS_MACOSX)
-  dict.SetMethod("canMakePayments", &in_app_purchase::CanMakePayments);
-  dict.SetMethod("getReceiptURL", &in_app_purchase::GetReceiptURL);
-  dict.SetMethod("purchaseProduct", &in_app_purchase::PurchaseProduct);
-  dict.SetMethod("addTransactionListener",
-                 &in_app_purchase::AddTransactionObserver);
+  v8::Isolate* isolate = context->GetIsolate();
+  mate::Dictionary dict(isolate, exports);
+  dict.Set("inAppPurchase", InAppPurchase::Create(isolate));
+  dict.Set("InAppPurchase",
+           InAppPurchase::GetConstructor(isolate)->GetFunction());
 #endif
 }
 

+ 22 - 18
atom/browser/api/atom_api_in_app_purchase.h

@@ -7,30 +7,34 @@
 
 #include <string>
 
+#include "atom/browser/api/event_emitter.h"
 #include "atom/browser/mac/in_app_purchase.h"
 #include "atom/browser/mac/in_app_purchase_observer.h"
-#include "native_mate/dictionary.h"
+#include "native_mate/handle.h"
 
-namespace mate {
+namespace atom {
 
-template <>
-struct Converter<in_app_purchase::Payment> {
-  static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
-                                   const in_app_purchase::Payment& val);
-  static bool FromV8(v8::Isolate* isolate,
-                     v8::Local<v8::Value> val,
-                     in_app_purchase::Payment* out);
-};
+namespace api {
+
+class InAppPurchase: public mate::EventEmitter<InAppPurchase> {
+ public:
+  static mate::Handle<InAppPurchase> Create(v8::Isolate* isolate);
+
+  static void BuildPrototype(v8::Isolate* isolate,
+                             v8::Local<v8::FunctionTemplate> prototype);
 
-template <>
-struct Converter<in_app_purchase::Transaction> {
-  static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
-                                   const in_app_purchase::Transaction& val);
-  static bool FromV8(v8::Isolate* isolate,
-                     v8::Local<v8::Value> val,
-                     in_app_purchase::Transaction* out);
+ protected:
+  explicit InAppPurchase(v8::Isolate* isolate);
+  ~InAppPurchase() override;
+
+  void PurchaseProduct(const std::string& product_id, mate::Arguments* args);
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(InAppPurchase);
 };
 
-}  // namespace mate
+}  // namespace api
+
+}  // namespace atom
 
 #endif  // ATOM_BROWSER_API_ATOM_API_IN_APP_PURCHASE_H_

+ 1 - 1
atom/browser/mac/in_app_purchase.h

@@ -22,7 +22,7 @@ bool CanMakePayments(void);
 std::string GetReceiptURL(void);
 
 void PurchaseProduct(const std::string& productID,
-                     const int quantity,
+                     int quantity,
                      const InAppPurchaseCallback& callback);
 
 }  // namespace in_app_purchase

+ 2 - 2
atom/browser/mac/in_app_purchase.mm

@@ -146,9 +146,9 @@ std::string GetReceiptURL() {
 }
 
 void PurchaseProduct(const std::string& productID,
-                     const int quantity,
+                     int quantity,
                      const InAppPurchaseCallback& callback) {
-  auto iap =
+  auto* iap =
       [[InAppPurchase alloc] initWithCallback:callback quantity:quantity];
 
   [iap purchaseProduct:base::SysUTF8ToNSString(productID)];

+ 2 - 0
atom/browser/mac/in_app_purchase_observer.mm

@@ -177,6 +177,8 @@
 namespace in_app_purchase {
 
 void AddTransactionObserver(const InAppTransactionCallback& callback) {
+  // This is leaked, but we should be fine since we don't have a way to remove
+  // callback and the inAppPurchase module is never unloaded.
   [[InAppTransactionObserver alloc] initWithCallback:callback];
 }
 

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

@@ -1,14 +1,16 @@
 # inAppPurchase  _macOS_
 
+> In-App Purchase on Mac App Store.
+
 Your application should add a listener before to purchase a product. If there are no listener attached to the queue, the payment queue does not synchronize its list of pending transactions with the Apple App Store.
 
-## Methods 
+## Methods
 
 The `inAppPurchase` module has the following methods:
 
 ### `inAppPurchase.addTransactionListener(listener)`
 
-* `listener` Function -  Called when transactions are updated by the payment queue.
+* `listener` Function - Called when transactions are updated by the payment queue.
   * `payment` Object
     * `productIdentifier` String
     * `quantity` Integer
@@ -19,9 +21,11 @@ The `inAppPurchase` module has the following methods:
     * `transactionState` String - The transaction sate (`"SKPaymentTransactionStatePurchasing"`, `"SKPaymentTransactionStatePurchased"`, `"SKPaymentTransactionStateFailed"`, `"SKPaymentTransactionStateRestored"`, or `"SKPaymentTransactionStateDeferred"`)
     * `errorCode` Integer
     * `errorMessage` String
-   
+
+Add a listener to transactions.
 
 ### `inAppPurchase.purchaseProduct(productID, quantity, callback)`
+
 * `productID` String - The id of the product to purchase. (the id of `com.example.app.product1` is `product1`).
 * `quantity` Integer (optional) - The number of items the user wants to purchase.
 * `callback` Function (optional) - The callback called when the payment is added to the PaymentQueue. (You should add a listener with `inAppPurchase.addTransactionsListener` to get the transaction status).
@@ -33,4 +37,4 @@ Returns `true` if the user can make a payment and `false` otherwise.
 
 ### `inAppPurchase.getReceiptURL()`
 
-Returns `String`, the path to the receipt.
+Returns `String`, the path to the receipt.

+ 6 - 35
lib/browser/api/in-app-purchase.js

@@ -1,39 +1,10 @@
 'use strict'
 
-const binding = process.atomBinding('in_app_purchase')
-const v8Util = process.atomBinding('v8_util')
+const {EventEmitter} = require('events')
+const {inAppPurchase, InAppPurchase} = process.atomBinding('in_app_purchase')
 
-module.exports = {
+// inAppPurchase is an EventEmitter.
+Object.setPrototypeOf(InAppPurchase.prototype, EventEmitter.prototype)
+EventEmitter.call(inAppPurchase)
 
-  canMakePayments: function () {
-    return binding.canMakePayments()
-  },
-
-  getReceiptURL: function () {
-    return binding.getReceiptURL()
-  },
-
-  purchaseProduct: function (productID, quantity = 1, callback) {
-    if (typeof productID !== 'string') {
-      throw new TypeError('productID must be a string')
-    }
-    if (typeof quantity === 'function') {
-      quantity = 1
-      callback = quantity
-    }
-    return binding.purchaseProduct(productID, quantity, callback)
-  },
-
-  addTransactionListener: function (callback) {
-    if (typeof callback !== 'function') {
-      throw new TypeError('callback must be a function')
-    }
-    return binding.addTransactionListener(callback)
-  }
-}
-
-// Mark standard asynchronous functions.
-v8Util.setHiddenValue(
-  module.exports.purchaseProduct, 'asynchronous', true)
-v8Util.setHiddenValue(
-  module.exports.addTransactionListener, 'asynchronous', true)
+module.exports = inAppPurchase

+ 7 - 0
spec/api-in-app-purchase-spec.js

@@ -22,4 +22,11 @@ describe('inAppPurchase module', () => {
       done()
     })
   })
+
+  it('purchaseProduct() accepts optional arguments', (done) => {
+    inAppPurchase.purchaseProduct('non-exist', () => {
+      inAppPurchase.purchaseProduct('non-exist', 1)
+      done()
+    })
+  })
 })