in_app_purchase_observer.mm 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. // Copyright (c) 2017 Amaplex Software, Inc.
  2. // Use of this source code is governed by the MIT license that can be
  3. // found in the LICENSE file.
  4. #include "atom/browser/mac/in_app_purchase_observer.h"
  5. #include "base/bind.h"
  6. #include "base/strings/sys_string_conversions.h"
  7. #include "content/public/browser/browser_thread.h"
  8. #import <CommonCrypto/CommonCrypto.h>
  9. #import <StoreKit/StoreKit.h>
  10. // ============================================================================
  11. // InAppTransactionObserver
  12. // ============================================================================
  13. namespace {
  14. using InAppTransactionCallback = base::RepeatingCallback<void(
  15. const std::vector<in_app_purchase::Transaction>&)>;
  16. } // namespace
  17. @interface InAppTransactionObserver : NSObject <SKPaymentTransactionObserver> {
  18. @private
  19. InAppTransactionCallback callback_;
  20. }
  21. - (id)initWithCallback:(const InAppTransactionCallback&)callback;
  22. @end
  23. @implementation InAppTransactionObserver
  24. /**
  25. * Init with a callback.
  26. *
  27. * @param callback - The callback that will be called for each transaction
  28. * update.
  29. */
  30. - (id)initWithCallback:(const InAppTransactionCallback&)callback {
  31. if ((self = [super init])) {
  32. callback_ = callback;
  33. [[SKPaymentQueue defaultQueue] addTransactionObserver:self];
  34. }
  35. return self;
  36. }
  37. /**
  38. * Cleanup.
  39. */
  40. - (void)dealloc {
  41. [[SKPaymentQueue defaultQueue] removeTransactionObserver:self];
  42. [super dealloc];
  43. }
  44. /**
  45. * Run the callback in the browser thread.
  46. *
  47. * @param transaction - The transaction to pass to the callback.
  48. */
  49. - (void)runCallback:(NSArray*)transactions {
  50. // Convert the transaction.
  51. std::vector<in_app_purchase::Transaction> converted;
  52. converted.reserve([transactions count]);
  53. for (SKPaymentTransaction* transaction in transactions) {
  54. converted.push_back([self skPaymentTransactionToStruct:transaction]);
  55. }
  56. // Send the callback to the browser thread.
  57. content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
  58. base::Bind(callback_, converted));
  59. }
  60. /**
  61. * Convert an NSDate to ISO String.
  62. *
  63. * @param date - The date to convert.
  64. */
  65. - (NSString*)dateToISOString:(NSDate*)date {
  66. NSDateFormatter* dateFormatter = [[NSDateFormatter alloc] init];
  67. NSLocale* enUSPOSIXLocale =
  68. [NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"];
  69. [dateFormatter setLocale:enUSPOSIXLocale];
  70. [dateFormatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ssZZZZZ"];
  71. return [dateFormatter stringFromDate:date];
  72. }
  73. /**
  74. * Convert a SKPayment object to a Payment structure.
  75. *
  76. * @param payment - The SKPayment object to convert.
  77. */
  78. - (in_app_purchase::Payment)skPaymentToStruct:(SKPayment*)payment {
  79. in_app_purchase::Payment paymentStruct;
  80. if (payment.productIdentifier != nil) {
  81. paymentStruct.productIdentifier = [payment.productIdentifier UTF8String];
  82. }
  83. if (payment.quantity >= 1) {
  84. paymentStruct.quantity = (int)payment.quantity;
  85. }
  86. return paymentStruct;
  87. }
  88. /**
  89. * Convert a SKPaymentTransaction object to a Transaction structure.
  90. *
  91. * @param transaction - The SKPaymentTransaction object to convert.
  92. */
  93. - (in_app_purchase::Transaction)skPaymentTransactionToStruct:
  94. (SKPaymentTransaction*)transaction {
  95. in_app_purchase::Transaction transactionStruct;
  96. if (transaction.transactionIdentifier != nil) {
  97. transactionStruct.transactionIdentifier =
  98. [transaction.transactionIdentifier UTF8String];
  99. }
  100. if (transaction.transactionDate != nil) {
  101. transactionStruct.transactionDate =
  102. [[self dateToISOString:transaction.transactionDate] UTF8String];
  103. }
  104. if (transaction.originalTransaction != nil) {
  105. transactionStruct.originalTransactionIdentifier =
  106. [transaction.originalTransaction.transactionIdentifier UTF8String];
  107. }
  108. if (transaction.error != nil) {
  109. transactionStruct.errorCode = (int)transaction.error.code;
  110. transactionStruct.errorMessage =
  111. [[transaction.error localizedDescription] UTF8String];
  112. }
  113. if (transaction.transactionState < 5) {
  114. transactionStruct.transactionState =
  115. [[@[ @"purchasing", @"purchased", @"failed", @"restored", @"deferred" ]
  116. objectAtIndex:transaction.transactionState] UTF8String];
  117. }
  118. if (transaction.payment != nil) {
  119. transactionStruct.payment = [self skPaymentToStruct:transaction.payment];
  120. }
  121. return transactionStruct;
  122. }
  123. #pragma mark -
  124. #pragma mark SKPaymentTransactionObserver methods
  125. /**
  126. * Executed when a transaction is updated.
  127. *
  128. * @param queue - The payment queue.
  129. * @param transactions - The list of transactions updated.
  130. */
  131. - (void)paymentQueue:(SKPaymentQueue*)queue
  132. updatedTransactions:(NSArray*)transactions {
  133. [self runCallback:transactions];
  134. }
  135. @end
  136. // ============================================================================
  137. // C++ in_app_purchase
  138. // ============================================================================
  139. namespace in_app_purchase {
  140. Transaction::Transaction() = default;
  141. Transaction::Transaction(const Transaction&) = default;
  142. Transaction::~Transaction() = default;
  143. TransactionObserver::TransactionObserver() : weak_ptr_factory_(this) {
  144. obeserver_ = [[InAppTransactionObserver alloc]
  145. initWithCallback:base::Bind(&TransactionObserver::OnTransactionsUpdated,
  146. weak_ptr_factory_.GetWeakPtr())];
  147. }
  148. TransactionObserver::~TransactionObserver() {
  149. [obeserver_ release];
  150. }
  151. } // namespace in_app_purchase