Skip to content

Commit 871a24b

Browse files
authored
[in_app_purchase] Convert storefront(), transactions(), canMakePayment(), and addPayment() to pigeon (flutter#5910)
Part 1 of flutter#117910 This PR converts storefront(), addPayment(), transactions(), and canMakePayment(). ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] All existing and new tests are passing.
1 parent 7b1ae1f commit 871a24b

24 files changed

+1910
-333
lines changed

packages/in_app_purchase/in_app_purchase_storekit/CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
## NEXT
1+
## 0.3.9
22

3+
* Converts `storefront()`, `transactions()`, `addPayment()`, `canMakePayment` to pigeon.
34
* Updates minimum iOS version to 12.0 and minimum Flutter version to 3.16.6.
45

56
## 0.3.8+1

packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/FIAObjectTranslator.h

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
#import <Foundation/Foundation.h>
66
#import <StoreKit/StoreKit.h>
7+
#import "messages.g.h"
78

89
NS_ASSUME_NONNULL_BEGIN
910

@@ -56,7 +57,19 @@ NS_ASSUME_NONNULL_BEGIN
5657
withError:(NSString *_Nullable *_Nullable)error
5758
API_AVAILABLE(ios(12.2));
5859

60+
+ (nullable SKPaymentTransactionMessage *)convertTransactionToPigeon:
61+
(nullable SKPaymentTransaction *)transaction;
62+
63+
+ (nullable SKStorefrontMessage *)convertStorefrontToPigeon:(nullable SKStorefront *)storefront
64+
API_AVAILABLE(ios(13.0));
65+
66+
+ (nullable SKPaymentDiscountMessage *)convertPaymentDiscountToPigeon:
67+
(nullable SKPaymentDiscount *)discount API_AVAILABLE(ios(12.2));
68+
69+
+ (nullable SKPaymentMessage *)convertPaymentToPigeon:(nullable SKPayment *)payment
70+
API_AVAILABLE(ios(12.2));
71+
72+
+ (nullable SKErrorMessage *)convertSKErrorToPigeon:(NSError *)error;
5973
@end
60-
;
6174

6275
NS_ASSUME_NONNULL_END

packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/FIAObjectTranslator.m

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,4 +288,92 @@ + (SKPaymentDiscount *)getSKPaymentDiscountFromMap:(NSDictionary *)map
288288
return discount;
289289
}
290290

291+
+ (nullable SKPaymentTransactionMessage *)convertTransactionToPigeon:
292+
(nullable SKPaymentTransaction *)transaction API_AVAILABLE(ios(12.2)) {
293+
if (!transaction) {
294+
return nil;
295+
}
296+
SKPaymentTransactionMessage *msg = [SKPaymentTransactionMessage
297+
makeWithPayment:[self convertPaymentToPigeon:transaction.payment]
298+
transactionState:[self convertTransactionStateToPigeon:transaction.transactionState]
299+
originalTransaction:transaction.originalTransaction
300+
? [self convertTransactionToPigeon:transaction.originalTransaction]
301+
: nil
302+
transactionTimeStamp:[NSNumber numberWithDouble:[transaction.transactionDate
303+
timeIntervalSince1970]]
304+
transactionIdentifier:transaction.transactionIdentifier
305+
error:[self convertSKErrorToPigeon:transaction.error]];
306+
return msg;
307+
}
308+
309+
+ (nullable SKErrorMessage *)convertSKErrorToPigeon:(NSError *)error {
310+
NSMutableDictionary *userInfo = [NSMutableDictionary new];
311+
for (NSErrorUserInfoKey key in error.userInfo) {
312+
id value = error.userInfo[key];
313+
userInfo[key] = [FIAObjectTranslator encodeNSErrorUserInfo:value];
314+
}
315+
316+
SKErrorMessage *msg = [SKErrorMessage makeWithCode:error.code
317+
domain:error.domain
318+
userInfo:userInfo];
319+
return msg;
320+
}
321+
322+
+ (SKPaymentTransactionStateMessage)convertTransactionStateToPigeon:
323+
(SKPaymentTransactionState)state {
324+
switch (state) {
325+
case SKPaymentTransactionStatePurchasing:
326+
return SKPaymentTransactionStateMessagePurchasing;
327+
case SKPaymentTransactionStatePurchased:
328+
return SKPaymentTransactionStateMessagePurchased;
329+
case SKPaymentTransactionStateFailed:
330+
return SKPaymentTransactionStateMessageFailed;
331+
case SKPaymentTransactionStateRestored:
332+
return SKPaymentTransactionStateMessageRestored;
333+
case SKPaymentTransactionStateDeferred:
334+
return SKPaymentTransactionStateMessageDeferred;
335+
}
336+
}
337+
338+
+ (nullable SKPaymentMessage *)convertPaymentToPigeon:(nullable SKPayment *)payment
339+
API_AVAILABLE(ios(12.2)) {
340+
if (!payment) {
341+
return nil;
342+
}
343+
SKPaymentMessage *msg = [SKPaymentMessage
344+
makeWithProductIdentifier:payment.productIdentifier
345+
applicationUsername:payment.applicationUsername
346+
requestData:[[NSString alloc] initWithData:payment.requestData
347+
encoding:NSUTF8StringEncoding]
348+
quantity:payment.quantity
349+
simulatesAskToBuyInSandbox:payment.simulatesAskToBuyInSandbox
350+
paymentDiscount:[self convertPaymentDiscountToPigeon:payment.paymentDiscount]];
351+
return msg;
352+
}
353+
354+
+ (nullable SKPaymentDiscountMessage *)convertPaymentDiscountToPigeon:
355+
(nullable SKPaymentDiscount *)discount API_AVAILABLE(ios(12.2)) {
356+
if (!discount) {
357+
return nil;
358+
}
359+
SKPaymentDiscountMessage *msg =
360+
[SKPaymentDiscountMessage makeWithIdentifier:discount.identifier
361+
keyIdentifier:discount.keyIdentifier
362+
nonce:[discount.nonce UUIDString]
363+
signature:discount.signature
364+
timestamp:[discount.timestamp intValue]];
365+
366+
return msg;
367+
}
368+
369+
+ (nullable SKStorefrontMessage *)convertStorefrontToPigeon:(nullable SKStorefront *)storefront
370+
API_AVAILABLE(ios(13.0)) {
371+
if (!storefront) {
372+
return nil;
373+
}
374+
SKStorefrontMessage *msg = [SKStorefrontMessage makeWithCountryCode:storefront.countryCode
375+
identifier:storefront.identifier];
376+
return msg;
377+
}
378+
291379
@end

packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,12 @@
77
#else
88
#import <Flutter/Flutter.h>
99
#endif
10+
#import "messages.g.h"
11+
1012
@class FIAPaymentQueueHandler;
1113
@class FIAPReceiptManager;
1214

13-
@interface InAppPurchasePlugin : NSObject <FlutterPlugin>
15+
@interface InAppPurchasePlugin : NSObject <FlutterPlugin, InAppPurchaseAPI>
1416

1517
@property(strong, nonatomic) FIAPaymentQueueHandler *paymentQueueHandler;
1618

packages/in_app_purchase/in_app_purchase_storekit/darwin/Classes/InAppPurchasePlugin.m

Lines changed: 30 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,11 @@ + (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar> *)registrar {
4040
FlutterMethodChannel *channel =
4141
[FlutterMethodChannel methodChannelWithName:@"plugins.flutter.io/in_app_purchase"
4242
binaryMessenger:[registrar messenger]];
43+
4344
InAppPurchasePlugin *instance = [[InAppPurchasePlugin alloc] initWithRegistrar:registrar];
4445
[registrar addMethodCallDelegate:instance channel:channel];
46+
[registrar addApplicationDelegate:instance];
47+
SetUpInAppPurchaseAPI(registrar.messenger, instance);
4548
}
4649

4750
- (instancetype)initWithReceiptManager:(FIAPReceiptManager *)receiptManager {
@@ -85,16 +88,8 @@ - (instancetype)initWithRegistrar:(NSObject<FlutterPluginRegistrar> *)registrar
8588
}
8689

8790
- (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result {
88-
if ([@"-[SKPaymentQueue canMakePayments:]" isEqualToString:call.method]) {
89-
[self canMakePayments:result];
90-
} else if ([@"-[SKPaymentQueue transactions]" isEqualToString:call.method]) {
91-
[self getPendingTransactions:result];
92-
} else if ([@"-[SKPaymentQueue storefront]" isEqualToString:call.method]) {
93-
[self getStorefront:result];
94-
} else if ([@"-[InAppPurchasePlugin startProductRequest:result:]" isEqualToString:call.method]) {
91+
if ([@"-[InAppPurchasePlugin startProductRequest:result:]" isEqualToString:call.method]) {
9592
[self handleProductRequestMethodCall:call result:result];
96-
} else if ([@"-[InAppPurchasePlugin addPayment:result:]" isEqualToString:call.method]) {
97-
[self addPayment:call result:result];
9893
} else if ([@"-[InAppPurchasePlugin finishTransaction:result:]" isEqualToString:call.method]) {
9994
[self finishTransaction:call result:result];
10095
} else if ([@"-[InAppPurchasePlugin restoreTransactions:result:]" isEqualToString:call.method]) {
@@ -127,34 +122,29 @@ - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result
127122
}
128123
}
129124

130-
- (void)canMakePayments:(FlutterResult)result {
131-
result(@([SKPaymentQueue canMakePayments]));
125+
- (nullable NSNumber *)canMakePaymentsWithError:
126+
(FlutterError *_Nullable __autoreleasing *_Nonnull)error {
127+
return @([SKPaymentQueue canMakePayments]);
132128
}
133129

134-
- (void)getPendingTransactions:(FlutterResult)result {
130+
- (nullable NSArray<SKPaymentTransactionMessage *> *)transactionsWithError:
131+
(FlutterError *_Nullable *_Nonnull)error {
135132
NSArray<SKPaymentTransaction *> *transactions =
136133
[self.paymentQueueHandler getUnfinishedTransactions];
137134
NSMutableArray *transactionMaps = [[NSMutableArray alloc] init];
138135
for (SKPaymentTransaction *transaction in transactions) {
139-
[transactionMaps addObject:[FIAObjectTranslator getMapFromSKPaymentTransaction:transaction]];
136+
[transactionMaps addObject:[FIAObjectTranslator convertTransactionToPigeon:transaction]];
140137
}
141-
result(transactionMaps);
138+
return transactionMaps;
142139
}
143140

144-
- (void)getStorefront:(FlutterResult)result {
145-
if (@available(iOS 13.0, macOS 10.15, *)) {
146-
SKStorefront *storefront = self.paymentQueueHandler.storefront;
147-
if (!storefront) {
148-
result(nil);
149-
return;
150-
}
151-
result([FIAObjectTranslator getMapFromSKStorefront:storefront]);
152-
return;
141+
- (nullable SKStorefrontMessage *)storefrontWithError:(FlutterError *_Nullable *_Nonnull)error
142+
API_AVAILABLE(ios(13.0), macos(10.15)) {
143+
SKStorefront *storefront = self.paymentQueueHandler.storefront;
144+
if (!storefront) {
145+
return nil;
153146
}
154-
155-
NSLog(@"storefront is not avaialbe in iOS below 13.0 or macOS below 10.15.");
156-
result(nil);
157-
return;
147+
return [FIAObjectTranslator convertStorefrontToPigeon:storefront];
158148
}
159149

160150
- (void)handleProductRequestMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result {
@@ -193,28 +183,23 @@ - (void)handleProductRequestMethodCall:(FlutterMethodCall *)call result:(Flutter
193183
}];
194184
}
195185

196-
- (void)addPayment:(FlutterMethodCall *)call result:(FlutterResult)result {
197-
if (![call.arguments isKindOfClass:[NSDictionary class]]) {
198-
result([FlutterError errorWithCode:@"storekit_invalid_argument"
199-
message:@"Argument type of addPayment is not a Dictionary"
200-
details:call.arguments]);
201-
return;
202-
}
203-
NSDictionary *paymentMap = (NSDictionary *)call.arguments;
186+
- (void)addPaymentPaymentMap:(nonnull NSDictionary *)paymentMap
187+
error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error {
204188
NSString *productID = [paymentMap objectForKey:@"productIdentifier"];
205189
// When a product is already fetched, we create a payment object with
206190
// the product to process the payment.
207191
SKProduct *product = [self getProduct:productID];
208192
if (!product) {
209-
result([FlutterError
193+
*error = [FlutterError
210194
errorWithCode:@"storekit_invalid_payment_object"
211195
message:
212196
@"You have requested a payment for an invalid product. Either the "
213197
@"`productIdentifier` of the payment is not valid or the product has not been "
214198
@"fetched before adding the payment to the payment queue."
215-
details:call.arguments]);
199+
details:paymentMap];
216200
return;
217201
}
202+
218203
SKMutablePayment *payment = [SKMutablePayment paymentWithProduct:product];
219204
payment.applicationUsername = [paymentMap objectForKey:@"applicationUsername"];
220205
NSNumber *quantity = [paymentMap objectForKey:@"quantity"];
@@ -227,34 +212,32 @@ - (void)addPayment:(FlutterMethodCall *)call result:(FlutterResult)result {
227212
if (@available(iOS 12.2, *)) {
228213
NSDictionary *paymentDiscountMap = [self getNonNullValueFromDictionary:paymentMap
229214
forKey:@"paymentDiscount"];
230-
NSString *error = nil;
215+
NSString *errorMsg = nil;
231216
SKPaymentDiscount *paymentDiscount =
232-
[FIAObjectTranslator getSKPaymentDiscountFromMap:paymentDiscountMap withError:&error];
217+
[FIAObjectTranslator getSKPaymentDiscountFromMap:paymentDiscountMap withError:&errorMsg];
233218

234-
if (error) {
235-
result([FlutterError
219+
if (errorMsg) {
220+
*error = [FlutterError
236221
errorWithCode:@"storekit_invalid_payment_discount_object"
237222
message:[NSString stringWithFormat:@"You have requested a payment and specified a "
238223
@"payment discount with invalid properties. %@",
239-
error]
240-
details:call.arguments]);
224+
errorMsg]
225+
details:paymentMap];
241226
return;
242227
}
243228

244229
payment.paymentDiscount = paymentDiscount;
245230
}
246-
247231
if (![self.paymentQueueHandler addPayment:payment]) {
248-
result([FlutterError
232+
*error = [FlutterError
249233
errorWithCode:@"storekit_duplicate_product_object"
250234
message:@"There is a pending transaction for the same product identifier. Please "
251235
@"either wait for it to be finished or finish it manually using "
252236
@"`completePurchase` to avoid edge cases."
253237

254-
details:call.arguments]);
238+
details:paymentMap];
255239
return;
256240
}
257-
result(nil);
258241
}
259242

260243
- (void)finishTransaction:(FlutterMethodCall *)call result:(FlutterResult)result {
@@ -465,5 +448,4 @@ - (SKProduct *)getProduct:(NSString *)productID {
465448
- (SKReceiptRefreshRequest *)getRefreshReceiptRequest:(NSDictionary *)properties {
466449
return [[SKReceiptRefreshRequest alloc] initWithReceiptProperties:properties];
467450
}
468-
469451
@end

0 commit comments

Comments
 (0)