Skip to content

Commit

Permalink
Add in-app purchase for MacOS
Browse files Browse the repository at this point in the history
  • Loading branch information
AdrienFery authored and zcbenz committed Jan 10, 2018
1 parent 143816b commit f3ae566
Show file tree
Hide file tree
Showing 12 changed files with 593 additions and 0 deletions.
63 changes: 63 additions & 0 deletions atom/browser/api/atom_api_in_app_purchase.cc
@@ -0,0 +1,63 @@
// Copyright (c) 2017 Amaplex Software, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.

#include <string>
#include <utility>
#include <vector>

#include "atom/browser/api/atom_api_in_app_purchase.h"
#include "atom/common/native_mate_converters/callback.h"
#include "native_mate/dictionary.h"

#include "atom/common/node_includes.h"

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();
}

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();
}
} // namespace mate

namespace {

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);
#endif
}

} // namespace

NODE_MODULE_CONTEXT_AWARE_BUILTIN(atom_browser_in_app_purchase, Initialize)
36 changes: 36 additions & 0 deletions atom/browser/api/atom_api_in_app_purchase.h
@@ -0,0 +1,36 @@
// Copyright (c) 2017 Amaplex Software, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.

#ifndef ATOM_BROWSER_API_ATOM_API_IN_APP_PURCHASE_H_
#define ATOM_BROWSER_API_ATOM_API_IN_APP_PURCHASE_H_

#include <string>

#include "atom/browser/in_app_purchase.h"
#include "atom/browser/in_app_purchase_observer.h"
#include "native_mate/dictionary.h"

namespace mate {

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);
};

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);
};

} // namespace mate

#endif // ATOM_BROWSER_API_ATOM_API_IN_APP_PURCHASE_H_
30 changes: 30 additions & 0 deletions atom/browser/in_app_purchase.h
@@ -0,0 +1,30 @@
// Copyright (c) 2017 Amaplex Software, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.

#ifndef ATOM_BROWSER_UI_IN_APP_PURCHASE_H_
#define ATOM_BROWSER_UI_IN_APP_PURCHASE_H_

#include <string>

#include "base/callback.h"

namespace in_app_purchase {

// --------------------------- Typedefs ---------------------------

typedef base::Callback<void(bool isProductValid)> InAppPurchaseCallback;

// --------------------------- Functions ---------------------------

bool CanMakePayments(void);

std::string GetReceiptURL(void);

void PurchaseProduct(const std::string& productID,
const int quantity,
const InAppPurchaseCallback& callback);

} // namespace in_app_purchase

#endif // ATOM_BROWSER_UI_IN_APP_PURCHASE_H_
149 changes: 149 additions & 0 deletions atom/browser/in_app_purchase_mac.mm
@@ -0,0 +1,149 @@
// Copyright (c) 2017 Amaplex Software, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.

#include "atom/browser/in_app_purchase.h"
#include "base/bind.h"
#include "base/strings/sys_string_conversions.h"
#include "content/public/browser/browser_thread.h"

#import <CommonCrypto/CommonCrypto.h>
#import <StoreKit/StoreKit.h>

// ============================================================================
// InAppPurchase
// ============================================================================

// --------------------------------- Interface --------------------------------

@interface InAppPurchase : NSObject<SKProductsRequestDelegate> {
@private
in_app_purchase::InAppPurchaseCallback callback_;
NSInteger quantity_;
}

- (id)initWithCallback:(const in_app_purchase::InAppPurchaseCallback&)callback
quantity:(NSInteger)quantity;

- (void)purchaseProduct:(NSString*)productID;

@end

// ------------------------------- Implementation -----------------------------

@implementation InAppPurchase

/**
* Init with a callback.
*
* @param callback - The callback that will be called when the payment is added
* to the queue.
*/
- (id)initWithCallback:(const in_app_purchase::InAppPurchaseCallback&)callback
quantity:(NSInteger)quantity {
if ((self = [super init])) {
callback_ = callback;
quantity_ = quantity;
}

return self;
}

/**
* Start the in-app purchase process.
*
* @param productID - The id of the product to purchase (the id of
* com.example.app.product1 is product1).
*/
- (void)purchaseProduct:(NSString*)productID {
// Retrieve the product information. (The products request retrieves,
// information about valid products along with a list of the invalid product
// identifiers, and then calls its delegate to process the result).
SKProductsRequest* productsRequest;
productsRequest = [[SKProductsRequest alloc]
initWithProductIdentifiers:[NSSet setWithObject:productID]];

productsRequest.delegate = self;
[productsRequest start];
}

/**
* Process product informations and start the payment.
*
* @param request - The product request.
* @param response - The informations about the list of products.
*/
- (void)productsRequest:(SKProductsRequest*)request
didReceiveResponse:(SKProductsResponse*)response {
// Get the first product.
NSArray* products = response.products;
SKProduct* product = [products count] == 1 ? [products firstObject] : nil;

// Return if the product is not found or invalid.
if (product == nil) {
[self runCallback:false];
return;
}

// Start the payment process.
[self checkout:product];
}

/**
* Submit a payment request to the App Store.
*
* @param product - The product to purchase.
*/
- (void)checkout:(SKProduct*)product {
// Add the payment to the transaction queue. (The observer will be called
// when the transaction is finished).
SKMutablePayment* payment = [SKMutablePayment paymentWithProduct:product];
payment.quantity = quantity_;

[[SKPaymentQueue defaultQueue] addPayment:payment];

// Notify that the payment has been added to the queue with success.
[self runCallback:true];
}

/**
* Submit a payment request to the App Store.
*
* @param product - The product to purchase.
*/
- (void)runCallback:(bool)isProductValid {
content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
base::Bind(callback_, isProductValid));
}

@end

// ============================================================================
// C++ in_app_purchase
// ============================================================================

namespace in_app_purchase {

bool CanMakePayments() {
return [SKPaymentQueue canMakePayments];
}

std::string GetReceiptURL() {
NSURL* receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
if (receiptURL != nil) {
return [[receiptURL absoluteString] UTF8String];
} else {
return "";
}
}

void PurchaseProduct(const std::string& productID,
const int quantity,
const InAppPurchaseCallback& callback) {
auto iap =
[[InAppPurchase alloc] initWithCallback:callback quantity:quantity];

[iap purchaseProduct:base::SysUTF8ToNSString(productID)];
}

} // namespace in_app_purchase
41 changes: 41 additions & 0 deletions atom/browser/in_app_purchase_observer.h
@@ -0,0 +1,41 @@
// Copyright (c) 2017 Amaplex Software, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.

#ifndef ATOM_BROWSER_UI_IN_APP_PURCHASE_OBSERVER_H_
#define ATOM_BROWSER_UI_IN_APP_PURCHASE_OBSERVER_H_

#include <string>

#include "base/callback.h"

namespace in_app_purchase {

// --------------------------- Structures ---------------------------

struct Payment {
std::string productIdentifier = "";
int quantity = 1;
};

struct Transaction {
std::string transactionIdentifier = "";
std::string transactionDate = "";
std::string originalTransactionIdentifier = "";
int errorCode = 0;
std::string errorMessage = "";
std::string transactionState = "";
};

// --------------------------- Typedefs ---------------------------

typedef base::RepeatingCallback<void(const Payment, const Transaction)>
InAppTransactionCallback;

// --------------------------- Functions ---------------------------

void AddTransactionObserver(const InAppTransactionCallback& callback);

} // namespace in_app_purchase

#endif // ATOM_BROWSER_UI_IN_APP_PURCHASE_OBSERVER_H_

0 comments on commit f3ae566

Please sign in to comment.