From a3b1b813fb6360018dbdb5cef63c82386ea4cbec Mon Sep 17 00:00:00 2001 From: Yazeed AlKhalaf Date: Wed, 3 May 2023 16:12:48 +0300 Subject: [PATCH 01/19] add the success/failure handling from dart for apple pay --- pay/example/ios/Podfile.lock | 6 +++--- pay/example/ios/Runner/Info.plist | 2 ++ pay/example/lib/main.dart | 8 +++++++- pay/lib/src/pay.dart | 6 ++++++ pay_ios/ios/Classes/PayPlugin.swift | 5 +++++ pay_ios/ios/Classes/PaymentHandler.swift | 15 ++++++++++++++- pay_platform_interface/lib/pay_channel.dart | 6 ++++++ .../lib/pay_platform_interface.dart | 3 +++ 8 files changed, 46 insertions(+), 5 deletions(-) diff --git a/pay/example/ios/Podfile.lock b/pay/example/ios/Podfile.lock index 33c5b2fa..ee56e1ad 100644 --- a/pay/example/ios/Podfile.lock +++ b/pay/example/ios/Podfile.lock @@ -19,10 +19,10 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/pay_ios/ios" SPEC CHECKSUMS: - Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a + Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 integration_test: a1e7d09bd98eca2fc37aefd79d4f41ad37bdbbe5 pay_ios: 8c7beb9c61d885f3f51b61f75f8793023fc8843a -PODFILE CHECKSUM: fc81e398f362bae88bdf55239bd5cf842faad39f +PODFILE CHECKSUM: ef19549a9bc3046e7bb7d2fab4d021637c0c58a3 -COCOAPODS: 1.11.3 +COCOAPODS: 1.12.1 diff --git a/pay/example/ios/Runner/Info.plist b/pay/example/ios/Runner/Info.plist index 0c4df0b5..b77ef2a9 100644 --- a/pay/example/ios/Runner/Info.plist +++ b/pay/example/ios/Runner/Info.plist @@ -43,5 +43,7 @@ CADisableMinimumFrameDurationOnPhone + UIApplicationSupportsIndirectInputEvents + diff --git a/pay/example/lib/main.dart b/pay/example/lib/main.dart index 62aef699..372f379b 100644 --- a/pay/example/lib/main.dart +++ b/pay/example/lib/main.dart @@ -72,6 +72,11 @@ class _PaySampleAppState extends State { void onApplePayResult(paymentResult) { debugPrint(paymentResult.toString()); + Pay({ + PayProvider.apple_pay: PaymentConfiguration.fromJsonString( + payment_configurations.defaultApplePay, + ), + }).updatePaymentStatus(true); } @override @@ -142,7 +147,8 @@ class _PaySampleAppState extends State { // Example pay button configured using a string ApplePayButton( paymentConfiguration: PaymentConfiguration.fromJsonString( - payment_configurations.defaultApplePay), + payment_configurations.defaultApplePay, + ), paymentItems: _paymentItems, style: ApplePayButtonStyle.black, type: ApplePayButtonType.buy, diff --git a/pay/lib/src/pay.dart b/pay/lib/src/pay.dart index 703cb373..a32b1e9e 100644 --- a/pay/lib/src/pay.dart +++ b/pay/lib/src/pay.dart @@ -76,6 +76,12 @@ class Pay { _configurations![provider]!, paymentItems); } + /// Update the payment status with the native platform. + Future updatePaymentStatus(bool isSuccess) async { + await _assetInitializationFuture; + return _payPlatform.updatePaymentStatus(isSuccess); + } + /// Verifies that the selected provider has been previously configured or /// throws otherwise. Future throwIfProviderIsNotDefined(PayProvider provider) async { diff --git a/pay_ios/ios/Classes/PayPlugin.swift b/pay_ios/ios/Classes/PayPlugin.swift index 0f6bb0fa..be4425c4 100644 --- a/pay_ios/ios/Classes/PayPlugin.swift +++ b/pay_ios/ios/Classes/PayPlugin.swift @@ -23,6 +23,7 @@ public class PayPlugin: NSObject, FlutterPlugin { private static let methodChannelName = "plugins.flutter.io/pay_channel" private let methodUserCanPay = "userCanPay" private let methodShowPaymentSelector = "showPaymentSelector" + private let methodUpdatePaymentStatus = "updatePaymentStatus" private let paymentHandler = PaymentHandler() @@ -48,6 +49,10 @@ public class PayPlugin: NSObject, FlutterPlugin { paymentConfiguration: arguments["payment_profile"] as! String, paymentItems: arguments["payment_items"] as! [[String: Any?]]) + case methodUpdatePaymentStatus: + let isSuccess = call.arguments as! Bool + paymentHandler.updatePaymentStatus(isSuccess: isSuccess) + default: result(FlutterMethodNotImplemented) } diff --git a/pay_ios/ios/Classes/PaymentHandler.swift b/pay_ios/ios/Classes/PaymentHandler.swift index 748aa9c0..ad094401 100644 --- a/pay_ios/ios/Classes/PaymentHandler.swift +++ b/pay_ios/ios/Classes/PaymentHandler.swift @@ -36,6 +36,9 @@ enum PaymentHandlerStatus { /// paymentHandler.canMakePayments(stringArguments) /// ``` class PaymentHandler: NSObject { + + /// Holds the completion handler so it can be updated later on from the Flutter side. + var completionHandler: PaymentCompletionHandler? /// Holds the current status of the payment process. var paymentHandlerStatus: PaymentHandlerStatus! @@ -169,6 +172,12 @@ class PaymentHandler: NSObject { return paymentRequest } + + func updatePaymentStatus(isSuccess: Bool) { + // Call completion handler with the given success status + completionHandler?(isSuccess) + completionHandler = nil + } } /// Extension that implements the completion methods in the delegate to respond to user selection. @@ -179,6 +188,11 @@ extension PaymentHandler: PKPaymentAuthorizationControllerDelegate { } func paymentAuthorizationController(_: PKPaymentAuthorizationController, didAuthorizePayment payment: PKPayment, handler completion: @escaping (PKPaymentAuthorizationResult) -> Void) { + // Store completion handler + completionHandler = { isSuccess in + let status: PKPaymentAuthorizationStatus = isSuccess ? .success : .failure + completion(PKPaymentAuthorizationResult(status: status, errors: nil)) + } // Collect payment result or error and return if no payment was selected guard let paymentResultData = try? JSONSerialization.data(withJSONObject: payment.toDictionary()) else { @@ -190,7 +204,6 @@ extension PaymentHandler: PKPaymentAuthorizationControllerDelegate { self.paymentResult(String(decoding: paymentResultData, as: UTF8.self)) paymentHandlerStatus = .authorized - completion(PKPaymentAuthorizationResult(status: PKPaymentAuthorizationStatus.success, errors: nil)) } func paymentAuthorizationControllerDidFinish(_ controller: PKPaymentAuthorizationController) { diff --git a/pay_platform_interface/lib/pay_channel.dart b/pay_platform_interface/lib/pay_channel.dart index f6ce03ad..cd2d05f1 100644 --- a/pay_platform_interface/lib/pay_channel.dart +++ b/pay_platform_interface/lib/pay_channel.dart @@ -65,4 +65,10 @@ class PayMethodChannel extends PayPlatform { return jsonDecode(paymentResult); } + + /// Update the payment status with the native platform. + @override + Future updatePaymentStatus(bool isSuccess) async { + return _channel.invokeMethod('updatePaymentStatus', isSuccess); + } } diff --git a/pay_platform_interface/lib/pay_platform_interface.dart b/pay_platform_interface/lib/pay_platform_interface.dart index f048ce9a..dff1e1d1 100644 --- a/pay_platform_interface/lib/pay_platform_interface.dart +++ b/pay_platform_interface/lib/pay_platform_interface.dart @@ -23,4 +23,7 @@ abstract class PayPlatform { Future> showPaymentSelector( PaymentConfiguration paymentConfiguration, List paymentItems); + + /// Update the payment status received from the backend server. + Future updatePaymentStatus(bool isSuccess); } From 78dfe7b2ce17c9727b060fd3310c3a59ba266d8e Mon Sep 17 00:00:00 2001 From: Yazeed AlKhalaf Date: Thu, 15 Jun 2023 15:32:08 +0300 Subject: [PATCH 02/19] enhance the example app Pay usage --- pay/example/lib/main.dart | 47 ++++++++++++++++++++++----------------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/pay/example/lib/main.dart b/pay/example/lib/main.dart index 372f379b..f83bc0e1 100644 --- a/pay/example/lib/main.dart +++ b/pay/example/lib/main.dart @@ -57,26 +57,30 @@ class PaySampleApp extends StatefulWidget { } class _PaySampleAppState extends State { + final _applePayConfig = PaymentConfiguration.fromJsonString( + payment_configurations.defaultApplePay, + ); late final Future _googlePayConfigFuture; + late Pay _pay; @override void initState() { super.initState(); - _googlePayConfigFuture = - PaymentConfiguration.fromAsset('default_google_pay_config.json'); + _googlePayConfigFuture = PaymentConfiguration.fromAsset( + 'default_google_pay_config.json', + ); + _pay = Pay({ + PayProvider.apple_pay: _applePayConfig, + }); } void onGooglePayResult(paymentResult) { debugPrint(paymentResult.toString()); } - void onApplePayResult(paymentResult) { + void onApplePayResult(paymentResult) async { debugPrint(paymentResult.toString()); - Pay({ - PayProvider.apple_pay: PaymentConfiguration.fromJsonString( - payment_configurations.defaultApplePay, - ), - }).updatePaymentStatus(true); + await _pay.updatePaymentStatus(true); } @override @@ -131,19 +135,20 @@ class _PaySampleAppState extends State { ), // Example pay button configured using an asset FutureBuilder( - future: _googlePayConfigFuture, - builder: (context, snapshot) => snapshot.hasData - ? GooglePayButton( - paymentConfiguration: snapshot.data!, - paymentItems: _paymentItems, - type: GooglePayButtonType.buy, - margin: const EdgeInsets.only(top: 15.0), - onPaymentResult: onGooglePayResult, - loadingIndicator: const Center( - child: CircularProgressIndicator(), - ), - ) - : const SizedBox.shrink()), + future: _googlePayConfigFuture, + builder: (context, snapshot) => snapshot.hasData + ? GooglePayButton( + paymentConfiguration: snapshot.data!, + paymentItems: _paymentItems, + type: GooglePayButtonType.buy, + margin: const EdgeInsets.only(top: 15.0), + onPaymentResult: onGooglePayResult, + loadingIndicator: const Center( + child: CircularProgressIndicator(), + ), + ) + : const SizedBox.shrink(), + ), // Example pay button configured using a string ApplePayButton( paymentConfiguration: PaymentConfiguration.fromJsonString( From fcab16306a5a8f5078e2d0464491303f92bc468f Mon Sep 17 00:00:00 2001 From: Yazeed AlKhalaf Date: Thu, 3 Aug 2023 01:09:50 +0300 Subject: [PATCH 03/19] move impl. of updatePaymentStatus to pay_ios and test it --- pay/example/ios/Podfile.lock | 2 +- pay/lib/src/pay.dart | 17 ++++++-- pay_ios/lib/pay_ios.dart | 2 + pay_ios/lib/src/ios_pay_channel.dart | 9 ++++ pay_ios/test/src/ios_pay_channel_test.dart | 43 +++++++++++++++++++ pay_platform_interface/lib/pay_channel.dart | 12 ++---- .../lib/pay_platform_interface.dart | 3 -- .../test/pay_channel_test.dart | 24 ++++++----- 8 files changed, 84 insertions(+), 28 deletions(-) create mode 100644 pay_ios/lib/src/ios_pay_channel.dart create mode 100644 pay_ios/test/src/ios_pay_channel_test.dart diff --git a/pay/example/ios/Podfile.lock b/pay/example/ios/Podfile.lock index ee56e1ad..00537ccf 100644 --- a/pay/example/ios/Podfile.lock +++ b/pay/example/ios/Podfile.lock @@ -20,7 +20,7 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 - integration_test: a1e7d09bd98eca2fc37aefd79d4f41ad37bdbbe5 + integration_test: 13825b8a9334a850581300559b8839134b124670 pay_ios: 8c7beb9c61d885f3f51b61f75f8793023fc8843a PODFILE CHECKSUM: ef19549a9bc3046e7bb7d2fab4d021637c0c58a3 diff --git a/pay/lib/src/pay.dart b/pay/lib/src/pay.dart index a32b1e9e..688eb3bc 100644 --- a/pay/lib/src/pay.dart +++ b/pay/lib/src/pay.dart @@ -31,14 +31,19 @@ class Pay { /// Creates an instance with a dictionary of [_configurations] and /// instantiates the [_payPlatform] to communicate with the native platforms. - Pay(this._configurations) : _payPlatform = PayMethodChannel(); + Pay(this._configurations) + : _payPlatform = defaultTargetPlatform == TargetPlatform.iOS + ? IOSPayMethodChannel() + : PayMethodChannel(); /// Alternative constructor to create a [Pay] object with a list of /// configurations in [String] format. @Deprecated( 'Prefer to use [Pay({ [PayProvider]: [PaymentConfiguration] })]. Take a look at the readme to see examples') Pay.withAssets(List configAssets) - : _payPlatform = PayMethodChannel() { + : _payPlatform = defaultTargetPlatform == TargetPlatform.iOS + ? IOSPayMethodChannel() + : PayMethodChannel() { _assetInitializationFuture = _loadConfigAssets(configAssets); } @@ -77,9 +82,13 @@ class Pay { } /// Update the payment status with the native platform. + /// Works only on iOS. Future updatePaymentStatus(bool isSuccess) async { - await _assetInitializationFuture; - return _payPlatform.updatePaymentStatus(isSuccess); + if (_payPlatform is IOSPayMethodChannel) { + await _assetInitializationFuture; + final iosPayPlatform = _payPlatform as IOSPayMethodChannel; + return iosPayPlatform.updatePaymentStatus(isSuccess); + } } /// Verifies that the selected provider has been previously configured or diff --git a/pay_ios/lib/pay_ios.dart b/pay_ios/lib/pay_ios.dart index 665148dd..474269eb 100644 --- a/pay_ios/lib/pay_ios.dart +++ b/pay_ios/lib/pay_ios.dart @@ -5,4 +5,6 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +export 'src/ios_pay_channel.dart'; + part 'src/widgets/apple_pay_button.dart'; diff --git a/pay_ios/lib/src/ios_pay_channel.dart b/pay_ios/lib/src/ios_pay_channel.dart new file mode 100644 index 00000000..adec179d --- /dev/null +++ b/pay_ios/lib/src/ios_pay_channel.dart @@ -0,0 +1,9 @@ +import 'package:pay_platform_interface/pay_channel.dart'; + +/// This implements the iOS specific functionality of the Pay plugin. +class IOSPayMethodChannel extends PayMethodChannel { + /// Update the payment status with the native platform. + Future updatePaymentStatus(bool isSuccess) async { + return channel.invokeMethod('updatePaymentStatus', isSuccess); + } +} diff --git a/pay_ios/test/src/ios_pay_channel_test.dart b/pay_ios/test/src/ios_pay_channel_test.dart new file mode 100644 index 00000000..78cabcfb --- /dev/null +++ b/pay_ios/test/src/ios_pay_channel_test.dart @@ -0,0 +1,43 @@ +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:pay_ios/pay_ios.dart'; + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + late final IOSPayMethodChannel _payChannel; + + setUpAll(() async { + _payChannel = IOSPayMethodChannel(); + }); + + group('Verify channel I/O for', () { + final log = []; + const testResponses = { + 'updatePaymentStatus': null, + }; + + setUp(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler( + _payChannel.channel, + (MethodCall methodCall) async { + log.add(methodCall); + final response = testResponses[methodCall.method]; + if (response is Exception) { + return Future.error(response); + } + return Future.value(response); + }, + ); + }); + + test('updatePaymentStatus', () async { + await _payChannel.updatePaymentStatus(true); + expect( + log, + [isMethodCall('updatePaymentStatus', arguments: true)], + ); + }); + }); +} diff --git a/pay_platform_interface/lib/pay_channel.dart b/pay_platform_interface/lib/pay_channel.dart index cd2d05f1..7ef6fb18 100644 --- a/pay_platform_interface/lib/pay_channel.dart +++ b/pay_platform_interface/lib/pay_channel.dart @@ -34,7 +34,7 @@ import 'pay_platform_interface.dart'; /// ``` class PayMethodChannel extends PayPlatform { // The channel used to send messages down the native pipe. - final MethodChannel _channel = + final MethodChannel channel = const MethodChannel('plugins.flutter.io/pay_channel'); /// Determines whether a user can pay with the provider in the configuration. @@ -43,7 +43,7 @@ class PayMethodChannel extends PayPlatform { /// returns a boolean for the [paymentConfiguration] specified. @override Future userCanPay(PaymentConfiguration paymentConfiguration) async { - return await _channel.invokeMethod( + return await channel.invokeMethod( 'userCanPay', jsonEncode(await paymentConfiguration.parameterMap())); } @@ -58,17 +58,11 @@ class PayMethodChannel extends PayPlatform { PaymentConfiguration paymentConfiguration, List paymentItems, ) async { - final paymentResult = await _channel.invokeMethod('showPaymentSelector', { + final paymentResult = await channel.invokeMethod('showPaymentSelector', { 'payment_profile': jsonEncode(await paymentConfiguration.parameterMap()), 'payment_items': paymentItems.map((item) => item.toMap()).toList(), }); return jsonDecode(paymentResult); } - - /// Update the payment status with the native platform. - @override - Future updatePaymentStatus(bool isSuccess) async { - return _channel.invokeMethod('updatePaymentStatus', isSuccess); - } } diff --git a/pay_platform_interface/lib/pay_platform_interface.dart b/pay_platform_interface/lib/pay_platform_interface.dart index dff1e1d1..f048ce9a 100644 --- a/pay_platform_interface/lib/pay_platform_interface.dart +++ b/pay_platform_interface/lib/pay_platform_interface.dart @@ -23,7 +23,4 @@ abstract class PayPlatform { Future> showPaymentSelector( PaymentConfiguration paymentConfiguration, List paymentItems); - - /// Update the payment status received from the backend server. - Future updatePaymentStatus(bool isSuccess); } diff --git a/pay_platform_interface/test/pay_channel_test.dart b/pay_platform_interface/test/pay_channel_test.dart index 8dc6d8f7..094019ce 100644 --- a/pay_platform_interface/test/pay_channel_test.dart +++ b/pay_platform_interface/test/pay_channel_test.dart @@ -17,14 +17,12 @@ import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:pay_platform_interface/core/payment_configuration.dart'; - import 'package:pay_platform_interface/pay_channel.dart'; void main() { TestWidgetsFlutterBinding.ensureInitialized(); late final PayMethodChannel _mobilePlatform; - const channel = MethodChannel('plugins.flutter.io/pay_channel'); const _providerApplePay = PayProvider.apple_pay; final _payConfigString = @@ -43,14 +41,18 @@ void main() { }; setUp(() { - channel.setMockMethodCallHandler((MethodCall methodCall) async { - log.add(methodCall); - final response = testResponses[methodCall.method]; - if (response is Exception) { - return Future.error(response); - } - return Future.value(response); - }); + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler( + _mobilePlatform.channel, + (MethodCall methodCall) async { + log.add(methodCall); + final response = testResponses[methodCall.method]; + if (response is Exception) { + return Future.error(response); + } + return Future.value(response); + }, + ); }); test('userCanPay', () async { @@ -75,7 +77,7 @@ void main() { }); tearDown(() async { - channel.setMockMethodCallHandler(null); + _mobilePlatform.channel.setMockMethodCallHandler(null); log.clear(); }); }); From 0d8c189266b2d58881e319f5294551f543ab66b1 Mon Sep 17 00:00:00 2001 From: Yazeed AlKhalaf Date: Thu, 3 Aug 2023 01:20:09 +0300 Subject: [PATCH 04/19] update the example app to showcase the new feature --- pay/example/lib/main.dart | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/pay/example/lib/main.dart b/pay/example/lib/main.dart index f83bc0e1..79c83913 100644 --- a/pay/example/lib/main.dart +++ b/pay/example/lib/main.dart @@ -12,6 +12,7 @@ /// See the License for the specific language governing permissions and /// limitations under the License. +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:pay/pay.dart'; @@ -44,6 +45,8 @@ class PayMaterialApp extends StatelessWidget { const Locale('es', ''), const Locale('de', ''), ], + theme: ThemeData.light(), + darkTheme: ThemeData.dark(), home: PaySampleApp(), ); } @@ -63,6 +66,8 @@ class _PaySampleAppState extends State { late final Future _googlePayConfigFuture; late Pay _pay; + bool shouldPaymentSucceedOnIos = true; + @override void initState() { super.initState(); @@ -80,7 +85,7 @@ class _PaySampleAppState extends State { void onApplePayResult(paymentResult) async { debugPrint(paymentResult.toString()); - await _pay.updatePaymentStatus(true); + await _pay.updatePaymentStatus(shouldPaymentSucceedOnIos); } @override @@ -163,6 +168,25 @@ class _PaySampleAppState extends State { child: CircularProgressIndicator(), ), ), + // This allows you to control whether the payment shall succeed or not. + // This is only available on iOS. + // Usually you would want to wait for your backend to return a value, + // before you update the payment status. + if (defaultTargetPlatform == TargetPlatform.iOS) + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text('Should the payment succeed?'), + Switch.adaptive( + value: shouldPaymentSucceedOnIos, + onChanged: (value) { + setState(() { + shouldPaymentSucceedOnIos = value; + }); + }, + ), + ], + ), const SizedBox(height: 15) ], ), From 23b63f8d1c3cb927175c02611dc5337e9bf07da9 Mon Sep 17 00:00:00 2001 From: Yazeed AlKhalaf Date: Thu, 3 Aug 2023 01:30:49 +0300 Subject: [PATCH 05/19] fix deprecated thing in a test --- pay_platform_interface/test/pay_channel_test.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pay_platform_interface/test/pay_channel_test.dart b/pay_platform_interface/test/pay_channel_test.dart index 094019ce..2812d266 100644 --- a/pay_platform_interface/test/pay_channel_test.dart +++ b/pay_platform_interface/test/pay_channel_test.dart @@ -77,7 +77,8 @@ void main() { }); tearDown(() async { - _mobilePlatform.channel.setMockMethodCallHandler(null); + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(_mobilePlatform.channel, null); log.clear(); }); }); From cddc0992e4411f9950c9b840d968460a1e738369 Mon Sep 17 00:00:00 2001 From: Yazeed AlKhalaf Date: Thu, 3 Aug 2023 01:35:22 +0300 Subject: [PATCH 06/19] try to match the styling of the other fellow files --- pay_ios/lib/src/ios_pay_channel.dart | 3 +++ pay_ios/test/src/ios_pay_channel_test.dart | 14 ++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/pay_ios/lib/src/ios_pay_channel.dart b/pay_ios/lib/src/ios_pay_channel.dart index adec179d..77714467 100644 --- a/pay_ios/lib/src/ios_pay_channel.dart +++ b/pay_ios/lib/src/ios_pay_channel.dart @@ -1,3 +1,6 @@ +/// Copyright 2023 Google LLC. +/// SPDX-License-Identifier: Apache-2.0 + import 'package:pay_platform_interface/pay_channel.dart'; /// This implements the iOS specific functionality of the Pay plugin. diff --git a/pay_ios/test/src/ios_pay_channel_test.dart b/pay_ios/test/src/ios_pay_channel_test.dart index 78cabcfb..51564482 100644 --- a/pay_ios/test/src/ios_pay_channel_test.dart +++ b/pay_ios/test/src/ios_pay_channel_test.dart @@ -1,3 +1,17 @@ +/// Copyright 2023 Google LLC +/// +/// Licensed under the Apache License, Version 2.0 (the "License"); +/// you may not use this file except in compliance with the License. +/// You may obtain a copy of the License at +/// +/// https://www.apache.org/licenses/LICENSE-2.0 +/// +/// Unless required by applicable law or agreed to in writing, software +/// distributed under the License is distributed on an "AS IS" BASIS, +/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +/// See the License for the specific language governing permissions and +/// limitations under the License. + import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:pay_ios/pay_ios.dart'; From 1323120c5b00806b1b3707583ead2c6d9c60d1e5 Mon Sep 17 00:00:00 2001 From: Yazeed AlKhalaf Date: Wed, 20 Dec 2023 19:40:04 +0300 Subject: [PATCH 07/19] remove dark theme from demo app --- pay/example/lib/main.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/pay/example/lib/main.dart b/pay/example/lib/main.dart index 79c83913..882bbd67 100644 --- a/pay/example/lib/main.dart +++ b/pay/example/lib/main.dart @@ -46,7 +46,6 @@ class PayMaterialApp extends StatelessWidget { const Locale('de', ''), ], theme: ThemeData.light(), - darkTheme: ThemeData.dark(), home: PaySampleApp(), ); } From 4a48cc85a5d2ffbe39e44f9bae07db6ddaadf472 Mon Sep 17 00:00:00 2001 From: Yazeed AlKhalaf Date: Wed, 20 Dec 2023 19:40:28 +0300 Subject: [PATCH 08/19] rename IOS to Ios in IOSPayMethodChannel --- pay/lib/src/pay.dart | 8 ++++---- pay_ios/lib/src/ios_pay_channel.dart | 2 +- pay_ios/test/src/ios_pay_channel_test.dart | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/pay/lib/src/pay.dart b/pay/lib/src/pay.dart index 688eb3bc..f11e3ec5 100644 --- a/pay/lib/src/pay.dart +++ b/pay/lib/src/pay.dart @@ -33,7 +33,7 @@ class Pay { /// instantiates the [_payPlatform] to communicate with the native platforms. Pay(this._configurations) : _payPlatform = defaultTargetPlatform == TargetPlatform.iOS - ? IOSPayMethodChannel() + ? IosPayMethodChannel() : PayMethodChannel(); /// Alternative constructor to create a [Pay] object with a list of @@ -42,7 +42,7 @@ class Pay { 'Prefer to use [Pay({ [PayProvider]: [PaymentConfiguration] })]. Take a look at the readme to see examples') Pay.withAssets(List configAssets) : _payPlatform = defaultTargetPlatform == TargetPlatform.iOS - ? IOSPayMethodChannel() + ? IosPayMethodChannel() : PayMethodChannel() { _assetInitializationFuture = _loadConfigAssets(configAssets); } @@ -84,9 +84,9 @@ class Pay { /// Update the payment status with the native platform. /// Works only on iOS. Future updatePaymentStatus(bool isSuccess) async { - if (_payPlatform is IOSPayMethodChannel) { + if (_payPlatform is IosPayMethodChannel) { await _assetInitializationFuture; - final iosPayPlatform = _payPlatform as IOSPayMethodChannel; + final iosPayPlatform = _payPlatform as IosPayMethodChannel; return iosPayPlatform.updatePaymentStatus(isSuccess); } } diff --git a/pay_ios/lib/src/ios_pay_channel.dart b/pay_ios/lib/src/ios_pay_channel.dart index 77714467..a0a307c8 100644 --- a/pay_ios/lib/src/ios_pay_channel.dart +++ b/pay_ios/lib/src/ios_pay_channel.dart @@ -4,7 +4,7 @@ import 'package:pay_platform_interface/pay_channel.dart'; /// This implements the iOS specific functionality of the Pay plugin. -class IOSPayMethodChannel extends PayMethodChannel { +class IosPayMethodChannel extends PayMethodChannel { /// Update the payment status with the native platform. Future updatePaymentStatus(bool isSuccess) async { return channel.invokeMethod('updatePaymentStatus', isSuccess); diff --git a/pay_ios/test/src/ios_pay_channel_test.dart b/pay_ios/test/src/ios_pay_channel_test.dart index 51564482..0363a01d 100644 --- a/pay_ios/test/src/ios_pay_channel_test.dart +++ b/pay_ios/test/src/ios_pay_channel_test.dart @@ -19,10 +19,10 @@ import 'package:pay_ios/pay_ios.dart'; void main() { TestWidgetsFlutterBinding.ensureInitialized(); - late final IOSPayMethodChannel _payChannel; + late final IosPayMethodChannel _payChannel; setUpAll(() async { - _payChannel = IOSPayMethodChannel(); + _payChannel = IosPayMethodChannel(); }); group('Verify channel I/O for', () { From a06e1ec1cba30e90363b574894520caa92bb748c Mon Sep 17 00:00:00 2001 From: Yazeed AlKhalaf Date: Wed, 20 Dec 2023 19:45:14 +0300 Subject: [PATCH 09/19] rename update payment status to update payment result --- pay/example/lib/main.dart | 2 +- pay/lib/src/pay.dart | 6 +++--- pay_ios/ios/Classes/PayPlugin.swift | 6 +++--- pay_ios/ios/Classes/PaymentHandler.swift | 2 +- pay_ios/lib/src/ios_pay_channel.dart | 6 +++--- pay_ios/test/src/ios_pay_channel_test.dart | 8 ++++---- 6 files changed, 15 insertions(+), 15 deletions(-) diff --git a/pay/example/lib/main.dart b/pay/example/lib/main.dart index 882bbd67..a8bc09c2 100644 --- a/pay/example/lib/main.dart +++ b/pay/example/lib/main.dart @@ -84,7 +84,7 @@ class _PaySampleAppState extends State { void onApplePayResult(paymentResult) async { debugPrint(paymentResult.toString()); - await _pay.updatePaymentStatus(shouldPaymentSucceedOnIos); + await _pay.updatePaymentResult(shouldPaymentSucceedOnIos); } @override diff --git a/pay/lib/src/pay.dart b/pay/lib/src/pay.dart index f11e3ec5..f8d3836f 100644 --- a/pay/lib/src/pay.dart +++ b/pay/lib/src/pay.dart @@ -81,13 +81,13 @@ class Pay { _configurations![provider]!, paymentItems); } - /// Update the payment status with the native platform. + /// Update the payment result with the native platform. /// Works only on iOS. - Future updatePaymentStatus(bool isSuccess) async { + Future updatePaymentResult(bool isSuccess) async { if (_payPlatform is IosPayMethodChannel) { await _assetInitializationFuture; final iosPayPlatform = _payPlatform as IosPayMethodChannel; - return iosPayPlatform.updatePaymentStatus(isSuccess); + return iosPayPlatform.updatePaymentResult(isSuccess); } } diff --git a/pay_ios/ios/Classes/PayPlugin.swift b/pay_ios/ios/Classes/PayPlugin.swift index be4425c4..2a9a2b91 100644 --- a/pay_ios/ios/Classes/PayPlugin.swift +++ b/pay_ios/ios/Classes/PayPlugin.swift @@ -23,7 +23,7 @@ public class PayPlugin: NSObject, FlutterPlugin { private static let methodChannelName = "plugins.flutter.io/pay_channel" private let methodUserCanPay = "userCanPay" private let methodShowPaymentSelector = "showPaymentSelector" - private let methodUpdatePaymentStatus = "updatePaymentStatus" + private let methodUpdatePaymentResult = "updatePaymentResult" private let paymentHandler = PaymentHandler() @@ -49,9 +49,9 @@ public class PayPlugin: NSObject, FlutterPlugin { paymentConfiguration: arguments["payment_profile"] as! String, paymentItems: arguments["payment_items"] as! [[String: Any?]]) - case methodUpdatePaymentStatus: + case methodUpdatePaymentResult: let isSuccess = call.arguments as! Bool - paymentHandler.updatePaymentStatus(isSuccess: isSuccess) + paymentHandler.updatePaymentResult(isSuccess: isSuccess) default: result(FlutterMethodNotImplemented) diff --git a/pay_ios/ios/Classes/PaymentHandler.swift b/pay_ios/ios/Classes/PaymentHandler.swift index ad094401..f3fc4d65 100644 --- a/pay_ios/ios/Classes/PaymentHandler.swift +++ b/pay_ios/ios/Classes/PaymentHandler.swift @@ -173,7 +173,7 @@ class PaymentHandler: NSObject { return paymentRequest } - func updatePaymentStatus(isSuccess: Bool) { + func updatePaymentResult(isSuccess: Bool) { // Call completion handler with the given success status completionHandler?(isSuccess) completionHandler = nil diff --git a/pay_ios/lib/src/ios_pay_channel.dart b/pay_ios/lib/src/ios_pay_channel.dart index a0a307c8..854bfadb 100644 --- a/pay_ios/lib/src/ios_pay_channel.dart +++ b/pay_ios/lib/src/ios_pay_channel.dart @@ -5,8 +5,8 @@ import 'package:pay_platform_interface/pay_channel.dart'; /// This implements the iOS specific functionality of the Pay plugin. class IosPayMethodChannel extends PayMethodChannel { - /// Update the payment status with the native platform. - Future updatePaymentStatus(bool isSuccess) async { - return channel.invokeMethod('updatePaymentStatus', isSuccess); + /// Update the payment result with the native platform. + Future updatePaymentResult(bool isSuccess) async { + return channel.invokeMethod('updatePaymentResult', isSuccess); } } diff --git a/pay_ios/test/src/ios_pay_channel_test.dart b/pay_ios/test/src/ios_pay_channel_test.dart index 0363a01d..477fd363 100644 --- a/pay_ios/test/src/ios_pay_channel_test.dart +++ b/pay_ios/test/src/ios_pay_channel_test.dart @@ -28,7 +28,7 @@ void main() { group('Verify channel I/O for', () { final log = []; const testResponses = { - 'updatePaymentStatus': null, + 'updatePaymentResult': null, }; setUp(() { @@ -46,11 +46,11 @@ void main() { ); }); - test('updatePaymentStatus', () async { - await _payChannel.updatePaymentStatus(true); + test('updatePaymentResult', () async { + await _payChannel.updatePaymentResult(true); expect( log, - [isMethodCall('updatePaymentStatus', arguments: true)], + [isMethodCall('updatePaymentResult', arguments: true)], ); }); }); From cd07e7cade0c87c3a8377c479d214c1c37a2ed36 Mon Sep 17 00:00:00 2001 From: Yazeed AlKhalaf Date: Wed, 20 Dec 2023 19:51:56 +0300 Subject: [PATCH 10/19] add docs for updatePaymentResult --- pay_ios/ios/Classes/PaymentHandler.swift | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/pay_ios/ios/Classes/PaymentHandler.swift b/pay_ios/ios/Classes/PaymentHandler.swift index f3fc4d65..978a1370 100644 --- a/pay_ios/ios/Classes/PaymentHandler.swift +++ b/pay_ios/ios/Classes/PaymentHandler.swift @@ -109,7 +109,7 @@ class PaymentHandler: NSObject { /// /// - parameter paymentConfigurationString: A JSON string with the configuration to execute /// this payment. - /// - returns: A list of recognized networks supported for this operation. + /// - returns: A list of recognized networks supported for this operation. private static func supportedNetworks(from paymentConfigurationString: String) -> [PKPaymentNetwork]? { guard let paymentConfiguration = extractPaymentConfiguration(from: paymentConfigurationString) else { return nil @@ -173,8 +173,12 @@ class PaymentHandler: NSObject { return paymentRequest } + /// Updates the payment result based on the value you pass to the `isSuccess` parameter. + /// + /// - parameter isSuccess: A boolean that determines whether the payment was successful or not. + /// - returns: nothing. func updatePaymentResult(isSuccess: Bool) { - // Call completion handler with the given success status + // Call completion handler with the given success status. completionHandler?(isSuccess) completionHandler = nil } From 87268b8444d42be49e21f97eafe6dcc69e9490fa Mon Sep 17 00:00:00 2001 From: Yazeed AlKhalaf Date: Wed, 20 Dec 2023 19:56:42 +0300 Subject: [PATCH 11/19] ran the project using xcode 15 --- pay/example/ios/Runner.xcodeproj/project.pbxproj | 7 +++++-- .../xcshareddata/xcschemes/Runner.xcscheme | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/pay/example/ios/Runner.xcodeproj/project.pbxproj b/pay/example/ios/Runner.xcodeproj/project.pbxproj index 20ee7217..c4ea2b2a 100644 --- a/pay/example/ios/Runner.xcodeproj/project.pbxproj +++ b/pay/example/ios/Runner.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 51; + objectVersion = 54; objects = { /* Begin PBXBuildFile section */ @@ -170,7 +170,7 @@ 97C146E61CF9000F007C117D /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 1300; + LastUpgradeCheck = 1430; ORGANIZATIONNAME = ""; TargetAttributes = { 97C146ED1CF9000F007C117D = { @@ -237,10 +237,12 @@ }; 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); inputPaths = ( + "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", ); name = "Thin Binary"; outputPaths = ( @@ -268,6 +270,7 @@ }; 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); diff --git a/pay/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/pay/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index c87d15a3..a6b826db 100644 --- a/pay/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/pay/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ Date: Wed, 20 Dec 2023 20:01:39 +0300 Subject: [PATCH 12/19] make the demo app show a regular integration path --- pay/example/lib/main.dart | 29 ++++++----------------------- 1 file changed, 6 insertions(+), 23 deletions(-) diff --git a/pay/example/lib/main.dart b/pay/example/lib/main.dart index a8bc09c2..f7555f8f 100644 --- a/pay/example/lib/main.dart +++ b/pay/example/lib/main.dart @@ -12,7 +12,6 @@ /// See the License for the specific language governing permissions and /// limitations under the License. -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:pay/pay.dart'; @@ -65,8 +64,6 @@ class _PaySampleAppState extends State { late final Future _googlePayConfigFuture; late Pay _pay; - bool shouldPaymentSucceedOnIos = true; - @override void initState() { super.initState(); @@ -84,7 +81,12 @@ class _PaySampleAppState extends State { void onApplePayResult(paymentResult) async { debugPrint(paymentResult.toString()); - await _pay.updatePaymentResult(shouldPaymentSucceedOnIos); + + // This is where the payment result is fetched from the backend, and the + // payment result is updated accordingly. + bool isPaymentSuccessfull = true; + + await _pay.updatePaymentResult(isPaymentSuccessfull); } @override @@ -167,25 +169,6 @@ class _PaySampleAppState extends State { child: CircularProgressIndicator(), ), ), - // This allows you to control whether the payment shall succeed or not. - // This is only available on iOS. - // Usually you would want to wait for your backend to return a value, - // before you update the payment status. - if (defaultTargetPlatform == TargetPlatform.iOS) - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text('Should the payment succeed?'), - Switch.adaptive( - value: shouldPaymentSucceedOnIos, - onChanged: (value) { - setState(() { - shouldPaymentSucceedOnIos = value; - }); - }, - ), - ], - ), const SizedBox(height: 15) ], ), From 8d236bbe1bb635497e53401f26d60e7025b3bc9c Mon Sep 17 00:00:00 2001 From: Yazeed AlKhalaf Date: Sat, 1 Jun 2024 19:55:49 +0300 Subject: [PATCH 13/19] move the defaultBinaryMessenger for test calling to a variable to increase readability --- pay_platform_interface/test/pay_channel_test.dart | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/pay_platform_interface/test/pay_channel_test.dart b/pay_platform_interface/test/pay_channel_test.dart index 2812d266..f1252608 100644 --- a/pay_platform_interface/test/pay_channel_test.dart +++ b/pay_platform_interface/test/pay_channel_test.dart @@ -24,6 +24,8 @@ void main() { late final PayMethodChannel _mobilePlatform; + final _defaultBinaryMessenger = + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger; const _providerApplePay = PayProvider.apple_pay; final _payConfigString = '{"provider": "${_providerApplePay.toSimpleString()}", "data": {}}'; @@ -41,8 +43,7 @@ void main() { }; setUp(() { - TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger - .setMockMethodCallHandler( + _defaultBinaryMessenger.setMockMethodCallHandler( _mobilePlatform.channel, (MethodCall methodCall) async { log.add(methodCall); @@ -77,8 +78,10 @@ void main() { }); tearDown(() async { - TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger - .setMockMethodCallHandler(_mobilePlatform.channel, null); + _defaultBinaryMessenger.setMockMethodCallHandler( + _mobilePlatform.channel, + null, + ); log.clear(); }); }); From 122d7bac653dca3bf4f86288fa3001b5bc953f19 Mon Sep 17 00:00:00 2001 From: Yazeed AlKhalaf Date: Sat, 1 Jun 2024 20:06:19 +0300 Subject: [PATCH 14/19] move completion handler after serialization is successful --- pay_ios/ios/Classes/PaymentHandler.swift | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pay_ios/ios/Classes/PaymentHandler.swift b/pay_ios/ios/Classes/PaymentHandler.swift index 978a1370..e1268fcc 100644 --- a/pay_ios/ios/Classes/PaymentHandler.swift +++ b/pay_ios/ios/Classes/PaymentHandler.swift @@ -192,12 +192,6 @@ extension PaymentHandler: PKPaymentAuthorizationControllerDelegate { } func paymentAuthorizationController(_: PKPaymentAuthorizationController, didAuthorizePayment payment: PKPayment, handler completion: @escaping (PKPaymentAuthorizationResult) -> Void) { - // Store completion handler - completionHandler = { isSuccess in - let status: PKPaymentAuthorizationStatus = isSuccess ? .success : .failure - completion(PKPaymentAuthorizationResult(status: status, errors: nil)) - } - // Collect payment result or error and return if no payment was selected guard let paymentResultData = try? JSONSerialization.data(withJSONObject: payment.toDictionary()) else { self.paymentResult(FlutterError(code: "paymentResultDeserializationFailed", message: nil, details: nil)) @@ -207,6 +201,12 @@ extension PaymentHandler: PKPaymentAuthorizationControllerDelegate { // Return the result back to the channel self.paymentResult(String(decoding: paymentResultData, as: UTF8.self)) + // Store completion handler + completionHandler = { isSuccess in + let status: PKPaymentAuthorizationStatus = isSuccess ? .success : .failure + completion(PKPaymentAuthorizationResult(status: status, errors: nil)) + } + paymentHandlerStatus = .authorized } From b09fd8caf37a379890327593fa717172ecf19e9c Mon Sep 17 00:00:00 2001 From: Yazeed AlKhalaf Date: Sat, 1 Jun 2024 20:09:36 +0300 Subject: [PATCH 15/19] enhance tests and their readability --- pay_ios/test/src/ios_pay_channel_test.dart | 14 ++++++++++++-- pay_platform_interface/test/pay_channel_test.dart | 1 + 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/pay_ios/test/src/ios_pay_channel_test.dart b/pay_ios/test/src/ios_pay_channel_test.dart index 477fd363..1e2b5ac2 100644 --- a/pay_ios/test/src/ios_pay_channel_test.dart +++ b/pay_ios/test/src/ios_pay_channel_test.dart @@ -21,6 +21,9 @@ void main() { late final IosPayMethodChannel _payChannel; + final _defaultBinaryMessenger = + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger; + setUpAll(() async { _payChannel = IosPayMethodChannel(); }); @@ -32,8 +35,7 @@ void main() { }; setUp(() { - TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger - .setMockMethodCallHandler( + _defaultBinaryMessenger.setMockMethodCallHandler( _payChannel.channel, (MethodCall methodCall) async { log.add(methodCall); @@ -53,5 +55,13 @@ void main() { [isMethodCall('updatePaymentResult', arguments: true)], ); }); + + tearDown(() async { + _defaultBinaryMessenger.setMockMethodCallHandler( + _payChannel.channel, + null, + ); + log.clear(); + }); }); } diff --git a/pay_platform_interface/test/pay_channel_test.dart b/pay_platform_interface/test/pay_channel_test.dart index f1252608..c1d38b8c 100644 --- a/pay_platform_interface/test/pay_channel_test.dart +++ b/pay_platform_interface/test/pay_channel_test.dart @@ -26,6 +26,7 @@ void main() { final _defaultBinaryMessenger = TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger; + const _providerApplePay = PayProvider.apple_pay; final _payConfigString = '{"provider": "${_providerApplePay.toSimpleString()}", "data": {}}'; From 73fa61c267e1bd61cfe91c629a5a1b386a854894 Mon Sep 17 00:00:00 2001 From: Yazeed AlKhalaf Date: Sat, 1 Jun 2024 20:21:11 +0300 Subject: [PATCH 16/19] make updatePaymentResult throw an exception instead of failing silently --- pay/lib/src/pay.dart | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/pay/lib/src/pay.dart b/pay/lib/src/pay.dart index f8d3836f..cbc0d4a5 100644 --- a/pay/lib/src/pay.dart +++ b/pay/lib/src/pay.dart @@ -88,6 +88,10 @@ class Pay { await _assetInitializationFuture; final iosPayPlatform = _payPlatform as IosPayMethodChannel; return iosPayPlatform.updatePaymentResult(isSuccess); + } else { + throw MethodNotForCurrentPlatformException( + 'The method "updatePaymentResult" can only be called when the "defaultTargetPlatform" is iOS.', + ); } } @@ -113,3 +117,15 @@ class ProviderNotConfiguredException implements Exception { @override String toString() => 'ProviderNotConfiguredException: $message'; } + +/// Thrown to indicate that the method called is not available for the current +/// platform is has been called from. +class MethodNotForCurrentPlatformException implements Exception { + MethodNotForCurrentPlatformException(this.message); + + /// A human-readable error message, possibly null. + final String? message; + + @override + String toString() => 'MethodNotForCurrentPlatformException: $message'; +} From b39bdddc143c74740bf42cec1780a4c3e084432e Mon Sep 17 00:00:00 2001 From: Yazeed AlKhalaf Date: Sat, 1 Jun 2024 20:34:43 +0300 Subject: [PATCH 17/19] fix the linter complaining --- pay/example/lib/main.dart | 8 ++++---- pay_ios/lib/src/ios_pay_channel.dart | 1 + pay_ios/test/src/ios_pay_channel_test.dart | 17 +++++++++-------- pay_platform_interface/lib/pay_channel.dart | 6 +++--- 4 files changed, 17 insertions(+), 15 deletions(-) diff --git a/pay/example/lib/main.dart b/pay/example/lib/main.dart index 90be35e9..00fce05b 100644 --- a/pay/example/lib/main.dart +++ b/pay/example/lib/main.dart @@ -35,19 +35,19 @@ class PayMaterialApp extends StatelessWidget { @override Widget build(BuildContext context) { - return const MaterialApp( + return MaterialApp( title: 'Pay for Flutter Demo', - localizationsDelegates: [ + localizationsDelegates: const [ ...GlobalMaterialLocalizations.delegates, GlobalWidgetsLocalizations.delegate, ], - supportedLocales: [ + supportedLocales: const [ Locale('en', ''), Locale('es', ''), Locale('de', ''), ], theme: ThemeData.light(), - home: PaySampleApp(), + home: const PaySampleApp(), ); } } diff --git a/pay_ios/lib/src/ios_pay_channel.dart b/pay_ios/lib/src/ios_pay_channel.dart index 854bfadb..320e9be9 100644 --- a/pay_ios/lib/src/ios_pay_channel.dart +++ b/pay_ios/lib/src/ios_pay_channel.dart @@ -1,5 +1,6 @@ /// Copyright 2023 Google LLC. /// SPDX-License-Identifier: Apache-2.0 +library; import 'package:pay_platform_interface/pay_channel.dart'; diff --git a/pay_ios/test/src/ios_pay_channel_test.dart b/pay_ios/test/src/ios_pay_channel_test.dart index 1e2b5ac2..f0d57257 100644 --- a/pay_ios/test/src/ios_pay_channel_test.dart +++ b/pay_ios/test/src/ios_pay_channel_test.dart @@ -11,6 +11,7 @@ /// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. /// See the License for the specific language governing permissions and /// limitations under the License. +library; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -19,13 +20,13 @@ import 'package:pay_ios/pay_ios.dart'; void main() { TestWidgetsFlutterBinding.ensureInitialized(); - late final IosPayMethodChannel _payChannel; + late final IosPayMethodChannel payChannel; - final _defaultBinaryMessenger = + final defaultBinaryMessenger = TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger; setUpAll(() async { - _payChannel = IosPayMethodChannel(); + payChannel = IosPayMethodChannel(); }); group('Verify channel I/O for', () { @@ -35,8 +36,8 @@ void main() { }; setUp(() { - _defaultBinaryMessenger.setMockMethodCallHandler( - _payChannel.channel, + defaultBinaryMessenger.setMockMethodCallHandler( + payChannel.channel, (MethodCall methodCall) async { log.add(methodCall); final response = testResponses[methodCall.method]; @@ -49,7 +50,7 @@ void main() { }); test('updatePaymentResult', () async { - await _payChannel.updatePaymentResult(true); + await payChannel.updatePaymentResult(true); expect( log, [isMethodCall('updatePaymentResult', arguments: true)], @@ -57,8 +58,8 @@ void main() { }); tearDown(() async { - _defaultBinaryMessenger.setMockMethodCallHandler( - _payChannel.channel, + defaultBinaryMessenger.setMockMethodCallHandler( + payChannel.channel, null, ); log.clear(); diff --git a/pay_platform_interface/lib/pay_channel.dart b/pay_platform_interface/lib/pay_channel.dart index 0a15a217..93d00b84 100644 --- a/pay_platform_interface/lib/pay_channel.dart +++ b/pay_platform_interface/lib/pay_channel.dart @@ -34,7 +34,7 @@ import 'pay_platform_interface.dart'; /// ``` class PayMethodChannel extends PayPlatform { // The channel used to send messages down the native pipe. - final MethodChannel _channel = + final MethodChannel channel = const MethodChannel('plugins.flutter.io/pay_channel'); /// Determines whether a user can pay with the provider in the configuration. @@ -43,7 +43,7 @@ class PayMethodChannel extends PayPlatform { /// returns a boolean for the [paymentConfiguration] specified. @override Future userCanPay(PaymentConfiguration paymentConfiguration) async { - return await _channel.invokeMethod( + return await channel.invokeMethod( 'userCanPay', jsonEncode(await paymentConfiguration.parameterMap())) as bool; } @@ -59,7 +59,7 @@ class PayMethodChannel extends PayPlatform { PaymentConfiguration paymentConfiguration, List paymentItems, ) async { - final paymentResult = await _channel.invokeMethod('showPaymentSelector', { + final paymentResult = await channel.invokeMethod('showPaymentSelector', { 'payment_profile': jsonEncode(await paymentConfiguration.parameterMap()), 'payment_items': paymentItems.map((item) => item.toMap()).toList(), }) as String; From 15fb1aa582c2f84a4e30a9436c1bfa965fa89409 Mon Sep 17 00:00:00 2001 From: Yazeed AlKhalaf Date: Sat, 1 Jun 2024 20:57:03 +0300 Subject: [PATCH 18/19] just ran the app with flutter 3.22.1 --- pay/example/ios/Flutter/AppFrameworkInfo.plist | 2 +- pay/example/ios/Podfile | 2 +- pay/example/ios/Podfile.lock | 8 ++++---- pay/example/ios/Runner.xcodeproj/project.pbxproj | 2 +- .../xcshareddata/xcschemes/Runner.xcscheme | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/pay/example/ios/Flutter/AppFrameworkInfo.plist b/pay/example/ios/Flutter/AppFrameworkInfo.plist index 4f8d4d24..8c6e5614 100644 --- a/pay/example/ios/Flutter/AppFrameworkInfo.plist +++ b/pay/example/ios/Flutter/AppFrameworkInfo.plist @@ -21,6 +21,6 @@ CFBundleVersion 1.0 MinimumOSVersion - 11.0 + 12.0 diff --git a/pay/example/ios/Podfile b/pay/example/ios/Podfile index 88359b22..279576f3 100644 --- a/pay/example/ios/Podfile +++ b/pay/example/ios/Podfile @@ -1,5 +1,5 @@ # Uncomment this line to define a global platform for your project -# platform :ios, '11.0' +# platform :ios, '12.0' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' diff --git a/pay/example/ios/Podfile.lock b/pay/example/ios/Podfile.lock index 00537ccf..5e87ce2e 100644 --- a/pay/example/ios/Podfile.lock +++ b/pay/example/ios/Podfile.lock @@ -19,10 +19,10 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/pay_ios/ios" SPEC CHECKSUMS: - Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 - integration_test: 13825b8a9334a850581300559b8839134b124670 + Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 + integration_test: ce0a3ffa1de96d1a89ca0ac26fca7ea18a749ef4 pay_ios: 8c7beb9c61d885f3f51b61f75f8793023fc8843a -PODFILE CHECKSUM: ef19549a9bc3046e7bb7d2fab4d021637c0c58a3 +PODFILE CHECKSUM: c4c93c5f6502fe2754f48404d3594bf779584011 -COCOAPODS: 1.12.1 +COCOAPODS: 1.15.2 diff --git a/pay/example/ios/Runner.xcodeproj/project.pbxproj b/pay/example/ios/Runner.xcodeproj/project.pbxproj index c4ea2b2a..b1d1dbe0 100644 --- a/pay/example/ios/Runner.xcodeproj/project.pbxproj +++ b/pay/example/ios/Runner.xcodeproj/project.pbxproj @@ -170,7 +170,7 @@ 97C146E61CF9000F007C117D /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 1430; + LastUpgradeCheck = 1510; ORGANIZATIONNAME = ""; TargetAttributes = { 97C146ED1CF9000F007C117D = { diff --git a/pay/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/pay/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index a6b826db..5e31d3d3 100644 --- a/pay/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/pay/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ Date: Sat, 1 Jun 2024 21:41:01 +0300 Subject: [PATCH 19/19] make the implementation better, thanks to @JlUgia --- pay/example/lib/main.dart | 11 ++--------- pay/lib/src/widgets/apple_pay_button.dart | 20 ++++++++++++++++++-- pay/lib/src/widgets/google_pay_button.dart | 11 +++++++++-- pay/lib/src/widgets/pay_button.dart | 10 ++++++---- 4 files changed, 35 insertions(+), 17 deletions(-) diff --git a/pay/example/lib/main.dart b/pay/example/lib/main.dart index 00fce05b..7b7e8e62 100644 --- a/pay/example/lib/main.dart +++ b/pay/example/lib/main.dart @@ -60,11 +60,7 @@ class PaySampleApp extends StatefulWidget { } class _PaySampleAppState extends State { - final _applePayConfig = PaymentConfiguration.fromJsonString( - payment_configurations.defaultApplePay, - ); late final Future _googlePayConfigFuture; - late Pay _pay; @override void initState() { @@ -72,23 +68,20 @@ class _PaySampleAppState extends State { _googlePayConfigFuture = PaymentConfiguration.fromAsset( 'default_google_pay_config.json', ); - _pay = Pay({ - PayProvider.apple_pay: _applePayConfig, - }); } void onGooglePayResult(paymentResult) { debugPrint(paymentResult.toString()); } - void onApplePayResult(paymentResult) async { + void onApplePayResult(paymentResult, ApplePaymentConfirmation handler) async { debugPrint(paymentResult.toString()); // This is where the payment result is fetched from the backend, and the // payment result is updated accordingly. bool isPaymentSuccessfull = true; - await _pay.updatePaymentResult(isPaymentSuccessfull); + await handler.updatePaymentResult(isPaymentSuccessfull); } @override diff --git a/pay/lib/src/widgets/apple_pay_button.dart b/pay/lib/src/widgets/apple_pay_button.dart index 2016667d..6355e76f 100644 --- a/pay/lib/src/widgets/apple_pay_button.dart +++ b/pay/lib/src/widgets/apple_pay_button.dart @@ -14,6 +14,18 @@ part of '../../pay.dart'; +class ApplePaymentConfirmation { + final Pay _payClient; + ApplePaymentConfirmation(Pay payClient) : _payClient = payClient; + + Future updatePaymentResult(bool completedSuccessfully) async { + await _payClient.updatePaymentResult(completedSuccessfully); + } +} + +typedef ApplePaymentConfirmCallback = Function( + Map result, ApplePaymentConfirmation handler); + /// A widget to show the Apple Pay button according to the rules and constraints /// specified in [PayButton]. /// @@ -38,7 +50,7 @@ class ApplePayButton extends PayButton { super.key, super.buttonProvider = PayProvider.apple_pay, required super.paymentConfiguration, - super.onPaymentResult, + required ApplePaymentConfirmCallback onPaymentResult, required List paymentItems, double? cornerRadius, ApplePayButtonStyle style = ApplePayButtonStyle.black, @@ -51,7 +63,11 @@ class ApplePayButton extends PayButton { super.childOnError, super.loadingIndicator, }) : assert(width >= RawApplePayButton.minimumButtonWidth), - assert(height >= RawApplePayButton.minimumButtonHeight) { + assert(height >= RawApplePayButton.minimumButtonHeight), + super(paymentCallback: (result) { + final pay = Pay({buttonProvider: paymentConfiguration}); + onPaymentResult(result, ApplePaymentConfirmation(pay)); + }) { _applePayButton = RawApplePayButton( style: style, type: type, diff --git a/pay/lib/src/widgets/google_pay_button.dart b/pay/lib/src/widgets/google_pay_button.dart index ec273662..2031390b 100644 --- a/pay/lib/src/widgets/google_pay_button.dart +++ b/pay/lib/src/widgets/google_pay_button.dart @@ -14,6 +14,8 @@ part of '../../pay.dart'; +typedef GooglePaymentCallback = Function(Map result); + /// A widget to show the Google Pay button according to the rules and /// constraints specified in [PayButton]. /// @@ -38,7 +40,7 @@ class GooglePayButton extends PayButton { super.key, super.buttonProvider = PayProvider.google_pay, required final PaymentConfiguration paymentConfiguration, - super.onPaymentResult, + required GooglePaymentCallback onPaymentResult, required List paymentItems, int cornerRadius = RawGooglePayButton.defaultButtonHeight ~/ 2, GooglePayButtonTheme theme = GooglePayButtonTheme.dark, @@ -52,7 +54,12 @@ class GooglePayButton extends PayButton { super.loadingIndicator, }) : assert(width >= RawGooglePayButton.minimumButtonWidth), assert(height >= RawGooglePayButton.defaultButtonHeight), - super(paymentConfiguration: paymentConfiguration) { + super( + paymentConfiguration: paymentConfiguration, + paymentCallback: (result) { + onPaymentResult(result); + }, + ) { _googlePayButton = RawGooglePayButton( paymentConfiguration: paymentConfiguration, cornerRadius: cornerRadius, diff --git a/pay/lib/src/widgets/pay_button.dart b/pay/lib/src/widgets/pay_button.dart index 55337e9c..1b3264ca 100644 --- a/pay/lib/src/widgets/pay_button.dart +++ b/pay/lib/src/widgets/pay_button.dart @@ -14,6 +14,8 @@ part of '../../pay.dart'; +typedef PaymentResultCallback = void Function(Map result); + /// A widget that handles the API logic to facilitate the integration. /// /// This widget provides an alternative UI-based integration path that wraps @@ -27,13 +29,13 @@ part of '../../pay.dart'; /// method which starts the payment process. abstract class PayButton extends StatefulWidget { /// A resident client to issue requests against the APIs. - late final Pay _payClient; + final Pay _payClient; /// Specifies the payment provider supported by the button final PayProvider buttonProvider; /// A function called when the payment process yields a result. - final void Function(Map result)? onPaymentResult; + final PaymentResultCallback? paymentCallback; final double width; final double height; @@ -56,7 +58,7 @@ abstract class PayButton extends StatefulWidget { super.key, required this.buttonProvider, required final PaymentConfiguration paymentConfiguration, - this.onPaymentResult, + this.paymentCallback, this.width = 0, this.height = 0, this.margin = const EdgeInsets.all(0), @@ -77,7 +79,7 @@ abstract class PayButton extends StatefulWidget { try { final result = await _payClient.showPaymentSelector(buttonProvider, paymentItems); - onPaymentResult?.call(result); + paymentCallback?.call(result); } catch (error) { onError?.call(error); }