Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/in_app_purchase/in_app_purchase_ios/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 0.1.0+3

* iOS: Fix treating missing App Store receipt as an exception.

## 0.1.0+2

* Changed the iOS payment queue handler in such a way that it only adds a listener to the SKPaymentQueue when there
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,16 @@

@interface InAppPurchasePluginTest : XCTestCase

@property(strong, nonatomic) FIAPReceiptManagerStub* receiptManagerStub;
@property(strong, nonatomic) InAppPurchasePlugin* plugin;

@end

@implementation InAppPurchasePluginTest

- (void)setUp {
self.plugin =
[[InAppPurchasePluginStub alloc] initWithReceiptManager:[FIAPReceiptManagerStub new]];
self.receiptManagerStub = [FIAPReceiptManagerStub new];
self.plugin = [[InAppPurchasePluginStub alloc] initWithReceiptManager:self.receiptManagerStub];
}

- (void)tearDown {
Expand Down Expand Up @@ -219,7 +220,7 @@ - (void)testRestoreTransactions {
XCTAssertTrue(callbackInvoked);
}

- (void)testRetrieveReceiptData {
- (void)testRetrieveReceiptDataSuccess {
XCTestExpectation* expectation = [self expectationWithDescription:@"receipt data retrieved"];
FlutterMethodCall* call = [FlutterMethodCall
methodCallWithMethodName:@"-[InAppPurchasePlugin retrieveReceiptData:result:]"
Expand All @@ -231,8 +232,25 @@ - (void)testRetrieveReceiptData {
[expectation fulfill];
}];
[self waitForExpectations:@[ expectation ] timeout:5];
NSLog(@"%@", result);
XCTAssertNotNil(result);
XCTAssert([result isKindOfClass:[NSString class]]);
}

- (void)testRetrieveReceiptDataError {
XCTestExpectation* expectation = [self expectationWithDescription:@"receipt data retrieved"];
FlutterMethodCall* call = [FlutterMethodCall
methodCallWithMethodName:@"-[InAppPurchasePlugin retrieveReceiptData:result:]"
arguments:nil];
__block NSDictionary* result;
self.receiptManagerStub.returnError = YES;
[self.plugin handleMethodCall:call
result:^(id r) {
result = r;
[expectation fulfill];
}];
[self waitForExpectations:@[ expectation ] timeout:5];
XCTAssertNotNil(result);
XCTAssert([result isKindOfClass:[FlutterError class]]);
}

- (void)testRefreshReceiptRequest {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ API_AVAILABLE(ios(11.2), macos(10.13.2))
@end

@interface FIAPReceiptManagerStub : FIAPReceiptManager
// Indicates whether getReceiptData of this stub is going to return an error.
// Setting this to true will let getReceiptData give a basic NSError and return nil.
@property(assign, nonatomic) BOOL returnError;
@end

@interface SKReceiptRefreshRequestStub : SKReceiptRefreshRequest
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,11 @@ - (instancetype)initWithMap:(NSDictionary *)map {

@implementation FIAPReceiptManagerStub : FIAPReceiptManager

- (NSData *)getReceiptData:(NSURL *)url {
- (NSData *)getReceiptData:(NSURL *)url error:(NSError **)error {
if (self.returnError) {
*error = [[NSError alloc] init];
return nil;
}
NSString *originalString = [NSString stringWithFormat:@"test"];
return [[NSData alloc] initWithBase64EncodedString:originalString options:kNilOptions];
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,41 @@
#import "FIAPReceiptManager.h"
#import <Flutter/Flutter.h>

@interface FIAPReceiptManager ()

- (NSData *)getReceiptData:(NSURL *)url error:(NSError **)error;

@end

@implementation FIAPReceiptManager

- (NSString *)retrieveReceiptWithError:(FlutterError **)error {
- (NSString *)retrieveReceiptWithError:(FlutterError **)flutterError {
NSURL *receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
NSData *receipt = [self getReceiptData:receiptURL];
NSError *receiptError;
NSData *receipt = [self getReceiptData:receiptURL error:&receiptError];
if (receiptError) {
if (flutterError) {
*flutterError = [FlutterError
errorWithCode:[[NSString alloc] initWithFormat:@"%li", (long)receiptError.code]
message:receiptError.domain
details:receiptError.userInfo];
}
return nil;
}
if (!receipt) {
*error = [FlutterError errorWithCode:@"storekit_no_receipt"
message:@"Cannot find receipt for the current main bundle."
details:nil];
if (flutterError) {
*flutterError = [FlutterError errorWithCode:@"0"
message:@"dataWithContentsOfURL returned nil without an "
@"error in retrieveReceiptWithError"
details:nil];
}
return nil;
}
return [receipt base64EncodedStringWithOptions:kNilOptions];
}

- (NSData *)getReceiptData:(NSURL *)url {
return [NSData dataWithContentsOfURL:url];
- (NSData *)getReceiptData:(NSURL *)url error:(NSError **)error {
return [NSData dataWithContentsOfURL:url options:NSDataReadingMappedIfSafe error:error];
}

@end
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,17 @@ class InAppPurchaseIosPlatformAddition extends InAppPurchasePlatformAddition {
/// If no results, a `null` value is returned.
Future<PurchaseVerificationData?> refreshPurchaseVerificationData() async {
await SKRequestMaker().startRefreshReceiptRequest();
final String? receipt = await SKReceiptManager.retrieveReceiptData();
if (receipt == null) {
try {
String receipt = await SKReceiptManager.retrieveReceiptData();
return PurchaseVerificationData(
localVerificationData: receipt,
serverVerificationData: receipt,
source: kIAPSource);
} catch (e) {
print(
'Something is wrong while fetching the receipt, this normally happens when the app is '
'running on a simulator: $e');
return null;
}
return PurchaseVerificationData(
localVerificationData: receipt,
serverVerificationData: receipt,
source: kIAPSource);
}
}
2 changes: 1 addition & 1 deletion packages/in_app_purchase/in_app_purchase_ios/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: in_app_purchase_ios
description: An implementation for the iOS platform of the Flutter `in_app_purchase` plugin. This uses the iOS StoreKit Framework.
repository: https://github.com/flutter/plugins/tree/master/packages/in_app_purchase/in_app_purchase_ios
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+in_app_purchase%22
version: 0.1.0+2
version: 0.1.0+3

environment:
sdk: ">=2.12.0 <3.0.0"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ void main() {
tearDown(() {
fakeIOSPlatform.testReturnNull = false;
fakeIOSPlatform.queueIsActive = null;
fakeIOSPlatform.getReceiptFailTest = false;
});

group('sk_request_maker', () {
Expand Down Expand Up @@ -74,6 +75,12 @@ void main() {
expect(fakeIOSPlatform.refreshReceiptParam,
<String, dynamic>{"isExpired": true});
});

test('should get null receipt if any exceptions are raised', () async {
fakeIOSPlatform.getReceiptFailTest = true;
expect(() async => SKReceiptManager.retrieveReceiptData(),
throwsA(TypeMatcher<PlatformException>()));
});
});

group('sk_receipt_manager', () {
Expand Down Expand Up @@ -166,6 +173,9 @@ class FakeIOSPlatform {
bool getProductRequestFailTest = false;
bool testReturnNull = false;

// get receipt request
bool getReceiptFailTest = false;

// refresh receipt request
int refreshReceipt = 0;
late Map<String, dynamic> refreshReceiptParam;
Expand Down Expand Up @@ -201,6 +211,9 @@ class FakeIOSPlatform {
return Future<void>.sync(() {});
// receipt manager
case '-[InAppPurchasePlugin retrieveReceiptData:result:]':
if (getReceiptFailTest) {
throw ("some arbitrary error");
}
return Future<String>.value('receipt data');
// payment queue
case '-[SKPaymentQueue canMakePayments:]':
Expand Down