Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.

[in_app_purchase] Add support for SKPaymentQueueDelegate and showPriceConsentIfNeeded #4085

Merged
merged 14 commits into from
Jun 28, 2021
Merged
Show file tree
Hide file tree
Changes from all 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
8 changes: 6 additions & 2 deletions packages/in_app_purchase/in_app_purchase_ios/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
## 0.1.1

* Added support to register a `SKPaymentQueueDelegateWrapper` and handle changes to active subscriptions accordingly (see also Store Kit's [SKPaymentQueueDelegate](https://developer.apple.com/documentation/storekit/skpaymentqueuedelegate?language=objc)).

## 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
is a listener to the Dart purchaseStream.
* Changed the iOS payment queue handler in such a way that it only adds a listener to the `SKPaymentQueue` when there
is a listener to the Dart `purchaseStream`.

## 0.1.0+1

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,12 @@ flutter_ios_podfile_setup

target 'Runner' do
flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))

target 'RunnerTests' do
inherit! :search_paths

# Matches in_app_purchase test_spec dependency.
pod 'OCMock','3.5'
pod 'OCMock', '~> 3.6'
end
end

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,15 @@
688DE35121F2A5A100EA2684 /* TranslatorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 688DE35021F2A5A100EA2684 /* TranslatorTests.m */; };
6896B34621E9363700D37AEF /* ProductRequestHandlerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 6896B34521E9363700D37AEF /* ProductRequestHandlerTests.m */; };
6896B34C21EEB4B800D37AEF /* Stubs.m in Sources */ = {isa = PBXBuildFile; fileRef = 6896B34B21EEB4B800D37AEF /* Stubs.m */; };
7E34217B7715B1918134647A /* libPods-RunnerTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 18D02AB334F1C07BB9A4374A /* libPods-RunnerTests.a */; };
978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; };
97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; };
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
A5279298219369C600FF69E6 /* StoreKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A5279297219369C600FF69E6 /* StoreKit.framework */; };
A59001A721E69658004A3E5E /* InAppPurchasePluginTests.m in Sources */ = {isa = PBXBuildFile; fileRef = A59001A621E69658004A3E5E /* InAppPurchasePluginTests.m */; };
AB7252348F077C046D6617D3 /* libPods-RunnerTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 630DD71BB3F145A22B1DE15D /* libPods-RunnerTests.a */; };
F67646F82681D9A80048C2EA /* FIAPPaymentQueueDeleteTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F67646F72681D9A80048C2EA /* FIAPPaymentQueueDeleteTests.m */; };
F78AF3142342BC89008449C7 /* PaymentQueueTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F78AF3132342BC89008449C7 /* PaymentQueueTests.m */; };
/* End PBXBuildFile section */

Expand Down Expand Up @@ -48,14 +49,13 @@
/* End PBXCopyFilesBuildPhase section */

/* Begin PBXFileReference section */
027D04BC80EACAAB3B5232B8 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = "<group>"; };
10B860DFD91A1DF639D7BE1D /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = "<group>"; };
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
1630769A874F9381BC761FE1 /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; };
194D4829A79EF6C7426B39F7 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = "<group>"; };
18D02AB334F1C07BB9A4374A /* libPods-RunnerTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-RunnerTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
2550EB3A5A3E749A54ADCA2D /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
630DD71BB3F145A22B1DE15D /* libPods-RunnerTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-RunnerTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
688DE35021F2A5A100EA2684 /* TranslatorTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TranslatorTests.m; sourceTree = "<group>"; };
6896B34521E9363700D37AEF /* ProductRequestHandlerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ProductRequestHandlerTests.m; sourceTree = "<group>"; };
6896B34A21EEB4B800D37AEF /* Stubs.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Stubs.h; sourceTree = "<group>"; };
Expand All @@ -71,11 +71,13 @@
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
9D681E092EB0D20D652F69FC /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = "<group>"; };
A5279297219369C600FF69E6 /* StoreKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = StoreKit.framework; path = System/Library/Frameworks/StoreKit.framework; sourceTree = SDKROOT; };
A59001A421E69658004A3E5E /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
A59001A621E69658004A3E5E /* InAppPurchasePluginTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = InAppPurchasePluginTests.m; sourceTree = "<group>"; };
A59001A821E69658004A3E5E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
E4F9651425A612301059769C /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
F67646F72681D9A80048C2EA /* FIAPPaymentQueueDeleteTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = FIAPPaymentQueueDeleteTests.m; sourceTree = "<group>"; };
F6E5D5F926131C4800C68BED /* Configuration.storekit */ = {isa = PBXFileReference; lastKnownFileType = text; path = Configuration.storekit; sourceTree = "<group>"; };
F78AF3132342BC89008449C7 /* PaymentQueueTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PaymentQueueTests.m; sourceTree = "<group>"; };
/* End PBXFileReference section */
Expand All @@ -94,7 +96,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
AB7252348F077C046D6617D3 /* libPods-RunnerTests.a in Frameworks */,
7E34217B7715B1918134647A /* libPods-RunnerTests.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand All @@ -106,8 +108,8 @@
children = (
E4F9651425A612301059769C /* Pods-Runner.debug.xcconfig */,
2550EB3A5A3E749A54ADCA2D /* Pods-Runner.release.xcconfig */,
194D4829A79EF6C7426B39F7 /* Pods-RunnerTests.debug.xcconfig */,
027D04BC80EACAAB3B5232B8 /* Pods-RunnerTests.release.xcconfig */,
9D681E092EB0D20D652F69FC /* Pods-RunnerTests.debug.xcconfig */,
10B860DFD91A1DF639D7BE1D /* Pods-RunnerTests.release.xcconfig */,
);
path = Pods;
sourceTree = "<group>";
Expand Down Expand Up @@ -187,6 +189,7 @@
6896B34521E9363700D37AEF /* ProductRequestHandlerTests.m */,
F78AF3132342BC89008449C7 /* PaymentQueueTests.m */,
688DE35021F2A5A100EA2684 /* TranslatorTests.m */,
F67646F72681D9A80048C2EA /* FIAPPaymentQueueDeleteTests.m */,
);
path = RunnerTests;
sourceTree = "<group>";
Expand All @@ -196,7 +199,7 @@
children = (
A5279297219369C600FF69E6 /* StoreKit.framework */,
1630769A874F9381BC761FE1 /* libPods-Runner.a */,
630DD71BB3F145A22B1DE15D /* libPods-RunnerTests.a */,
18D02AB334F1C07BB9A4374A /* libPods-RunnerTests.a */,
);
name = Frameworks;
sourceTree = "<group>";
Expand Down Expand Up @@ -229,7 +232,7 @@
isa = PBXNativeTarget;
buildConfigurationList = A59001AD21E69658004A3E5E /* Build configuration list for PBXNativeTarget "RunnerTests" */;
buildPhases = (
321E2F5767F55B0A360AA77E /* [CP] Check Pods Manifest.lock */,
95C7A5986B77A8DF76F6DF3A /* [CP] Check Pods Manifest.lock */,
A59001A021E69658004A3E5E /* Sources */,
A59001A121E69658004A3E5E /* Frameworks */,
A59001A221E69658004A3E5E /* Resources */,
Expand Down Expand Up @@ -310,41 +313,41 @@
/* End PBXResourcesBuildPhase section */

/* Begin PBXShellScriptBuildPhase section */
321E2F5767F55B0A360AA77E /* [CP] Check Pods Manifest.lock */ = {
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
name = "Thin Binary";
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
};
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
95C7A5986B77A8DF76F6DF3A /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
name = "Thin Binary";
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
Expand Down Expand Up @@ -400,6 +403,7 @@
buildActionMask = 2147483647;
files = (
F78AF3142342BC89008449C7 /* PaymentQueueTests.m in Sources */,
F67646F82681D9A80048C2EA /* FIAPPaymentQueueDeleteTests.m in Sources */,
6896B34621E9363700D37AEF /* ProductRequestHandlerTests.m in Sources */,
688DE35121F2A5A100EA2684 /* TranslatorTests.m in Sources */,
A59001A721E69658004A3E5E /* InAppPurchasePluginTests.m in Sources */,
Expand Down Expand Up @@ -593,7 +597,7 @@
};
A59001AB21E69658004A3E5E /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 194D4829A79EF6C7426B39F7 /* Pods-RunnerTests.debug.xcconfig */;
baseConfigurationReference = 9D681E092EB0D20D652F69FC /* Pods-RunnerTests.debug.xcconfig */;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
Expand All @@ -616,7 +620,7 @@
};
A59001AC21E69658004A3E5E /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 027D04BC80EACAAB3B5232B8 /* Pods-RunnerTests.release.xcconfig */;
baseConfigurationReference = 10B860DFD91A1DF639D7BE1D /* Pods-RunnerTests.release.xcconfig */;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
{
"identifier" : "6073E9A3",
"nonRenewingSubscriptions" : [

],
"products" : [
{
"displayPrice" : "0.99",
Expand Down Expand Up @@ -46,7 +50,7 @@
"adHocOffers" : [

],
"displayPrice" : "3.99",
"displayPrice" : "4.99",
"familyShareable" : false,
"groupNumber" : 1,
"internalID" : "922EB597",
Expand All @@ -59,7 +63,7 @@
}
],
"productID" : "subscription_silver",
"recurringSubscriptionPeriod" : "P1M",
"recurringSubscriptionPeriod" : "P1W",
"referenceName" : "subscription_silver",
"subscriptionGroupID" : "D0FEE8D8",
"type" : "RecurringSubscription"
Expand Down Expand Up @@ -91,6 +95,6 @@
],
"version" : {
"major" : 1,
"minor" : 0
"minor" : 1
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#import <OCMock/OCMock.h>
#import <XCTest/XCTest.h>
#import "FIAObjectTranslator.h"
#import "FIAPaymentQueueHandler.h"
#import "Stubs.h"

@import in_app_purchase_ios;

API_AVAILABLE(ios(13.0))
@interface FIAPPaymentQueueDelegateTests : XCTestCase

@property(strong, nonatomic) FlutterMethodChannel *channel;
@property(strong, nonatomic) SKPaymentTransaction *transaction;
@property(strong, nonatomic) SKStorefront *storefront;

@end

@implementation FIAPPaymentQueueDelegateTests

- (void)setUp {
self.channel = OCMClassMock(FlutterMethodChannel.class);

NSDictionary *transactionMap = @{
@"transactionIdentifier" : [NSNull null],
@"transactionState" : @(SKPaymentTransactionStatePurchasing),
@"payment" : [NSNull null],
@"error" : [FIAObjectTranslator getMapFromNSError:[NSError errorWithDomain:@"test_stub"
code:123
userInfo:@{}]],
@"transactionTimeStamp" : @([NSDate date].timeIntervalSince1970),
@"originalTransaction" : [NSNull null],
};
self.transaction = [[SKPaymentTransactionStub alloc] initWithMap:transactionMap];

NSDictionary *storefrontMap = @{
@"countryCode" : @"USA",
@"identifier" : @"unique_identifier",
};
self.storefront = [[SKStorefrontStub alloc] initWithMap:storefrontMap];
}

- (void)tearDown {
self.channel = nil;
}

- (void)testShouldContinueTransaction {
if (@available(iOS 13.0, *)) {
FIAPPaymentQueueDelegate *delegate =
[[FIAPPaymentQueueDelegate alloc] initWithMethodChannel:self.channel];

OCMStub([self.channel
invokeMethod:@"shouldContinueTransaction"
arguments:[FIAObjectTranslator getMapFromSKStorefront:self.storefront
andSKPaymentTransaction:self.transaction]
result:([OCMArg invokeBlockWithArgs:[NSNumber numberWithBool:NO], nil])]);

BOOL shouldContinue = [delegate paymentQueue:OCMClassMock(SKPaymentQueue.class)
shouldContinueTransaction:self.transaction
inStorefront:self.storefront];

XCTAssertFalse(shouldContinue);
}
}

- (void)testShouldContinueTransaction_should_default_to_yes {
if (@available(iOS 13.0, *)) {
FIAPPaymentQueueDelegate *delegate =
[[FIAPPaymentQueueDelegate alloc] initWithMethodChannel:self.channel];

OCMStub([self.channel invokeMethod:@"shouldContinueTransaction"
arguments:[FIAObjectTranslator getMapFromSKStorefront:self.storefront
andSKPaymentTransaction:self.transaction]
result:[OCMArg any]]);

BOOL shouldContinue = [delegate paymentQueue:OCMClassMock(SKPaymentQueue.class)
shouldContinueTransaction:self.transaction
inStorefront:self.storefront];

XCTAssertTrue(shouldContinue);
}
}

- (void)testShouldShowPriceConsentIfNeeded {
if (@available(iOS 13.4, *)) {
FIAPPaymentQueueDelegate *delegate =
[[FIAPPaymentQueueDelegate alloc] initWithMethodChannel:self.channel];

OCMStub([self.channel
invokeMethod:@"shouldShowPriceConsent"
arguments:nil
result:([OCMArg invokeBlockWithArgs:[NSNumber numberWithBool:NO], nil])]);

BOOL shouldShow =
[delegate paymentQueueShouldShowPriceConsent:OCMClassMock(SKPaymentQueue.class)];

XCTAssertFalse(shouldShow);
}
}

- (void)testShouldShowPriceConsentIfNeeded_should_default_to_yes {
if (@available(iOS 13.4, *)) {
FIAPPaymentQueueDelegate *delegate =
[[FIAPPaymentQueueDelegate alloc] initWithMethodChannel:self.channel];

OCMStub([self.channel invokeMethod:@"shouldShowPriceConsent"
arguments:nil
result:[OCMArg any]]);

BOOL shouldShow =
[delegate paymentQueueShouldShowPriceConsent:OCMClassMock(SKPaymentQueue.class)];

XCTAssertTrue(shouldShow);
}
}

@end
Loading