Turn InAppPurchase into an EventEmitter

This commit is contained in:
Cheng Zhao 2018-01-10 16:37:05 +09:00
parent 400bfb3c5a
commit ac6f895f64
8 changed files with 123 additions and 90 deletions

View file

@ -15,47 +15,92 @@
namespace mate { namespace mate {
v8::Local<v8::Value> Converter<in_app_purchase::Payment>::ToV8( template <>
v8::Isolate* isolate, struct Converter<in_app_purchase::Payment> {
const in_app_purchase::Payment& payment) { static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
mate::Dictionary dict = mate::Dictionary::CreateEmpty(isolate); const in_app_purchase::Payment& payment) {
dict.SetHidden("simple", true); mate::Dictionary dict = mate::Dictionary::CreateEmpty(isolate);
dict.Set("productIdentifier", payment.productIdentifier); dict.SetHidden("simple", true);
dict.Set("quantity", payment.quantity); dict.Set("productIdentifier", payment.productIdentifier);
return dict.GetHandle(); dict.Set("quantity", payment.quantity);
} return dict.GetHandle();
}
};
v8::Local<v8::Value> Converter<in_app_purchase::Transaction>::ToV8( template <>
v8::Isolate* isolate, struct Converter<in_app_purchase::Transaction> {
const in_app_purchase::Transaction& transaction) { static v8::Local<v8::Value> ToV8(v8::Isolate* isolate,
mate::Dictionary dict = mate::Dictionary::CreateEmpty(isolate); const in_app_purchase::Transaction& val) {
dict.SetHidden("simple", true); mate::Dictionary dict = mate::Dictionary::CreateEmpty(isolate);
dict.Set("transactionIdentifier", transaction.transactionIdentifier); dict.SetHidden("simple", true);
dict.Set("transactionDate", transaction.transactionDate); dict.Set("transactionIdentifier", val.transactionIdentifier);
dict.Set("originalTransactionIdentifier", dict.Set("transactionDate", val.transactionDate);
transaction.originalTransactionIdentifier); dict.Set("originalTransactionIdentifier",
dict.Set("transactionState", transaction.transactionState); val.originalTransactionIdentifier);
dict.Set("transactionState", val.transactionState);
dict.Set("errorCode", val.errorCode);
dict.Set("errorMessage", val.errorMessage);
return dict.GetHandle();
}
};
dict.Set("errorCode", transaction.errorCode);
dict.Set("errorMessage", transaction.errorMessage);
return dict.GetHandle();
}
} // namespace mate } // namespace mate
namespace atom {
namespace api {
// static
mate::Handle<InAppPurchase> InAppPurchase::Create(v8::Isolate* isolate) {
return mate::CreateHandle(isolate, new InAppPurchase(isolate));
}
// 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 api
} // namespace atom
namespace { namespace {
using atom::api::InAppPurchase;
void Initialize(v8::Local<v8::Object> exports, void Initialize(v8::Local<v8::Object> exports,
v8::Local<v8::Value> unused, v8::Local<v8::Value> unused,
v8::Local<v8::Context> context, v8::Local<v8::Context> context,
void* priv) { void* priv) {
mate::Dictionary dict(context->GetIsolate(), exports);
#if defined(OS_MACOSX) #if defined(OS_MACOSX)
dict.SetMethod("canMakePayments", &in_app_purchase::CanMakePayments); v8::Isolate* isolate = context->GetIsolate();
dict.SetMethod("getReceiptURL", &in_app_purchase::GetReceiptURL); mate::Dictionary dict(isolate, exports);
dict.SetMethod("purchaseProduct", &in_app_purchase::PurchaseProduct); dict.Set("inAppPurchase", InAppPurchase::Create(isolate));
dict.SetMethod("addTransactionListener", dict.Set("InAppPurchase",
&in_app_purchase::AddTransactionObserver); InAppPurchase::GetConstructor(isolate)->GetFunction());
#endif #endif
} }

View file

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

View file

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

View file

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

View file

@ -177,6 +177,8 @@
namespace in_app_purchase { namespace in_app_purchase {
void AddTransactionObserver(const InAppTransactionCallback& callback) { 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]; [[InAppTransactionObserver alloc] initWithCallback:callback];
} }

View file

@ -1,14 +1,16 @@
# inAppPurchase _macOS_ # 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. 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: The `inAppPurchase` module has the following methods:
### `inAppPurchase.addTransactionListener(listener)` ### `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 * `payment` Object
* `productIdentifier` String * `productIdentifier` String
* `quantity` Integer * `quantity` Integer
@ -19,9 +21,11 @@ The `inAppPurchase` module has the following methods:
* `transactionState` String - The transaction sate (`"SKPaymentTransactionStatePurchasing"`, `"SKPaymentTransactionStatePurchased"`, `"SKPaymentTransactionStateFailed"`, `"SKPaymentTransactionStateRestored"`, or `"SKPaymentTransactionStateDeferred"`) * `transactionState` String - The transaction sate (`"SKPaymentTransactionStatePurchasing"`, `"SKPaymentTransactionStatePurchased"`, `"SKPaymentTransactionStateFailed"`, `"SKPaymentTransactionStateRestored"`, or `"SKPaymentTransactionStateDeferred"`)
* `errorCode` Integer * `errorCode` Integer
* `errorMessage` String * `errorMessage` String
Add a listener to transactions.
### `inAppPurchase.purchaseProduct(productID, quantity, callback)` ### `inAppPurchase.purchaseProduct(productID, quantity, callback)`
* `productID` String - The id of the product to purchase. (the id of `com.example.app.product1` is `product1`). * `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. * `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). * `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()` ### `inAppPurchase.getReceiptURL()`
Returns `String`, the path to the receipt. Returns `String`, the path to the receipt.

View file

@ -1,39 +1,10 @@
'use strict' 'use strict'
const binding = process.atomBinding('in_app_purchase') const {EventEmitter} = require('events')
const v8Util = process.atomBinding('v8_util') 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 () { module.exports = inAppPurchase
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)

View file

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