6177214e-ce7c-49e3-99de-ff9721b26f63 — Commit 3398e071
Changed files
azure/templates/build-ios.yml | 68 +- comwell_key_app/ios/Podfile | 12 +- .../ios/Runner.xcodeproj/project.pbxproj | 2 +- .../services/models/user_permissions.g.dart | 8 +- .../booking_details/bloc/booking_details_bloc.dart | 7 +- .../bloc/booking_details_event.dart | 4 + .../components/check_in_button_timer.dart | 28 +- .../lib/check_out/bloc/check_out_cubit.dart | 2 +- comwell_key_app/lib/check_out/check_out_flow.dart | 4 +- .../check_out/models/payment_configurations.dart | 26 - comwell_key_app/lib/comwell_app.dart | 12 +- comwell_key_app/lib/main.dart | 110 +++- .../lib/my_booking/cubit/my_booking_cubit.dart | 3 +- .../lib/my_booking/my_booking_page.dart | 4 +- .../overview/components/bill_download_modal.dart | 16 +- .../lib/payment/cubit/payment_cubit.dart | 49 -- .../payment/cubit/payment_processing_state.dart | 23 - .../lib/payment/payment_processing_page.dart | 127 ---- .../payment_cards/bloc/payment_cards_cubit.dart | 188 ------ .../payment_cards/bloc/payment_cards_state.dart | 73 --- .../lib/payment_cards/components/add_card.dart | 75 --- .../components/approve_conditions_widget.dart | 51 -- .../payment_cards/components/edit_card_dialog.dart | 201 ------ .../components/remove_card_button.dart | 103 --- .../payment_cards/components/save_card_button.dart | 33 - .../models/add_card_payment_method.dart | 21 - .../lib/payment_cards/payment_cards_page.dart | 180 ------ .../payment_cards/payment_cards_repository.dart | 26 - .../lib/pregistration/components/card_item.dart | 84 --- .../pregistration/cubit/preregistration_cubit.dart | 14 +- .../pregistration/cubit/preregistration_state.dart | 3 +- .../pregistration/pages/prereg_address_page.dart | 2 +- .../pages/prereg_confirmation_page.dart | 59 +- .../pregistration/pregistration_repository.dart | 13 +- .../lib/pregistration/preregistration_flow.dart | 17 +- comwell_key_app/lib/pregistration/utils/utils.dart | 7 +- .../profile/components/comwell_club_container.dart | 11 +- .../lib/profile/cubit/profile_cubit.dart | 14 +- .../lib/profile/cubit/profile_state.dart | 2 +- .../lib/profile_settings/model/user.dart | 4 +- comwell_key_app/lib/routing/app_router.dart | 14 +- .../lib/services/adyen/adyen_amount.dart | 21 - .../lib/services/adyen/payment_event_handler.dart | 80 --- .../lib/services/adyen/payment_method.dart | 39 -- .../lib/services/adyen/stored_payment_method.dart | 37 -- .../adyen/stored_payment_methods_response.dart | 17 - comwell_key_app/lib/services/api.dart | 15 +- .../lib/services/mappers/user_mapper.dart | 4 +- comwell_key_app/lib/services/models/user_dto.dart | 2 +- .../lib/services/models/user_permissions.dart | 16 +- .../lib/services/utils/api_endpoints.dart | 16 - comwell_key_app/lib/utils/locator.dart | 5 +- comwell_key_app/lib/utils/seos_repository.dart | 2 +- comwell_key_app/pubspec.yaml | 10 +- .../android/.gradle/8.1.1/checksums/checksums.lock | Bin 17 -> 17 bytes .../.gradle/8.1.1/checksums/md5-checksums.bin | Bin 31147 -> 35197 bytes .../.gradle/8.1.1/checksums/sha1-checksums.bin | Bin 82319 -> 109964 bytes payment_plugin/.fvmrc | 3 + payment_plugin/.gitignore | 36 ++ payment_plugin/.metadata | 36 ++ payment_plugin/.vscode/settings.json | 3 + payment_plugin/CHANGELOG.md | 3 + payment_plugin/LICENSE | 1 + payment_plugin/README.md | 15 + payment_plugin/SETUP_GUIDE.md | 163 +++++ payment_plugin/analysis_options.yaml | 4 + payment_plugin/android/.gitignore | 9 + payment_plugin/android/build.gradle | 66 ++ payment_plugin/android/settings.gradle | 1 + .../com/example/payment_plugin/PaymentPlugin.kt | 33 + .../example/payment_plugin/PaymentPluginTest.kt | 27 + payment_plugin/assets/images/maestro.svg | 7 + payment_plugin/assets/images/master.svg | 7 + payment_plugin/assets/images/visa copy.svg | 5 + payment_plugin/assets/images/visa.svg | 5 + payment_plugin/build.yaml | 19 + payment_plugin/example/.gitignore | 45 ++ payment_plugin/example/README.md | 16 + payment_plugin/example/analysis_options.yaml | 28 + payment_plugin/example/android/.gitignore | 14 + .../example/android/app/build.gradle.kts | 44 ++ .../android/app/src/debug/AndroidManifest.xml | 7 + .../android/app/src/main/AndroidManifest.xml | 45 ++ .../example/payment_plugin_example/MainActivity.kt | 5 + .../main/res/drawable-v21/launch_background.xml | 12 + .../src/main/res/drawable/launch_background.xml | 12 + .../app/src/main/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 544 bytes .../app/src/main/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 442 bytes .../app/src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 721 bytes .../app/src/main/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 1031 bytes .../src/main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 1443 bytes .../app/src/main/res/values-night/styles.xml | 18 + .../android/app/src/main/res/values/styles.xml | 18 + .../android/app/src/profile/AndroidManifest.xml | 7 + payment_plugin/example/android/build.gradle.kts | 21 + payment_plugin/example/android/gradle.properties | 3 + .../gradle/wrapper/gradle-wrapper.properties | 5 + payment_plugin/example/android/settings.gradle.kts | 25 + .../integration_test/plugin_integration_test.dart | 26 + payment_plugin/example/ios/.gitignore | 34 + .../example/ios/Flutter/AppFrameworkInfo.plist | 26 + payment_plugin/example/ios/Flutter/Debug.xcconfig | 2 + .../example/ios/Flutter/Release.xcconfig | 2 + payment_plugin/example/ios/Podfile | 43 ++ .../example/ios/Runner.xcodeproj/project.pbxproj | 641 +++++++++++++++++++ .../project.xcworkspace/contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/WorkspaceSettings.xcsettings | 8 + .../xcshareddata/xcschemes/Runner.xcscheme | 119 ++++ .../Runner.xcworkspace/contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/WorkspaceSettings.xcsettings | 8 + .../example/ios/Runner/AppDelegate.swift | 13 + .../AppIcon.appiconset/Contents.json | 122 ++++ .../AppIcon.appiconset/Icon-App-1024x1024@1x.png | Bin 0 -> 10932 bytes .../AppIcon.appiconset/Icon-App-20x20@1x.png | Bin 0 -> 295 bytes .../AppIcon.appiconset/Icon-App-20x20@2x.png | Bin 0 -> 406 bytes .../AppIcon.appiconset/Icon-App-20x20@3x.png | Bin 0 -> 450 bytes .../AppIcon.appiconset/Icon-App-29x29@1x.png | Bin 0 -> 282 bytes .../AppIcon.appiconset/Icon-App-29x29@2x.png | Bin 0 -> 462 bytes .../AppIcon.appiconset/Icon-App-29x29@3x.png | Bin 0 -> 704 bytes .../AppIcon.appiconset/Icon-App-40x40@1x.png | Bin 0 -> 406 bytes .../AppIcon.appiconset/Icon-App-40x40@2x.png | Bin 0 -> 586 bytes .../AppIcon.appiconset/Icon-App-40x40@3x.png | Bin 0 -> 862 bytes .../AppIcon.appiconset/Icon-App-60x60@2x.png | Bin 0 -> 862 bytes .../AppIcon.appiconset/Icon-App-60x60@3x.png | Bin 0 -> 1674 bytes .../AppIcon.appiconset/Icon-App-76x76@1x.png | Bin 0 -> 762 bytes .../AppIcon.appiconset/Icon-App-76x76@2x.png | Bin 0 -> 1226 bytes .../AppIcon.appiconset/Icon-App-83.5x83.5@2x.png | Bin 0 -> 1418 bytes .../LaunchImage.imageset/Contents.json | 23 + .../LaunchImage.imageset/LaunchImage.png | Bin 0 -> 68 bytes .../LaunchImage.imageset/LaunchImage@2x.png | Bin 0 -> 68 bytes .../LaunchImage.imageset/LaunchImage@3x.png | Bin 0 -> 68 bytes .../Assets.xcassets/LaunchImage.imageset/README.md | 5 + .../ios/Runner/Base.lproj/LaunchScreen.storyboard | 37 ++ .../example/ios/Runner/Base.lproj/Main.storyboard | 26 + payment_plugin/example/ios/Runner/Info.plist | 49 ++ .../example/ios/Runner/Runner-Bridging-Header.h | 1 + .../example/ios/RunnerTests/RunnerTests.swift | 29 + payment_plugin/example/lib/main.dart | 81 +++ payment_plugin/example/pubspec.lock | 709 +++++++++++++++++++++ payment_plugin/example/pubspec.yaml | 85 +++ payment_plugin/example/test/widget_test.dart | 27 + payment_plugin/example/web/favicon.png | Bin 0 -> 917 bytes payment_plugin/example/web/icons/Icon-192.png | Bin 0 -> 5292 bytes payment_plugin/example/web/icons/Icon-512.png | Bin 0 -> 8252 bytes .../example/web/icons/Icon-maskable-192.png | Bin 0 -> 5594 bytes .../example/web/icons/Icon-maskable-512.png | Bin 0 -> 20998 bytes payment_plugin/example/web/index.html | 38 ++ payment_plugin/example/web/manifest.json | 35 + payment_plugin/ios/.gitignore | 38 ++ payment_plugin/ios/payment_plugin.podspec | 29 + payment_plugin/ios/payment_plugin/Package.swift | 32 + .../Sources/payment_plugin/PaymentPlugin.swift | 19 + .../Sources/payment_plugin/PrivacyInfo.xcprivacy | 14 + .../_generated/data/remote/api/api_client.g.dart | 252 ++++++++ .../data/remote/models/api_response.g.dart | 12 + .../data/remote/models/payment_event_handler.dart | 80 +++ .../models/stored_payment_methods_response.g.dart | 27 + .../domain/models/add_card_payment_method.g.dart | 31 + .../_generated/domain/models/payment_method.g.dart | 38 ++ .../domain/models/stored_payment_method.g.dart | 28 + .../presentation/components/payment_details.g.dart | 33 + payment_plugin/lib/common/constants.dart | 4 + payment_plugin/lib/data/remote/api/api_client.dart | 52 ++ .../lib/data/remote/models/api_response.dart | 13 + .../models/stored_payment_methods_response.dart | 17 + .../lib/domain/models/add_card_payment_method.dart | 41 ++ .../lib/domain/models/payment_configurations.dart | 26 + .../lib/domain/models/payment_method.dart | 39 ++ .../lib/domain/models/stored_payment_method.dart | 37 ++ .../lib/domain/repositories/adyen_repository.dart | 275 ++++++++ payment_plugin/lib/payment_config.dart | 22 + payment_plugin/lib/payment_plugin.dart | 69 ++ payment_plugin/lib/payment_plugin_core.dart | 85 +++ .../presentation/app/bloc/payment_cards_cubit.dart | 181 ++++++ .../presentation/app/bloc/payment_cards_state.dart | 73 +++ .../lib/presentation/app/bloc/payment_cubit.dart | 50 ++ .../app/bloc/payment_processing_state.dart | 23 + .../lib/presentation/components/add_card.dart | 75 +++ .../lib/presentation/components/card_item.dart | 85 +++ .../components/comwell_error_widget.dart | 60 ++ .../components/content_placeholder.dart | 63 ++ .../presentation/components/edit_card_dialog.dart | 201 ++++++ .../components/payment_card_image.dart | 25 + .../components/payment_cards_shimmer_loader.dart | 53 ++ .../presentation/components/payment_details.dart | 54 ++ .../components/remove_card_button.dart | 102 +++ .../presentation/components/save_card_button.dart | 33 + .../presentation/components/title_placeholder.dart | 34 + .../presentation/screens/payment_cards_page.dart | 181 ++++++ .../screens/payment_processing_page.dart | 127 ++++ payment_plugin/lib/scripts/new_model.dart | 143 +++++ payment_plugin/lib/scripts/utils.dart | 15 + payment_plugin/lib/themes/comwell_colors.dart | 49 ++ payment_plugin/lib/themes/dark_theme.dart | 98 +++ payment_plugin/lib/themes/light_theme.dart | 107 ++++ payment_plugin/lib/utils/json.dart | 1 + payment_plugin/lib/utils/lottie_utils.dart | 25 + payment_plugin/pubspec.yaml | 97 +++ .../test/payment_plugin_method_channel_test.dart | 27 + payment_plugin/test/payment_plugin_test.dart | 15 + 202 files changed, 6573 insertions(+), 1688 deletions(-)
Diff
diff --git a/azure/templates/build-ios.yml b/azure/templates/build-ios.yml
index fc2a095a..6fffe256 100644
--- a/azure/templates/build-ios.yml
+++ b/azure/templates/build-ios.yml
@@ -7,10 +7,7 @@ parameters:
- production
- staging
- develop
-- name: exportOptionsPlistPath
- displayName: ExportOptions.plist path
- type: string
- default: comwell_key_app/ios/ExportOptions.plist
+
steps:
- template: download-env.yml
@@ -50,6 +47,31 @@ steps:
flutter config --enable-swift-package-manager
displayName: "Enable Swift Pacakge Manager"
+# Debug: Verify the Develop scheme configuration
+- script: |
+ echo "=== Checking Xcode schemes ==="
+ ls -la $(Build.SourcesDirectory)/comwell_key_app/ios/Runner.xcodeproj/xcshareddata/xcschemes/
+ echo ""
+ echo "=== Develop.xcscheme ArchiveAction ==="
+ cat $(Build.SourcesDirectory)/comwell_key_app/ios/Runner.xcodeproj/xcshareddata/xcschemes/Develop.xcscheme | grep -A2 "ArchiveAction"
+ echo ""
+ echo "=== Checking Release-Develop bundle ID in project.pbxproj ==="
+ grep -A30 "Release-Develop" $(Build.SourcesDirectory)/comwell_key_app/ios/Runner.xcodeproj/project.pbxproj | grep "PRODUCT_BUNDLE_IDENTIFIER"
+ echo ""
+ echo "=== Flutter build flavor variable ==="
+ echo "Flavor: $(flavor)"
+ displayName: 'Debug: Verify Xcode configuration'
+
+# Clean build to avoid cached artifacts from previous builds
+- script: |
+ cd $(Build.SourcesDirectory)/comwell_key_app
+ flutter clean
+ flutter pub get
+ rm -rf ios/build
+ rm -rf ios/.symlinks
+ rm -rf ~/Library/Developer/Xcode/DerivedData/*Runner*
+ displayName: 'Clean build artifacts'
+
# - script: |
# sudo xcode-select -s /Applications/Xcode_16.2.app/Contents/Developer
# displayName: 'Select XCode version'
@@ -58,13 +80,23 @@ steps:
displayName: "Build app"
inputs:
buildNumber: "$(Build.BuildId)"
- target: "ipa"
+ target: "ios"
buildFlavour: $(flavor)
projectDirectory: "$(Build.SourcesDirectory)/comwell_key_app"
- debugMode: false
- profileMode: false
- iosCodesign: true
- extraArgs: '--export-options-plist=$(Build.SourcesDirectory)/comwell_key_app/ios/$(iosExportOptionsPlist)'
+ iosCodesign: false
+ exportOptionsPlist: "$(Build.SourcesDirectory)/comwell_key_app/ios/$(iosExportOptionsPlist)"
+
+- task: Xcode@5
+ displayName: "Archive app"
+ inputs:
+ actions: "archive" # Make sure to use the correct scheme that includes the custom XCFramework
+ configuration: "$(iosConfiguration)"
+ xcWorkspacePath: "$(Build.SourcesDirectory)/comwell_key_app/ios/Runner.xcworkspace"
+ packageApp: true
+ args: "-verbose"
+ signingOption: "manual"
+ signingIdentity: "$(APPLE_CERTIFICATE_SIGNING_IDENTITY)"
+ provisioningProfileUuid: "$(APPLE_PROV_PROFILE_UUID)"
- script: ls -lR comwell_key_app/build/ios/ipa
displayName: 'List IPA output directory'
@@ -80,22 +112,8 @@ steps:
# PathtoPublish: 'build/ios/ipa'
# ArtifactName: 'ios-app'
# publishLocation: 'Container'
-# - task: Xcode@5
-# displayName: "Archive app"
-# inputs:
-# actions: "build"
-# scheme: "$(iosScheme)" # Make sure to use the correct scheme that includes the custom XCFramework
-# configuration: "$(iosConfiguration)"
-# sdk: "iphoneos"
-# xcWorkspacePath: "$(Build.SourcesDirectory)/comwell_key_app/ios/Runner.xcworkspace"
-# xcodeVersion: "16"
-# xcodeDeveloperDir: '/Applications/Xcode_16.2.app/Contents/Developer'
-# exportPath: 'output'
-# packageApp: true
-# args: "-verbose"
-# signingOption: "manual"
-# signingIdentity: "$(APPLE_CERTIFICATE_SIGNING_IDENTITY)"
-# provisioningProfileUuid: "$(APPLE_PROV_PROFILE_UUID)"
+
+
- task: AppStoreRelease@1
diff --git a/comwell_key_app/ios/Podfile b/comwell_key_app/ios/Podfile
index 7e5ec7ed..a9141302 100644
--- a/comwell_key_app/ios/Podfile
+++ b/comwell_key_app/ios/Podfile
@@ -6,13 +6,15 @@ ENV['COCOAPODS_DISABLE_STATS'] = 'true'
project 'Runner', {
'Debug' => :debug,
+ 'Debug-Develop' => :debug,
+ 'Debug-Stage' => :debug,
+ 'Debug-Test' => :debug,
'Profile' => :release,
'Release' => :release,
- 'Develop' => :debug,
- 'Test-Debug' => :debug,
- 'Test-Release' => :release,
- 'Stage' => :release,
- 'Prod' => :release,
+ 'Release-Develop' => :release,
+ 'Release-Stage' => :release,
+ 'Release-Prod' => :release,
+ 'Release-Test' => :release,
}
def flutter_root
diff --git a/comwell_key_app/ios/Runner.xcodeproj/project.pbxproj b/comwell_key_app/ios/Runner.xcodeproj/project.pbxproj
index b9904cbe..a63fd582 100644
--- a/comwell_key_app/ios/Runner.xcodeproj/project.pbxproj
+++ b/comwell_key_app/ios/Runner.xcodeproj/project.pbxproj
@@ -711,7 +711,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/RunnerRelease.entitlements;
- CODE_SIGN_IDENTITY = "Apple Development";
+ CODE_SIGN_IDENTITY = "iPhone Distribution: Comwell AS (8RNV6AX4ZL)";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
DEVELOPMENT_TEAM = "";
diff --git a/comwell_key_app/lib/.generated/services/models/user_permissions.g.dart b/comwell_key_app/lib/.generated/services/models/user_permissions.g.dart
index e8c28615..b54d001c 100644
--- a/comwell_key_app/lib/.generated/services/models/user_permissions.g.dart
+++ b/comwell_key_app/lib/.generated/services/models/user_permissions.g.dart
@@ -7,10 +7,10 @@ part of '../../../services/models/user_permissions.dart';
// **************************************************************************
UserPermissions _$UserPermissionsFromJson(Map json) => UserPermissions(
- guestOptIn1: json['GuestOptIn1'] as bool,
- guestOptIn2: json['GuestOptIn2'] as bool,
- guestOptIn3: json['GuestOptIn3'] as bool,
- guestOptIn4: json['GuestOptIn4'] as bool,
+ guestOptIn1: json['GuestOptIn1'] as bool? ?? false,
+ guestOptIn2: json['GuestOptIn2'] as bool? ?? false,
+ guestOptIn3: json['GuestOptIn3'] as bool? ?? false,
+ guestOptIn4: json['GuestOptIn4'] as bool? ?? false,
);
Map<String, dynamic> _$UserPermissionsToJson(UserPermissions instance) =>
diff --git a/comwell_key_app/lib/booking_details/bloc/booking_details_bloc.dart b/comwell_key_app/lib/booking_details/bloc/booking_details_bloc.dart
index c22f38e7..a6237aa2 100644
--- a/comwell_key_app/lib/booking_details/bloc/booking_details_bloc.dart
+++ b/comwell_key_app/lib/booking_details/bloc/booking_details_bloc.dart
@@ -84,7 +84,7 @@ class BookingDetailsBloc
on<GetUpSalesEvent>((event, emit) async {
try {
- await getUpSales(emit);
+ await getUpSales(emit, fetchRemote: true);
emit(state.main());
} catch (e) {
if (kDebugMode) print("err=$e");
@@ -120,6 +120,11 @@ class BookingDetailsBloc
on<GetUserEvent>((event, emit) async {
await getUser(emit, fetchRemote: event.fetchRemote);
});
+
+ on<BypassTimerEvent>((event, emit) {
+ _timer?.cancel();
+ emit(state.updateRemainingTime(const Duration(seconds: -1)));
+ });
}
Future<void> getUpSales(Emitter<BookingDetailsState> emit,
diff --git a/comwell_key_app/lib/booking_details/bloc/booking_details_event.dart b/comwell_key_app/lib/booking_details/bloc/booking_details_event.dart
index 90a676d4..f557baa4 100644
--- a/comwell_key_app/lib/booking_details/bloc/booking_details_event.dart
+++ b/comwell_key_app/lib/booking_details/bloc/booking_details_event.dart
@@ -82,3 +82,7 @@ final class OrderHouseKeepingEvent extends BookingDetailsEvent {
@override
List<Object> get props => [selectedServices];
}
+
+final class BypassTimerEvent extends BookingDetailsEvent {
+ const BypassTimerEvent();
+}
diff --git a/comwell_key_app/lib/booking_details/components/check_in_button_timer.dart b/comwell_key_app/lib/booking_details/components/check_in_button_timer.dart
index 9b0ada09..a41c961f 100644
--- a/comwell_key_app/lib/booking_details/components/check_in_button_timer.dart
+++ b/comwell_key_app/lib/booking_details/components/check_in_button_timer.dart
@@ -4,6 +4,7 @@ import 'package:comwell_key_app/themes/light_theme.dart';
import 'package:comwell_key_app/utils/time_utils.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/flutter_svg.dart';
@@ -30,13 +31,18 @@ class _CheckInButtonTimerState extends State<CheckInButtonTimer> {
child: child,
);
},
- child: const CheckInButton(key: ValueKey('check_in_button'))); // TODO: state.remainingTime.isNegative
- //? const CheckInButton(key: ValueKey('check_in_button'))
- //: getTimerWidget());
+ child: state.remainingTime.isNegative
+ ? const CheckInButton(key: ValueKey('check_in_button'))
+ : getTimerWidget());
},
);
}
+ bool get _isDevOrStage {
+ final flavor = appFlavor?.toLowerCase();
+ return flavor == 'develop' || flavor == 'stage';
+ }
+
Widget getTimerWidget() {
return BlocBuilder<BookingDetailsBloc, BookingDetailsState>(
key: const ValueKey('timer_view'),
@@ -60,11 +66,11 @@ class _CheckInButtonTimerState extends State<CheckInButtonTimer> {
: "check_in_button_timer_seconds"
.tr(args: [seconds.toString()]);
- return Container(
+ final timerContent = Container(
margin: const EdgeInsets.symmetric(horizontal: 10),
decoration: BoxDecoration(
color: Colors.grey[900]?.withValues(alpha: 0.8),
- borderRadius: BorderRadius.circular(30),
+ borderRadius: BorderRadius.circular(8),
),
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 8),
child: Row(
@@ -118,6 +124,18 @@ class _CheckInButtonTimerState extends State<CheckInButtonTimer> {
],
),
);
+
+ if (_isDevOrStage) {
+ return GestureDetector(
+ onTap: () {
+ // Bypass timer in dev/stage - show check-in button
+ cubit.add(const BypassTimerEvent());
+ },
+ child: timerContent,
+ );
+ }
+
+ return timerContent;
},
);
}
diff --git a/comwell_key_app/lib/check_out/bloc/check_out_cubit.dart b/comwell_key_app/lib/check_out/bloc/check_out_cubit.dart
index 11fc2647..67243db3 100644
--- a/comwell_key_app/lib/check_out/bloc/check_out_cubit.dart
+++ b/comwell_key_app/lib/check_out/bloc/check_out_cubit.dart
@@ -10,12 +10,12 @@ import 'package:comwell_key_app/profile/profile_repository.dart';
import 'package:comwell_key_app/profile/utils/urls.dart';
import 'package:comwell_key_app/routing/app_routes.dart';
import 'package:comwell_key_app/services/models/booking_dto.dart';
-import 'package:comwell_key_app/payment/cubit/payment_cubit.dart';
import 'package:comwell_key_app/tracking/comwell_tracking.dart';
import 'package:comwell_key_app/tracking/models/analytics_event_item.dart';
import 'package:comwell_key_app/utils/locator.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
+import 'package:payment_plugin/presentation/app/bloc/payment_cubit.dart';
import 'package:url_launcher/url_launcher.dart';
class CheckoutCubit extends Cubit<CheckoutState> {
diff --git a/comwell_key_app/lib/check_out/check_out_flow.dart b/comwell_key_app/lib/check_out/check_out_flow.dart
index 04118467..e27afabd 100644
--- a/comwell_key_app/lib/check_out/check_out_flow.dart
+++ b/comwell_key_app/lib/check_out/check_out_flow.dart
@@ -1,14 +1,14 @@
import 'package:comwell_key_app/check_out/bloc/check_out_cubit.dart';
import 'package:comwell_key_app/check_out/components/check_out_bottom_sheet.dart';
import 'package:comwell_key_app/check_out/pages/check_out_processing_page.dart';
-import 'package:comwell_key_app/payment/cubit/payment_cubit.dart';
-import 'package:comwell_key_app/payment/cubit/payment_processing_state.dart';
import 'package:comwell_key_app/check_out/pages/check_out_page.dart';
import 'package:comwell_key_app/common/components/comwell_app_bar.dart';
import 'package:comwell_key_app/routing/app_routes.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:go_router/go_router.dart';
+import 'package:payment_plugin/presentation/app/bloc/payment_cubit.dart';
+import 'package:payment_plugin/presentation/app/bloc/payment_processing_state.dart';
class CheckOutFlow extends StatelessWidget {
const CheckOutFlow({super.key});
diff --git a/comwell_key_app/lib/check_out/models/payment_configurations.dart b/comwell_key_app/lib/check_out/models/payment_configurations.dart
deleted file mode 100644
index feeac7ba..00000000
--- a/comwell_key_app/lib/check_out/models/payment_configurations.dart
+++ /dev/null
@@ -1,26 +0,0 @@
-import 'package:adyen_checkout/adyen_checkout.dart';
-
-class PaymentConfigurations {
- final SessionCheckout sessionCheckout;
- final GooglePayComponentConfiguration googlePayConfiguration;
- final DropInConfiguration dropInConfiguration;
- final CardComponentConfiguration cardComponentConfiguration;
- final ApplePayComponentConfiguration applePayConfiguration;
- final bool isFullyPaidWithPoints;
- final List<Map<String, dynamic>> availablePaymentMethods;
-
- PaymentConfigurations({
- required this.cardComponentConfiguration,
- required this.sessionCheckout,
- required this.googlePayConfiguration,
- required this.dropInConfiguration,
- required this.applePayConfiguration,
- required this.isFullyPaidWithPoints,
- required this.availablePaymentMethods,
- });
-
- @override
- String toString() {
- return 'PaymentConfigurations(sessionCheckout: $sessionCheckout, googlePayConfiguration: $googlePayConfiguration, dropInConfiguration: $dropInConfiguration, cardComponentConfiguration: $cardComponentConfiguration, applePayConfiguration: $applePayConfiguration, isFullyPaidWithPoints: $isFullyPaidWithPoints)';
- }
-}
diff --git a/comwell_key_app/lib/comwell_app.dart b/comwell_key_app/lib/comwell_app.dart
index 331982f6..15c31936 100644
--- a/comwell_key_app/lib/comwell_app.dart
+++ b/comwell_key_app/lib/comwell_app.dart
@@ -4,21 +4,21 @@ import 'package:comwell_key_app/key/bloc/key_bloc.dart';
import 'package:comwell_key_app/key/repository/key_repository.dart';
import 'package:comwell_key_app/overview/cubit/overview_cubit.dart';
import 'package:comwell_key_app/overview/repository/overview_repository.dart';
-import 'package:comwell_key_app/pregistration/pregistration_repository.dart';
import 'package:comwell_key_app/profile/cubit/profile_cubit.dart';
import 'package:comwell_key_app/profile/profile_repository.dart';
import 'package:comwell_key_app/profile_settings/cubit/profile_settings_cubit.dart';
import 'package:comwell_key_app/profile_settings/repostiory/profile_settings_repository.dart';
import 'package:comwell_key_app/routing/app_router.dart';
-import 'package:comwell_key_app/payment/cubit/payment_cubit.dart';
import 'package:comwell_key_app/themes/dark_theme.dart';
import 'package:comwell_key_app/themes/light_theme.dart';
import 'package:comwell_key_app/utils/locator.dart';
import 'package:comwell_key_app/utils/seos_repository.dart';
-
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:payment_plugin/domain/repositories/adyen_repository.dart';
+import 'package:payment_plugin/presentation/app/bloc/payment_cards_cubit.dart';
+import 'package:payment_plugin/presentation/app/bloc/payment_cubit.dart';
class ComwellApp extends StatelessWidget {
const ComwellApp({super.key});
@@ -70,5 +70,9 @@ final List<BlocProvider> blocProviderList = [
BlocProvider<PaymentCubit>(
lazy: false,
create: (context) => PaymentCubit(
- preregistrationRepository: locator<PreregistrationRepository>()))
+ adyenRepository: locator<AdyenRepository>())),
+ BlocProvider<PaymentCardsCubit>(
+ lazy: false,
+ create: (context) => PaymentCardsCubit(
+ adyenRepository: locator<AdyenRepository>()))
];
diff --git a/comwell_key_app/lib/main.dart b/comwell_key_app/lib/main.dart
index e4a93574..19439487 100644
--- a/comwell_key_app/lib/main.dart
+++ b/comwell_key_app/lib/main.dart
@@ -1,33 +1,33 @@
-import 'dart:io';
import 'package:comwell_key_app/authentication/authentication_repository.dart';
import 'package:comwell_key_app/utils/firebase.dart';
import 'package:comwell_key_app/utils/locator.dart';
+import 'package:comwell_key_app/services/http_client.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:sentry_flutter/sentry_flutter.dart';
-import 'package:sqlcipher_flutter_libs/sqlcipher_flutter_libs.dart';
import 'comwell_app.dart';
import 'firebase_options_dev.dart' as fb_dev;
import 'firebase_options_stage.dart' as fb_stage;
import 'firebase_options_prod.dart' as fb_prod;
+import 'package:payment_plugin/payment_plugin.dart';
void main() async {
final flavor = appFlavor?.toLowerCase();
if (flavor == 'develop') {
- runMainApp(fb_dev.DefaultFirebaseOptions.currentPlatform, '.env.dev');
+ await runMainApp(fb_dev.DefaultFirebaseOptions.currentPlatform, '.env.dev');
} else if (flavor == 'stage') {
- runMainApp(fb_stage.DefaultFirebaseOptions.currentPlatform, '.env.stage');
+ await runMainApp(fb_stage.DefaultFirebaseOptions.currentPlatform, '.env.stage');
} else if (flavor == 'prod') {
- runMainApp(fb_prod.DefaultFirebaseOptions.currentPlatform, '.env.prod');
+ await runMainApp(fb_prod.DefaultFirebaseOptions.currentPlatform, '.env.prod');
} else {
throw UnsupportedError('Invalid appFlavor: $appFlavor');
}
}
-void runMainApp(FirebaseOptions firebaseOptions, String envFile) async {
+Future<void> runMainApp(FirebaseOptions firebaseOptions, String envFile) async {
WidgetsFlutterBinding.ensureInitialized();
@@ -45,24 +45,55 @@ void runMainApp(FirebaseOptions firebaseOptions, String envFile) async {
rethrow;
}
- await configureFirebase();
- SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
- setupLocator();
- await SentryFlutter.init((options) {
- options.dsn =
- dotenv.env['SENTRY_DSN']!; // Add your Sentry DSN to your .env file.
- // Set tracesSampleRate to 1.0 to capture 100% of transactions for tracing.
- // We recommend adjusting this value in production.
- options.tracesSampleRate = 1.0;
- // The sampling rate for profiling is relative to tracesSampleRate
- // Setting to 1.0 will profile 100% of sampled transactions:
- options.profilesSampleRate = 1.0;
- options.environment = appFlavor;
- });
- await locator<AuthenticationRepository>().init();
-
- //Setting SysemUIOverlay
- SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle(
+ // Render UI immediately; do heavy initialization after first frame to avoid ANR.
+ runApp(
+ EasyLocalization(
+ supportedLocales: const [Locale('en', 'US'), Locale('da', 'DK')],
+ path: 'assets/translations',
+ fallbackLocale: const Locale('da', 'DK'),
+ child: const _BootstrapApp(),
+ ),
+ );
+}
+
+class _BootstrapApp extends StatefulWidget {
+ const _BootstrapApp();
+
+ @override
+ State<_BootstrapApp> createState() => _BootstrapAppState();
+}
+
+class _BootstrapAppState extends State<_BootstrapApp> {
+ late final Future<void> _initFuture;
+
+ @override
+ void initState() {
+ super.initState();
+ _initFuture = _initialize();
+ }
+
+ Future<void> _initialize() async {
+ await configureFirebase();
+ await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
+ setupLocator();
+
+ await SentryFlutter.init((options) {
+ options.dsn = dotenv.env['SENTRY_DSN']!;
+ options.tracesSampleRate = 1.0;
+ options.profilesSampleRate = 1.0;
+ options.environment = appFlavor;
+ });
+
+ await locator<AuthenticationRepository>().init();
+
+ await PaymentPlugin.initialize(
+ config: PaymentConfig(
+ dio: HttpClient().dio,
+ ),
+ );
+
+ // Setting System UI overlay
+ SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle(
statusBarColor: Colors.transparent,
statusBarBrightness: Brightness.dark,
systemStatusBarContrastEnforced: false,
@@ -70,14 +101,25 @@ void runMainApp(FirebaseOptions firebaseOptions, String envFile) async {
systemNavigationBarDividerColor: Colors.transparent,
systemNavigationBarContrastEnforced: false,
systemNavigationBarIconBrightness: Brightness.dark,
- statusBarIconBrightness: Brightness.dark));
-
- //Setting SystmeUIMode to edgeToEdge
- //SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge, overlays: [SystemUiOverlay.top, SystemUiOverlay.bottom]);
- runApp(SentryWidget(
- child: EasyLocalization(
- supportedLocales: const [Locale('en', 'US'), Locale('da', 'DK')],
- path: 'assets/translations',
- fallbackLocale: const Locale('da', 'DK'),
- child: const ComwellApp())));
+ statusBarIconBrightness: Brightness.dark,
+ ));
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return FutureBuilder<void>(
+ future: _initFuture,
+ builder: (context, snapshot) {
+ if (snapshot.connectionState != ConnectionState.done) {
+ return const MaterialApp(
+ home: Scaffold(
+ body: Center(child: CircularProgressIndicator()),
+ ),
+ );
+ }
+
+ return SentryWidget(child: const ComwellApp());
+ },
+ );
+ }
}
diff --git a/comwell_key_app/lib/my_booking/cubit/my_booking_cubit.dart b/comwell_key_app/lib/my_booking/cubit/my_booking_cubit.dart
index adf890dd..5bc79cf3 100644
--- a/comwell_key_app/lib/my_booking/cubit/my_booking_cubit.dart
+++ b/comwell_key_app/lib/my_booking/cubit/my_booking_cubit.dart
@@ -1,13 +1,14 @@
import 'package:comwell_key_app/my_booking/cubit/my_booking_state.dart';
import 'package:comwell_key_app/my_booking/my_booking_repository.dart';
import 'package:comwell_key_app/overview/models/booking.dart';
-import 'package:comwell_key_app/payment/cubit/payment_cubit.dart';
+
import 'package:comwell_key_app/profile/profile_repository.dart';
import 'package:comwell_key_app/profile/utils/urls.dart';
import 'package:comwell_key_app/tracking/comwell_tracking.dart';
import 'package:comwell_key_app/tracking/models/analytics_event_item.dart';
import 'package:comwell_key_app/utils/locator.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:payment_plugin/presentation/app/bloc/payment_cubit.dart';
import 'package:url_launcher/url_launcher.dart';
class MyBookingCubit extends Cubit<MyBookingState> {
diff --git a/comwell_key_app/lib/my_booking/my_booking_page.dart b/comwell_key_app/lib/my_booking/my_booking_page.dart
index e723c5e3..bee48fa4 100644
--- a/comwell_key_app/lib/my_booking/my_booking_page.dart
+++ b/comwell_key_app/lib/my_booking/my_booking_page.dart
@@ -6,8 +6,6 @@ import 'package:comwell_key_app/housekeeping/housekeeping_repository.dart';
import 'package:comwell_key_app/my_booking/cubit/my_booking_cubit.dart';
import 'package:comwell_key_app/my_booking/cubit/my_booking_state.dart';
import 'package:comwell_key_app/overview/models/booking.dart';
-import 'package:comwell_key_app/payment/cubit/payment_cubit.dart';
-import 'package:comwell_key_app/payment/cubit/payment_processing_state.dart';
import 'package:comwell_key_app/profile/profile_repository.dart';
import 'package:comwell_key_app/routing/app_routes.dart';
import 'package:comwell_key_app/services/mappers/booking_mapper.dart';
@@ -18,6 +16,8 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_svg/flutter_svg.dart';
+import 'package:payment_plugin/presentation/app/bloc/payment_cubit.dart';
+import 'package:payment_plugin/presentation/app/bloc/payment_processing_state.dart';
class MyBookingPage extends StatelessWidget {
final Booking booking;
diff --git a/comwell_key_app/lib/overview/components/bill_download_modal.dart b/comwell_key_app/lib/overview/components/bill_download_modal.dart
index ff779e22..40005e60 100644
--- a/comwell_key_app/lib/overview/components/bill_download_modal.dart
+++ b/comwell_key_app/lib/overview/components/bill_download_modal.dart
@@ -4,16 +4,19 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
class BillDownloadModal extends StatelessWidget {
- final TextEditingController emailController = TextEditingController();
+ final TextEditingController emailController;
final String initialEmail;
final VoidCallback onDownload;
BillDownloadModal(
- {super.key, required this.onDownload, required this.initialEmail});
+ {super.key, required this.onDownload, required this.initialEmail})
+ : emailController = TextEditingController(text: initialEmail);
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
+ final bottomInset = MediaQuery.of(context).viewInsets.bottom;
+
return ClipRRect(
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(16),
@@ -22,8 +25,13 @@ class BillDownloadModal extends StatelessWidget {
child: Container(
color: Colors.white,
child: SafeArea(
- child: Padding(
- padding: const EdgeInsets.all(16.0),
+ child: SingleChildScrollView(
+ padding: EdgeInsets.only(
+ left: 16,
+ right: 16,
+ top: 16,
+ bottom: 16 + bottomInset,
+ ),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
diff --git a/comwell_key_app/lib/payment/cubit/payment_cubit.dart b/comwell_key_app/lib/payment/cubit/payment_cubit.dart
deleted file mode 100644
index cf573618..00000000
--- a/comwell_key_app/lib/payment/cubit/payment_cubit.dart
+++ /dev/null
@@ -1,49 +0,0 @@
-import 'package:adyen_checkout/adyen_checkout.dart';
-import 'package:comwell_key_app/pregistration/pregistration_repository.dart';
-import 'package:comwell_key_app/payment/cubit/payment_processing_state.dart';
-import 'package:flutter_bloc/flutter_bloc.dart';
-
-class PaymentCubit extends Cubit<PaymentProcessingState> {
- final PreregistrationRepository preregistrationRepository;
-
- PaymentCubit({required this.preregistrationRepository})
- : super(PaymentProcessingStateNotStarted());
-
- Future<void> createSession(int price, String confirmationId,
- bool applyClubPoints, String hotelCode) async {
- try {
-
- emit(PaymentProcessingStateProcessing());
-
- final paymentConfigurations = await preregistrationRepository
- .sessionCheckout(hotelCode, confirmationId, applyClubPoints);
- if (paymentConfigurations == null) {
- emit(PaymentProcessingStateConfirmed());
- } else {
- emit(PaymentProcessingStateSessionReceived(
- paymentConfigurations: paymentConfigurations));
- }
- } catch (e) {
- emit(PaymentProcessingStateError(message: "Error creating session"));
- await Future<void>.delayed(const Duration(milliseconds: 1000));
- }
- }
-
- Future<void> onPaymentResult(PaymentResult result) async {
- switch (result) {
- case PaymentAdvancedFinished():
- case PaymentSessionFinished():
- emit(PaymentProcessingStateConfirmed());
- break;
- case PaymentCancelledByUser():
- case PaymentError():
- emit(PaymentProcessingStateError(message: "Error processing payment"));
- }
- }
-
- void onUserDismissPaymentPopup() {
- emit(PaymentProcessingStateNotStarted());
- }
-
-
-}
diff --git a/comwell_key_app/lib/payment/cubit/payment_processing_state.dart b/comwell_key_app/lib/payment/cubit/payment_processing_state.dart
deleted file mode 100644
index 9749b879..00000000
--- a/comwell_key_app/lib/payment/cubit/payment_processing_state.dart
+++ /dev/null
@@ -1,23 +0,0 @@
-import 'package:comwell_key_app/check_out/models/payment_configurations.dart';
-
-sealed class PaymentProcessingState {}
-
-class PaymentProcessingStateNotStarted extends PaymentProcessingState {}
-
-class PaymentProcessingStateProcessing extends PaymentProcessingState {}
-
-class PaymentProcessingStateSessionReceived extends PaymentProcessingState {
- final PaymentConfigurations paymentConfigurations;
-
- PaymentProcessingStateSessionReceived({required this.paymentConfigurations});
-}
-
-class PaymentProcessingPaymentStateSuccess extends PaymentProcessingState {}
-
-class PaymentProcessingStateConfirmed extends PaymentProcessingState {}
-
-class PaymentProcessingStateError extends PaymentProcessingState {
- final String message;
-
- PaymentProcessingStateError({required this.message});
-}
diff --git a/comwell_key_app/lib/payment/payment_processing_page.dart b/comwell_key_app/lib/payment/payment_processing_page.dart
deleted file mode 100644
index 35b4dc74..00000000
--- a/comwell_key_app/lib/payment/payment_processing_page.dart
+++ /dev/null
@@ -1,127 +0,0 @@
-import 'package:adyen_checkout/adyen_checkout.dart';
-import 'package:comwell_key_app/payment/cubit/payment_cubit.dart';
-import 'package:comwell_key_app/payment/cubit/payment_processing_state.dart';
-import 'package:comwell_key_app/themes/dark_theme.dart';
-import 'package:comwell_key_app/utils/lottie_utils.dart';
-import 'package:flutter/material.dart';
-import 'package:flutter_bloc/flutter_bloc.dart';
-import 'package:lottie/lottie.dart';
-
-class PaymentProcessingPage extends StatefulWidget {
- const PaymentProcessingPage({super.key});
-
- @override
- State<PaymentProcessingPage> createState() => _PaymentProcessingPageState();
-}
-
-class _PaymentProcessingPageState extends State<PaymentProcessingPage>
- with SingleTickerProviderStateMixin {
- LottieComposition? loadingComposition;
- late final AnimationController animationController;
-
- @override
- void initState() {
- animationController = AnimationController(vsync: this);
- super.initState();
- }
-
- @override
- void dispose() {
- animationController.dispose();
- super.dispose();
- }
-
- void onAnimationEnd() {
- playLoading();
- }
-
- void playLoading() {
- loadingComposition?.playBetween(
- animationController,
- "spinner",
- markerEnd: "success",
- repeat: true,
- );
- }
-
- void playError() async {
- loadingComposition?.playBetween(
- animationController,
- "error",
- repeat: true,
- );
- await Future<void>.delayed(const Duration(seconds: 1));
- if (mounted) Navigator.of(context).pop();
- }
-
- void playSuccess() async {
- await loadingComposition?.playBetween(
- animationController,
- "success",
- markerEnd: "error",
- );
- await Future<void>.delayed(const Duration(seconds: 1));
- if (mounted) Navigator.of(context).pop();
- }
-
- void showAdyenModal(BuildContext context) async {
- final cubit = context.read<PaymentCubit>();
- final processingState = cubit.state;
- dynamic response;
- if (processingState is! PaymentProcessingStateSessionReceived) return;
- final paymentConfig = processingState.paymentConfigurations;
- if (!mounted) return;
-
- final dropInConfig = paymentConfig.dropInConfiguration;
- response = await AdyenCheckout.session.startDropIn(
- dropInConfiguration: dropInConfig,
- checkout: paymentConfig.sessionCheckout,
- );
-
- if (!mounted) return;
- if (response is PaymentResult) {
- cubit.onPaymentResult(response);
- } else {
- cubit.onUserDismissPaymentPopup();
- }
- }
-
- @override
- Widget build(BuildContext context) {
- final cubit = context.read<PaymentCubit>();
- return Scaffold(
- body: Container(
- alignment: Alignment.center,
- color: sandColor[80],
- child: BlocListener<PaymentCubit, PaymentProcessingState>(
- listener: (context, state) {
- if (state is PaymentProcessingStateSessionReceived) {
- showAdyenModal(context);
- }
- },
- child: Lottie.asset(
- 'assets/animations/load_animation.json',
- controller: animationController,
- onLoaded: (composition) {
- if (loadingComposition == null) {
- loadingComposition = composition;
- animationController.duration = composition.duration;
- switch (cubit.state) {
- case PaymentProcessingStateConfirmed _:
- playSuccess();
- case PaymentProcessingStateError _:
- playError();
- default:
- playLoading();
- }
- }
- },
- fit: BoxFit.cover,
- width: 64,
- height: 64,
- ),
- ),
- ),
- );
- }
-}
diff --git a/comwell_key_app/lib/payment_cards/bloc/payment_cards_cubit.dart b/comwell_key_app/lib/payment_cards/bloc/payment_cards_cubit.dart
deleted file mode 100644
index f48ad7dd..00000000
--- a/comwell_key_app/lib/payment_cards/bloc/payment_cards_cubit.dart
+++ /dev/null
@@ -1,188 +0,0 @@
-import 'dart:io';
-
-import 'package:adyen_checkout/adyen_checkout.dart';
-import 'package:bloc/bloc.dart';
-import 'package:comwell_key_app/check_out/models/payment_configurations.dart';
-import 'package:comwell_key_app/payment_cards/bloc/payment_cards_state.dart';
-import 'package:comwell_key_app/payment_cards/payment_cards_repository.dart';
-import 'package:comwell_key_app/pregistration/pregistration_repository.dart';
-import 'package:comwell_key_app/profile/profile_repository.dart';
-import 'package:comwell_key_app/services/adyen/payment_event_handler.dart';
-import 'package:comwell_key_app/services/adyen/payment_method.dart';
-import 'package:comwell_key_app/services/adyen/stored_payment_method.dart';
-import 'package:comwell_key_app/services/api.dart';
-import 'package:comwell_key_app/utils/locator.dart';
-import 'package:flutter/material.dart';
-import 'package:flutter/services.dart';
-import 'package:go_router/go_router.dart';
-
-class PaymentCardsCubit extends Cubit<PaymentCardsState> {
- final PaymentCardsRepository _paymentCardsRepository =
- PaymentCardsRepository();
- final PreregistrationRepository _preregistrationRepository =
- locator<PreregistrationRepository>();
- final ProfileRepository profileRepository = locator<ProfileRepository>();
- final Api _api = Api();
- late PaymentConfigurations? paymentConfigurations;
- void Function(StoredPaymentMethod)? onPaymentMethodSelected;
- void Function()? onPaymentMethodValidationError;
-
- PaymentCardsCubit({
- this.onPaymentMethodSelected,
- this.onPaymentMethodValidationError,
- StoredPaymentMethod? initialSelectedPaymentMethod,
- }) : super(PaymentCardsState.initial()) {
- if (initialSelectedPaymentMethod != null) {
- emit(state.paymentMethodSelected(initialSelectedPaymentMethod));
- }
- init();
- }
-
- bool get isSelectedPaymentMethod => state.selectedPaymentMethod != null;
-
- Future<PaymentConfigurations?> fetchPaymentConfiguration() async {
- try {
- final session =
- await _preregistrationRepository.fetchSessionForAddingCard();
-
- if (session == null) {
- return null;
- }
- return session;
- } catch (e) {
- debugPrint("error: $e");
- return null;
- }
- }
-
- void init() async {
- try {
- final cards = await _paymentCardsRepository.getCards();
- paymentConfigurations = await fetchPaymentConfiguration();
-
- if (cards.isEmpty) {
- emit(state.cardsFetchedEmpty());
- } else {
- emit(state.cardsFetched(cards));
- }
- } catch (e) {
- debugPrint("error: $e");
- emit(state.cardsFetchedError());
- }
- }
-
- Future<void> onPaymentResult(
- PaymentResult result, BuildContext context) async {
- switch (result) {
- case PaymentAdvancedFinished():
- debugPrint("Result advanced: ${result.resultCode}");
-
- if (context.mounted) {
- context.pop();
- init();
- }
- break;
- case PaymentSessionFinished():
- debugPrint("Result session: ${result.sessionData}");
- if (context.mounted) {
- context.pop();
- init();
- }
- break;
- case PaymentCancelledByUser():
- case PaymentError():
- emit(state.cardsFetchedError());
- }
- }
-
- Future<void> storePaymentMethod(PaymentMethod paymentMethod) async {
- debugPrint("Payment method: $paymentMethod");
- try {
- final response = await _paymentCardsRepository.addCard(paymentMethod);
-
- if (response != null) {
- //emit(state.cardsFetched(response));
- } else {
- emit(state.cardsFetchedError());
- }
- } catch (e) {
- debugPrint("error: $e");
- }
- }
-
- SessionCheckout sessionCheckout() {
- return paymentConfigurations?.sessionCheckout ??
- SessionCheckout(
- id: "123",
- sessionData: "123",
- paymentMethods: {
- "scheme": {
- "type": "scheme",
- "brands": ["visa", "mc"],
- },
- },
- );
- }
-
- Checkout advancedCheckout() {
- return AdvancedCheckout(
- onSubmit: (data, [extra]) async {
- debugPrint("Data: $data");
- debugPrint("Extra: $extra");
- try {
- final user = await profileRepository.fetchProfileSettings();
- final shopperRef = user.id.toString();
- final merchantAccount =
- paymentConfigurations?.sessionCheckout.id as String;
- final response =
- await _api.submitPayment(data, shopperRef, merchantAccount);
- return PaymentEventHandler().handleResponse(jsonResponse: response);
- } catch (e) {
- return Error(errorMessage: "$e");
- }
- },
- onAdditionalDetails: (data) async {
- final response = await _api.postPaymentsDetails(data);
-
- return PaymentEventHandler().handleResponse(jsonResponse: response!);
- },
- );
- }
-
- void disposeWebViewResources() {
- // Force WebView cleanup
- if (Platform.isAndroid) {
- // Android-specific WebView cleanup
- SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual,
- overlays: SystemUiOverlay.values);
- }
- }
-
- Future<void> onRemoveCard(String cardId) async {
- try {
- emit(state.loading(true));
- await _paymentCardsRepository.removeCard(cardId);
- // If the removed card was selected, clear the selection
- if (state.selectedPaymentMethod?.id == cardId) {
- emit(state.clearSelectedPaymentMethod());
- }
- init();
- } catch (e) {
- debugPrint("error: $e");
- }
- }
-
- void selectPaymentMethod(StoredPaymentMethod paymentMethod) {
- emit(state.paymentMethodSelected(paymentMethod).setMissingPaymentMethod(false));
- onPaymentMethodSelected?.call(paymentMethod);
- }
-
- void setMissingPaymentMethodError() {
- emit(state.setMissingPaymentMethod(true));
- onPaymentMethodValidationError?.call();
- }
-
- void clearMissingPaymentMethodError() {
- emit(state.setMissingPaymentMethod(false));
- }
-}
diff --git a/comwell_key_app/lib/payment_cards/bloc/payment_cards_state.dart b/comwell_key_app/lib/payment_cards/bloc/payment_cards_state.dart
deleted file mode 100644
index 922fdb45..00000000
--- a/comwell_key_app/lib/payment_cards/bloc/payment_cards_state.dart
+++ /dev/null
@@ -1,73 +0,0 @@
-import 'package:comwell_key_app/services/adyen/stored_payment_method.dart';
-import 'package:equatable/equatable.dart';
-
-class PaymentCardsState extends Equatable {
- final bool isLoading;
- final bool hasError;
- final Iterable<StoredPaymentMethod> cards;
- final StoredPaymentMethod? selectedPaymentMethod;
- final bool missingPaymentMethod;
-
- const PaymentCardsState._({
- required this.cards,
- required this.isLoading,
- required this.hasError,
- this.selectedPaymentMethod,
- this.missingPaymentMethod = false,
- });
-
- PaymentCardsState.initial()
- : cards = [],
- isLoading = true,
- hasError = false,
- selectedPaymentMethod = null,
- missingPaymentMethod = false;
-
- PaymentCardsState cardsFetched(Iterable<StoredPaymentMethod> cards) {
- return _copyWith(cards: cards, isLoading: false);
- }
-
- PaymentCardsState cardsFetchedEmpty() {
- return _copyWith(cards: [], isLoading: false, hasCards: false);
- }
-
- PaymentCardsState cardsFetchedError() =>
- _copyWith(isLoading: false, hasError: true);
-
- PaymentCardsState loading(bool isLoading) {
- return _copyWith(isLoading: isLoading);
- }
-
- PaymentCardsState _copyWith({
- Iterable<StoredPaymentMethod>? cards,
- bool? isLoading,
- bool? hasError,
- bool? hasCards,
- StoredPaymentMethod? selectedPaymentMethod,
- bool? missingPaymentMethod,
- }) {
- return PaymentCardsState._(
- cards: cards ?? this.cards,
- isLoading: isLoading ?? this.isLoading,
- hasError: hasError ?? this.hasError,
- selectedPaymentMethod: selectedPaymentMethod ?? this.selectedPaymentMethod,
- missingPaymentMethod: missingPaymentMethod ?? this.missingPaymentMethod,
- );
- }
-
- PaymentCardsState paymentMethodSelected(StoredPaymentMethod? paymentMethod) {
- return _copyWith(selectedPaymentMethod: paymentMethod);
- }
-
- PaymentCardsState clearSelectedPaymentMethod() {
- return _copyWith(selectedPaymentMethod: null);
- }
-
- PaymentCardsState setMissingPaymentMethod(bool value) {
- return _copyWith(missingPaymentMethod: value);
- }
-
- @override
- List<Object?> get props => [isLoading, hasError, cards, selectedPaymentMethod, missingPaymentMethod];
-
-}
diff --git a/comwell_key_app/lib/payment_cards/components/add_card.dart b/comwell_key_app/lib/payment_cards/components/add_card.dart
deleted file mode 100644
index 754dca46..00000000
--- a/comwell_key_app/lib/payment_cards/components/add_card.dart
+++ /dev/null
@@ -1,75 +0,0 @@
-import 'package:adyen_checkout/adyen_checkout.dart';
-import 'package:comwell_key_app/payment_cards/bloc/payment_cards_cubit.dart';
-import 'package:comwell_key_app/themes/light_theme.dart';
-import 'package:easy_localization/easy_localization.dart';
-import 'package:flutter/material.dart';
-import 'package:flutter_bloc/flutter_bloc.dart';
-import 'package:flutter_svg/flutter_svg.dart';
-
-class AddCard extends StatelessWidget {
- const AddCard({super.key});
-
- Future<void> showPaymentDialog(BuildContext context) async {
- final cubit = context.read<PaymentCardsCubit>();
- //Extracting the scheme payment method config from the available payment methods
- final paymentMethodConfig = cubit
- .paymentConfigurations?.availablePaymentMethods
- .firstWhere((element) => element["type"] == "scheme");
-
- await showModalBottomSheet<void>(
- isDismissible: true,
- isScrollControlled: true,
- context: context,
- builder: (BuildContext context) {
- return SafeArea(
- child: Padding(
- padding: MediaQuery.of(context).viewInsets,
- child: AdyenCardComponent(
- configuration:
- cubit.paymentConfigurations?.cardComponentConfiguration ??
- CardComponentConfiguration(
- environment: Environment.test,
- countryCode: "DK",
- clientKey: "test",
- ),
- paymentMethod: paymentMethodConfig ?? const {"type": "", "name": ""},
- checkout: cubit.sessionCheckout(),
- onPaymentResult: (result) => cubit.onPaymentResult(result, context),
- ),
- ));
- },
- );
- }
-
- @override
- Widget build(BuildContext context) {
- return InkWell(
- onTap: () async {
- await showPaymentDialog(context);
- },
- child: Container(
- decoration: BoxDecoration(
- border: Border.all(color: colorDivider),
- ),
- padding: const EdgeInsets.all(12),
- child: Row(
- crossAxisAlignment: CrossAxisAlignment.center,
- children: [
- Text(
- "preregistration_payment_add_card".tr(),
- style: Theme.of(context).textTheme.bodyMedium,
- ),
- const SizedBox(width: 12),
- SvgPicture.asset("assets/icons/visa.svg"),
- const SizedBox(width: 4),
- SvgPicture.asset("assets/icons/mastercard.svg"),
- const SizedBox(width: 4),
- SvgPicture.asset("assets/icons/maestro.svg"),
- const Expanded(child: SizedBox()),
- SvgPicture.asset("assets/icons/ic_plus.svg"),
- ],
- ),
- ),
- );
- }
-}
diff --git a/comwell_key_app/lib/payment_cards/components/approve_conditions_widget.dart b/comwell_key_app/lib/payment_cards/components/approve_conditions_widget.dart
deleted file mode 100644
index b5832598..00000000
--- a/comwell_key_app/lib/payment_cards/components/approve_conditions_widget.dart
+++ /dev/null
@@ -1,51 +0,0 @@
-import 'package:comwell_key_app/pregistration/cubit/preregistration_cubit.dart';
-import 'package:comwell_key_app/pregistration/cubit/preregistration_state.dart';
-import 'package:comwell_key_app/themes/light_theme.dart';
-import 'package:easy_localization/easy_localization.dart';
-import 'package:flutter/material.dart';
-import 'package:flutter_bloc/flutter_bloc.dart';
-
-class ApproveConditionsWidget extends StatelessWidget {
- const ApproveConditionsWidget({super.key});
-
- @override
- Widget build(BuildContext context) {
- final theme = Theme.of(context);
- return BlocBuilder<PreregistrationCubit, PreregistrationState>(
- builder: (context, state) {
- final cubit = context.read<PreregistrationCubit>();
- return Row(
- crossAxisAlignment: CrossAxisAlignment.center,
- children: [
- Checkbox(
- activeColor: sandColor,
- checkColor: Colors.white,
- side: const BorderSide(color: Colors.grey),
- value: cubit.state.termsAndConditionsAccepted,
- onChanged: (value) {
- cubit.onTermsAndConditionsToggled(value ?? false);
- },
- ),
- Expanded(
- child: Wrap(
- crossAxisAlignment: WrapCrossAlignment.center,
- children: [
- Text('approve_conditions_title'.tr(), style: theme.textTheme.bodyMedium?.copyWith(
- color: colorHeadlineText,
- )),
- Text(
- 'approve_conditions_subtitle'.tr(),
- style: theme.textTheme.bodyMedium?.copyWith(
- color: sandColor,
- decoration: TextDecoration.underline,
- ),
- ),
- ],
- ),
- ),
- ],
- );
- },
- );
- }
-}
diff --git a/comwell_key_app/lib/payment_cards/components/edit_card_dialog.dart b/comwell_key_app/lib/payment_cards/components/edit_card_dialog.dart
deleted file mode 100644
index 537844dd..00000000
--- a/comwell_key_app/lib/payment_cards/components/edit_card_dialog.dart
+++ /dev/null
@@ -1,201 +0,0 @@
-import 'package:comwell_key_app/payment_cards/components/remove_card_button.dart';
-import 'package:comwell_key_app/services/adyen/stored_payment_method.dart';
-import 'package:comwell_key_app/themes/light_theme.dart';
-import 'package:easy_localization/easy_localization.dart';
-import 'package:flutter/material.dart';
-
-class EditCardDialog extends StatelessWidget {
- final StoredPaymentMethod storedPaymentMethod;
- final void Function() onRemoveCard;
-
- const EditCardDialog({
- super.key,
- required this.storedPaymentMethod,
- required this.onRemoveCard,
- });
-
- @override
- Widget build(BuildContext context) {
- final theme = Theme.of(context);
- final expirationDate =
- "${storedPaymentMethod.expiryMonth}/${storedPaymentMethod.expiryYear}";
-
- return Column(
- mainAxisAlignment: MainAxisAlignment.start,
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- const SizedBox(height: 12),
- Row(
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- Text("payment_cards_edit_card_title".tr(),
- style: Theme.of(context).textTheme.headlineLarge),
- IconButton(
- style: IconButton.styleFrom(
- backgroundColor: sandColor[40],
- ),
- onPressed: () {
- Navigator.of(context).pop();
- },
- icon: const Icon(Icons.close),
- ),
- ],
- ),
- const SizedBox(height: 36),
- Text(storedPaymentMethod.holderName,
- style: Theme.of(context).textTheme.headlineMedium),
- const SizedBox(height: 20),
- Container(
- height: 62,
- width: double.infinity,
- decoration: BoxDecoration(
- borderRadius: BorderRadius.circular(8),
- border: Border.all(color: colorDivider),
- ),
- child: Padding(
- padding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 10),
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Text("name_on_card_hint".tr(),
- style: theme.textTheme.bodySmall
- ?.copyWith(color: colorHeadlineText)),
- Text(
- storedPaymentMethod.holderName,
- style: theme.textTheme.headlineSmall,
- ),
- ],
- ),
- ),
- ),
- const SizedBox(height: 8),
- Container(
- height: 62,
- decoration: BoxDecoration(
- borderRadius: BorderRadius.circular(8),
- border: Border.all(color: colorDivider),
- ),
- child: Padding(
- padding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 10),
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Text("card_number_hint".tr(),
- style: theme.textTheme.bodySmall
- ?.copyWith(color: colorHeadlineText)),
- Row(
- crossAxisAlignment: CrossAxisAlignment.center,
- children: [
- ..."**** **** **** ".characters.map((char) {
- if (char == '*') {
- return Padding(
- padding: const EdgeInsets.symmetric(horizontal: 1.0),
- child: Container(
- width: 5,
- height: 5,
- decoration: const BoxDecoration(
- shape: BoxShape.circle, color: colorTertiary),
- ),
- );
- }
- return const SizedBox(width: 8);
- }),
- Text(
- storedPaymentMethod.lastFour,
- style: theme.textTheme.headlineSmall,
- )
- ],
- ),
- ],
- ),
- ),
- ),
- const SizedBox(height: 4),
- Row(
- children: [
- Expanded(
- child: Container(
- height: 62,
- decoration: BoxDecoration(
- borderRadius: BorderRadius.circular(8),
- border: Border.all(color: colorDivider),
- ),
- child: Padding(
- padding: const EdgeInsets.symmetric(
- horizontal: 12.0, vertical: 10),
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Text("expiry_date_hint".tr(),
- style: theme.textTheme.bodySmall
- ?.copyWith(color: colorHeadlineText)),
- Text(
- expirationDate,
- style: theme.textTheme.headlineSmall,
- ),
- ],
- ),
- ),
- ),
- ),
- const SizedBox(width: 4),
- Expanded(
- child: Container(
- height: 62,
- decoration: BoxDecoration(
- borderRadius: BorderRadius.circular(8),
- border: Border.all(color: colorDivider),
- ),
- child: Padding(
- padding: const EdgeInsets.symmetric(
- horizontal: 12.0, vertical: 10),
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- Text("cvc_hint".tr(),
- style: theme.textTheme.bodySmall
- ?.copyWith(color: colorHeadlineText)),
-
- Row(
- crossAxisAlignment: CrossAxisAlignment.center,
- children: [
- ..."***".characters.map((char) {
- if (char == '*') {
- return Padding(
- padding:
- const EdgeInsets.symmetric(horizontal: 1.0),
- child: Container(
- width: 5,
- height: 5,
- decoration: const BoxDecoration(
- shape: BoxShape.circle,
- color: colorTertiary),
- ),
- );
- }
- return const SizedBox(width: 8);
- }),
- ],
- ),
- const SizedBox.shrink(),
- ],
- ),
- ),
- ),
- ),
- ],
- ),
- const SizedBox(height: 24),
- Row(
- mainAxisAlignment: MainAxisAlignment.center,
- children: [
- RemoveCardButton(onRemoveCard: () {
- onRemoveCard();
- }),
- ],
- ),
- ],
- );
- }
-}
diff --git a/comwell_key_app/lib/payment_cards/components/remove_card_button.dart b/comwell_key_app/lib/payment_cards/components/remove_card_button.dart
deleted file mode 100644
index c32dbf4d..00000000
--- a/comwell_key_app/lib/payment_cards/components/remove_card_button.dart
+++ /dev/null
@@ -1,103 +0,0 @@
-import 'package:comwell_key_app/themes/comwell_colors.dart';
-import 'package:comwell_key_app/themes/light_theme.dart'
- hide colorDivider, colorBackground;
-import 'package:easy_localization/easy_localization.dart';
-import 'package:flutter/material.dart';
-import 'package:go_router/go_router.dart';
-
-class RemoveCardButton extends StatelessWidget {
- final void Function() onRemoveCard;
-
- const RemoveCardButton({super.key, required this.onRemoveCard});
-
- @override
- Widget build(BuildContext context) {
- final theme = Theme.of(context);
- return InkWell(
- onTap: () async {
- final response = await showDialog<dynamic>(
- context: context,
- builder: (context) {
- return Dialog(
- backgroundColor: colorBackground,
- child: Padding(
- padding: const EdgeInsets.all(24.0),
- child: Column(
- mainAxisSize: MainAxisSize.min,
- children: [
- Text(
- "payment_cards_confirm_remove_title".tr(),
- textAlign: TextAlign.center,
- style: theme.textTheme.headlineMedium,
- ),
- const SizedBox(height: 32),
- Text(
- "payment_cards_confirm_remove_subtitle".tr(),
- style: theme.textTheme.bodySmall
- ?.copyWith(color: colorHeadlineText),
- textAlign: TextAlign.center,
- ),
- const SizedBox(height: 32),
- ElevatedButton(
- style: ButtonStyle(
- minimumSize: const WidgetStatePropertyAll(
- Size(double.infinity, 50)),
- shape: WidgetStatePropertyAll(RoundedRectangleBorder(
- borderRadius: BorderRadius.circular(30),
- )),
- ),
- onPressed: () {
- context.pop(true);
- },
- child: Text(
- "generic_confirm".tr(),
- style: theme.textTheme.bodyMedium?.copyWith(color: colorBackground),
- ),
- ),
- const SizedBox(height: 8),
- OutlinedButton(
- style: ButtonStyle(
- minimumSize: const WidgetStatePropertyAll(
- Size(double.infinity, 50)),
- shape:
- WidgetStatePropertyAll(RoundedRectangleBorder(
- borderRadius: BorderRadius.circular(30),
- )),
- ),
- child: Text(
- "cancel".tr(),
- style: theme.textTheme.bodyMedium,
- ),
- onPressed: () {
- context.pop(false);
- }),
- ],
- ),
- ),
- );
- });
- if (response is bool && response == true) {
- onRemoveCard();
- if (context.mounted) {
- context.pop();
- }
- }
- },
- child: Container(
- decoration: BoxDecoration(
- border: Border.all(
- color: colorDivider,
- ),
- borderRadius: BorderRadius.circular(30),
- ),
- child: Padding(
- padding: const EdgeInsets.symmetric(vertical: 10.0, horizontal: 32),
- child: Text(
- "payment_cards_remove_card_button".tr(),
- style: theme.textTheme.bodySmall?.copyWith(color: colorError),
- ),
- ),
- ),
- );
- }
-}
diff --git a/comwell_key_app/lib/payment_cards/components/save_card_button.dart b/comwell_key_app/lib/payment_cards/components/save_card_button.dart
deleted file mode 100644
index 6f949ca0..00000000
--- a/comwell_key_app/lib/payment_cards/components/save_card_button.dart
+++ /dev/null
@@ -1,33 +0,0 @@
-import 'package:comwell_key_app/themes/comwell_colors.dart';
-import 'package:easy_localization/easy_localization.dart';
-import 'package:flutter/material.dart';
-
-class SaveCardButton extends StatelessWidget {
- final void Function() onSaveCard;
-
- const SaveCardButton({super.key, required this.onSaveCard});
-
- @override
- Widget build(BuildContext context) {
- return InkWell(
- onTap: () async {},
- child: Container(
- decoration: BoxDecoration(
- border: Border.all(
- color: colorDivider,
- ),
- borderRadius: BorderRadius.circular(30),
- ),
- child: Padding(
- padding: const EdgeInsets.symmetric(vertical: 10.0, horizontal: 32),
- child: Text(
- "payment_cards_remove_card_button".tr(),
- style: Theme.of(context)
- .textTheme
- .bodySmall,
- ),
- ),
- ),
- );
- }
-}
diff --git a/comwell_key_app/lib/payment_cards/models/add_card_payment_method.dart b/comwell_key_app/lib/payment_cards/models/add_card_payment_method.dart
deleted file mode 100644
index e4a9fe62..00000000
--- a/comwell_key_app/lib/payment_cards/models/add_card_payment_method.dart
+++ /dev/null
@@ -1,21 +0,0 @@
-import 'package:comwell_key_app/utils/json.dart';
-import 'package:json_annotation/json_annotation.dart';
-
-part '../../.generated/payment_cards/models/add_card_payment_method.g.dart';
-
-enum CardType { visa, mc, amex }
-
-@JsonSerializable()
-class AddCardPaymentMethod {
- final Iterable<CardType> brands;
- final String name;
- final String type;
-
- AddCardPaymentMethod({
- required this.brands,
- required this.name,
- required this.type,
- });
-
- Json toJson() => _$AddCardPaymentMethodToJson(this);
-}
diff --git a/comwell_key_app/lib/payment_cards/payment_cards_page.dart b/comwell_key_app/lib/payment_cards/payment_cards_page.dart
deleted file mode 100644
index ccd49527..00000000
--- a/comwell_key_app/lib/payment_cards/payment_cards_page.dart
+++ /dev/null
@@ -1,180 +0,0 @@
-import 'package:comwell_key_app/common/components/comwell_app_bar.dart';
-import 'package:comwell_key_app/common/components/comwell_error_widget.dart';
-import 'package:comwell_key_app/payment_cards/bloc/payment_cards_state.dart';
-import 'package:comwell_key_app/payment_cards/components/add_card.dart';
-import 'package:comwell_key_app/payment_cards/components/edit_card_dialog.dart';
-import 'package:comwell_key_app/pregistration/components/card_item.dart';
-import 'package:comwell_key_app/services/adyen/stored_payment_method.dart';
-import 'package:comwell_key_app/themes/comwell_colors.dart';
-import 'package:easy_localization/easy_localization.dart';
-import 'package:flutter/material.dart';
-import 'package:flutter_bloc/flutter_bloc.dart';
-import 'bloc/payment_cards_cubit.dart';
-import '../common/components/shimmer_loader/payment_cards_shimmer_loader.dart';
-
-class PaymentCardsPage extends StatelessWidget {
- final bool needScaffold;
- const PaymentCardsPage({super.key, this.needScaffold = false});
-
- @override
- Widget build(BuildContext context) {
- if (needScaffold) {
- return Scaffold(
- backgroundColor: colorBackground,
- appBar: const ComwellAppBar(shouldShowProfileButton: true),
- body: BlocBuilder<PaymentCardsCubit, PaymentCardsState>(
- builder: (context, state) {
- final cubit = context.read<PaymentCardsCubit>();
- if (cubit.state.isLoading) {
- return const Center(child: PaymentCardsShimmerLoader());
- }
- if (cubit.state.hasError) {
- return Column(
- crossAxisAlignment: CrossAxisAlignment.center,
- mainAxisAlignment: MainAxisAlignment.center,
- children: [
- ComwellErrorWidget(
- title: 'generic_error_title'.tr(),
- subtitle: 'payment_cards_error_subtitle'.tr(),
- border: false,
- ),
- ],
- );
- }
- return _buildPaymentCards(context, state, cubit);
- },
- ),
- );
- } else {
- return BlocBuilder<PaymentCardsCubit, PaymentCardsState>(
- builder: (context, state) {
- final cubit = context.read<PaymentCardsCubit>();
- if (cubit.state.isLoading) {
- return const Center(child: PaymentCardsShimmerLoader());
- }
- if (cubit.state.hasError) {
- return Column(
- crossAxisAlignment: CrossAxisAlignment.center,
- mainAxisAlignment: MainAxisAlignment.center,
- children: [
- ComwellErrorWidget(
- title: 'generic_error_title'.tr(),
- subtitle: 'payment_cards_error_subtitle'.tr(),
- border: false,
- ),
- ],
- );
- }
- return _buildPaymentCards(context, state, cubit);
- },
- );
- }
- }
-
- Widget _buildPaymentCards(
- BuildContext context, PaymentCardsState state, PaymentCardsCubit cubit) {
- final cards = state.cards;
- final theme = Theme.of(context);
- final showPaymentError =
- state.missingPaymentMethod && state.selectedPaymentMethod == null;
-
- return _buildCardsList(context, theme, cards, cubit, showPaymentError);
- }
-
- Widget _buildCardsList(
- BuildContext context,
- ThemeData theme,
- Iterable<StoredPaymentMethod> cards,
- PaymentCardsCubit cubit,
- bool showPaymentError,
- ) {
- return ListView(
- children: [
- Padding(
- padding: const EdgeInsets.symmetric(horizontal: 16.0),
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- const SizedBox(height: 40),
- Text(
- "payment_cards_title".tr(),
- style: theme.textTheme.headlineLarge,
- ),
- const SizedBox(height: 40),
- Text("payment_cards_my_cards".tr(),
- style: theme.textTheme.headlineMedium),
- const SizedBox(height: 20),
- if (showPaymentError) ...[
- Container(
- padding: const EdgeInsets.all(12),
- margin: const EdgeInsets.only(bottom: 12),
- decoration: BoxDecoration(
- color: colorError.withValues(alpha: 0.1),
- border: Border.all(color: colorError),
- borderRadius: BorderRadius.circular(8),
- ),
- child: Row(
- children: [
- const Icon(Icons.error_outline,
- color: colorError, size: 20),
- const SizedBox(width: 8),
- Expanded(
- child: Text(
- "payment_cards_missing_payment_method_subtitle".tr(),
- style: theme.textTheme.bodyMedium?.copyWith(
- color: colorError,
- ),
- ),
- ),
- ],
- ),
- ),
- ],
- ...cards.map(
- (card) => InkWell(
- onTap: () {
- showModalBottomSheet<void>(
- context: context,
- backgroundColor: colorBackground,
- builder: (context) {
- return Padding(
- padding: const EdgeInsets.all(16.0),
- child: EditCardDialog(
- storedPaymentMethod: card,
- onRemoveCard: () {
- cubit.onRemoveCard(card.id);
- },
- ),
- );
- },
- );
- },
- child: Padding(
- padding: const EdgeInsets.symmetric(vertical: 4.0),
- child: Container(
- decoration: BoxDecoration(
- border: Border.all(color: colorDivider),
- ),
- padding: const EdgeInsets.all(12),
- child: CardItem(
- needsScaffold: needScaffold,
- paymentMethod: card,
- onSelect: () {
- cubit.selectPaymentMethod(card);
- },
- isSelectedPaymentMethod:
- cubit.state.selectedPaymentMethod?.id == card.id,
- ),
- ),
- ),
- ),
- ),
- const SizedBox(height: 12),
- const AddCard(),
- ],
- ),
- ),
- ],
- );
- }
-}
diff --git a/comwell_key_app/lib/payment_cards/payment_cards_repository.dart b/comwell_key_app/lib/payment_cards/payment_cards_repository.dart
deleted file mode 100644
index 66c53824..00000000
--- a/comwell_key_app/lib/payment_cards/payment_cards_repository.dart
+++ /dev/null
@@ -1,26 +0,0 @@
-import 'package:comwell_key_app/services/adyen/payment_method.dart';
-import 'package:comwell_key_app/services/adyen/stored_payment_method.dart';
-import 'package:comwell_key_app/services/api.dart';
-
-class PaymentCardsRepository {
- final _api = Api();
-
- Future<Iterable<StoredPaymentMethod>> getCards() async {
- final cards = await _api.getPaymentMethods();
- return cards?.storedPaymentMethods ?? [];
- }
-
- Future<void> removeCard(String cardId) async {
- final response = await _api.removePaymentMethod(cardId);
- return response;
- }
-
- Future<dynamic> addCard(PaymentMethod paymentMethod) async {
- final response = await _api.storePaymentMethod(paymentMethod);
- return response;
- }
-
- Future<void> updateCard(StoredPaymentMethod card) async {
-
- }
-}
diff --git a/comwell_key_app/lib/pregistration/components/card_item.dart b/comwell_key_app/lib/pregistration/components/card_item.dart
deleted file mode 100644
index 2df8f357..00000000
--- a/comwell_key_app/lib/pregistration/components/card_item.dart
+++ /dev/null
@@ -1,84 +0,0 @@
-import 'package:comwell_key_app/common/components/payment_card_image.dart';
-import 'package:comwell_key_app/overview/models/payment_details.dart';
-import 'package:comwell_key_app/services/adyen/stored_payment_method.dart';
-import 'package:comwell_key_app/themes/light_theme.dart';
-import 'package:flutter/material.dart';
-import 'package:flutter_svg/flutter_svg.dart';
-
-class CardItem extends StatelessWidget {
- final StoredPaymentMethod paymentMethod;
- final bool isSelectedPaymentMethod;
- final VoidCallback? onSelect;
- final bool needsScaffold;
-
- const CardItem({
- super.key,
- required this.paymentMethod,
- required this.isSelectedPaymentMethod,
- this.onSelect,
- this.needsScaffold = false,
- });
-
- @override
- Widget build(BuildContext context) {
- return Row(
- crossAxisAlignment: CrossAxisAlignment.center,
- mainAxisAlignment: MainAxisAlignment.spaceBetween,
- children: [
- PaymentCardImage(cardType: CardType.fromString(paymentMethod.brand)),
- const SizedBox(width: 12),
- Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Text(
- paymentMethod.holderName,
- style: Theme.of(context).textTheme.bodySmall,
- ),
- Row(
- crossAxisAlignment: CrossAxisAlignment.center,
- children: [
- ..."**** **** **** ".characters.map((char) {
- if (char == '*') {
- return Padding(
- padding: const EdgeInsets.symmetric(horizontal: 1.0),
- child: Container(
- width: 5,
- height: 5,
- decoration: const BoxDecoration(
- shape: BoxShape.circle, color: colorTertiary),
- ),
- );
- }
- return const SizedBox(width: 8);
- }),
- Text(
- paymentMethod.lastFour,
- style: Theme.of(context).textTheme.bodyMedium,
- ),
- ],
- ),
- ],
- ),
- const Expanded(child: SizedBox()),
- !needsScaffold ? InkWell(
- customBorder: const CircleBorder(),
- radius: 18,
- onTap: onSelect,
- child: isSelectedPaymentMethod
- ? SvgPicture.asset("assets/icons/ic_checkmark.svg", width: 24, height: 24)
- : Container(
- width: 24,
- height: 24,
- decoration: BoxDecoration(
- shape: BoxShape.circle,
- border: Border.all(
- color: colorTertiary,
- width: 1.0,
- ),
- ),
- ),
- ) : const Icon(Icons.arrow_forward_ios, color: colorTertiary, size: 24),
- ],
- );
- }
-}
diff --git a/comwell_key_app/lib/pregistration/cubit/preregistration_cubit.dart b/comwell_key_app/lib/pregistration/cubit/preregistration_cubit.dart
index 280234f8..eb15622e 100644
--- a/comwell_key_app/lib/pregistration/cubit/preregistration_cubit.dart
+++ b/comwell_key_app/lib/pregistration/cubit/preregistration_cubit.dart
@@ -7,7 +7,6 @@ import 'package:comwell_key_app/pregistration/utils/utils.dart';
import 'package:comwell_key_app/profile/profile_repository.dart';
import 'package:comwell_key_app/profile_settings/model/address.dart';
import 'package:comwell_key_app/profile_settings/repostiory/profile_settings_repository.dart';
-import 'package:comwell_key_app/services/adyen/stored_payment_method.dart';
import 'package:comwell_key_app/tracking/comwell_tracking.dart';
import 'package:comwell_key_app/tracking/models/analytics_event_item.dart';
import 'package:comwell_key_app/up_sales/models/addon_list.dart';
@@ -22,6 +21,7 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:go_router/go_router.dart';
+import 'package:payment_plugin/domain/models/stored_payment_method.dart';
class PreregistrationCubit extends Cubit<PreregistrationState> {
final _profileRepository = locator<ProfileRepository>();
@@ -208,10 +208,10 @@ class PreregistrationCubit extends Cubit<PreregistrationState> {
case PreregistrationPage.address:
onAddressContinueClicked();
break;
- case PreregistrationPage.payment:
+ /* case PreregistrationPage.payment:
// Payment validation is handled in preregistration_flow.dart
onPaymentContinueClicked();
- break;
+ break; */
case PreregistrationPage.upSales:
onUpSalesContinueClicked();
break;
@@ -325,9 +325,9 @@ class PreregistrationCubit extends Cubit<PreregistrationState> {
_navigateTo(PreregistrationPage.address);
}
- void onEditPaymentMethodCLickked() {
+ /* void onEditPaymentMethodCLickked() {
_navigateTo(PreregistrationPage.payment);
- }
+ } */
void onEditExtrasClicked() {
_navigateTo(PreregistrationPage.upSales);
@@ -453,8 +453,6 @@ class PreregistrationCubit extends Cubit<PreregistrationState> {
return isFirstNameValid && isLastNameValid && isPhoneNumberValid;
case PreregistrationPage.address:
return isAddressValid && isPostalCodeValid && isCityValid;
- case PreregistrationPage.payment:
- return true;
case PreregistrationPage.upSales:
return true;
case PreregistrationPage.confirmation:
@@ -470,8 +468,6 @@ class PreregistrationCubit extends Cubit<PreregistrationState> {
return "generic_continue".tr();
case PreregistrationPage.address:
return "generic_continue".tr();
- case PreregistrationPage.payment:
- return "generic_continue".tr();
case PreregistrationPage.upSales:
if (selectedAddOnUpgrades.isEmpty &&
state.selectedRoomUpgrade.isEmpty) {
diff --git a/comwell_key_app/lib/pregistration/cubit/preregistration_state.dart b/comwell_key_app/lib/pregistration/cubit/preregistration_state.dart
index 32ab38bb..726174ac 100644
--- a/comwell_key_app/lib/pregistration/cubit/preregistration_state.dart
+++ b/comwell_key_app/lib/pregistration/cubit/preregistration_state.dart
@@ -1,10 +1,11 @@
-import 'package:comwell_key_app/services/adyen/stored_payment_method.dart';
+
import 'package:comwell_key_app/up_sales/models/addon_upgrade.dart';
import 'package:comwell_key_app/up_sales/models/room_upgrade.dart';
import 'package:comwell_key_app/up_sales/models/up_sales.dart';
import 'package:comwell_key_app/up_sales/models/upgrade.dart';
import 'package:country_code_picker/country_code_picker.dart';
import 'package:equatable/equatable.dart';
+import 'package:payment_plugin/domain/models/stored_payment_method.dart';
import '../../profile_settings/model/user.dart';
diff --git a/comwell_key_app/lib/pregistration/pages/prereg_address_page.dart b/comwell_key_app/lib/pregistration/pages/prereg_address_page.dart
index 25682f14..0f70ddbf 100644
--- a/comwell_key_app/lib/pregistration/pages/prereg_address_page.dart
+++ b/comwell_key_app/lib/pregistration/pages/prereg_address_page.dart
@@ -112,7 +112,7 @@ class PreregAddressPage extends StatelessWidget {
const EdgeInsets.symmetric(vertical: 10, horizontal: 12),
child: DropdownButtonFormField<String>(
hint: Text("document_type".tr()),
- initialValue: state.selectedDocumentType.isNotEmpty
+ value: state.selectedDocumentType.isNotEmpty
? state.selectedDocumentType
: null,
style: Theme.of(context)
diff --git a/comwell_key_app/lib/pregistration/pages/prereg_confirmation_page.dart b/comwell_key_app/lib/pregistration/pages/prereg_confirmation_page.dart
index 77558720..2de32f1e 100644
--- a/comwell_key_app/lib/pregistration/pages/prereg_confirmation_page.dart
+++ b/comwell_key_app/lib/pregistration/pages/prereg_confirmation_page.dart
@@ -1,12 +1,11 @@
-import 'package:comwell_key_app/payment_cards/bloc/payment_cards_cubit.dart';
-import 'package:comwell_key_app/payment_cards/bloc/payment_cards_state.dart';
import 'package:comwell_key_app/pregistration/cubit/preregistration_cubit.dart';
import 'package:comwell_key_app/pregistration/components/information_card.dart';
import 'package:comwell_key_app/themes/light_theme.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
-import 'package:flutter_svg/svg.dart';
+import 'package:payment_plugin/presentation/app/bloc/payment_cards_cubit.dart';
+import 'package:payment_plugin/presentation/app/bloc/payment_cards_state.dart';
class PreregConfirmationPage extends StatelessWidget {
const PreregConfirmationPage({super.key});
@@ -91,60 +90,6 @@ class PreregConfirmationPage extends StatelessWidget {
),
),
const SizedBox(height: 12),
- InformationCard(
- title: "payment_card_profile_menu".tr(),
- titleStyle: theme.textTheme.titleMedium?.copyWith(
- fontWeight: FontWeight.w500,
- color: colorHeadlineText,
- ),
- onEditClick: cubit.onEditPaymentMethodCLickked,
- child: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Row(
- mainAxisAlignment: MainAxisAlignment.start,
- children: [
- SvgPicture.asset(
- cubit.paymentMethodImage,
- height: 30),
- const SizedBox(width: 12),
- Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Text(
- cubit.state.selectedPaymentMethod?.holderName ??
- '',
- style: theme.textTheme.bodyMedium),
- Row(
- spacing: 4,
- children: [
- ...'•••• •••• •••• '.characters.map((char) {
- if (char == ' ') {
- return const SizedBox(width: 4);
- }
- return Container(
- width: 5,
- height: 5,
- decoration: const BoxDecoration(
- shape: BoxShape.circle,
- color: colorTertiary,
- ),
- );
- }),
- Text(
- cubit.state.selectedPaymentMethod?.lastFour ?? '',
- style: theme.textTheme.bodyMedium,
- ),
- ],
- ),
- ],
- ),
- ],
- ),
- ],
- ),
- ),
- const SizedBox(height: 12),
InformationCard(
title: extrasTitleText,
titleStyle: theme.textTheme.titleMedium?.copyWith(
diff --git a/comwell_key_app/lib/pregistration/pregistration_repository.dart b/comwell_key_app/lib/pregistration/pregistration_repository.dart
index ed9bd0c9..ca97b728 100644
--- a/comwell_key_app/lib/pregistration/pregistration_repository.dart
+++ b/comwell_key_app/lib/pregistration/pregistration_repository.dart
@@ -1,12 +1,7 @@
-import 'package:adyen_checkout/adyen_checkout.dart';
-import 'package:comwell_key_app/check_out/models/payment_configurations.dart';
import 'package:comwell_key_app/pregistration/prereg_request_model.dart';
-import 'package:comwell_key_app/profile/utils/constants.dart';
import 'package:comwell_key_app/profile_settings/repostiory/profile_settings_repository.dart';
-import 'package:comwell_key_app/services/adyen/stored_payment_method.dart';
import 'package:comwell_key_app/services/api.dart';
import 'package:comwell_key_app/utils/locator.dart';
-import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
class PreregistrationRepository {
@@ -16,7 +11,7 @@ class PreregistrationRepository {
PreregistrationRepository();
- Future<PaymentConfigurations?> sessionCheckout(
+/* Future<PaymentConfigurations?> sessionCheckout(
String hotelCode,
String bookingId,
bool usePoints,
@@ -91,7 +86,7 @@ class PreregistrationRepository {
applePayConfiguration: _applePayComponentConfiguration(
clientKey, Amount(value: 0, currency: "DKK"), true),
isFullyPaidWithPoints: false);
- }
+ } */
Future<dynamic> createPreregistration(PreregRequestDto request) async {
try {
@@ -104,7 +99,8 @@ class PreregistrationRepository {
return null;
}
}
-
+}
+/*
CardComponentConfiguration cardComponentConfiguration(
Amount amount,
String clientKey,
@@ -207,3 +203,4 @@ Map<String, dynamic>? _findPaymentMethod(
}
return null;
}
+*/
\ No newline at end of file
diff --git a/comwell_key_app/lib/pregistration/preregistration_flow.dart b/comwell_key_app/lib/pregistration/preregistration_flow.dart
index a45a15b2..1fb04148 100644
--- a/comwell_key_app/lib/pregistration/preregistration_flow.dart
+++ b/comwell_key_app/lib/pregistration/preregistration_flow.dart
@@ -1,13 +1,16 @@
import 'package:comwell_key_app/common/components/comwell_app_bar.dart';
-import 'package:comwell_key_app/payment_cards/bloc/payment_cards_cubit.dart';
-import 'package:comwell_key_app/payment_cards/bloc/payment_cards_state.dart';
+
import 'package:comwell_key_app/pregistration/cubit/preregistration_state.dart';
import 'package:comwell_key_app/pregistration/cubit/preregistration_cubit.dart';
import 'package:comwell_key_app/pregistration/utils/utils.dart';
import 'package:comwell_key_app/themes/light_theme.dart';
+import 'package:comwell_key_app/utils/locator.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:go_router/go_router.dart';
+import 'package:payment_plugin/domain/repositories/adyen_repository.dart';
+import 'package:payment_plugin/presentation/app/bloc/payment_cards_cubit.dart';
+import 'package:payment_plugin/presentation/app/bloc/payment_cards_state.dart';
import '../common/components/shimmer_loader/prereg_flow_shimmer_loader.dart';
class PreregistrationFlow extends StatefulWidget {
@@ -22,6 +25,7 @@ class _PreregistrationFlowState extends State<PreregistrationFlow> {
Widget build(BuildContext context) {
return BlocProvider<PaymentCardsCubit>(
create: (context) => PaymentCardsCubit(
+ adyenRepository: locator<AdyenRepository>(),
onPaymentMethodSelected: (paymentMethod) {
context
.read<PreregistrationCubit>()
@@ -86,14 +90,9 @@ class _PreregistrationFlowState extends State<PreregistrationFlow> {
16.0, 16.0, 16.0, 40.0),
child: ElevatedButton(
onPressed: () {
- if (cubit.currentPage == PreregistrationPage.payment) {
- final success = cubit.onPaymentContinueClicked();
- if (!success) {
- context.read<PaymentCardsCubit>().setMissingPaymentMethodError();
- }
- } else {
+
cubit.onContinueClicked(context);
- }
+
},
style: ElevatedButton.styleFrom(
foregroundColor: colorBackground),
diff --git a/comwell_key_app/lib/pregistration/utils/utils.dart b/comwell_key_app/lib/pregistration/utils/utils.dart
index d28aa9c6..73a58a55 100644
--- a/comwell_key_app/lib/pregistration/utils/utils.dart
+++ b/comwell_key_app/lib/pregistration/utils/utils.dart
@@ -1,4 +1,3 @@
-import 'package:comwell_key_app/payment_cards/payment_cards_page.dart';
import 'package:comwell_key_app/pregistration/pages/prereg_address_page.dart';
import 'package:comwell_key_app/pregistration/pages/prereg_confirmation_page.dart';
import 'package:comwell_key_app/pregistration/pages/prereg_profile_page.dart';
@@ -8,7 +7,7 @@ import 'package:flutter/material.dart';
enum PreregistrationPage {
profile,
address,
- payment,
+ //payment,
upSales,
confirmation;
@@ -23,8 +22,8 @@ enum PreregistrationPage {
return PreregProfilePage(key: key);
case PreregistrationPage.address:
return PreregAddressPage(key: key);
- case PreregistrationPage.payment:
- return const PaymentCardsPage();
+ /* case PreregistrationPage.payment:
+ return const PaymentCardsPage(); */
case PreregistrationPage.upSales:
return const PreregUpSalesCatalogPage();
case PreregistrationPage.confirmation:
diff --git a/comwell_key_app/lib/profile/components/comwell_club_container.dart b/comwell_key_app/lib/profile/components/comwell_club_container.dart
index d2bd505f..cc04bc00 100644
--- a/comwell_key_app/lib/profile/components/comwell_club_container.dart
+++ b/comwell_key_app/lib/profile/components/comwell_club_container.dart
@@ -1,8 +1,10 @@
import 'package:comwell_key_app/profile/components/comwell_club_signup_bottom_sheet.dart';
+import 'package:comwell_key_app/profile/cubit/profile_cubit.dart';
import 'package:comwell_key_app/profile_settings/model/user.dart';
import 'package:comwell_key_app/themes/light_theme.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
class ComwellClubContainer extends StatelessWidget {
final User user;
@@ -11,6 +13,8 @@ class ComwellClubContainer extends StatelessWidget {
@override
Widget build(BuildContext context) {
+ final cubit = context.read<ProfileCubit>();
+
return Container(
margin: const EdgeInsets.symmetric(horizontal: 16),
padding: const EdgeInsets.symmetric(vertical: 16),
@@ -28,10 +32,13 @@ class ComwellClubContainer extends StatelessWidget {
context: context,
isScrollControlled: true,
backgroundColor: Colors.white,
- builder: (context) => ComwellClubSignupBottomSheet(user: user),
+ builder: (_) => BlocProvider.value(
+ value: cubit,
+ child: ComwellClubSignupBottomSheet(user: user),
+ ),
);
if (context.mounted) {
- onSignupClick;
+ onSignupClick();
}
},
child: Column(
diff --git a/comwell_key_app/lib/profile/cubit/profile_cubit.dart b/comwell_key_app/lib/profile/cubit/profile_cubit.dart
index 87a70e81..d517bbde 100644
--- a/comwell_key_app/lib/profile/cubit/profile_cubit.dart
+++ b/comwell_key_app/lib/profile/cubit/profile_cubit.dart
@@ -60,7 +60,7 @@ class ProfileCubit extends Cubit<ProfileState> {
sendPageViewEvent();
if (!isClosed) emit(ProfileState(user: user, isLoading: false));
} catch (e) {
- if (!isClosed) emit(state.setError(Error()));
+ if (!isClosed) emit(ProfileState(isLoading: false, error: Error()));
}
}
@@ -71,7 +71,7 @@ class ProfileCubit extends Cubit<ProfileState> {
if (!isClosed) emit(ProfileState(user: user, isLoading: false));
} catch (e) {
- if (!isClosed) emit(state.setError(Error()));
+ if (!isClosed) emit(ProfileState(isLoading: false, error: Error()));
}
}
@@ -95,6 +95,14 @@ class ProfileCubit extends Cubit<ProfileState> {
}
Future<dynamic> onComwellClubSignupClick(User user) async {
- return await profileRepository.signupForComwellClub(user);
+ try {
+ emit(const ProfileState(isLoading: true, user: null, error: null));
+ User updatedUser = await profileRepository.signupForComwellClub(user);
+ emit(ProfileState(user: updatedUser, isLoading: false, error: null));
+ return updatedUser;
+ } catch (e) {
+ emit(ProfileState(isLoading: false, error: Error()));
+ rethrow;
+ }
}
}
diff --git a/comwell_key_app/lib/profile/cubit/profile_state.dart b/comwell_key_app/lib/profile/cubit/profile_state.dart
index d8b93cf1..173bdbac 100644
--- a/comwell_key_app/lib/profile/cubit/profile_state.dart
+++ b/comwell_key_app/lib/profile/cubit/profile_state.dart
@@ -10,7 +10,7 @@ class ProfileState extends Equatable {
const ProfileState({this.error, required this.isLoading, this.user, this.isToSAccepted = false, this.isNewsletterAccepted = false});
@override
- List<Object> get props => [isLoading, isToSAccepted, isNewsletterAccepted];
+ List<Object?> get props => [isLoading, isToSAccepted, isNewsletterAccepted, error, user];
const ProfileState.initial()
: isLoading = false,
diff --git a/comwell_key_app/lib/profile_settings/model/user.dart b/comwell_key_app/lib/profile_settings/model/user.dart
index c332f027..4e89e862 100644
--- a/comwell_key_app/lib/profile_settings/model/user.dart
+++ b/comwell_key_app/lib/profile_settings/model/user.dart
@@ -23,7 +23,7 @@ class User {
final DateTime? signUpDate;
final String? signUpCampaign;
final String? signUpSource;
- final String locale;
+ final String? locale;
final bool wasRecentlyCreated;
final String? nationality;
@@ -50,7 +50,7 @@ class User {
this.signUpDate,
this.signUpCampaign,
this.signUpSource,
- required this.locale,
+ this.locale,
required this.wasRecentlyCreated,
this.nationality,
});
diff --git a/comwell_key_app/lib/routing/app_router.dart b/comwell_key_app/lib/routing/app_router.dart
index db62cceb..735e24c8 100644
--- a/comwell_key_app/lib/routing/app_router.dart
+++ b/comwell_key_app/lib/routing/app_router.dart
@@ -13,6 +13,7 @@ import 'package:comwell_key_app/choose_share_room/choose_share_room_repository.d
import 'package:comwell_key_app/choose_share_room/cubit/choose_share_room_cubit.dart';
import 'package:comwell_key_app/choose_share_room/pages/room_info_page.dart';
import 'package:comwell_key_app/choose_share_room/pages/share_room_page.dart';
+import 'package:comwell_key_app/common/components/comwell_app_bar.dart';
import 'package:comwell_key_app/common/const.dart';
import 'package:comwell_key_app/contact/contact_page.dart';
import 'package:comwell_key_app/contact/cubit/contact_cubit.dart';
@@ -43,9 +44,6 @@ import 'package:comwell_key_app/overview/overview_page.dart';
import 'package:comwell_key_app/overview/past_cancelled_booking_detail_page.dart';
import 'package:comwell_key_app/overview/cubit/overview_cubit.dart';
import 'package:comwell_key_app/overview/repository/overview_repository.dart';
-import 'package:comwell_key_app/payment/payment_processing_page.dart';
-import 'package:comwell_key_app/payment_cards/bloc/payment_cards_cubit.dart';
-import 'package:comwell_key_app/payment_cards/payment_cards_page.dart';
import 'package:comwell_key_app/pregistration/cubit/preregistration_cubit.dart';
import 'package:comwell_key_app/pregistration/preregistration_flow.dart';
import 'package:comwell_key_app/profile/cubit/profile_cubit.dart';
@@ -62,7 +60,6 @@ import 'package:comwell_key_app/received_shared_room/received_shared_room_page.d
import 'package:comwell_key_app/redeem_debug/redeem_page.dart';
import 'package:comwell_key_app/routing/app_routes.dart';
import 'package:comwell_key_app/routing/go_router_observer.dart';
-import 'package:comwell_key_app/payment/cubit/payment_cubit.dart';
import 'package:comwell_key_app/share/cubit/share_booking_cubit.dart';
import 'package:comwell_key_app/share/share_booking_page.dart';
import 'package:comwell_key_app/up_sales/cubit/up_sales_cubit.dart';
@@ -83,6 +80,11 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:go_router/go_router.dart';
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
+import 'package:payment_plugin/domain/repositories/adyen_repository.dart';
+import 'package:payment_plugin/presentation/app/bloc/payment_cards_cubit.dart';
+import 'package:payment_plugin/presentation/app/bloc/payment_cubit.dart';
+import 'package:payment_plugin/presentation/screens/payment_cards_page.dart';
+import 'package:payment_plugin/presentation/screens/payment_processing_page.dart';
import '../hotel_information/repository/hotel_information_repository.dart';
import '../utils/locator.dart';
@@ -374,11 +376,11 @@ final router = GoRouter(
name: AppRoutes.paymentCards.name,
builder: (context, state) {
return BlocProvider(
- create: (context) => PaymentCardsCubit(),
+ create: (context) => PaymentCardsCubit(adyenRepository: locator<AdyenRepository>()),
child: Builder(builder: (context) {
final scaffold =
state.uri.queryParameters[needsScaffold] == 'true';
- return PaymentCardsPage(needScaffold: scaffold);
+ return PaymentCardsPage(needScaffold: scaffold, appBar: const ComwellAppBar(shouldShowProfileButton: false));
}));
}),
GoRoute(
diff --git a/comwell_key_app/lib/services/adyen/adyen_amount.dart b/comwell_key_app/lib/services/adyen/adyen_amount.dart
deleted file mode 100644
index 2bfeaec8..00000000
--- a/comwell_key_app/lib/services/adyen/adyen_amount.dart
+++ /dev/null
@@ -1,21 +0,0 @@
-import 'package:comwell_key_app/utils/json.dart';
-import 'package:json_annotation/json_annotation.dart';
-
-part '../../.generated/services/adyen/adyen_amount.g.dart';
-
-@JsonSerializable()
-final class AdyenAmount {
- final String currency;
- final int value;
-
- factory AdyenAmount.fromJson(Json json) => _$AdyenAmountFromJson(json);
-
- factory AdyenAmount.zeroPayment() => AdyenAmount(currency: "DKK", value: 0);
-
- AdyenAmount({
- required this.currency,
- required this.value,
- });
-
- Json toJson() => _$AdyenAmountToJson(this);
-}
diff --git a/comwell_key_app/lib/services/adyen/payment_event_handler.dart b/comwell_key_app/lib/services/adyen/payment_event_handler.dart
deleted file mode 100644
index 38762a8d..00000000
--- a/comwell_key_app/lib/services/adyen/payment_event_handler.dart
+++ /dev/null
@@ -1,80 +0,0 @@
-import 'package:adyen_checkout/adyen_checkout.dart';
-import 'package:comwell_key_app/utils/json.dart';
-
-class PaymentEventHandler {
- //Response keys
- static const errorCodeKey = "errorCode";
- static const resultCodeKey = "resultCode";
- static const actionKey = "action";
- static const messageKey = "message";
- static const refusalReasonKey = "refusalReason";
-
- PaymentEvent handleResponse({
- required Json jsonResponse,
- Json updatedPaymentMethodsJson = const {},
- }) {
- if (_isError(jsonResponse)) {
- final message = jsonResponse[messageKey] as String?;
- final reason = jsonResponse[refusalReasonKey] as String;
- return Error(
- errorMessage: message,
- reason: message ?? reason,
- dismissDropIn: true,
- );
- }
-
- if (_isRefusedInPartialPaymentFlow(jsonResponse)) {
- return Error(
- errorMessage: "Payment is refused",
- reason: "Refused",
- dismissDropIn: true,
- );
- }
-
- if (_isAction(jsonResponse)) {
- return Action(actionResponse: jsonResponse[actionKey] as Json);
- }
-
- if (_isNonFullyPaidOrder(jsonResponse) &&
- updatedPaymentMethodsJson.isNotEmpty) {
- return Update(
- orderJson: jsonResponse["order"] as Json,
- paymentMethodsJson: updatedPaymentMethodsJson,
- );
- }
-
- if (jsonResponse.containsKey(resultCodeKey)) {
- return Finished(resultCode: jsonResponse[resultCodeKey] as String);
- }
-
- return Finished(resultCode: "EMPTY");
- }
-
- bool _isError(Json jsonResponse) {
- final hasErrorCodeKey = jsonResponse.containsKey(errorCodeKey);
- final hasErrorResultCode = (jsonResponse[resultCodeKey] as String?)
- ?.toUpperCase()
- .contains(ResultCode.error.name.toUpperCase()) ??
- false;
- return hasErrorCodeKey || hasErrorResultCode;
- }
-
- bool _isRefusedInPartialPaymentFlow(Json jsonResponse) =>
- _isRefused(jsonResponse) && _isNonFullyPaidOrder(jsonResponse);
-
- bool _isRefused(Json jsonResponse) => jsonResponse[resultCodeKey]
- .toString()
- .toUpperCase()
- .contains(ResultCode.refused.name.toUpperCase());
-
- bool _isAction(Json jsonResponse) => jsonResponse.containsKey(actionKey);
-
- bool _isNonFullyPaidOrder(Json jsonResponse) {
- if (jsonResponse.containsKey("order")) {
- final remainingAmount = jsonResponse["order"]["remainingAmount"]["value"] as num;
- return remainingAmount > 0;
- } else {
- return false;
- }
- }
-}
diff --git a/comwell_key_app/lib/services/adyen/payment_method.dart b/comwell_key_app/lib/services/adyen/payment_method.dart
deleted file mode 100644
index dfa4dfa4..00000000
--- a/comwell_key_app/lib/services/adyen/payment_method.dart
+++ /dev/null
@@ -1,39 +0,0 @@
-import 'package:comwell_key_app/utils/json.dart';
-import 'package:json_annotation/json_annotation.dart';
-
-part '../../.generated/services/adyen/payment_method.g.dart';
-
-@JsonSerializable()
-class PaymentMethod {
- final String? brand;
- final String? cvc;
- final String? encryptedCard;
- final String? encryptedCardNumber;
- final String? encryptedExpiryMonth;
- final String? encryptedExpiryYear;
- final String? encryptedSecurityCode;
- final String? expiryMonth;
- final String? expiryYear;
- final String? holderName;
- final String? number;
- final String? type;
-
- PaymentMethod({
- this.brand,
- this.cvc,
- this.encryptedCard,
- this.encryptedCardNumber,
- this.encryptedExpiryMonth,
- this.encryptedExpiryYear,
- this.encryptedSecurityCode,
- this.expiryMonth,
- this.expiryYear,
- this.holderName,
- this.number,
- this.type,
- });
-
- factory PaymentMethod.fromJson(Json json) => _$PaymentMethodFromJson(json);
-
- Json toJson() => _$PaymentMethodToJson(this);
-}
\ No newline at end of file
diff --git a/comwell_key_app/lib/services/adyen/stored_payment_method.dart b/comwell_key_app/lib/services/adyen/stored_payment_method.dart
deleted file mode 100644
index 793b2ff2..00000000
--- a/comwell_key_app/lib/services/adyen/stored_payment_method.dart
+++ /dev/null
@@ -1,37 +0,0 @@
-import 'package:comwell_key_app/utils/json.dart';
-import 'package:equatable/equatable.dart';
-import 'package:json_annotation/json_annotation.dart';
-
-part '../../.generated/services/adyen/stored_payment_method.g.dart';
-
-@JsonSerializable()
-class StoredPaymentMethod extends Equatable {
- final String brand;
- final String expiryMonth;
- final String expiryYear;
- final String holderName;
- final String id;
- final String lastFour;
-
- const StoredPaymentMethod({
- required this.brand,
- required this.expiryMonth,
- required this.expiryYear,
- required this.holderName,
- required this.id,
- required this.lastFour,
- });
-
- factory StoredPaymentMethod.fromJson(Json json) =>
- _$StoredPaymentMethodFromJson(json);
-
- Json toJson() => _$StoredPaymentMethodToJson(this);
-
- @override
- String toString() {
- return 'StoredPaymentMethod(brand: $brand, expiryMonth: $expiryMonth, expiryYear: $expiryYear, holderName: $holderName, id: $id, lastFour: $lastFour)';
- }
-
- @override
- List<Object?> get props => [id, brand, expiryMonth, expiryYear, holderName, lastFour];
-}
diff --git a/comwell_key_app/lib/services/adyen/stored_payment_methods_response.dart b/comwell_key_app/lib/services/adyen/stored_payment_methods_response.dart
deleted file mode 100644
index 399035fa..00000000
--- a/comwell_key_app/lib/services/adyen/stored_payment_methods_response.dart
+++ /dev/null
@@ -1,17 +0,0 @@
-import 'package:comwell_key_app/services/adyen/stored_payment_method.dart';
-import 'package:comwell_key_app/utils/json.dart';
-import 'package:json_annotation/json_annotation.dart';
-
-part '../../.generated/services/adyen/stored_payment_methods_response.g.dart';
-
-@JsonSerializable()
-class StoredPaymentsResponse {
- final String merchantAccount;
- final String shopperReference;
- final Iterable<StoredPaymentMethod>? storedPaymentMethods;
-
- StoredPaymentsResponse({required this.merchantAccount, required this.shopperReference, required this.storedPaymentMethods});
-
- factory StoredPaymentsResponse.fromJson(Json json) => _$StoredPaymentsResponseFromJson(json);
-
-}
\ No newline at end of file
diff --git a/comwell_key_app/lib/services/api.dart b/comwell_key_app/lib/services/api.dart
index 5dfd59fa..7be286ee 100644
--- a/comwell_key_app/lib/services/api.dart
+++ b/comwell_key_app/lib/services/api.dart
@@ -4,7 +4,6 @@ import 'package:comwell_key_app/hotel_information/models/hotel.dart';
import 'package:comwell_key_app/housekeeping/models/housekeeping.dart';
import 'package:comwell_key_app/notifications/models/notification_permission.dart';
import 'package:comwell_key_app/pregistration/prereg_request_model.dart';
-import 'package:comwell_key_app/services/adyen/payment_method.dart';
import 'package:comwell_key_app/services/http_client.dart';
import 'package:comwell_key_app/services/models/booking_dto.dart';
import 'package:comwell_key_app/services/models/simple_user_dto.dart';
@@ -20,8 +19,6 @@ import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:package_info_plus/package_info_plus.dart';
-import 'adyen/stored_payment_methods_response.dart';
-
class Api {
Dio? _dio;
@@ -79,12 +76,12 @@ class Api {
return response.data!.map((e) => BookingDTO.fromJson(e as Json)).toList();
}
- Future<StoredPaymentsResponse?> getPaymentMethods() async {
+/* Future<StoredPaymentsResponse?> getPaymentMethods() async {
final response = await dio.get<Json>(ApiEndpoints.storedPaymentMethods);
return StoredPaymentsResponse.fromJson(response.data!);
- }
+ } */
- Future<dynamic> createAdyenSession(String bookingId, bool usePoints, String hotelCode) async {
+ /* Future<dynamic> createAdyenSession(String bookingId, bool usePoints, String hotelCode) async {
final body = {
"hotelCode": hotelCode,
"bookingConfirmationNumber": bookingId,
@@ -155,7 +152,7 @@ class Api {
));
return response.data!;
}
-
+ */
Future<Response<dynamic>> fetchProfileSettings() async {
final response = await dio.get<dynamic>(ApiEndpoints.getGuestProfile);
return response;
@@ -274,7 +271,7 @@ class Api {
// TODO implement
}
- Future<Json?> postPaymentsDetails(Json body) async {
+/* Future<Json?> postPaymentsDetails(Json body) async {
final Json headers = {
"content-type": "application/json",
"x-API-key":
@@ -297,7 +294,7 @@ class Api {
headers: headers,
));
return response.data!;
- }
+ } */
Future<String> createEndpointRegistration() async {
final response =
diff --git a/comwell_key_app/lib/services/mappers/user_mapper.dart b/comwell_key_app/lib/services/mappers/user_mapper.dart
index d0d34b14..9a7f728b 100644
--- a/comwell_key_app/lib/services/mappers/user_mapper.dart
+++ b/comwell_key_app/lib/services/mappers/user_mapper.dart
@@ -33,7 +33,7 @@ extension UserMapper on UserDto {
signUpDate: signUpDate,
signUpCampaign: signUpCampaign,
signUpSource: signUpSource,
- locale: locale,
+ locale: null,
wasRecentlyCreated: wasRecentlyCreated,
nationality: nationality,
);
@@ -66,7 +66,7 @@ extension UserDtoMapper on User {
signUpDate: signUpDate,
signUpCampaign: signUpCampaign,
signUpSource: signUpSource,
- locale: locale,
+ locale: null,
wasRecentlyCreated: wasRecentlyCreated,
permissions: null, // Permissions are not stored in User model
nationality: nationality,
diff --git a/comwell_key_app/lib/services/models/user_dto.dart b/comwell_key_app/lib/services/models/user_dto.dart
index e6e9f063..a11f1907 100644
--- a/comwell_key_app/lib/services/models/user_dto.dart
+++ b/comwell_key_app/lib/services/models/user_dto.dart
@@ -28,7 +28,7 @@ class UserDto {
final DateTime? signUpDate;
final String? signUpCampaign;
final String? signUpSource;
- final String locale;
+ final String? locale;
final bool wasRecentlyCreated;
final UserPermissions? permissions;
final String? nationality;
diff --git a/comwell_key_app/lib/services/models/user_permissions.dart b/comwell_key_app/lib/services/models/user_permissions.dart
index 2eb8a2c7..cfabe26a 100644
--- a/comwell_key_app/lib/services/models/user_permissions.dart
+++ b/comwell_key_app/lib/services/models/user_permissions.dart
@@ -4,23 +4,23 @@ part '../../.generated/services/models/user_permissions.g.dart';
@JsonSerializable()
class UserPermissions {
- @JsonKey(name: 'GuestOptIn1')
+ @JsonKey(name: 'GuestOptIn1', defaultValue: false)
final bool guestOptIn1;
- @JsonKey(name: 'GuestOptIn2')
+ @JsonKey(name: 'GuestOptIn2', defaultValue: false)
final bool guestOptIn2;
- @JsonKey(name: 'GuestOptIn3')
+ @JsonKey(name: 'GuestOptIn3', defaultValue: false)
final bool guestOptIn3;
- @JsonKey(name: 'GuestOptIn4')
+ @JsonKey(name: 'GuestOptIn4', defaultValue: false)
final bool guestOptIn4;
UserPermissions({
- required this.guestOptIn1,
- required this.guestOptIn2,
- required this.guestOptIn3,
- required this.guestOptIn4,
+ this.guestOptIn1 = false,
+ this.guestOptIn2 = false,
+ this.guestOptIn3 = false,
+ this.guestOptIn4 = false,
});
factory UserPermissions.fromJson(Map<String, dynamic> json) =>
diff --git a/comwell_key_app/lib/services/utils/api_endpoints.dart b/comwell_key_app/lib/services/utils/api_endpoints.dart
index 8eecbd55..59154815 100644
--- a/comwell_key_app/lib/services/utils/api_endpoints.dart
+++ b/comwell_key_app/lib/services/utils/api_endpoints.dart
@@ -19,22 +19,6 @@ class ApiEndpoints {
//Default endpoints
static const String orderHousekeeping = '/Content/v1/api/v1/house-keeping';
- // Payment endpoints
- static const String storedPaymentMethods =
- '/Payment/v1/stored-payment-methods';
- static const String createAdyenSession =
- '/Payment/v1/CreateAdyenSessionForBooking';
- static const String createAdyenSessionForCards =
- '/Payment/v1/CreatePaymentSessionForAddingCards';
- static const String adyenPayments =
- 'https://checkout-test.adyen.com/v71/payments';
- static const String adyenPaymentsDetails =
- 'https://checkout-test.adyen.com/v71/payments/details';
- static const String returnUrl =
- 'adyencheckout://com.comwell.phoenix.test/adyenPayment';
- static const String listAvailablePaymentMethods =
- '/Payment/v1/list-available-payment-methods';
-
// Member endpoints
static const String getGuestProfile = '/Members/v1/GetGuestProfile';
static const String deleteGuest = '/Members/v1/DeleteGuest';
diff --git a/comwell_key_app/lib/utils/locator.dart b/comwell_key_app/lib/utils/locator.dart
index cfaf30a5..09e62ad4 100644
--- a/comwell_key_app/lib/utils/locator.dart
+++ b/comwell_key_app/lib/utils/locator.dart
@@ -13,6 +13,7 @@ import 'package:comwell_key_app/overview/repository/overview_repository.dart';
import 'package:comwell_key_app/pregistration/pregistration_repository.dart';
import 'package:comwell_key_app/profile/profile_repository.dart';
import 'package:comwell_key_app/profile_settings/repostiory/profile_settings_repository.dart';
+import 'package:comwell_key_app/services/http_client.dart';
import 'package:comwell_key_app/share/share_booking_repository.dart';
import 'package:comwell_key_app/push_notifications/push_notification_repository.dart';
import 'package:comwell_key_app/tracking/comwell_tracking.dart';
@@ -24,6 +25,7 @@ import 'package:flutter/material.dart';
import 'package:device_info_plus/device_info_plus.dart';
import 'package:flutter/foundation.dart';
import 'package:get_it/get_it.dart';
+import 'package:payment_plugin/domain/repositories/adyen_repository.dart';
import 'package:seos_mobile_keys_plugin/seos_mobile_keys_plugin.dart';
import '../booking_details/booking_details_repository.dart';
@@ -78,5 +80,6 @@ void setupLocator() {
locator.registerSingleton<SecureStorage>(SecureStorage());
locator.registerFactory<HouseKeepingRepository>(() => HouseKeepingRepository());
locator.registerFactory<PushNotificationRepository>(() => PushNotificationRepository());
- }
+ locator.registerFactory<AdyenRepository>(() => AdyenRepository(dio: HttpClient().dio,
+ ));}
}
diff --git a/comwell_key_app/lib/utils/seos_repository.dart b/comwell_key_app/lib/utils/seos_repository.dart
index 4ab55cce..c53c0dec 100644
--- a/comwell_key_app/lib/utils/seos_repository.dart
+++ b/comwell_key_app/lib/utils/seos_repository.dart
@@ -1,7 +1,7 @@
import 'package:comwell_key_app/services/api.dart';
import 'package:comwell_key_app/utils/locator.dart';
import 'package:comwell_key_app/utils/secure_storage.dart';
-import 'package:flutter/gestures.dart';
+
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
diff --git a/comwell_key_app/pubspec.yaml b/comwell_key_app/pubspec.yaml
index ad19ce20..52b2df1c 100644
--- a/comwell_key_app/pubspec.yaml
+++ b/comwell_key_app/pubspec.yaml
@@ -2,7 +2,7 @@ name: comwell_key_app
description: This app needs a description
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
-version: 0.0.2+19
+version: 0.0.2+21
environment:
sdk: '>=3.5.0 <4.0.0'
dependencies:
@@ -10,15 +10,17 @@ dependencies:
sdk: flutter
seos_mobile_keys_plugin:
path: ../mobilekeys_sdk_plugin
+ payment_plugin:
+ path: ../payment_plugin
cupertino_icons: ^1.0.2
focus_detector: ^2.0.0
permission_handler: ^11.3.0
- go_router: ^14.7.2
+ go_router: ^16.2.0
bloc: ^9.1.0
flutter_bloc: ^9.1.1
easy_localization: ^3.0.7
- dotenv: ^4.2.0
- flutter_dotenv: ^5.1.0
+
+ flutter_dotenv: ^6.0.0
equatable: ^2.0.5
flutter_secure_storage: ^9.2.2
lottie: ^3.1.2
diff --git a/mobilekeys_sdk_plugin/android/.gradle/8.1.1/checksums/checksums.lock b/mobilekeys_sdk_plugin/android/.gradle/8.1.1/checksums/checksums.lock
index 12f72781..e8086aea 100644
Binary files a/mobilekeys_sdk_plugin/android/.gradle/8.1.1/checksums/checksums.lock and b/mobilekeys_sdk_plugin/android/.gradle/8.1.1/checksums/checksums.lock differ
diff --git a/mobilekeys_sdk_plugin/android/.gradle/8.1.1/checksums/md5-checksums.bin b/mobilekeys_sdk_plugin/android/.gradle/8.1.1/checksums/md5-checksums.bin
index 83d43322..62e14f09 100644
Binary files a/mobilekeys_sdk_plugin/android/.gradle/8.1.1/checksums/md5-checksums.bin and b/mobilekeys_sdk_plugin/android/.gradle/8.1.1/checksums/md5-checksums.bin differ
diff --git a/mobilekeys_sdk_plugin/android/.gradle/8.1.1/checksums/sha1-checksums.bin b/mobilekeys_sdk_plugin/android/.gradle/8.1.1/checksums/sha1-checksums.bin
index 6a44d5d0..3476a7fd 100644
Binary files a/mobilekeys_sdk_plugin/android/.gradle/8.1.1/checksums/sha1-checksums.bin and b/mobilekeys_sdk_plugin/android/.gradle/8.1.1/checksums/sha1-checksums.bin differ
diff --git a/payment_plugin/.fvmrc b/payment_plugin/.fvmrc
new file mode 100644
index 00000000..d70a803f
--- /dev/null
+++ b/payment_plugin/.fvmrc
@@ -0,0 +1,3 @@
+{
+ "flutter": "3.35.6"
+}
\ No newline at end of file
diff --git a/payment_plugin/.gitignore b/payment_plugin/.gitignore
new file mode 100644
index 00000000..6278dfed
--- /dev/null
+++ b/payment_plugin/.gitignore
@@ -0,0 +1,36 @@
+# Miscellaneous
+*.class
+*.log
+*.pyc
+*.swp
+.DS_Store
+.atom/
+.build/
+.buildlog/
+.history
+.svn/
+.swiftpm/
+migrate_working_dir/
+
+# IntelliJ related
+*.iml
+*.ipr
+*.iws
+.idea/
+
+# The .vscode folder contains launch configuration and tasks you configure in
+# VS Code which you may wish to be included in version control, so this line
+# is commented out by default.
+#.vscode/
+
+# Flutter/Dart/Pub related
+# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
+/pubspec.lock
+**/doc/api/
+.dart_tool/
+.flutter-plugins
+.flutter-plugins-dependencies
+build/
+
+# FVM Version Cache
+.fvm/
\ No newline at end of file
diff --git a/payment_plugin/.metadata b/payment_plugin/.metadata
new file mode 100644
index 00000000..0bb5e10c
--- /dev/null
+++ b/payment_plugin/.metadata
@@ -0,0 +1,36 @@
+# This file tracks properties of this Flutter project.
+# Used by Flutter tool to assess capabilities and perform upgrades etc.
+#
+# This file should be version controlled and should not be manually edited.
+
+version:
+ revision: "b25305a8832cfc6ba632a7f87ad455e319dccce8"
+ channel: "stable"
+
+project_type: plugin
+
+# Tracks metadata for the flutter migrate command
+migration:
+ platforms:
+ - platform: root
+ create_revision: b25305a8832cfc6ba632a7f87ad455e319dccce8
+ base_revision: b25305a8832cfc6ba632a7f87ad455e319dccce8
+ - platform: android
+ create_revision: b25305a8832cfc6ba632a7f87ad455e319dccce8
+ base_revision: b25305a8832cfc6ba632a7f87ad455e319dccce8
+ - platform: ios
+ create_revision: b25305a8832cfc6ba632a7f87ad455e319dccce8
+ base_revision: b25305a8832cfc6ba632a7f87ad455e319dccce8
+ - platform: web
+ create_revision: b25305a8832cfc6ba632a7f87ad455e319dccce8
+ base_revision: b25305a8832cfc6ba632a7f87ad455e319dccce8
+
+ # User provided section
+
+ # List of Local paths (relative to this file) that should be
+ # ignored by the migrate tool.
+ #
+ # Files that are not part of the templates will be ignored by default.
+ unmanaged_files:
+ - 'lib/main.dart'
+ - 'ios/Runner.xcodeproj/project.pbxproj'
diff --git a/payment_plugin/.vscode/settings.json b/payment_plugin/.vscode/settings.json
new file mode 100644
index 00000000..f6a4c367
--- /dev/null
+++ b/payment_plugin/.vscode/settings.json
@@ -0,0 +1,3 @@
+{
+ "dart.flutterSdkPath": ".fvm/versions/3.35.6"
+}
\ No newline at end of file
diff --git a/payment_plugin/CHANGELOG.md b/payment_plugin/CHANGELOG.md
new file mode 100644
index 00000000..41cc7d81
--- /dev/null
+++ b/payment_plugin/CHANGELOG.md
@@ -0,0 +1,3 @@
+## 0.0.1
+
+* TODO: Describe initial release.
diff --git a/payment_plugin/LICENSE b/payment_plugin/LICENSE
new file mode 100644
index 00000000..ba75c69f
--- /dev/null
+++ b/payment_plugin/LICENSE
@@ -0,0 +1 @@
+TODO: Add your license here.
diff --git a/payment_plugin/README.md b/payment_plugin/README.md
new file mode 100644
index 00000000..6f222e8c
--- /dev/null
+++ b/payment_plugin/README.md
@@ -0,0 +1,15 @@
+# payment_plugin
+
+A new Flutter plugin project.
+
+## Getting Started
+
+This project is a starting point for a Flutter
+[plug-in package](https://flutter.dev/to/develop-plugins),
+a specialized package that includes platform-specific implementation code for
+Android and/or iOS.
+
+For help getting started with Flutter development, view the
+[online documentation](https://docs.flutter.dev), which offers tutorials,
+samples, guidance on mobile development, and a full API reference.
+
diff --git a/payment_plugin/SETUP_GUIDE.md b/payment_plugin/SETUP_GUIDE.md
new file mode 100644
index 00000000..cae8e068
--- /dev/null
+++ b/payment_plugin/SETUP_GUIDE.md
@@ -0,0 +1,163 @@
+# Payment Plugin Setup Guide
+
+## ✅ Plugin Setup Complete!
+
+Your Adyen payment plugin is now ready to use with token authentication from your main app.
+
+## 🔧 How It Works
+
+The plugin uses **Dio** with an **interceptor** that automatically adds your auth token to every API request.
+
+### Architecture
+
+```
+Main App → Token Provider → Plugin (Dio + Interceptor) → API with Token
+```
+
+## 📝 Usage in Your Main App
+
+### 1. Add the Plugin to Your Main App's `pubspec.yaml`
+
+```yaml
+dependencies:
+ payment_plugin:
+ path: ../payment_plugin # Adjust path as needed
+```
+
+### 2. Initialize the AdyenRepository
+
+```dart
+import 'package:payment_plugin/payment_plugin.dart';
+
+// Initialize with your token provider
+final adyenRepo = AdyenRepository(
+ tokenProvider: () async {
+ // Get token from your auth service in main app
+ // This function is called every time the plugin makes an API call
+ return await yourAuthService.getToken();
+ },
+ baseUrl: 'https://your-api.com',
+);
+```
+
+### 3. Use the Repository
+
+```dart
+// Create a payment session for checkout
+final config = await adyenRepo.sessionCheckout(
+ 'hotel123', // hotelCode
+ 'booking456', // bookingId
+ false, // usePoints
+);
+
+// Add a new card
+final cardConfig = await adyenRepo.fetchSessionForAddingCard();
+
+// Get stored payment methods
+final storedMethods = await adyenRepo.mockStoredPaymentData;
+```
+
+## 🔐 Token Authentication Flow
+
+1. **Main app** calls a payment method on `adyenRepo`
+2. **Plugin** needs to make an API call
+3. **Dio interceptor** calls your `tokenProvider()` function
+4. **Main app** returns the current auth token
+5. **Interceptor** adds `Authorization: Bearer {token}` header
+6. **API request** is sent with authentication
+
+### Benefits
+
+✅ Token stays in your main app (never stored in plugin)
+✅ Works with token refresh (always gets latest token)
+✅ Automatic authentication on all requests
+✅ Clean separation of concerns
+
+## 📦 Available API Methods
+
+The plugin includes the following API endpoints:
+
+- `getPaymentMethods()` - Get stored payment methods
+- `createAdyenSession()` - Create session for booking payment
+- `createAdyenSessionForCards()` - Create session for adding cards
+- `listAvailablePaymentMethods()` - List available payment methods
+- `storePaymentMethod()` - Store a new payment method
+- `removePaymentMethod()` - Remove a payment method
+- `submitPayment()` - Submit a payment
+
+## 🏗️ Plugin Structure
+
+```
+payment_plugin/
+├── lib/
+│ ├── adyen/
+│ │ ├── adyen_repository.dart # Main repository with business logic
+│ │ ├── api_client.dart # Retrofit API client
+│ │ └── models/ # Adyen-specific models
+│ ├── data/
+│ │ └── remote/
+│ │ └── models/ # API response models
+│ └── payment_plugin.dart # Main export file
+├── android/ # Android-specific code
+├── ios/ # iOS-specific code
+└── example/ # Example usage app
+```
+
+## 🧪 Testing
+
+Run the example app to test the plugin:
+
+```bash
+cd example
+flutter run
+```
+
+## 🔄 Development Workflow
+
+When you make changes to models with `@JsonSerializable` or `@RestApi`:
+
+```bash
+# Regenerate code
+flutter pub run build_runner build --delete-conflicting-outputs
+
+# Or watch for changes
+flutter pub run build_runner watch --delete-conflicting-outputs
+```
+
+## 📱 AndroidManifest Configuration
+
+The plugin already includes necessary permissions in:
+`android/src/main/AndroidManifest.xml`
+
+For Adyen-specific configuration, you may need to add activities or permissions to your **main app's** AndroidManifest.
+
+## 🎯 Next Steps
+
+1. ✅ Plugin structure created
+2. ✅ Token authentication configured
+3. ✅ Code generation setup
+4. ✅ API client with Retrofit
+5. 🔜 Add plugin to your main app
+6. 🔜 Test with real API endpoints
+7. 🔜 Implement payment flows
+
+## 💡 Tips
+
+- The `tokenProvider` function is called for **every API request**, so make sure it's efficient
+- If you need to refresh tokens, do it in your main app's auth service, not in the plugin
+- The plugin uses **Dio** which supports interceptors, retries, and more advanced features
+- All API responses are wrapped in `ApiResponse<T>` for consistent error handling
+
+## 📚 Key Files to Know
+
+- **`lib/adyen/adyen_repository.dart`** - Your main entry point
+- **`lib/adyen/api_client.dart`** - REST API definitions
+- **`lib/payment_plugin.dart`** - Public API exports
+- **`pubspec.yaml`** - Dependencies (Dio, Retrofit, Adyen SDK)
+
+---
+
+**Ready to integrate!** 🚀
+
+For questions or issues, check the example app in `example/lib/main.dart`.
+
diff --git a/payment_plugin/analysis_options.yaml b/payment_plugin/analysis_options.yaml
new file mode 100644
index 00000000..a5744c1c
--- /dev/null
+++ b/payment_plugin/analysis_options.yaml
@@ -0,0 +1,4 @@
+include: package:flutter_lints/flutter.yaml
+
+# Additional information about this file can be found at
+# https://dart.dev/guides/language/analysis-options
diff --git a/payment_plugin/android/.gitignore b/payment_plugin/android/.gitignore
new file mode 100644
index 00000000..161bdcda
--- /dev/null
+++ b/payment_plugin/android/.gitignore
@@ -0,0 +1,9 @@
+*.iml
+.gradle
+/local.properties
+/.idea/workspace.xml
+/.idea/libraries
+.DS_Store
+/build
+/captures
+.cxx
diff --git a/payment_plugin/android/build.gradle b/payment_plugin/android/build.gradle
new file mode 100644
index 00000000..e8508362
--- /dev/null
+++ b/payment_plugin/android/build.gradle
@@ -0,0 +1,66 @@
+group = "com.example.payment_plugin"
+version = "1.0-SNAPSHOT"
+
+buildscript {
+ ext.kotlin_version = "2.1.0"
+ repositories {
+ google()
+ mavenCentral()
+ }
+
+ dependencies {
+ classpath("com.android.tools.build:gradle:8.7.3")
+ classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version")
+ }
+}
+
+allprojects {
+ repositories {
+ google()
+ mavenCentral()
+ }
+}
+
+apply plugin: "com.android.library"
+apply plugin: "kotlin-android"
+
+android {
+ namespace = "com.example.payment_plugin"
+
+ compileSdk = 35
+
+ compileOptions {
+ sourceCompatibility = JavaVersion.VERSION_11
+ targetCompatibility = JavaVersion.VERSION_11
+ }
+
+ kotlinOptions {
+ jvmTarget = JavaVersion.VERSION_11
+ }
+
+ sourceSets {
+ main.java.srcDirs += "src/main/kotlin"
+ test.java.srcDirs += "src/test/kotlin"
+ }
+
+ defaultConfig {
+ minSdk = 21
+ }
+
+ dependencies {
+ testImplementation("org.jetbrains.kotlin:kotlin-test")
+ testImplementation("org.mockito:mockito-core:5.0.0")
+ }
+
+ testOptions {
+ unitTests.all {
+ useJUnitPlatform()
+
+ testLogging {
+ events "passed", "skipped", "failed", "standardOut", "standardError"
+ outputs.upToDateWhen {false}
+ showStandardStreams = true
+ }
+ }
+ }
+}
diff --git a/payment_plugin/android/settings.gradle b/payment_plugin/android/settings.gradle
new file mode 100644
index 00000000..7e99f60f
--- /dev/null
+++ b/payment_plugin/android/settings.gradle
@@ -0,0 +1 @@
+rootProject.name = 'payment_plugin'
diff --git a/payment_plugin/android/src/main/kotlin/com/example/payment_plugin/PaymentPlugin.kt b/payment_plugin/android/src/main/kotlin/com/example/payment_plugin/PaymentPlugin.kt
new file mode 100644
index 00000000..fafed77e
--- /dev/null
+++ b/payment_plugin/android/src/main/kotlin/com/example/payment_plugin/PaymentPlugin.kt
@@ -0,0 +1,33 @@
+package com.example.payment_plugin
+
+import io.flutter.embedding.engine.plugins.FlutterPlugin
+import io.flutter.plugin.common.MethodCall
+import io.flutter.plugin.common.MethodChannel
+import io.flutter.plugin.common.MethodChannel.MethodCallHandler
+import io.flutter.plugin.common.MethodChannel.Result
+
+/** PaymentPlugin */
+class PaymentPlugin: FlutterPlugin, MethodCallHandler {
+ /// The MethodChannel that will the communication between Flutter and native Android
+ ///
+ /// This local reference serves to register the plugin with the Flutter Engine and unregister it
+ /// when the Flutter Engine is detached from the Activity
+ private lateinit var channel : MethodChannel
+
+ override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
+ channel = MethodChannel(flutterPluginBinding.binaryMessenger, "payment_plugin")
+ channel.setMethodCallHandler(this)
+ }
+
+ override fun onMethodCall(call: MethodCall, result: Result) {
+ if (call.method == "getPlatformVersion") {
+ result.success("Android ${android.os.Build.VERSION.RELEASE}")
+ } else {
+ result.notImplemented()
+ }
+ }
+
+ override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
+ channel.setMethodCallHandler(null)
+ }
+}
diff --git a/payment_plugin/android/src/test/kotlin/com/example/payment_plugin/PaymentPluginTest.kt b/payment_plugin/android/src/test/kotlin/com/example/payment_plugin/PaymentPluginTest.kt
new file mode 100644
index 00000000..4cc39c4d
--- /dev/null
+++ b/payment_plugin/android/src/test/kotlin/com/example/payment_plugin/PaymentPluginTest.kt
@@ -0,0 +1,27 @@
+package com.example.payment_plugin
+
+import io.flutter.plugin.common.MethodCall
+import io.flutter.plugin.common.MethodChannel
+import kotlin.test.Test
+import org.mockito.Mockito
+
+/*
+ * This demonstrates a simple unit test of the Kotlin portion of this plugin's implementation.
+ *
+ * Once you have built the plugin's example app, you can run these tests from the command
+ * line by running `./gradlew testDebugUnitTest` in the `example/android/` directory, or
+ * you can run them directly from IDEs that support JUnit such as Android Studio.
+ */
+
+internal class PaymentPluginTest {
+ @Test
+ fun onMethodCall_getPlatformVersion_returnsExpectedValue() {
+ val plugin = PaymentPlugin()
+
+ val call = MethodCall("getPlatformVersion", null)
+ val mockResult: MethodChannel.Result = Mockito.mock(MethodChannel.Result::class.java)
+ plugin.onMethodCall(call, mockResult)
+
+ Mockito.verify(mockResult).success("Android " + android.os.Build.VERSION.RELEASE)
+ }
+}
diff --git a/payment_plugin/assets/images/maestro.svg b/payment_plugin/assets/images/maestro.svg
new file mode 100644
index 00000000..3cc553e6
--- /dev/null
+++ b/payment_plugin/assets/images/maestro.svg
@@ -0,0 +1,7 @@
+<svg width="40" height="28" viewBox="0 0 40 28" fill="none" xmlns="http://www.w3.org/2000/svg">
+<rect x="0.5" y="0.5" width="39" height="27" rx="1.5" fill="white"/>
+<rect x="0.5" y="0.5" width="39" height="27" rx="1.5" stroke="#E0E0E0"/>
+<path d="M24.7236 6.75C28.743 6.75016 32.001 9.99603 32.001 14C32.001 18.004 28.743 21.2498 24.7236 21.25C22.9205 21.25 21.2714 20.5955 20 19.5137C18.7288 20.595 17.081 21.2499 15.2783 21.25C11.2588 21.25 8 18.0041 8 14C8 9.99594 11.2588 6.75 15.2783 6.75C17.0807 6.75007 18.7289 7.40426 20 8.48535C21.2713 7.4038 22.9207 6.75 24.7236 6.75Z" fill="#ED0006"/>
+<path d="M24.7236 6.75C28.7432 6.75 32.002 9.99594 32.002 14C32.002 18.0041 28.7432 21.25 24.7236 21.25C22.9207 21.25 21.2723 20.5953 20.001 19.5137C21.564 18.1839 22.5566 16.2081 22.5566 14C22.5566 11.7917 21.5643 9.81512 20.001 8.48535C21.2722 7.404 22.9209 6.75 24.7236 6.75Z" fill="#0099DF"/>
+<path d="M19.9922 8.48535C21.5557 9.81512 22.5479 11.7915 22.5479 14C22.5479 16.2083 21.5554 18.1839 19.9922 19.5137C18.4294 18.1839 17.4375 16.2079 17.4375 14C17.4375 11.7919 18.4291 9.81511 19.9922 8.48535Z" fill="#6C6BBD"/>
+</svg>
diff --git a/payment_plugin/assets/images/master.svg b/payment_plugin/assets/images/master.svg
new file mode 100644
index 00000000..08656527
--- /dev/null
+++ b/payment_plugin/assets/images/master.svg
@@ -0,0 +1,7 @@
+<svg width="40" height="28" viewBox="0 0 40 28" fill="none" xmlns="http://www.w3.org/2000/svg">
+<rect x="0.5" y="0.5" width="39" height="27" rx="1.5" fill="white"/>
+<rect x="0.5" y="0.5" width="39" height="27" rx="1.5" stroke="#E0E0E0"/>
+<path d="M24.7236 6.75C28.743 6.75016 32.001 9.99603 32.001 14C32.001 18.004 28.743 21.2498 24.7236 21.25C22.9205 21.25 21.2714 20.5955 20 19.5137C18.7288 20.595 17.081 21.2499 15.2783 21.25C11.2588 21.25 8 18.0041 8 14C8 9.99594 11.2588 6.75 15.2783 6.75C17.0807 6.75007 18.7289 7.40426 20 8.48535C21.2713 7.4038 22.9207 6.75 24.7236 6.75Z" fill="#ED0006"/>
+<path d="M24.7236 6.75C28.7432 6.75 32.002 9.99594 32.002 14C32.002 18.0041 28.7432 21.25 24.7236 21.25C22.9207 21.25 21.2723 20.5953 20.001 19.5137C21.564 18.1839 22.5566 16.2081 22.5566 14C22.5566 11.7917 21.5643 9.81512 20.001 8.48535C21.2722 7.404 22.9209 6.75 24.7236 6.75Z" fill="#F9A000"/>
+<path d="M19.9922 8.48535C21.5557 9.81512 22.5479 11.7915 22.5479 14C22.5479 16.2083 21.5554 18.1839 19.9922 19.5137C18.4294 18.1839 17.4375 16.2079 17.4375 14C17.4375 11.7919 18.4291 9.81511 19.9922 8.48535Z" fill="#FF5E00"/>
+</svg>
diff --git a/payment_plugin/assets/images/visa copy.svg b/payment_plugin/assets/images/visa copy.svg
new file mode 100644
index 00000000..2b852a9d
--- /dev/null
+++ b/payment_plugin/assets/images/visa copy.svg
@@ -0,0 +1,5 @@
+<svg width="40" height="28" viewBox="0 0 40 28" fill="none" xmlns="http://www.w3.org/2000/svg">
+<rect x="0.5" y="0.5" width="39" height="27" rx="1.5" fill="white"/>
+<rect x="0.5" y="0.5" width="39" height="27" rx="1.5" stroke="#E0E0E0"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M12.0003 18.43H9.57707L7.75995 11.448C7.6737 11.1268 7.49057 10.8429 7.22119 10.7091C6.54893 10.3728 5.80814 10.1051 5 9.97016V9.70136H8.90362C9.44238 9.70136 9.84645 10.1051 9.91379 10.5741L10.8566 15.6104L13.2787 9.70136H15.6345L12.0003 18.43ZM16.9799 18.43H14.6914L16.5759 9.70136H18.8644L16.9799 18.43ZM21.8256 12.1194C21.8929 11.6493 22.297 11.3805 22.7684 11.3805C23.5092 11.313 24.3161 11.448 24.9896 11.7831L25.3937 9.90379C24.7202 9.63498 23.9794 9.5 23.3072 9.5C21.086 9.5 19.4697 10.709 19.4697 12.387C19.4697 13.6636 20.6146 14.3338 21.4227 14.7376C22.297 15.1402 22.6337 15.4091 22.5664 15.8117C22.5664 16.4156 21.8929 16.6844 21.2207 16.6844C20.4125 16.6844 19.6044 16.4831 18.8648 16.1468L18.4607 18.0273C19.2688 18.3624 20.1431 18.4974 20.9513 18.4974C23.4418 18.5637 24.9896 17.3559 24.9896 15.5429C24.9896 13.2598 21.8256 13.126 21.8256 12.1194ZM32.9985 18.43L31.1814 9.70136H29.2296C28.8255 9.70136 28.4214 9.97016 28.2867 10.3728L24.9219 18.43H27.2778L27.748 17.1546H30.6426L30.912 18.43H32.9985ZM29.5677 12.052L30.2399 15.3416H28.3555L29.5677 12.052Z" fill="#172B85"/>
+</svg>
diff --git a/payment_plugin/assets/images/visa.svg b/payment_plugin/assets/images/visa.svg
new file mode 100644
index 00000000..2b852a9d
--- /dev/null
+++ b/payment_plugin/assets/images/visa.svg
@@ -0,0 +1,5 @@
+<svg width="40" height="28" viewBox="0 0 40 28" fill="none" xmlns="http://www.w3.org/2000/svg">
+<rect x="0.5" y="0.5" width="39" height="27" rx="1.5" fill="white"/>
+<rect x="0.5" y="0.5" width="39" height="27" rx="1.5" stroke="#E0E0E0"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M12.0003 18.43H9.57707L7.75995 11.448C7.6737 11.1268 7.49057 10.8429 7.22119 10.7091C6.54893 10.3728 5.80814 10.1051 5 9.97016V9.70136H8.90362C9.44238 9.70136 9.84645 10.1051 9.91379 10.5741L10.8566 15.6104L13.2787 9.70136H15.6345L12.0003 18.43ZM16.9799 18.43H14.6914L16.5759 9.70136H18.8644L16.9799 18.43ZM21.8256 12.1194C21.8929 11.6493 22.297 11.3805 22.7684 11.3805C23.5092 11.313 24.3161 11.448 24.9896 11.7831L25.3937 9.90379C24.7202 9.63498 23.9794 9.5 23.3072 9.5C21.086 9.5 19.4697 10.709 19.4697 12.387C19.4697 13.6636 20.6146 14.3338 21.4227 14.7376C22.297 15.1402 22.6337 15.4091 22.5664 15.8117C22.5664 16.4156 21.8929 16.6844 21.2207 16.6844C20.4125 16.6844 19.6044 16.4831 18.8648 16.1468L18.4607 18.0273C19.2688 18.3624 20.1431 18.4974 20.9513 18.4974C23.4418 18.5637 24.9896 17.3559 24.9896 15.5429C24.9896 13.2598 21.8256 13.126 21.8256 12.1194ZM32.9985 18.43L31.1814 9.70136H29.2296C28.8255 9.70136 28.4214 9.97016 28.2867 10.3728L24.9219 18.43H27.2778L27.748 17.1546H30.6426L30.912 18.43H32.9985ZM29.5677 12.052L30.2399 15.3416H28.3555L29.5677 12.052Z" fill="#172B85"/>
+</svg>
diff --git a/payment_plugin/build.yaml b/payment_plugin/build.yaml
new file mode 100644
index 00000000..e7c27e27
--- /dev/null
+++ b/payment_plugin/build.yaml
@@ -0,0 +1,19 @@
+targets:
+ $default:
+ builders:
+ source_gen:combining_builder:
+ options:
+ build_extensions:
+ '^lib/{{}}.dart': 'lib/_generated/{{}}.g.dart'
+ json_serializable:
+ options:
+ explicit_to_json: true
+ any_map: true
+ generate_for:
+ exclude:
+ - 'lib/database/**'
+ freezed|freezed:
+ enabled: true
+ options:
+ build_extensions:
+ '^lib/{{}}.dart': 'lib/_generated/{{}}.freezed.dart'
\ No newline at end of file
diff --git a/payment_plugin/example/.gitignore b/payment_plugin/example/.gitignore
new file mode 100644
index 00000000..79c113f9
--- /dev/null
+++ b/payment_plugin/example/.gitignore
@@ -0,0 +1,45 @@
+# Miscellaneous
+*.class
+*.log
+*.pyc
+*.swp
+.DS_Store
+.atom/
+.build/
+.buildlog/
+.history
+.svn/
+.swiftpm/
+migrate_working_dir/
+
+# IntelliJ related
+*.iml
+*.ipr
+*.iws
+.idea/
+
+# The .vscode folder contains launch configuration and tasks you configure in
+# VS Code which you may wish to be included in version control, so this line
+# is commented out by default.
+#.vscode/
+
+# Flutter/Dart/Pub related
+**/doc/api/
+**/ios/Flutter/.last_build_id
+.dart_tool/
+.flutter-plugins
+.flutter-plugins-dependencies
+.pub-cache/
+.pub/
+/build/
+
+# Symbolication related
+app.*.symbols
+
+# Obfuscation related
+app.*.map.json
+
+# Android Studio will place build artifacts here
+/android/app/debug
+/android/app/profile
+/android/app/release
diff --git a/payment_plugin/example/README.md b/payment_plugin/example/README.md
new file mode 100644
index 00000000..7c136a86
--- /dev/null
+++ b/payment_plugin/example/README.md
@@ -0,0 +1,16 @@
+# payment_plugin_example
+
+Demonstrates how to use the payment_plugin plugin.
+
+## Getting Started
+
+This project is a starting point for a Flutter application.
+
+A few resources to get you started if this is your first Flutter project:
+
+- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab)
+- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook)
+
+For help getting started with Flutter development, view the
+[online documentation](https://docs.flutter.dev/), which offers tutorials,
+samples, guidance on mobile development, and a full API reference.
diff --git a/payment_plugin/example/analysis_options.yaml b/payment_plugin/example/analysis_options.yaml
new file mode 100644
index 00000000..0d290213
--- /dev/null
+++ b/payment_plugin/example/analysis_options.yaml
@@ -0,0 +1,28 @@
+# This file configures the analyzer, which statically analyzes Dart code to
+# check for errors, warnings, and lints.
+#
+# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
+# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
+# invoked from the command line by running `flutter analyze`.
+
+# The following line activates a set of recommended lints for Flutter apps,
+# packages, and plugins designed to encourage good coding practices.
+include: package:flutter_lints/flutter.yaml
+
+linter:
+ # The lint rules applied to this project can be customized in the
+ # section below to disable rules from the `package:flutter_lints/flutter.yaml`
+ # included above or to enable additional rules. A list of all available lints
+ # and their documentation is published at https://dart.dev/lints.
+ #
+ # Instead of disabling a lint rule for the entire project in the
+ # section below, it can also be suppressed for a single line of code
+ # or a specific dart file by using the `// ignore: name_of_lint` and
+ # `// ignore_for_file: name_of_lint` syntax on the line or in the file
+ # producing the lint.
+ rules:
+ # avoid_print: false # Uncomment to disable the `avoid_print` rule
+ # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
+
+# Additional information about this file can be found at
+# https://dart.dev/guides/language/analysis-options
diff --git a/payment_plugin/example/android/.gitignore b/payment_plugin/example/android/.gitignore
new file mode 100644
index 00000000..be3943c9
--- /dev/null
+++ b/payment_plugin/example/android/.gitignore
@@ -0,0 +1,14 @@
+gradle-wrapper.jar
+/.gradle
+/captures/
+/gradlew
+/gradlew.bat
+/local.properties
+GeneratedPluginRegistrant.java
+.cxx/
+
+# Remember to never publicly share your keystore.
+# See https://flutter.dev/to/reference-keystore
+key.properties
+**/*.keystore
+**/*.jks
diff --git a/payment_plugin/example/android/app/build.gradle.kts b/payment_plugin/example/android/app/build.gradle.kts
new file mode 100644
index 00000000..7618bc05
--- /dev/null
+++ b/payment_plugin/example/android/app/build.gradle.kts
@@ -0,0 +1,44 @@
+plugins {
+ id("com.android.application")
+ id("kotlin-android")
+ // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
+ id("dev.flutter.flutter-gradle-plugin")
+}
+
+android {
+ namespace = "com.example.payment_plugin_example"
+ compileSdk = flutter.compileSdkVersion
+ ndkVersion = flutter.ndkVersion
+
+ compileOptions {
+ sourceCompatibility = JavaVersion.VERSION_11
+ targetCompatibility = JavaVersion.VERSION_11
+ }
+
+ kotlinOptions {
+ jvmTarget = JavaVersion.VERSION_11.toString()
+ }
+
+ defaultConfig {
+ // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
+ applicationId = "com.example.payment_plugin_example"
+ // You can update the following values to match your application needs.
+ // For more information, see: https://flutter.dev/to/review-gradle-config.
+ minSdk = flutter.minSdkVersion
+ targetSdk = flutter.targetSdkVersion
+ versionCode = flutter.versionCode
+ versionName = flutter.versionName
+ }
+
+ buildTypes {
+ release {
+ // TODO: Add your own signing config for the release build.
+ // Signing with the debug keys for now, so `flutter run --release` works.
+ signingConfig = signingConfigs.getByName("debug")
+ }
+ }
+}
+
+flutter {
+ source = "../.."
+}
diff --git a/payment_plugin/example/android/app/src/debug/AndroidManifest.xml b/payment_plugin/example/android/app/src/debug/AndroidManifest.xml
new file mode 100644
index 00000000..399f6981
--- /dev/null
+++ b/payment_plugin/example/android/app/src/debug/AndroidManifest.xml
@@ -0,0 +1,7 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android">
+ <!-- The INTERNET permission is required for development. Specifically,
+ the Flutter tool needs it to communicate with the running application
+ to allow setting breakpoints, to provide hot reload, etc.
+ -->
+ <uses-permission android:name="android.permission.INTERNET"/>
+</manifest>
diff --git a/payment_plugin/example/android/app/src/main/AndroidManifest.xml b/payment_plugin/example/android/app/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..f2063c6d
--- /dev/null
+++ b/payment_plugin/example/android/app/src/main/AndroidManifest.xml
@@ -0,0 +1,45 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android">
+ <application
+ android:label="payment_plugin_example"
+ android:name="${applicationName}"
+ android:icon="@mipmap/ic_launcher">
+ <activity
+ android:name=".MainActivity"
+ android:exported="true"
+ android:launchMode="singleTop"
+ android:taskAffinity=""
+ android:theme="@style/LaunchTheme"
+ android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
+ android:hardwareAccelerated="true"
+ android:windowSoftInputMode="adjustResize">
+ <!-- Specifies an Android theme to apply to this Activity as soon as
+ the Android process has started. This theme is visible to the user
+ while the Flutter UI initializes. After that, this theme continues
+ to determine the Window background behind the Flutter UI. -->
+ <meta-data
+ android:name="io.flutter.embedding.android.NormalTheme"
+ android:resource="@style/NormalTheme"
+ />
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
+ <!-- Don't delete the meta-data below.
+ This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
+ <meta-data
+ android:name="flutterEmbedding"
+ android:value="2" />
+ </application>
+ <!-- Required to query activities that can process text, see:
+ https://developer.android.com/training/package-visibility and
+ https://developer.android.com/reference/android/content/Intent#ACTION_PROCESS_TEXT.
+
+ In particular, this is used by the Flutter engine in io.flutter.plugin.text.ProcessTextPlugin. -->
+ <queries>
+ <intent>
+ <action android:name="android.intent.action.PROCESS_TEXT"/>
+ <data android:mimeType="text/plain"/>
+ </intent>
+ </queries>
+</manifest>
diff --git a/payment_plugin/example/android/app/src/main/kotlin/com/example/payment_plugin_example/MainActivity.kt b/payment_plugin/example/android/app/src/main/kotlin/com/example/payment_plugin_example/MainActivity.kt
new file mode 100644
index 00000000..474bb6af
--- /dev/null
+++ b/payment_plugin/example/android/app/src/main/kotlin/com/example/payment_plugin_example/MainActivity.kt
@@ -0,0 +1,5 @@
+package com.example.payment_plugin_example
+
+import io.flutter.embedding.android.FlutterActivity
+
+class MainActivity : FlutterActivity()
diff --git a/payment_plugin/example/android/app/src/main/res/drawable-v21/launch_background.xml b/payment_plugin/example/android/app/src/main/res/drawable-v21/launch_background.xml
new file mode 100644
index 00000000..f74085f3
--- /dev/null
+++ b/payment_plugin/example/android/app/src/main/res/drawable-v21/launch_background.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Modify this file to customize your launch splash screen -->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:drawable="?android:colorBackground" />
+
+ <!-- You can insert your own image assets here -->
+ <!-- <item>
+ <bitmap
+ android:gravity="center"
+ android:src="@mipmap/launch_image" />
+ </item> -->
+</layer-list>
diff --git a/payment_plugin/example/android/app/src/main/res/drawable/launch_background.xml b/payment_plugin/example/android/app/src/main/res/drawable/launch_background.xml
new file mode 100644
index 00000000..304732f8
--- /dev/null
+++ b/payment_plugin/example/android/app/src/main/res/drawable/launch_background.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Modify this file to customize your launch splash screen -->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:drawable="@android:color/white" />
+
+ <!-- You can insert your own image assets here -->
+ <!-- <item>
+ <bitmap
+ android:gravity="center"
+ android:src="@mipmap/launch_image" />
+ </item> -->
+</layer-list>
diff --git a/payment_plugin/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/payment_plugin/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 00000000..db77bb4b
Binary files /dev/null and b/payment_plugin/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/payment_plugin/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/payment_plugin/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 00000000..17987b79
Binary files /dev/null and b/payment_plugin/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/payment_plugin/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/payment_plugin/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 00000000..09d43914
Binary files /dev/null and b/payment_plugin/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/payment_plugin/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/payment_plugin/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 00000000..d5f1c8d3
Binary files /dev/null and b/payment_plugin/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/payment_plugin/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/payment_plugin/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 00000000..4d6372ee
Binary files /dev/null and b/payment_plugin/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/payment_plugin/example/android/app/src/main/res/values-night/styles.xml b/payment_plugin/example/android/app/src/main/res/values-night/styles.xml
new file mode 100644
index 00000000..06952be7
--- /dev/null
+++ b/payment_plugin/example/android/app/src/main/res/values-night/styles.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
+ <style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
+ <!-- Show a splash screen on the activity. Automatically removed when
+ the Flutter engine draws its first frame -->
+ <item name="android:windowBackground">@drawable/launch_background</item>
+ </style>
+ <!-- Theme applied to the Android Window as soon as the process has started.
+ This theme determines the color of the Android Window while your
+ Flutter UI initializes, as well as behind your Flutter UI while its
+ running.
+
+ This Theme is only used starting with V2 of Flutter's Android embedding. -->
+ <style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
+ <item name="android:windowBackground">?android:colorBackground</item>
+ </style>
+</resources>
diff --git a/payment_plugin/example/android/app/src/main/res/values/styles.xml b/payment_plugin/example/android/app/src/main/res/values/styles.xml
new file mode 100644
index 00000000..cb1ef880
--- /dev/null
+++ b/payment_plugin/example/android/app/src/main/res/values/styles.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
+ <style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
+ <!-- Show a splash screen on the activity. Automatically removed when
+ the Flutter engine draws its first frame -->
+ <item name="android:windowBackground">@drawable/launch_background</item>
+ </style>
+ <!-- Theme applied to the Android Window as soon as the process has started.
+ This theme determines the color of the Android Window while your
+ Flutter UI initializes, as well as behind your Flutter UI while its
+ running.
+
+ This Theme is only used starting with V2 of Flutter's Android embedding. -->
+ <style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
+ <item name="android:windowBackground">?android:colorBackground</item>
+ </style>
+</resources>
diff --git a/payment_plugin/example/android/app/src/profile/AndroidManifest.xml b/payment_plugin/example/android/app/src/profile/AndroidManifest.xml
new file mode 100644
index 00000000..399f6981
--- /dev/null
+++ b/payment_plugin/example/android/app/src/profile/AndroidManifest.xml
@@ -0,0 +1,7 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android">
+ <!-- The INTERNET permission is required for development. Specifically,
+ the Flutter tool needs it to communicate with the running application
+ to allow setting breakpoints, to provide hot reload, etc.
+ -->
+ <uses-permission android:name="android.permission.INTERNET"/>
+</manifest>
diff --git a/payment_plugin/example/android/build.gradle.kts b/payment_plugin/example/android/build.gradle.kts
new file mode 100644
index 00000000..89176ef4
--- /dev/null
+++ b/payment_plugin/example/android/build.gradle.kts
@@ -0,0 +1,21 @@
+allprojects {
+ repositories {
+ google()
+ mavenCentral()
+ }
+}
+
+val newBuildDir: Directory = rootProject.layout.buildDirectory.dir("../../build").get()
+rootProject.layout.buildDirectory.value(newBuildDir)
+
+subprojects {
+ val newSubprojectBuildDir: Directory = newBuildDir.dir(project.name)
+ project.layout.buildDirectory.value(newSubprojectBuildDir)
+}
+subprojects {
+ project.evaluationDependsOn(":app")
+}
+
+tasks.register<Delete>("clean") {
+ delete(rootProject.layout.buildDirectory)
+}
diff --git a/payment_plugin/example/android/gradle.properties b/payment_plugin/example/android/gradle.properties
new file mode 100644
index 00000000..f018a618
--- /dev/null
+++ b/payment_plugin/example/android/gradle.properties
@@ -0,0 +1,3 @@
+org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError
+android.useAndroidX=true
+android.enableJetifier=true
diff --git a/payment_plugin/example/android/gradle/wrapper/gradle-wrapper.properties b/payment_plugin/example/android/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 00000000..ac3b4792
--- /dev/null
+++ b/payment_plugin/example/android/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-all.zip
diff --git a/payment_plugin/example/android/settings.gradle.kts b/payment_plugin/example/android/settings.gradle.kts
new file mode 100644
index 00000000..ab39a10a
--- /dev/null
+++ b/payment_plugin/example/android/settings.gradle.kts
@@ -0,0 +1,25 @@
+pluginManagement {
+ val flutterSdkPath = run {
+ val properties = java.util.Properties()
+ file("local.properties").inputStream().use { properties.load(it) }
+ val flutterSdkPath = properties.getProperty("flutter.sdk")
+ require(flutterSdkPath != null) { "flutter.sdk not set in local.properties" }
+ flutterSdkPath
+ }
+
+ includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
+
+ repositories {
+ google()
+ mavenCentral()
+ gradlePluginPortal()
+ }
+}
+
+plugins {
+ id("dev.flutter.flutter-plugin-loader") version "1.0.0"
+ id("com.android.application") version "8.7.3" apply false
+ id("org.jetbrains.kotlin.android") version "2.1.0" apply false
+}
+
+include(":app")
diff --git a/payment_plugin/example/integration_test/plugin_integration_test.dart b/payment_plugin/example/integration_test/plugin_integration_test.dart
new file mode 100644
index 00000000..e1a118e1
--- /dev/null
+++ b/payment_plugin/example/integration_test/plugin_integration_test.dart
@@ -0,0 +1,26 @@
+// This is a basic Flutter integration test.
+//
+// Since integration tests run in a full Flutter application, they can interact
+// with the host side of a plugin implementation, unlike Dart unit tests.
+//
+// For more information about Flutter integration tests, please see
+// https://flutter.dev/to/integration-testing
+
+
+import 'package:flutter_test/flutter_test.dart';
+import 'package:integration_test/integration_test.dart';
+
+import 'package:payment_plugin/payment_plugin.dart';
+
+void main() {
+ IntegrationTestWidgetsFlutterBinding.ensureInitialized();
+
+ testWidgets('AdyenRepository initialization test', (WidgetTester tester) async {
+ final repo = AdyenRepository(
+ tokenProvider: () async => 'test-token',
+ baseUrl: 'https://test-api.com',
+ );
+
+ expect(repo, isNotNull);
+ });
+}
diff --git a/payment_plugin/example/ios/.gitignore b/payment_plugin/example/ios/.gitignore
new file mode 100644
index 00000000..7a7f9873
--- /dev/null
+++ b/payment_plugin/example/ios/.gitignore
@@ -0,0 +1,34 @@
+**/dgph
+*.mode1v3
+*.mode2v3
+*.moved-aside
+*.pbxuser
+*.perspectivev3
+**/*sync/
+.sconsign.dblite
+.tags*
+**/.vagrant/
+**/DerivedData/
+Icon?
+**/Pods/
+**/.symlinks/
+profile
+xcuserdata
+**/.generated/
+Flutter/App.framework
+Flutter/Flutter.framework
+Flutter/Flutter.podspec
+Flutter/Generated.xcconfig
+Flutter/ephemeral/
+Flutter/app.flx
+Flutter/app.zip
+Flutter/flutter_assets/
+Flutter/flutter_export_environment.sh
+ServiceDefinitions.json
+Runner/GeneratedPluginRegistrant.*
+
+# Exceptions to above rules.
+!default.mode1v3
+!default.mode2v3
+!default.pbxuser
+!default.perspectivev3
diff --git a/payment_plugin/example/ios/Flutter/AppFrameworkInfo.plist b/payment_plugin/example/ios/Flutter/AppFrameworkInfo.plist
new file mode 100644
index 00000000..7c569640
--- /dev/null
+++ b/payment_plugin/example/ios/Flutter/AppFrameworkInfo.plist
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>en</string>
+ <key>CFBundleExecutable</key>
+ <string>App</string>
+ <key>CFBundleIdentifier</key>
+ <string>io.flutter.flutter.app</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>App</string>
+ <key>CFBundlePackageType</key>
+ <string>FMWK</string>
+ <key>CFBundleShortVersionString</key>
+ <string>1.0</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleVersion</key>
+ <string>1.0</string>
+ <key>MinimumOSVersion</key>
+ <string>12.0</string>
+</dict>
+</plist>
diff --git a/payment_plugin/example/ios/Flutter/Debug.xcconfig b/payment_plugin/example/ios/Flutter/Debug.xcconfig
new file mode 100644
index 00000000..ec97fc6f
--- /dev/null
+++ b/payment_plugin/example/ios/Flutter/Debug.xcconfig
@@ -0,0 +1,2 @@
+#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
+#include "Generated.xcconfig"
diff --git a/payment_plugin/example/ios/Flutter/Release.xcconfig b/payment_plugin/example/ios/Flutter/Release.xcconfig
new file mode 100644
index 00000000..c4855bfe
--- /dev/null
+++ b/payment_plugin/example/ios/Flutter/Release.xcconfig
@@ -0,0 +1,2 @@
+#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
+#include "Generated.xcconfig"
diff --git a/payment_plugin/example/ios/Podfile b/payment_plugin/example/ios/Podfile
new file mode 100644
index 00000000..620e46eb
--- /dev/null
+++ b/payment_plugin/example/ios/Podfile
@@ -0,0 +1,43 @@
+# Uncomment this line to define a global platform for your project
+# platform :ios, '13.0'
+
+# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
+ENV['COCOAPODS_DISABLE_STATS'] = 'true'
+
+project 'Runner', {
+ 'Debug' => :debug,
+ 'Profile' => :release,
+ 'Release' => :release,
+}
+
+def flutter_root
+ generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
+ unless File.exist?(generated_xcode_build_settings_path)
+ raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
+ end
+
+ File.foreach(generated_xcode_build_settings_path) do |line|
+ matches = line.match(/FLUTTER_ROOT\=(.*)/)
+ return matches[1].strip if matches
+ end
+ raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
+end
+
+require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
+
+flutter_ios_podfile_setup
+
+target 'Runner' do
+ use_frameworks!
+
+ flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
+ target 'RunnerTests' do
+ inherit! :search_paths
+ end
+end
+
+post_install do |installer|
+ installer.pods_project.targets.each do |target|
+ flutter_additional_ios_build_settings(target)
+ end
+end
diff --git a/payment_plugin/example/ios/Runner.xcodeproj/project.pbxproj b/payment_plugin/example/ios/Runner.xcodeproj/project.pbxproj
new file mode 100644
index 00000000..62a0d24f
--- /dev/null
+++ b/payment_plugin/example/ios/Runner.xcodeproj/project.pbxproj
@@ -0,0 +1,641 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 54;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
+ 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; };
+ 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
+ 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
+ 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */ = {isa = PBXBuildFile; productRef = 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */; };
+ 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 */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXContainerItemProxy section */
+ 331C8085294A63A400263BE5 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 97C146E61CF9000F007C117D /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 97C146ED1CF9000F007C117D;
+ remoteInfo = Runner;
+ };
+/* End PBXContainerItemProxy section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+ 9705A1C41CF9048500538489 /* Embed Frameworks */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = "";
+ dstSubfolderSpec = 10;
+ files = (
+ );
+ name = "Embed Frameworks";
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+ 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>"; };
+ 331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; };
+ 331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
+ 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
+ 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
+ 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
+ 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
+ 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
+ 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
+ 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
+ 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
+ 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>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 97C146EB1CF9000F007C117D /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 331C8082294A63A400263BE5 /* RunnerTests */ = {
+ isa = PBXGroup;
+ children = (
+ 331C807B294A618700263BE5 /* RunnerTests.swift */,
+ );
+ path = RunnerTests;
+ sourceTree = "<group>";
+ };
+ 9740EEB11CF90186004384FC /* Flutter */ = {
+ isa = PBXGroup;
+ children = (
+ 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
+ 9740EEB21CF90195004384FC /* Debug.xcconfig */,
+ 7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
+ 9740EEB31CF90195004384FC /* Generated.xcconfig */,
+ );
+ name = Flutter;
+ sourceTree = "<group>";
+ };
+ 97C146E51CF9000F007C117D = {
+ isa = PBXGroup;
+ children = (
+ 9740EEB11CF90186004384FC /* Flutter */,
+ 97C146F01CF9000F007C117D /* Runner */,
+ 97C146EF1CF9000F007C117D /* Products */,
+ 331C8082294A63A400263BE5 /* RunnerTests */,
+ );
+ sourceTree = "<group>";
+ };
+ 97C146EF1CF9000F007C117D /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 97C146EE1CF9000F007C117D /* Runner.app */,
+ 331C8081294A63A400263BE5 /* RunnerTests.xctest */,
+ );
+ name = Products;
+ sourceTree = "<group>";
+ };
+ 97C146F01CF9000F007C117D /* Runner */ = {
+ isa = PBXGroup;
+ children = (
+ 97C146FA1CF9000F007C117D /* Main.storyboard */,
+ 97C146FD1CF9000F007C117D /* Assets.xcassets */,
+ 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
+ 97C147021CF9000F007C117D /* Info.plist */,
+ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
+ 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
+ 74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
+ 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
+ );
+ path = Runner;
+ sourceTree = "<group>";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ 331C8080294A63A400263BE5 /* RunnerTests */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */;
+ buildPhases = (
+ 331C807D294A63A400263BE5 /* Sources */,
+ 331C807F294A63A400263BE5 /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ 331C8086294A63A400263BE5 /* PBXTargetDependency */,
+ );
+ name = RunnerTests;
+ productName = RunnerTests;
+ productReference = 331C8081294A63A400263BE5 /* RunnerTests.xctest */;
+ productType = "com.apple.product-type.bundle.unit-test";
+ };
+ 97C146ED1CF9000F007C117D /* Runner */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
+ buildPhases = (
+ 9740EEB61CF901F6004384FC /* Run Script */,
+ 97C146EA1CF9000F007C117D /* Sources */,
+ 97C146EB1CF9000F007C117D /* Frameworks */,
+ 97C146EC1CF9000F007C117D /* Resources */,
+ 9705A1C41CF9048500538489 /* Embed Frameworks */,
+ 3B06AD1E1E4923F5004D2608 /* Thin Binary */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = Runner;
+ packageProductDependencies = (
+ 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */,
+ );
+ productName = Runner;
+ productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
+ productType = "com.apple.product-type.application";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 97C146E61CF9000F007C117D /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ BuildIndependentTargetsInParallel = YES;
+ LastUpgradeCheck = 1510;
+ ORGANIZATIONNAME = "";
+ TargetAttributes = {
+ 331C8080294A63A400263BE5 = {
+ CreatedOnToolsVersion = 14.0;
+ TestTargetID = 97C146ED1CF9000F007C117D;
+ };
+ 97C146ED1CF9000F007C117D = {
+ CreatedOnToolsVersion = 7.3.1;
+ LastSwiftMigration = 1100;
+ };
+ };
+ };
+ buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
+ compatibilityVersion = "Xcode 9.3";
+ developmentRegion = en;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ Base,
+ );
+ mainGroup = 97C146E51CF9000F007C117D;
+ packageReferences = (
+ 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage" */,
+ );
+ productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ 97C146ED1CF9000F007C117D /* Runner */,
+ 331C8080294A63A400263BE5 /* RunnerTests */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ 331C807F294A63A400263BE5 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 97C146EC1CF9000F007C117D /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
+ 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
+ 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
+ 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXShellScriptBuildPhase section */
+ 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
+ isa = PBXShellScriptBuildPhase;
+ alwaysOutOfDate = 1;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}",
+ );
+ name = "Thin Binary";
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
+ };
+ 9740EEB61CF901F6004384FC /* Run Script */ = {
+ isa = PBXShellScriptBuildPhase;
+ alwaysOutOfDate = 1;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ );
+ name = "Run Script";
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
+ };
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 331C807D294A63A400263BE5 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 97C146EA1CF9000F007C117D /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
+ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXTargetDependency section */
+ 331C8086294A63A400263BE5 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 97C146ED1CF9000F007C117D /* Runner */;
+ targetProxy = 331C8085294A63A400263BE5 /* PBXContainerItemProxy */;
+ };
+/* End PBXTargetDependency section */
+
+/* Begin PBXVariantGroup section */
+ 97C146FA1CF9000F007C117D /* Main.storyboard */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 97C146FB1CF9000F007C117D /* Base */,
+ );
+ name = Main.storyboard;
+ sourceTree = "<group>";
+ };
+ 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 97C147001CF9000F007C117D /* Base */,
+ );
+ name = LaunchScreen.storyboard;
+ sourceTree = "<group>";
+ };
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+ 249021D3217E4FDB00AE95B9 /* Profile */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_USER_SCRIPT_SANDBOXING = NO;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 12.0;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ SDKROOT = iphoneos;
+ SUPPORTED_PLATFORMS = iphoneos;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ VALIDATE_PRODUCT = YES;
+ };
+ name = Profile;
+ };
+ 249021D4217E4FDB00AE95B9 /* Profile */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_MODULES = YES;
+ CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
+ DEVELOPMENT_TEAM = 8RNV6AX4ZL;
+ ENABLE_BITCODE = NO;
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = com.example.paymentPluginExample;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
+ SWIFT_VERSION = 5.0;
+ VERSIONING_SYSTEM = "apple-generic";
+ };
+ name = Profile;
+ };
+ 331C8088294A63A400263BE5 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ BUNDLE_LOADER = "$(TEST_HOST)";
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 1;
+ GENERATE_INFOPLIST_FILE = YES;
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = com.example.paymentPluginExample.RunnerTests;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ SWIFT_VERSION = 5.0;
+ TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
+ };
+ name = Debug;
+ };
+ 331C8089294A63A400263BE5 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ BUNDLE_LOADER = "$(TEST_HOST)";
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 1;
+ GENERATE_INFOPLIST_FILE = YES;
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = com.example.paymentPluginExample.RunnerTests;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_VERSION = 5.0;
+ TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
+ };
+ name = Release;
+ };
+ 331C808A294A63A400263BE5 /* Profile */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ BUNDLE_LOADER = "$(TEST_HOST)";
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 1;
+ GENERATE_INFOPLIST_FILE = YES;
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = com.example.paymentPluginExample.RunnerTests;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_VERSION = 5.0;
+ TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
+ };
+ name = Profile;
+ };
+ 97C147031CF9000F007C117D /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = dwarf;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_TESTABILITY = YES;
+ ENABLE_USER_SCRIPT_SANDBOXING = NO;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 12.0;
+ MTL_ENABLE_DEBUG_INFO = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ SDKROOT = iphoneos;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Debug;
+ };
+ 97C147041CF9000F007C117D /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_USER_SCRIPT_SANDBOXING = NO;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 12.0;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ SDKROOT = iphoneos;
+ SUPPORTED_PLATFORMS = iphoneos;
+ SWIFT_COMPILATION_MODE = wholemodule;
+ SWIFT_OPTIMIZATION_LEVEL = "-O";
+ TARGETED_DEVICE_FAMILY = "1,2";
+ VALIDATE_PRODUCT = YES;
+ };
+ name = Release;
+ };
+ 97C147061CF9000F007C117D /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_MODULES = YES;
+ CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
+ DEVELOPMENT_TEAM = 8RNV6AX4ZL;
+ ENABLE_BITCODE = NO;
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = com.example.paymentPluginExample;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ SWIFT_VERSION = 5.0;
+ VERSIONING_SYSTEM = "apple-generic";
+ };
+ name = Debug;
+ };
+ 97C147071CF9000F007C117D /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_MODULES = YES;
+ CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
+ DEVELOPMENT_TEAM = 8RNV6AX4ZL;
+ ENABLE_BITCODE = NO;
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = com.example.paymentPluginExample;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
+ SWIFT_VERSION = 5.0;
+ VERSIONING_SYSTEM = "apple-generic";
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 331C8088294A63A400263BE5 /* Debug */,
+ 331C8089294A63A400263BE5 /* Release */,
+ 331C808A294A63A400263BE5 /* Profile */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 97C147031CF9000F007C117D /* Debug */,
+ 97C147041CF9000F007C117D /* Release */,
+ 249021D3217E4FDB00AE95B9 /* Profile */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 97C147061CF9000F007C117D /* Debug */,
+ 97C147071CF9000F007C117D /* Release */,
+ 249021D4217E4FDB00AE95B9 /* Profile */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+
+/* Begin XCLocalSwiftPackageReference section */
+ 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage" */ = {
+ isa = XCLocalSwiftPackageReference;
+ relativePath = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage;
+ };
+/* End XCLocalSwiftPackageReference section */
+
+/* Begin XCSwiftPackageProductDependency section */
+ 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */ = {
+ isa = XCSwiftPackageProductDependency;
+ productName = FlutterGeneratedPluginSwiftPackage;
+ };
+/* End XCSwiftPackageProductDependency section */
+ };
+ rootObject = 97C146E61CF9000F007C117D /* Project object */;
+}
diff --git a/payment_plugin/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/payment_plugin/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 00000000..919434a6
--- /dev/null
+++ b/payment_plugin/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Workspace
+ version = "1.0">
+ <FileRef
+ location = "self:">
+ </FileRef>
+</Workspace>
diff --git a/payment_plugin/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/payment_plugin/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 00000000..18d98100
--- /dev/null
+++ b/payment_plugin/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>IDEDidComputeMac32BitWarning</key>
+ <true/>
+</dict>
+</plist>
diff --git a/payment_plugin/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/payment_plugin/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
new file mode 100644
index 00000000..f9b0d7c5
--- /dev/null
+++ b/payment_plugin/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>PreviewsEnabled</key>
+ <false/>
+</dict>
+</plist>
diff --git a/payment_plugin/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/payment_plugin/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
new file mode 100644
index 00000000..c3fedb29
--- /dev/null
+++ b/payment_plugin/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
@@ -0,0 +1,119 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+ LastUpgradeVersion = "1510"
+ version = "1.3">
+ <BuildAction
+ parallelizeBuildables = "YES"
+ buildImplicitDependencies = "YES">
+ <PreActions>
+ <ExecutionAction
+ ActionType = "Xcode.IDEStandardExecutionActionsCore.ExecutionActionType.ShellScriptAction">
+ <ActionContent
+ title = "Run Prepare Flutter Framework Script"
+ scriptText = "/bin/sh "$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh" prepare ">
+ <EnvironmentBuildable>
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "97C146ED1CF9000F007C117D"
+ BuildableName = "Runner.app"
+ BlueprintName = "Runner"
+ ReferencedContainer = "container:Runner.xcodeproj">
+ </BuildableReference>
+ </EnvironmentBuildable>
+ </ActionContent>
+ </ExecutionAction>
+ </PreActions>
+ <BuildActionEntries>
+ <BuildActionEntry
+ buildForTesting = "YES"
+ buildForRunning = "YES"
+ buildForProfiling = "YES"
+ buildForArchiving = "YES"
+ buildForAnalyzing = "YES">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "97C146ED1CF9000F007C117D"
+ BuildableName = "Runner.app"
+ BlueprintName = "Runner"
+ ReferencedContainer = "container:Runner.xcodeproj">
+ </BuildableReference>
+ </BuildActionEntry>
+ </BuildActionEntries>
+ </BuildAction>
+ <TestAction
+ buildConfiguration = "Debug"
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit"
+ shouldUseLaunchSchemeArgsEnv = "YES">
+ <MacroExpansion>
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "97C146ED1CF9000F007C117D"
+ BuildableName = "Runner.app"
+ BlueprintName = "Runner"
+ ReferencedContainer = "container:Runner.xcodeproj">
+ </BuildableReference>
+ </MacroExpansion>
+ <Testables>
+ <TestableReference
+ skipped = "NO"
+ parallelizable = "YES">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "331C8080294A63A400263BE5"
+ BuildableName = "RunnerTests.xctest"
+ BlueprintName = "RunnerTests"
+ ReferencedContainer = "container:Runner.xcodeproj">
+ </BuildableReference>
+ </TestableReference>
+ </Testables>
+ </TestAction>
+ <LaunchAction
+ buildConfiguration = "Debug"
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+ customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit"
+ launchStyle = "0"
+ useCustomWorkingDirectory = "NO"
+ ignoresPersistentStateOnLaunch = "NO"
+ debugDocumentVersioning = "YES"
+ debugServiceExtension = "internal"
+ enableGPUValidationMode = "1"
+ allowLocationSimulation = "YES">
+ <BuildableProductRunnable
+ runnableDebuggingMode = "0">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "97C146ED1CF9000F007C117D"
+ BuildableName = "Runner.app"
+ BlueprintName = "Runner"
+ ReferencedContainer = "container:Runner.xcodeproj">
+ </BuildableReference>
+ </BuildableProductRunnable>
+ </LaunchAction>
+ <ProfileAction
+ buildConfiguration = "Profile"
+ shouldUseLaunchSchemeArgsEnv = "YES"
+ savedToolIdentifier = ""
+ useCustomWorkingDirectory = "NO"
+ debugDocumentVersioning = "YES">
+ <BuildableProductRunnable
+ runnableDebuggingMode = "0">
+ <BuildableReference
+ BuildableIdentifier = "primary"
+ BlueprintIdentifier = "97C146ED1CF9000F007C117D"
+ BuildableName = "Runner.app"
+ BlueprintName = "Runner"
+ ReferencedContainer = "container:Runner.xcodeproj">
+ </BuildableReference>
+ </BuildableProductRunnable>
+ </ProfileAction>
+ <AnalyzeAction
+ buildConfiguration = "Debug">
+ </AnalyzeAction>
+ <ArchiveAction
+ buildConfiguration = "Release"
+ revealArchiveInOrganizer = "YES">
+ </ArchiveAction>
+</Scheme>
diff --git a/payment_plugin/example/ios/Runner.xcworkspace/contents.xcworkspacedata b/payment_plugin/example/ios/Runner.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 00000000..1d526a16
--- /dev/null
+++ b/payment_plugin/example/ios/Runner.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Workspace
+ version = "1.0">
+ <FileRef
+ location = "group:Runner.xcodeproj">
+ </FileRef>
+</Workspace>
diff --git a/payment_plugin/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/payment_plugin/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 00000000..18d98100
--- /dev/null
+++ b/payment_plugin/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>IDEDidComputeMac32BitWarning</key>
+ <true/>
+</dict>
+</plist>
diff --git a/payment_plugin/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/payment_plugin/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
new file mode 100644
index 00000000..f9b0d7c5
--- /dev/null
+++ b/payment_plugin/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>PreviewsEnabled</key>
+ <false/>
+</dict>
+</plist>
diff --git a/payment_plugin/example/ios/Runner/AppDelegate.swift b/payment_plugin/example/ios/Runner/AppDelegate.swift
new file mode 100644
index 00000000..62666446
--- /dev/null
+++ b/payment_plugin/example/ios/Runner/AppDelegate.swift
@@ -0,0 +1,13 @@
+import Flutter
+import UIKit
+
+@main
+@objc class AppDelegate: FlutterAppDelegate {
+ override func application(
+ _ application: UIApplication,
+ didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
+ ) -> Bool {
+ GeneratedPluginRegistrant.register(with: self)
+ return super.application(application, didFinishLaunchingWithOptions: launchOptions)
+ }
+}
diff --git a/payment_plugin/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/payment_plugin/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 00000000..d36b1fab
--- /dev/null
+++ b/payment_plugin/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,122 @@
+{
+ "images" : [
+ {
+ "size" : "20x20",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-20x20@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "20x20",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-20x20@3x.png",
+ "scale" : "3x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-29x29@1x.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-29x29@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-29x29@3x.png",
+ "scale" : "3x"
+ },
+ {
+ "size" : "40x40",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-40x40@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "40x40",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-40x40@3x.png",
+ "scale" : "3x"
+ },
+ {
+ "size" : "60x60",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-60x60@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "60x60",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-60x60@3x.png",
+ "scale" : "3x"
+ },
+ {
+ "size" : "20x20",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-20x20@1x.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "20x20",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-20x20@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-29x29@1x.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-29x29@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "40x40",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-40x40@1x.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "40x40",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-40x40@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "76x76",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-76x76@1x.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "76x76",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-76x76@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "83.5x83.5",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-83.5x83.5@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "1024x1024",
+ "idiom" : "ios-marketing",
+ "filename" : "Icon-App-1024x1024@1x.png",
+ "scale" : "1x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
diff --git a/payment_plugin/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/payment_plugin/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
new file mode 100644
index 00000000..dc9ada47
Binary files /dev/null and b/payment_plugin/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png differ
diff --git a/payment_plugin/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/payment_plugin/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
new file mode 100644
index 00000000..7353c41e
Binary files /dev/null and b/payment_plugin/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png differ
diff --git a/payment_plugin/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/payment_plugin/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
new file mode 100644
index 00000000..797d452e
Binary files /dev/null and b/payment_plugin/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png differ
diff --git a/payment_plugin/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/payment_plugin/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
new file mode 100644
index 00000000..6ed2d933
Binary files /dev/null and b/payment_plugin/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png differ
diff --git a/payment_plugin/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/payment_plugin/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
new file mode 100644
index 00000000..4cd7b009
Binary files /dev/null and b/payment_plugin/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png differ
diff --git a/payment_plugin/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/payment_plugin/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
new file mode 100644
index 00000000..fe730945
Binary files /dev/null and b/payment_plugin/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png differ
diff --git a/payment_plugin/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/payment_plugin/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
new file mode 100644
index 00000000..321773cd
Binary files /dev/null and b/payment_plugin/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png differ
diff --git a/payment_plugin/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/payment_plugin/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
new file mode 100644
index 00000000..797d452e
Binary files /dev/null and b/payment_plugin/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png differ
diff --git a/payment_plugin/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/payment_plugin/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
new file mode 100644
index 00000000..502f463a
Binary files /dev/null and b/payment_plugin/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png differ
diff --git a/payment_plugin/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/payment_plugin/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
new file mode 100644
index 00000000..0ec30343
Binary files /dev/null and b/payment_plugin/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png differ
diff --git a/payment_plugin/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/payment_plugin/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
new file mode 100644
index 00000000..0ec30343
Binary files /dev/null and b/payment_plugin/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png differ
diff --git a/payment_plugin/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/payment_plugin/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
new file mode 100644
index 00000000..e9f5fea2
Binary files /dev/null and b/payment_plugin/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png differ
diff --git a/payment_plugin/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/payment_plugin/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png
new file mode 100644
index 00000000..84ac32ae
Binary files /dev/null and b/payment_plugin/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png differ
diff --git a/payment_plugin/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/payment_plugin/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
new file mode 100644
index 00000000..8953cba0
Binary files /dev/null and b/payment_plugin/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png differ
diff --git a/payment_plugin/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/payment_plugin/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
new file mode 100644
index 00000000..0467bf12
Binary files /dev/null and b/payment_plugin/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png differ
diff --git a/payment_plugin/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/payment_plugin/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json
new file mode 100644
index 00000000..0bedcf2f
--- /dev/null
+++ b/payment_plugin/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json
@@ -0,0 +1,23 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "LaunchImage.png",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "LaunchImage@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "LaunchImage@3x.png",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
diff --git a/payment_plugin/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/payment_plugin/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
new file mode 100644
index 00000000..9da19eac
Binary files /dev/null and b/payment_plugin/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png differ
diff --git a/payment_plugin/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/payment_plugin/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
new file mode 100644
index 00000000..9da19eac
Binary files /dev/null and b/payment_plugin/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png differ
diff --git a/payment_plugin/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/payment_plugin/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
new file mode 100644
index 00000000..9da19eac
Binary files /dev/null and b/payment_plugin/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png differ
diff --git a/payment_plugin/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/payment_plugin/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md
new file mode 100644
index 00000000..89c2725b
--- /dev/null
+++ b/payment_plugin/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md
@@ -0,0 +1,5 @@
+# Launch Screen Assets
+
+You can customize the launch screen with your own desired assets by replacing the image files in this directory.
+
+You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.
\ No newline at end of file
diff --git a/payment_plugin/example/ios/Runner/Base.lproj/LaunchScreen.storyboard b/payment_plugin/example/ios/Runner/Base.lproj/LaunchScreen.storyboard
new file mode 100644
index 00000000..f2e259c7
--- /dev/null
+++ b/payment_plugin/example/ios/Runner/Base.lproj/LaunchScreen.storyboard
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12121" systemVersion="16G29" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
+ <dependencies>
+ <deployment identifier="iOS"/>
+ <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12089"/>
+ </dependencies>
+ <scenes>
+ <!--View Controller-->
+ <scene sceneID="EHf-IW-A2E">
+ <objects>
+ <viewController id="01J-lp-oVM" sceneMemberID="viewController">
+ <layoutGuides>
+ <viewControllerLayoutGuide type="top" id="Ydg-fD-yQy"/>
+ <viewControllerLayoutGuide type="bottom" id="xbc-2k-c8Z"/>
+ </layoutGuides>
+ <view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
+ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+ <subviews>
+ <imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="YRO-k0-Ey4">
+ </imageView>
+ </subviews>
+ <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+ <constraints>
+ <constraint firstItem="YRO-k0-Ey4" firstAttribute="centerX" secondItem="Ze5-6b-2t3" secondAttribute="centerX" id="1a2-6s-vTC"/>
+ <constraint firstItem="YRO-k0-Ey4" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="centerY" id="4X2-HB-R7a"/>
+ </constraints>
+ </view>
+ </viewController>
+ <placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
+ </objects>
+ <point key="canvasLocation" x="53" y="375"/>
+ </scene>
+ </scenes>
+ <resources>
+ <image name="LaunchImage" width="168" height="185"/>
+ </resources>
+</document>
diff --git a/payment_plugin/example/ios/Runner/Base.lproj/Main.storyboard b/payment_plugin/example/ios/Runner/Base.lproj/Main.storyboard
new file mode 100644
index 00000000..f3c28516
--- /dev/null
+++ b/payment_plugin/example/ios/Runner/Base.lproj/Main.storyboard
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10117" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r">
+ <dependencies>
+ <deployment identifier="iOS"/>
+ <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
+ </dependencies>
+ <scenes>
+ <!--Flutter View Controller-->
+ <scene sceneID="tne-QT-ifu">
+ <objects>
+ <viewController id="BYZ-38-t0r" customClass="FlutterViewController" sceneMemberID="viewController">
+ <layoutGuides>
+ <viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
+ <viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
+ </layoutGuides>
+ <view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
+ <rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
+ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+ <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
+ </view>
+ </viewController>
+ <placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
+ </objects>
+ </scene>
+ </scenes>
+</document>
diff --git a/payment_plugin/example/ios/Runner/Info.plist b/payment_plugin/example/ios/Runner/Info.plist
new file mode 100644
index 00000000..44718074
--- /dev/null
+++ b/payment_plugin/example/ios/Runner/Info.plist
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>$(DEVELOPMENT_LANGUAGE)</string>
+ <key>CFBundleDisplayName</key>
+ <string>Payment Plugin</string>
+ <key>CFBundleExecutable</key>
+ <string>$(EXECUTABLE_NAME)</string>
+ <key>CFBundleIdentifier</key>
+ <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>payment_plugin_example</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleShortVersionString</key>
+ <string>$(FLUTTER_BUILD_NAME)</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleVersion</key>
+ <string>$(FLUTTER_BUILD_NUMBER)</string>
+ <key>LSRequiresIPhoneOS</key>
+ <true/>
+ <key>UILaunchStoryboardName</key>
+ <string>LaunchScreen</string>
+ <key>UIMainStoryboardFile</key>
+ <string>Main</string>
+ <key>UISupportedInterfaceOrientations</key>
+ <array>
+ <string>UIInterfaceOrientationPortrait</string>
+ <string>UIInterfaceOrientationLandscapeLeft</string>
+ <string>UIInterfaceOrientationLandscapeRight</string>
+ </array>
+ <key>UISupportedInterfaceOrientations~ipad</key>
+ <array>
+ <string>UIInterfaceOrientationPortrait</string>
+ <string>UIInterfaceOrientationPortraitUpsideDown</string>
+ <string>UIInterfaceOrientationLandscapeLeft</string>
+ <string>UIInterfaceOrientationLandscapeRight</string>
+ </array>
+ <key>CADisableMinimumFrameDurationOnPhone</key>
+ <true/>
+ <key>UIApplicationSupportsIndirectInputEvents</key>
+ <true/>
+</dict>
+</plist>
diff --git a/payment_plugin/example/ios/Runner/Runner-Bridging-Header.h b/payment_plugin/example/ios/Runner/Runner-Bridging-Header.h
new file mode 100644
index 00000000..308a2a56
--- /dev/null
+++ b/payment_plugin/example/ios/Runner/Runner-Bridging-Header.h
@@ -0,0 +1 @@
+#import "GeneratedPluginRegistrant.h"
diff --git a/payment_plugin/example/ios/RunnerTests/RunnerTests.swift b/payment_plugin/example/ios/RunnerTests/RunnerTests.swift
new file mode 100644
index 00000000..2396c9cb
--- /dev/null
+++ b/payment_plugin/example/ios/RunnerTests/RunnerTests.swift
@@ -0,0 +1,29 @@
+import Flutter
+import UIKit
+import XCTest
+
+// If your plugin has been explicitly set to "type: .dynamic" in the Package.swift,
+// you will need to add your plugin as a dependency of RunnerTests within Xcode.
+
+@testable import payment_plugin
+
+// This demonstrates a simple unit test of the Swift portion of this plugin's implementation.
+//
+// See https://developer.apple.com/documentation/xctest for more information about using XCTest.
+
+class RunnerTests: XCTestCase {
+
+ func testGetPlatformVersion() {
+ let plugin = PaymentPlugin()
+
+ let call = FlutterMethodCall(methodName: "getPlatformVersion", arguments: [])
+
+ let resultExpectation = expectation(description: "result block must be called.")
+ plugin.handle(call) { result in
+ XCTAssertEqual(result as! String, "iOS " + UIDevice.current.systemVersion)
+ resultExpectation.fulfill()
+ }
+ waitForExpectations(timeout: 1)
+ }
+
+}
diff --git a/payment_plugin/example/lib/main.dart b/payment_plugin/example/lib/main.dart
new file mode 100644
index 00000000..263b3ea3
--- /dev/null
+++ b/payment_plugin/example/lib/main.dart
@@ -0,0 +1,81 @@
+import 'package:flutter/material.dart';
+import 'package:payment_plugin/payment_plugin.dart';
+
+void main() async {
+ WidgetsFlutterBinding.ensureInitialized();
+
+ // Initialize the payment plugin once
+ await PaymentPlugin.initialize(
+ config: PaymentConfig(
+ tokenProvider: () async {
+ // In a real app, get the token from your auth service
+ return 'your-auth-token-here';
+ },
+ baseUrl: 'https://your-api.com',
+ ),
+ );
+
+ runApp(const MyApp());
+}
+
+class MyApp extends StatefulWidget {
+ const MyApp({super.key});
+
+ @override
+ State<MyApp> createState() => _MyAppState();
+}
+
+class _MyAppState extends State<MyApp> {
+ String _status = 'Plugin initialized';
+
+ @override
+ void initState() {
+ super.initState();
+
+ // Plugin is already initialized in main()
+ // You can access it anywhere using PaymentPlugin.instance
+ _loadCards();
+ }
+
+ Future<void> _loadCards() async {
+ try {
+ // Example: Get stored cards
+ final cards = await PaymentPlugin.instance.getCards();
+ setState(() {
+ _status = 'Plugin ready. Found ${cards.length} stored cards.';
+ });
+ } catch (e) {
+ setState(() {
+ _status = 'Plugin ready. Error loading cards: $e';
+ });
+ }
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return MaterialApp(
+ home: Scaffold(
+ appBar: AppBar(
+ title: const Text('Payment Plugin Example'),
+ ),
+ body: Center(
+ child: Column(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ Text(_status),
+ const SizedBox(height: 20),
+ const Text(
+ 'This is an example app showing how to use the payment plugin.\n\n'
+ 'Initialize once in main():\n'
+ 'await PaymentPlugin.initialize(config: PaymentConfig(...))\n\n'
+ 'Then use anywhere:\n'
+ 'PaymentPlugin.instance.getCards()',
+ textAlign: TextAlign.center,
+ ),
+ ],
+ ),
+ ),
+ ),
+ );
+ }
+}
diff --git a/payment_plugin/example/pubspec.lock b/payment_plugin/example/pubspec.lock
new file mode 100644
index 00000000..0abd50e0
--- /dev/null
+++ b/payment_plugin/example/pubspec.lock
@@ -0,0 +1,709 @@
+# Generated by pub
+# See https://dart.dev/tools/pub/glossary#lockfile
+packages:
+ adyen_checkout:
+ dependency: transitive
+ description:
+ name: adyen_checkout
+ sha256: "9e24b2970e3883cdb1e723ef039f0f2695077415d8224ebf8010ea95e8e19e91"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.7.0"
+ archive:
+ dependency: transitive
+ description:
+ name: archive
+ sha256: "2fde1607386ab523f7a36bb3e7edb43bd58e6edaf2ffb29d8a6d578b297fdbbd"
+ url: "https://pub.dev"
+ source: hosted
+ version: "4.0.7"
+ args:
+ dependency: transitive
+ description:
+ name: args
+ sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.7.0"
+ async:
+ dependency: transitive
+ description:
+ name: async
+ sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.13.0"
+ bloc:
+ dependency: transitive
+ description:
+ name: bloc
+ sha256: a2cebb899f91d36eeeaa55c7b20b5915db5a9df1b8fd4a3c9c825e22e474537d
+ url: "https://pub.dev"
+ source: hosted
+ version: "9.1.0"
+ boolean_selector:
+ dependency: transitive
+ description:
+ name: boolean_selector
+ sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.1.2"
+ characters:
+ dependency: transitive
+ description:
+ name: characters
+ sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.4.0"
+ clock:
+ dependency: transitive
+ description:
+ name: clock
+ sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.1.2"
+ collection:
+ dependency: transitive
+ description:
+ name: collection
+ sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.19.1"
+ crypto:
+ dependency: transitive
+ description:
+ name: crypto
+ sha256: c8ea0233063ba03258fbcf2ca4d6dadfefe14f02fab57702265467a19f27fadf
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.0.7"
+ cupertino_icons:
+ dependency: "direct main"
+ description:
+ name: cupertino_icons
+ sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.0.8"
+ dio:
+ dependency: transitive
+ description:
+ name: dio
+ sha256: d90ee57923d1828ac14e492ca49440f65477f4bb1263575900be731a3dac66a9
+ url: "https://pub.dev"
+ source: hosted
+ version: "5.9.0"
+ dio_web_adapter:
+ dependency: transitive
+ description:
+ name: dio_web_adapter
+ sha256: "7586e476d70caecaf1686d21eee7247ea43ef5c345eab9e0cc3583ff13378d78"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.1.1"
+ easy_localization:
+ dependency: transitive
+ description:
+ name: easy_localization
+ sha256: "2ccdf9db8fe4d9c5a75c122e6275674508fd0f0d49c827354967b8afcc56bbed"
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.0.8"
+ easy_logger:
+ dependency: transitive
+ description:
+ name: easy_logger
+ sha256: c764a6e024846f33405a2342caf91c62e357c24b02c04dbc712ef232bf30ffb7
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.0.2"
+ equatable:
+ dependency: transitive
+ description:
+ name: equatable
+ sha256: "567c64b3cb4cf82397aac55f4f0cbd3ca20d77c6c03bedbc4ceaddc08904aef7"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.0.7"
+ fake_async:
+ dependency: transitive
+ description:
+ name: fake_async
+ sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.3.3"
+ ffi:
+ dependency: transitive
+ description:
+ name: ffi
+ sha256: "289279317b4b16eb2bb7e271abccd4bf84ec9bdcbe999e278a94b804f5630418"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.1.4"
+ file:
+ dependency: transitive
+ description:
+ name: file
+ sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4
+ url: "https://pub.dev"
+ source: hosted
+ version: "7.0.1"
+ flutter:
+ dependency: "direct main"
+ description: flutter
+ source: sdk
+ version: "0.0.0"
+ flutter_bloc:
+ dependency: transitive
+ description:
+ name: flutter_bloc
+ sha256: cf51747952201a455a1c840f8171d273be009b932c75093020f9af64f2123e38
+ url: "https://pub.dev"
+ source: hosted
+ version: "9.1.1"
+ flutter_dotenv:
+ dependency: transitive
+ description:
+ name: flutter_dotenv
+ sha256: d4130c4a43e0b13fefc593bc3961f2cb46e30cb79e253d4a526b1b5d24ae1ce4
+ url: "https://pub.dev"
+ source: hosted
+ version: "6.0.0"
+ flutter_driver:
+ dependency: transitive
+ description: flutter
+ source: sdk
+ version: "0.0.0"
+ flutter_lints:
+ dependency: "direct dev"
+ description:
+ name: flutter_lints
+ sha256: "5398f14efa795ffb7a33e9b6a08798b26a180edac4ad7db3f231e40f82ce11e1"
+ url: "https://pub.dev"
+ source: hosted
+ version: "5.0.0"
+ flutter_localizations:
+ dependency: transitive
+ description: flutter
+ source: sdk
+ version: "0.0.0"
+ flutter_svg:
+ dependency: transitive
+ description:
+ name: flutter_svg
+ sha256: "87fbd7c534435b6c5d9d98b01e1fd527812b82e68ddd8bd35fc45ed0fa8f0a95"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.2.3"
+ flutter_test:
+ dependency: "direct dev"
+ description: flutter
+ source: sdk
+ version: "0.0.0"
+ flutter_web_plugins:
+ dependency: transitive
+ description: flutter
+ source: sdk
+ version: "0.0.0"
+ freezed_annotation:
+ dependency: transitive
+ description:
+ name: freezed_annotation
+ sha256: "7294967ff0a6d98638e7acb774aac3af2550777accd8149c90af5b014e6d44d8"
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.1.0"
+ fuchsia_remote_debug_protocol:
+ dependency: transitive
+ description: flutter
+ source: sdk
+ version: "0.0.0"
+ go_router:
+ dependency: transitive
+ description:
+ name: go_router
+ sha256: d8f590a69729f719177ea68eb1e598295e8dbc41bbc247fed78b2c8a25660d7c
+ url: "https://pub.dev"
+ source: hosted
+ version: "16.3.0"
+ http:
+ dependency: transitive
+ description:
+ name: http
+ sha256: "87721a4a50b19c7f1d49001e51409bddc46303966ce89a65af4f4e6004896412"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.6.0"
+ http_parser:
+ dependency: transitive
+ description:
+ name: http_parser
+ sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571"
+ url: "https://pub.dev"
+ source: hosted
+ version: "4.1.2"
+ integration_test:
+ dependency: "direct dev"
+ description: flutter
+ source: sdk
+ version: "0.0.0"
+ intl:
+ dependency: transitive
+ description:
+ name: intl
+ sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.20.2"
+ json_annotation:
+ dependency: transitive
+ description:
+ name: json_annotation
+ sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1"
+ url: "https://pub.dev"
+ source: hosted
+ version: "4.9.0"
+ leak_tracker:
+ dependency: transitive
+ description:
+ name: leak_tracker
+ sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de"
+ url: "https://pub.dev"
+ source: hosted
+ version: "11.0.2"
+ leak_tracker_flutter_testing:
+ dependency: transitive
+ description:
+ name: leak_tracker_flutter_testing
+ sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1"
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.0.10"
+ leak_tracker_testing:
+ dependency: transitive
+ description:
+ name: leak_tracker_testing
+ sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1"
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.0.2"
+ lints:
+ dependency: transitive
+ description:
+ name: lints
+ sha256: c35bb79562d980e9a453fc715854e1ed39e24e7d0297a880ef54e17f9874a9d7
+ url: "https://pub.dev"
+ source: hosted
+ version: "5.1.1"
+ logging:
+ dependency: transitive
+ description:
+ name: logging
+ sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.3.0"
+ lottie:
+ dependency: transitive
+ description:
+ name: lottie
+ sha256: "8ae0be46dbd9e19641791dc12ee480d34e1fd3f84c749adc05f3ad9342b71b95"
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.3.2"
+ matcher:
+ dependency: transitive
+ description:
+ name: matcher
+ sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.12.17"
+ material_color_utilities:
+ dependency: transitive
+ description:
+ name: material_color_utilities
+ sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.11.1"
+ meta:
+ dependency: transitive
+ description:
+ name: meta
+ sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.16.0"
+ mime:
+ dependency: transitive
+ description:
+ name: mime
+ sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.0.0"
+ nested:
+ dependency: transitive
+ description:
+ name: nested
+ sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.0.0"
+ path:
+ dependency: transitive
+ description:
+ name: path
+ sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.9.1"
+ path_parsing:
+ dependency: transitive
+ description:
+ name: path_parsing
+ sha256: "883402936929eac138ee0a45da5b0f2c80f89913e6dc3bf77eb65b84b409c6ca"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.1.0"
+ path_provider_linux:
+ dependency: transitive
+ description:
+ name: path_provider_linux
+ sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.2.1"
+ path_provider_platform_interface:
+ dependency: transitive
+ description:
+ name: path_provider_platform_interface
+ sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.1.2"
+ path_provider_windows:
+ dependency: transitive
+ description:
+ name: path_provider_windows
+ sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.3.0"
+ pay:
+ dependency: transitive
+ description:
+ name: pay
+ sha256: e012871f065c188e4c275e30b0000c174f6df99101ef137f9cc52ed13fad80e8
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.3.0"
+ pay_android:
+ dependency: transitive
+ description:
+ name: pay_android
+ sha256: "27dab62d564290aea584b6b39dbbda390b935d6d7296b54bc28de65579bfb049"
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.2.0"
+ pay_ios:
+ dependency: transitive
+ description:
+ name: pay_ios
+ sha256: ca4a7a12b4396b1205f314bebbc92e5268c627b65e5e6b3ab30efc2326da0a28
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.1.0"
+ pay_platform_interface:
+ dependency: transitive
+ description:
+ name: pay_platform_interface
+ sha256: "39a9f5fa4bafc59ce103bd5e84a6362113c6e58c361aed8dde9494b182fac400"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.0.0"
+ payment_plugin:
+ dependency: "direct main"
+ description:
+ path: ".."
+ relative: true
+ source: path
+ version: "0.0.1"
+ petitparser:
+ dependency: transitive
+ description:
+ name: petitparser
+ sha256: "1a97266a94f7350d30ae522c0af07890c70b8e62c71e8e3920d1db4d23c057d1"
+ url: "https://pub.dev"
+ source: hosted
+ version: "7.0.1"
+ platform:
+ dependency: transitive
+ description:
+ name: platform
+ sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984"
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.1.6"
+ plugin_platform_interface:
+ dependency: transitive
+ description:
+ name: plugin_platform_interface
+ sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.1.8"
+ posix:
+ dependency: transitive
+ description:
+ name: posix
+ sha256: "6323a5b0fa688b6a010df4905a56b00181479e6d10534cecfecede2aa55add61"
+ url: "https://pub.dev"
+ source: hosted
+ version: "6.0.3"
+ process:
+ dependency: transitive
+ description:
+ name: process
+ sha256: "107d8be718f120bbba9dcd1e95e3bd325b1b4a4f07db64154635ba03f2567a0d"
+ url: "https://pub.dev"
+ source: hosted
+ version: "5.0.3"
+ provider:
+ dependency: transitive
+ description:
+ name: provider
+ sha256: "4e82183fa20e5ca25703ead7e05de9e4cceed1fbd1eadc1ac3cb6f565a09f272"
+ url: "https://pub.dev"
+ source: hosted
+ version: "6.1.5+1"
+ retrofit:
+ dependency: transitive
+ description:
+ name: retrofit
+ sha256: "84063c18a00d55af41d6b8401edf8473e8c215bd7068ef7ec5e34c60657ffdbe"
+ url: "https://pub.dev"
+ source: hosted
+ version: "4.9.1"
+ shared_preferences:
+ dependency: transitive
+ description:
+ name: shared_preferences
+ sha256: "6e8bf70b7fef813df4e9a36f658ac46d107db4b4cfe1048b477d4e453a8159f5"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.5.3"
+ shared_preferences_android:
+ dependency: transitive
+ description:
+ name: shared_preferences_android
+ sha256: bd14436108211b0d4ee5038689a56d4ae3620fd72fd6036e113bf1345bc74d9e
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.4.13"
+ shared_preferences_foundation:
+ dependency: transitive
+ description:
+ name: shared_preferences_foundation
+ sha256: "6a52cfcdaeac77cad8c97b539ff688ccfc458c007b4db12be584fbe5c0e49e03"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.5.4"
+ shared_preferences_linux:
+ dependency: transitive
+ description:
+ name: shared_preferences_linux
+ sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.4.1"
+ shared_preferences_platform_interface:
+ dependency: transitive
+ description:
+ name: shared_preferences_platform_interface
+ sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.4.1"
+ shared_preferences_web:
+ dependency: transitive
+ description:
+ name: shared_preferences_web
+ sha256: c49bd060261c9a3f0ff445892695d6212ff603ef3115edbb448509d407600019
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.4.3"
+ shared_preferences_windows:
+ dependency: transitive
+ description:
+ name: shared_preferences_windows
+ sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.4.1"
+ shimmer:
+ dependency: transitive
+ description:
+ name: shimmer
+ sha256: "5f88c883a22e9f9f299e5ba0e4f7e6054857224976a5d9f839d4ebdc94a14ac9"
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.0.0"
+ sky_engine:
+ dependency: transitive
+ description: flutter
+ source: sdk
+ version: "0.0.0"
+ source_span:
+ dependency: transitive
+ description:
+ name: source_span
+ sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.10.1"
+ stack_trace:
+ dependency: transitive
+ description:
+ name: stack_trace
+ sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.12.1"
+ stream_channel:
+ dependency: transitive
+ description:
+ name: stream_channel
+ sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.1.4"
+ string_scanner:
+ dependency: transitive
+ description:
+ name: string_scanner
+ sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.4.1"
+ sync_http:
+ dependency: transitive
+ description:
+ name: sync_http
+ sha256: "7f0cd72eca000d2e026bcd6f990b81d0ca06022ef4e32fb257b30d3d1014a961"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.3.1"
+ term_glyph:
+ dependency: transitive
+ description:
+ name: term_glyph
+ sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.2.2"
+ test_api:
+ dependency: transitive
+ description:
+ name: test_api
+ sha256: "522f00f556e73044315fa4585ec3270f1808a4b186c936e612cab0b565ff1e00"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.7.6"
+ typed_data:
+ dependency: transitive
+ description:
+ name: typed_data
+ sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.4.0"
+ vector_graphics:
+ dependency: transitive
+ description:
+ name: vector_graphics
+ sha256: a4f059dc26fc8295b5921376600a194c4ec7d55e72f2fe4c7d2831e103d461e6
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.1.19"
+ vector_graphics_codec:
+ dependency: transitive
+ description:
+ name: vector_graphics_codec
+ sha256: "99fd9fbd34d9f9a32efd7b6a6aae14125d8237b10403b422a6a6dfeac2806146"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.1.13"
+ vector_graphics_compiler:
+ dependency: transitive
+ description:
+ name: vector_graphics_compiler
+ sha256: d354a7ec6931e6047785f4db12a1f61ec3d43b207fc0790f863818543f8ff0dc
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.1.19"
+ vector_math:
+ dependency: transitive
+ description:
+ name: vector_math
+ sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.2.0"
+ vm_service:
+ dependency: transitive
+ description:
+ name: vm_service
+ sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02
+ url: "https://pub.dev"
+ source: hosted
+ version: "15.0.0"
+ web:
+ dependency: transitive
+ description:
+ name: web
+ sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.1.1"
+ webdriver:
+ dependency: transitive
+ description:
+ name: webdriver
+ sha256: "2f3a14ca026957870cfd9c635b83507e0e51d8091568e90129fbf805aba7cade"
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.1.0"
+ xdg_directories:
+ dependency: transitive
+ description:
+ name: xdg_directories
+ sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.1.0"
+ xml:
+ dependency: transitive
+ description:
+ name: xml
+ sha256: "971043b3a0d3da28727e40ed3e0b5d18b742fa5a68665cca88e74b7876d5e025"
+ url: "https://pub.dev"
+ source: hosted
+ version: "6.6.1"
+ yaml:
+ dependency: transitive
+ description:
+ name: yaml
+ sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce
+ url: "https://pub.dev"
+ source: hosted
+ version: "3.1.3"
+sdks:
+ dart: ">=3.9.0 <4.0.0"
+ flutter: ">=3.35.0"
diff --git a/payment_plugin/example/pubspec.yaml b/payment_plugin/example/pubspec.yaml
new file mode 100644
index 00000000..52393613
--- /dev/null
+++ b/payment_plugin/example/pubspec.yaml
@@ -0,0 +1,85 @@
+name: payment_plugin_example
+description: "Demonstrates how to use the payment_plugin plugin."
+# The following line prevents the package from being accidentally published to
+# pub.dev using `flutter pub publish`. This is preferred for private packages.
+publish_to: 'none' # Remove this line if you wish to publish to pub.dev
+
+environment:
+ sdk: ^3.8.1
+
+# Dependencies specify other packages that your package needs in order to work.
+# To automatically upgrade your package dependencies to the latest versions
+# consider running `flutter pub upgrade --major-versions`. Alternatively,
+# dependencies can be manually updated by changing the version numbers below to
+# the latest version available on pub.dev. To see which dependencies have newer
+# versions available, run `flutter pub outdated`.
+dependencies:
+ flutter:
+ sdk: flutter
+
+ payment_plugin:
+ # When depending on this package from a real application you should use:
+ # payment_plugin: ^x.y.z
+ # See https://dart.dev/tools/pub/dependencies#version-constraints
+ # The example app is bundled with the plugin so we use a path dependency on
+ # the parent directory to use the current plugin's version.
+ path: ../
+
+ # The following adds the Cupertino Icons font to your application.
+ # Use with the CupertinoIcons class for iOS style icons.
+ cupertino_icons: ^1.0.8
+
+dev_dependencies:
+ integration_test:
+ sdk: flutter
+ flutter_test:
+ sdk: flutter
+
+ # The "flutter_lints" package below contains a set of recommended lints to
+ # encourage good coding practices. The lint set provided by the package is
+ # activated in the `analysis_options.yaml` file located at the root of your
+ # package. See that file for information about deactivating specific lint
+ # rules and activating additional ones.
+ flutter_lints: ^5.0.0
+
+# For information on the generic Dart part of this file, see the
+# following page: https://dart.dev/tools/pub/pubspec
+
+# The following section is specific to Flutter packages.
+flutter:
+
+ # The following line ensures that the Material Icons font is
+ # included with your application, so that you can use the icons in
+ # the material Icons class.
+ uses-material-design: true
+
+ # To add assets to your application, add an assets section, like this:
+ # assets:
+ # - images/a_dot_burr.jpeg
+ # - images/a_dot_ham.jpeg
+
+ # An image asset can refer to one or more resolution-specific "variants", see
+ # https://flutter.dev/to/resolution-aware-images
+
+ # For details regarding adding assets from package dependencies, see
+ # https://flutter.dev/to/asset-from-package
+
+ # To add custom fonts to your application, add a fonts section here,
+ # in this "flutter" section. Each entry in this list should have a
+ # "family" key with the font family name, and a "fonts" key with a
+ # list giving the asset and other descriptors for the font. For
+ # example:
+ # fonts:
+ # - family: Schyler
+ # fonts:
+ # - asset: fonts/Schyler-Regular.ttf
+ # - asset: fonts/Schyler-Italic.ttf
+ # style: italic
+ # - family: Trajan Pro
+ # fonts:
+ # - asset: fonts/TrajanPro.ttf
+ # - asset: fonts/TrajanPro_Bold.ttf
+ # weight: 700
+ #
+ # For details regarding fonts from package dependencies,
+ # see https://flutter.dev/to/font-from-package
diff --git a/payment_plugin/example/test/widget_test.dart b/payment_plugin/example/test/widget_test.dart
new file mode 100644
index 00000000..00f5c848
--- /dev/null
+++ b/payment_plugin/example/test/widget_test.dart
@@ -0,0 +1,27 @@
+// This is a basic Flutter widget test.
+//
+// To perform an interaction with a widget in your test, use the WidgetTester
+// utility in the flutter_test package. For example, you can send tap and scroll
+// gestures. You can also use WidgetTester to find child widgets in the widget
+// tree, read text, and verify that the values of widget properties are correct.
+
+import 'package:flutter/material.dart';
+import 'package:flutter_test/flutter_test.dart';
+
+import 'package:payment_plugin_example/main.dart';
+
+void main() {
+ testWidgets('Verify Platform version', (WidgetTester tester) async {
+ // Build our app and trigger a frame.
+ await tester.pumpWidget(const MyApp());
+
+ // Verify that platform version is retrieved.
+ expect(
+ find.byWidgetPredicate(
+ (Widget widget) => widget is Text &&
+ widget.data!.startsWith('Running on:'),
+ ),
+ findsOneWidget,
+ );
+ });
+}
diff --git a/payment_plugin/example/web/favicon.png b/payment_plugin/example/web/favicon.png
new file mode 100644
index 00000000..8aaa46ac
Binary files /dev/null and b/payment_plugin/example/web/favicon.png differ
diff --git a/payment_plugin/example/web/icons/Icon-192.png b/payment_plugin/example/web/icons/Icon-192.png
new file mode 100644
index 00000000..b749bfef
Binary files /dev/null and b/payment_plugin/example/web/icons/Icon-192.png differ
diff --git a/payment_plugin/example/web/icons/Icon-512.png b/payment_plugin/example/web/icons/Icon-512.png
new file mode 100644
index 00000000..88cfd48d
Binary files /dev/null and b/payment_plugin/example/web/icons/Icon-512.png differ
diff --git a/payment_plugin/example/web/icons/Icon-maskable-192.png b/payment_plugin/example/web/icons/Icon-maskable-192.png
new file mode 100644
index 00000000..eb9b4d76
Binary files /dev/null and b/payment_plugin/example/web/icons/Icon-maskable-192.png differ
diff --git a/payment_plugin/example/web/icons/Icon-maskable-512.png b/payment_plugin/example/web/icons/Icon-maskable-512.png
new file mode 100644
index 00000000..d69c5669
Binary files /dev/null and b/payment_plugin/example/web/icons/Icon-maskable-512.png differ
diff --git a/payment_plugin/example/web/index.html b/payment_plugin/example/web/index.html
new file mode 100644
index 00000000..12c45eb0
--- /dev/null
+++ b/payment_plugin/example/web/index.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <!--
+ If you are serving your web app in a path other than the root, change the
+ href value below to reflect the base path you are serving from.
+
+ The path provided below has to start and end with a slash "/" in order for
+ it to work correctly.
+
+ For more details:
+ * https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base
+
+ This is a placeholder for base href that will be replaced by the value of
+ the `--base-href` argument provided to `flutter build`.
+ -->
+ <base href="$FLUTTER_BASE_HREF">
+
+ <meta charset="UTF-8">
+ <meta content="IE=Edge" http-equiv="X-UA-Compatible">
+ <meta name="description" content="Demonstrates how to use the payment_plugin plugin.">
+
+ <!-- iOS meta tags & icons -->
+ <meta name="mobile-web-app-capable" content="yes">
+ <meta name="apple-mobile-web-app-status-bar-style" content="black">
+ <meta name="apple-mobile-web-app-title" content="payment_plugin_example">
+ <link rel="apple-touch-icon" href="icons/Icon-192.png">
+
+ <!-- Favicon -->
+ <link rel="icon" type="image/png" href="favicon.png"/>
+
+ <title>payment_plugin_example</title>
+ <link rel="manifest" href="manifest.json">
+</head>
+<body>
+ <script src="flutter_bootstrap.js" async></script>
+</body>
+</html>
diff --git a/payment_plugin/example/web/manifest.json b/payment_plugin/example/web/manifest.json
new file mode 100644
index 00000000..1aa2569a
--- /dev/null
+++ b/payment_plugin/example/web/manifest.json
@@ -0,0 +1,35 @@
+{
+ "name": "payment_plugin_example",
+ "short_name": "payment_plugin_example",
+ "start_url": ".",
+ "display": "standalone",
+ "background_color": "#0175C2",
+ "theme_color": "#0175C2",
+ "description": "Demonstrates how to use the payment_plugin plugin.",
+ "orientation": "portrait-primary",
+ "prefer_related_applications": false,
+ "icons": [
+ {
+ "src": "icons/Icon-192.png",
+ "sizes": "192x192",
+ "type": "image/png"
+ },
+ {
+ "src": "icons/Icon-512.png",
+ "sizes": "512x512",
+ "type": "image/png"
+ },
+ {
+ "src": "icons/Icon-maskable-192.png",
+ "sizes": "192x192",
+ "type": "image/png",
+ "purpose": "maskable"
+ },
+ {
+ "src": "icons/Icon-maskable-512.png",
+ "sizes": "512x512",
+ "type": "image/png",
+ "purpose": "maskable"
+ }
+ ]
+}
diff --git a/payment_plugin/ios/.gitignore b/payment_plugin/ios/.gitignore
new file mode 100644
index 00000000..034771fc
--- /dev/null
+++ b/payment_plugin/ios/.gitignore
@@ -0,0 +1,38 @@
+.idea/
+.vagrant/
+.sconsign.dblite
+.svn/
+
+.DS_Store
+*.swp
+profile
+
+DerivedData/
+build/
+GeneratedPluginRegistrant.h
+GeneratedPluginRegistrant.m
+
+.generated/
+
+*.pbxuser
+*.mode1v3
+*.mode2v3
+*.perspectivev3
+
+!default.pbxuser
+!default.mode1v3
+!default.mode2v3
+!default.perspectivev3
+
+xcuserdata
+
+*.moved-aside
+
+*.pyc
+*sync/
+Icon?
+.tags*
+
+/Flutter/Generated.xcconfig
+/Flutter/ephemeral/
+/Flutter/flutter_export_environment.sh
diff --git a/payment_plugin/ios/payment_plugin.podspec b/payment_plugin/ios/payment_plugin.podspec
new file mode 100644
index 00000000..664c2a63
--- /dev/null
+++ b/payment_plugin/ios/payment_plugin.podspec
@@ -0,0 +1,29 @@
+#
+# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html.
+# Run `pod lib lint payment_plugin.podspec` to validate before publishing.
+#
+Pod::Spec.new do |s|
+ s.name = 'payment_plugin'
+ s.version = '0.0.1'
+ s.summary = 'A new Flutter plugin project.'
+ s.description = <<-DESC
+A new Flutter plugin project.
+ DESC
+ s.homepage = 'http://example.com'
+ s.license = { :file => '../LICENSE' }
+ s.author = { 'Your Company' => 'email@example.com' }
+ s.source = { :path => '.' }
+ s.source_files = 'payment_plugin/Sources/payment_plugin/**/*'
+ s.dependency 'Flutter'
+ s.platform = :ios, '12.0'
+
+ # Flutter.framework does not contain a i386 slice.
+ s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' }
+ s.swift_version = '5.0'
+
+ # If your plugin requires a privacy manifest, for example if it uses any
+ # required reason APIs, update the PrivacyInfo.xcprivacy file to describe your
+ # plugin's privacy impact, and then uncomment this line. For more information,
+ # see https://developer.apple.com/documentation/bundleresources/privacy_manifest_files
+ # s.resource_bundles = {'payment_plugin_privacy' => ['payment_plugin/Sources/payment_plugin/PrivacyInfo.xcprivacy']}
+end
diff --git a/payment_plugin/ios/payment_plugin/Package.swift b/payment_plugin/ios/payment_plugin/Package.swift
new file mode 100644
index 00000000..33164d5b
--- /dev/null
+++ b/payment_plugin/ios/payment_plugin/Package.swift
@@ -0,0 +1,32 @@
+// swift-tools-version: 5.9
+// The swift-tools-version declares the minimum version of Swift required to build this package.
+
+import PackageDescription
+
+let package = Package(
+ name: "payment_plugin",
+ platforms: [
+ .iOS("12.0")
+ ],
+ products: [
+ .library(name: "payment-plugin", targets: ["payment_plugin"])
+ ],
+ dependencies: [],
+ targets: [
+ .target(
+ name: "payment_plugin",
+ dependencies: [],
+ resources: [
+ // If your plugin requires a privacy manifest, for example if it uses any required
+ // reason APIs, update the PrivacyInfo.xcprivacy file to describe your plugin's
+ // privacy impact, and then uncomment these lines. For more information, see
+ // https://developer.apple.com/documentation/bundleresources/privacy_manifest_files
+ // .process("PrivacyInfo.xcprivacy"),
+
+ // If you have other resources that need to be bundled with your plugin, refer to
+ // the following instructions to add them:
+ // https://developer.apple.com/documentation/xcode/bundling-resources-with-a-swift-package
+ ]
+ )
+ ]
+)
diff --git a/payment_plugin/ios/payment_plugin/Sources/payment_plugin/PaymentPlugin.swift b/payment_plugin/ios/payment_plugin/Sources/payment_plugin/PaymentPlugin.swift
new file mode 100644
index 00000000..7e5bf28b
--- /dev/null
+++ b/payment_plugin/ios/payment_plugin/Sources/payment_plugin/PaymentPlugin.swift
@@ -0,0 +1,19 @@
+import Flutter
+import UIKit
+
+public class PaymentPlugin: NSObject, FlutterPlugin {
+ public static func register(with registrar: FlutterPluginRegistrar) {
+ let channel = FlutterMethodChannel(name: "payment_plugin", binaryMessenger: registrar.messenger())
+ let instance = PaymentPlugin()
+ registrar.addMethodCallDelegate(instance, channel: channel)
+ }
+
+ public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
+ switch call.method {
+ case "getPlatformVersion":
+ result("iOS " + UIDevice.current.systemVersion)
+ default:
+ result(FlutterMethodNotImplemented)
+ }
+ }
+}
diff --git a/payment_plugin/ios/payment_plugin/Sources/payment_plugin/PrivacyInfo.xcprivacy b/payment_plugin/ios/payment_plugin/Sources/payment_plugin/PrivacyInfo.xcprivacy
new file mode 100644
index 00000000..a34b7e2e
--- /dev/null
+++ b/payment_plugin/ios/payment_plugin/Sources/payment_plugin/PrivacyInfo.xcprivacy
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>NSPrivacyTrackingDomains</key>
+ <array/>
+ <key>NSPrivacyAccessedAPITypes</key>
+ <array/>
+ <key>NSPrivacyCollectedDataTypes</key>
+ <array/>
+ <key>NSPrivacyTracking</key>
+ <false/>
+</dict>
+</plist>
diff --git a/payment_plugin/lib/_generated/data/remote/api/api_client.g.dart b/payment_plugin/lib/_generated/data/remote/api/api_client.g.dart
new file mode 100644
index 00000000..c9353cbc
--- /dev/null
+++ b/payment_plugin/lib/_generated/data/remote/api/api_client.g.dart
@@ -0,0 +1,252 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of '../../../../data/remote/api/api_client.dart';
+
+// dart format off
+
+// **************************************************************************
+// RetrofitGenerator
+// **************************************************************************
+
+// ignore_for_file: unnecessary_brace_in_string_interps,no_leading_underscores_for_local_identifiers,unused_element,unnecessary_string_interpolations,unused_element_parameter,avoid_unused_constructor_parameters,unreachable_from_main
+
+class _ApiClient implements ApiClient {
+ _ApiClient(this._dio, {this.baseUrl, this.errorLogger});
+
+ final Dio _dio;
+
+ String? baseUrl;
+
+ final ParseErrorLogger? errorLogger;
+
+ @override
+ Future<StoredPaymentsResponse> getPaymentMethods() async {
+ final _extra = <String, dynamic>{};
+ final queryParameters = <String, dynamic>{};
+ final _headers = <String, dynamic>{};
+ const Map<String, dynamic>? _data = null;
+ final _options = _setStreamType<StoredPaymentsResponse>(
+ Options(method: 'GET', headers: _headers, extra: _extra)
+ .compose(
+ _dio.options,
+ '/Payment/v1/stored-payment-methods',
+ queryParameters: queryParameters,
+ data: _data,
+ )
+ .copyWith(baseUrl: _combineBaseUrls(_dio.options.baseUrl, baseUrl)),
+ );
+ final _result = await _dio.fetch<Map<String, dynamic>>(_options);
+ late StoredPaymentsResponse _value;
+ try {
+ _value = StoredPaymentsResponse.fromJson(_result.data!);
+ } on Object catch (e, s) {
+ errorLogger?.logError(e, s, _options, _result);
+ rethrow;
+ }
+ return _value;
+ }
+
+ @override
+ Future<dynamic> createAdyenSession(String data) async {
+ final _extra = <String, dynamic>{};
+ final queryParameters = <String, dynamic>{r'data': data};
+ final _headers = <String, dynamic>{};
+ const Map<String, dynamic>? _data = null;
+ final _options = _setStreamType<dynamic>(
+ Options(method: 'POST', headers: _headers, extra: _extra)
+ .compose(
+ _dio.options,
+ '/Payment/v1/CreateAdyenSessionForBooking',
+ queryParameters: queryParameters,
+ data: _data,
+ )
+ .copyWith(baseUrl: _combineBaseUrls(_dio.options.baseUrl, baseUrl)),
+ );
+ final _result = await _dio.fetch(_options);
+ final _value = _result.data;
+ return _value;
+ }
+
+ @override
+ Future<dynamic> createAdyenSessionForCards(String data) async {
+ final _extra = <String, dynamic>{};
+ final queryParameters = <String, dynamic>{};
+ final _headers = <String, dynamic>{};
+ final _data = data;
+ final _options = _setStreamType<dynamic>(
+ Options(method: 'POST', headers: _headers, extra: _extra)
+ .compose(
+ _dio.options,
+ '/Payment/v1/CreatePaymentSessionForAddingCards',
+ queryParameters: queryParameters,
+ data: _data,
+ )
+ .copyWith(baseUrl: _combineBaseUrls(_dio.options.baseUrl, baseUrl)),
+ );
+ final _result = await _dio.fetch(_options);
+ final _value = _result.data;
+ return _value;
+ }
+
+ @override
+ Future<dynamic> listAvailablePaymentMethods() async {
+ final _extra = <String, dynamic>{};
+ final queryParameters = <String, dynamic>{};
+ final _headers = <String, dynamic>{};
+ const Map<String, dynamic>? _data = null;
+ final _options = _setStreamType<dynamic>(
+ Options(method: 'GET', headers: _headers, extra: _extra)
+ .compose(
+ _dio.options,
+ '/Payment/v1/list-available-payment-methods',
+ queryParameters: queryParameters,
+ data: _data,
+ )
+ .copyWith(baseUrl: _combineBaseUrls(_dio.options.baseUrl, baseUrl)),
+ );
+ final _result = await _dio.fetch(_options);
+ final _value = _result.data;
+ return _value;
+ }
+
+ @override
+ Future<dynamic> storePaymentMethod(PaymentMethod storedPaymentMethod) async {
+ final _extra = <String, dynamic>{};
+ final queryParameters = <String, dynamic>{};
+ final _headers = <String, dynamic>{};
+ final _data = <String, dynamic>{};
+ _data.addAll(storedPaymentMethod.toJson());
+ final _options = _setStreamType<dynamic>(
+ Options(method: 'POST', headers: _headers, extra: _extra)
+ .compose(
+ _dio.options,
+ '/Payment/v1/stored-payment-methods',
+ queryParameters: queryParameters,
+ data: _data,
+ )
+ .copyWith(baseUrl: _combineBaseUrls(_dio.options.baseUrl, baseUrl)),
+ );
+ final _result = await _dio.fetch(_options);
+ final _value = _result.data;
+ return _value;
+ }
+
+ @override
+ Future<dynamic> removePaymentMethod(String cardId) async {
+ final _extra = <String, dynamic>{};
+ final queryParameters = <String, dynamic>{};
+ final _headers = <String, dynamic>{};
+ const Map<String, dynamic>? _data = null;
+ final _options = _setStreamType<dynamic>(
+ Options(method: 'DELETE', headers: _headers, extra: _extra)
+ .compose(
+ _dio.options,
+ '/Payment/v1/stored-payment-methods/${cardId}',
+ queryParameters: queryParameters,
+ data: _data,
+ )
+ .copyWith(baseUrl: _combineBaseUrls(_dio.options.baseUrl, baseUrl)),
+ );
+ final _result = await _dio.fetch(_options);
+ final _value = _result.data;
+ return _value;
+ }
+
+ @override
+ Future<dynamic> submitPayment(
+ Map<String, dynamic> data,
+ String apiKey,
+ String contentType,
+ ) async {
+ final _extra = <String, dynamic>{};
+ final queryParameters = <String, dynamic>{r'data': data};
+ final _headers = <String, dynamic>{
+ r'X-API-KEY': apiKey,
+ r'Content-Type': contentType,
+ };
+ _headers.removeWhere((k, v) => v == null);
+ const Map<String, dynamic>? _data = null;
+ final _options = _setStreamType<dynamic>(
+ Options(
+ method: 'POST',
+ headers: _headers,
+ extra: _extra,
+ contentType: contentType,
+ )
+ .compose(
+ _dio.options,
+ 'https://checkout-test.adyen.com/v71/payments',
+ queryParameters: queryParameters,
+ data: _data,
+ )
+ .copyWith(baseUrl: _combineBaseUrls(_dio.options.baseUrl, baseUrl)),
+ );
+ final _result = await _dio.fetch(_options);
+ final _value = _result.data;
+ return _value;
+ }
+
+ @override
+ Future<dynamic> postPaymentsDetails(
+ Map<String, dynamic> data,
+ String contentType,
+ String apiKey,
+ ) async {
+ final _extra = <String, dynamic>{};
+ final queryParameters = <String, dynamic>{};
+ final _headers = <String, dynamic>{
+ r'Content-Type': contentType,
+ r'X-API-KEY': apiKey,
+ };
+ _headers.removeWhere((k, v) => v == null);
+ final _data = <String, dynamic>{};
+ _data.addAll(data);
+ final _options = _setStreamType<dynamic>(
+ Options(
+ method: 'POST',
+ headers: _headers,
+ extra: _extra,
+ contentType: contentType,
+ )
+ .compose(
+ _dio.options,
+ '/Payment/v1/post-payments-details',
+ queryParameters: queryParameters,
+ data: _data,
+ )
+ .copyWith(baseUrl: _combineBaseUrls(_dio.options.baseUrl, baseUrl)),
+ );
+ final _result = await _dio.fetch(_options);
+ final _value = _result.data;
+ return _value;
+ }
+
+ RequestOptions _setStreamType<T>(RequestOptions requestOptions) {
+ if (T != dynamic &&
+ !(requestOptions.responseType == ResponseType.bytes ||
+ requestOptions.responseType == ResponseType.stream)) {
+ if (T == String) {
+ requestOptions.responseType = ResponseType.plain;
+ } else {
+ requestOptions.responseType = ResponseType.json;
+ }
+ }
+ return requestOptions;
+ }
+
+ String _combineBaseUrls(String dioBaseUrl, String? baseUrl) {
+ if (baseUrl == null || baseUrl.trim().isEmpty) {
+ return dioBaseUrl;
+ }
+
+ final url = Uri.parse(baseUrl);
+
+ if (url.isAbsolute) {
+ return url.toString();
+ }
+
+ return Uri.parse(dioBaseUrl).resolveUri(url).toString();
+ }
+}
+
+// dart format on
diff --git a/payment_plugin/lib/_generated/data/remote/models/api_response.g.dart b/payment_plugin/lib/_generated/data/remote/models/api_response.g.dart
new file mode 100644
index 00000000..528f108e
--- /dev/null
+++ b/payment_plugin/lib/_generated/data/remote/models/api_response.g.dart
@@ -0,0 +1,12 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of '../../../../data/remote/models/api_response.dart';
+
+// **************************************************************************
+// JsonSerializableGenerator
+// **************************************************************************
+
+ApiResponse<T> _$ApiResponseFromJson<T>(
+ Map json,
+ T Function(Object? json) fromJsonT,
+) => ApiResponse<T>(data: fromJsonT(json['data']));
diff --git a/payment_plugin/lib/_generated/data/remote/models/payment_event_handler.dart b/payment_plugin/lib/_generated/data/remote/models/payment_event_handler.dart
new file mode 100644
index 00000000..934dc1e1
--- /dev/null
+++ b/payment_plugin/lib/_generated/data/remote/models/payment_event_handler.dart
@@ -0,0 +1,80 @@
+import 'package:adyen_checkout/adyen_checkout.dart';
+import 'package:payment_plugin/utils/json.dart';
+
+class PaymentEventHandler {
+ //Response keys
+ static const errorCodeKey = "errorCode";
+ static const resultCodeKey = "resultCode";
+ static const actionKey = "action";
+ static const messageKey = "message";
+ static const refusalReasonKey = "refusalReason";
+
+ PaymentEvent handleResponse({
+ required Json jsonResponse,
+ Json updatedPaymentMethodsJson = const {},
+ }) {
+ if (_isError(jsonResponse)) {
+ final message = jsonResponse[messageKey] as String?;
+ final reason = jsonResponse[refusalReasonKey] as String;
+ return Error(
+ errorMessage: message,
+ reason: message ?? reason,
+ dismissDropIn: true,
+ );
+ }
+
+ if (_isRefusedInPartialPaymentFlow(jsonResponse)) {
+ return Error(
+ errorMessage: "Payment is refused",
+ reason: "Refused",
+ dismissDropIn: true,
+ );
+ }
+
+ if (_isAction(jsonResponse)) {
+ return Action(actionResponse: jsonResponse[actionKey] as Json);
+ }
+
+ if (_isNonFullyPaidOrder(jsonResponse) &&
+ updatedPaymentMethodsJson.isNotEmpty) {
+ return Update(
+ orderJson: jsonResponse["order"] as Json,
+ paymentMethodsJson: updatedPaymentMethodsJson,
+ );
+ }
+
+ if (jsonResponse.containsKey(resultCodeKey)) {
+ return Finished(resultCode: jsonResponse[resultCodeKey] as String);
+ }
+
+ return Finished(resultCode: "EMPTY");
+ }
+
+ bool _isError(Json jsonResponse) {
+ final hasErrorCodeKey = jsonResponse.containsKey(errorCodeKey);
+ final hasErrorResultCode = (jsonResponse[resultCodeKey] as String?)
+ ?.toUpperCase()
+ .contains(ResultCode.error.name.toUpperCase()) ??
+ false;
+ return hasErrorCodeKey || hasErrorResultCode;
+ }
+
+ bool _isRefusedInPartialPaymentFlow(Json jsonResponse) =>
+ _isRefused(jsonResponse) && _isNonFullyPaidOrder(jsonResponse);
+
+ bool _isRefused(Json jsonResponse) => jsonResponse[resultCodeKey]
+ .toString()
+ .toUpperCase()
+ .contains(ResultCode.refused.name.toUpperCase());
+
+ bool _isAction(Json jsonResponse) => jsonResponse.containsKey(actionKey);
+
+ bool _isNonFullyPaidOrder(Json jsonResponse) {
+ if (jsonResponse.containsKey("order")) {
+ final remainingAmount = jsonResponse["order"]["remainingAmount"]["value"] as num;
+ return remainingAmount > 0;
+ } else {
+ return false;
+ }
+ }
+}
diff --git a/payment_plugin/lib/_generated/data/remote/models/stored_payment_methods_response.g.dart b/payment_plugin/lib/_generated/data/remote/models/stored_payment_methods_response.g.dart
new file mode 100644
index 00000000..dc801a2e
--- /dev/null
+++ b/payment_plugin/lib/_generated/data/remote/models/stored_payment_methods_response.g.dart
@@ -0,0 +1,27 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of '../../../../data/remote/models/stored_payment_methods_response.dart';
+
+// **************************************************************************
+// JsonSerializableGenerator
+// **************************************************************************
+
+StoredPaymentsResponse _$StoredPaymentsResponseFromJson(
+ Map json,
+) => StoredPaymentsResponse(
+ merchantAccount: json['merchantAccount'] as String,
+ shopperReference: json['shopperReference'] as String,
+ storedPaymentMethods: (json['storedPaymentMethods'] as List<dynamic>?)?.map(
+ (e) => StoredPaymentMethod.fromJson(Map<String, dynamic>.from(e as Map)),
+ ),
+);
+
+Map<String, dynamic> _$StoredPaymentsResponseToJson(
+ StoredPaymentsResponse instance,
+) => <String, dynamic>{
+ 'merchantAccount': instance.merchantAccount,
+ 'shopperReference': instance.shopperReference,
+ 'storedPaymentMethods': instance.storedPaymentMethods
+ ?.map((e) => e.toJson())
+ .toList(),
+};
diff --git a/payment_plugin/lib/_generated/domain/models/add_card_payment_method.g.dart b/payment_plugin/lib/_generated/domain/models/add_card_payment_method.g.dart
new file mode 100644
index 00000000..5864034c
--- /dev/null
+++ b/payment_plugin/lib/_generated/domain/models/add_card_payment_method.g.dart
@@ -0,0 +1,31 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of '../../../domain/models/add_card_payment_method.dart';
+
+// **************************************************************************
+// JsonSerializableGenerator
+// **************************************************************************
+
+AddCardPaymentMethod _$AddCardPaymentMethodFromJson(Map json) =>
+ AddCardPaymentMethod(
+ brands: (json['brands'] as List<dynamic>).map(
+ (e) => $enumDecode(_$CardTypeEnumMap, e),
+ ),
+ name: json['name'] as String,
+ type: json['type'] as String,
+ );
+
+Map<String, dynamic> _$AddCardPaymentMethodToJson(
+ AddCardPaymentMethod instance,
+) => <String, dynamic>{
+ 'brands': instance.brands.map((e) => _$CardTypeEnumMap[e]!).toList(),
+ 'name': instance.name,
+ 'type': instance.type,
+};
+
+const _$CardTypeEnumMap = {
+ CardType.visa: 'visa',
+ CardType.mastercard: 'mastercard',
+ CardType.amexgooglepay: 'amexgooglepay',
+ CardType.maestro: 'maestro',
+};
diff --git a/payment_plugin/lib/_generated/domain/models/payment_method.g.dart b/payment_plugin/lib/_generated/domain/models/payment_method.g.dart
new file mode 100644
index 00000000..be83a47c
--- /dev/null
+++ b/payment_plugin/lib/_generated/domain/models/payment_method.g.dart
@@ -0,0 +1,38 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of '../../../domain/models/payment_method.dart';
+
+// **************************************************************************
+// JsonSerializableGenerator
+// **************************************************************************
+
+PaymentMethod _$PaymentMethodFromJson(Map json) => PaymentMethod(
+ brand: json['brand'] as String?,
+ cvc: json['cvc'] as String?,
+ encryptedCard: json['encryptedCard'] as String?,
+ encryptedCardNumber: json['encryptedCardNumber'] as String?,
+ encryptedExpiryMonth: json['encryptedExpiryMonth'] as String?,
+ encryptedExpiryYear: json['encryptedExpiryYear'] as String?,
+ encryptedSecurityCode: json['encryptedSecurityCode'] as String?,
+ expiryMonth: json['expiryMonth'] as String?,
+ expiryYear: json['expiryYear'] as String?,
+ holderName: json['holderName'] as String?,
+ number: json['number'] as String?,
+ type: json['type'] as String?,
+);
+
+Map<String, dynamic> _$PaymentMethodToJson(PaymentMethod instance) =>
+ <String, dynamic>{
+ 'brand': instance.brand,
+ 'cvc': instance.cvc,
+ 'encryptedCard': instance.encryptedCard,
+ 'encryptedCardNumber': instance.encryptedCardNumber,
+ 'encryptedExpiryMonth': instance.encryptedExpiryMonth,
+ 'encryptedExpiryYear': instance.encryptedExpiryYear,
+ 'encryptedSecurityCode': instance.encryptedSecurityCode,
+ 'expiryMonth': instance.expiryMonth,
+ 'expiryYear': instance.expiryYear,
+ 'holderName': instance.holderName,
+ 'number': instance.number,
+ 'type': instance.type,
+ };
diff --git a/payment_plugin/lib/_generated/domain/models/stored_payment_method.g.dart b/payment_plugin/lib/_generated/domain/models/stored_payment_method.g.dart
new file mode 100644
index 00000000..a4d41dfd
--- /dev/null
+++ b/payment_plugin/lib/_generated/domain/models/stored_payment_method.g.dart
@@ -0,0 +1,28 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of '../../../domain/models/stored_payment_method.dart';
+
+// **************************************************************************
+// JsonSerializableGenerator
+// **************************************************************************
+
+StoredPaymentMethod _$StoredPaymentMethodFromJson(Map json) =>
+ StoredPaymentMethod(
+ brand: json['brand'] as String,
+ expiryMonth: json['expiryMonth'] as String,
+ expiryYear: json['expiryYear'] as String,
+ holderName: json['holderName'] as String,
+ id: json['id'] as String,
+ lastFour: json['lastFour'] as String,
+ );
+
+Map<String, dynamic> _$StoredPaymentMethodToJson(
+ StoredPaymentMethod instance,
+) => <String, dynamic>{
+ 'brand': instance.brand,
+ 'expiryMonth': instance.expiryMonth,
+ 'expiryYear': instance.expiryYear,
+ 'holderName': instance.holderName,
+ 'id': instance.id,
+ 'lastFour': instance.lastFour,
+};
diff --git a/payment_plugin/lib/_generated/presentation/components/payment_details.g.dart b/payment_plugin/lib/_generated/presentation/components/payment_details.g.dart
new file mode 100644
index 00000000..5182d8b3
--- /dev/null
+++ b/payment_plugin/lib/_generated/presentation/components/payment_details.g.dart
@@ -0,0 +1,33 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of '../../../presentation/components/payment_details.dart';
+
+// **************************************************************************
+// JsonSerializableGenerator
+// **************************************************************************
+
+PaymentDetails _$PaymentDetailsFromJson(Map json) => PaymentDetails(
+ cardNumber: json['cardNumber'] as String,
+ cardHolder: json['cardHolder'] as String,
+ expiryDate: json['expiryDate'] as String?,
+ cvc: json['cvc'] as String?,
+ cardType: $enumDecode(_$CardTypeEnumMap, json['cardType']),
+ cardName: json['cardName'] as String?,
+);
+
+Map<String, dynamic> _$PaymentDetailsToJson(PaymentDetails instance) =>
+ <String, dynamic>{
+ 'cardNumber': instance.cardNumber,
+ 'cardHolder': instance.cardHolder,
+ 'expiryDate': instance.expiryDate,
+ 'cvc': instance.cvc,
+ 'cardType': _$CardTypeEnumMap[instance.cardType]!,
+ 'cardName': instance.cardName,
+ };
+
+const _$CardTypeEnumMap = {
+ CardType.visa: 'visa',
+ CardType.mastercard: 'mastercard',
+ CardType.amexgooglepay: 'amexgooglepay',
+ CardType.maestro: 'maestro',
+};
diff --git a/payment_plugin/lib/common/constants.dart b/payment_plugin/lib/common/constants.dart
new file mode 100644
index 00000000..4f1f934a
--- /dev/null
+++ b/payment_plugin/lib/common/constants.dart
@@ -0,0 +1,4 @@
+const String merchantId = "merchant.ComwellHotelsECOM";
+const String merchantName = "Comwell";
+const String returnUrl =
+ 'adyencheckout://com.comwell.phoenix.test/adyenPayment';
\ No newline at end of file
diff --git a/payment_plugin/lib/data/remote/api/api_client.dart b/payment_plugin/lib/data/remote/api/api_client.dart
new file mode 100644
index 00000000..2c835e4e
--- /dev/null
+++ b/payment_plugin/lib/data/remote/api/api_client.dart
@@ -0,0 +1,52 @@
+import 'package:dio/dio.dart';
+import 'package:payment_plugin/domain/models/payment_method.dart';
+import 'package:payment_plugin/data/remote/models/stored_payment_methods_response.dart';
+import 'package:retrofit/retrofit.dart';
+
+import '../models/api_response.dart';
+
+part '../../../_generated/data/remote/api/api_client.g.dart';
+
+@RestApi()
+abstract class ApiClient {
+ factory ApiClient(Dio dio) = _ApiClient;
+
+ @GET("/Payment/v1/stored-payment-methods")
+ Future<StoredPaymentsResponse> getPaymentMethods();
+
+ @POST("/Payment/v1/CreateAdyenSessionForBooking")
+ Future<dynamic> createAdyenSession(
+ @Query("data") String data,
+ );
+
+ @POST("/Payment/v1/CreatePaymentSessionForAddingCards")
+ Future<dynamic> createAdyenSessionForCards(
+ @Body() String data);
+
+ @GET("/Payment/v1/list-available-payment-methods")
+ Future<dynamic> listAvailablePaymentMethods();
+
+ @POST("/Payment/v1/stored-payment-methods")
+ Future<dynamic> storePaymentMethod(
+ @Body() PaymentMethod storedPaymentMethod,
+ );
+
+ @DELETE("/Payment/v1/stored-payment-methods/{cardId}")
+ Future<dynamic> removePaymentMethod(
+ @Path("cardId") String cardId,
+ );
+
+ @POST("https://checkout-test.adyen.com/v71/payments")
+ Future<dynamic> submitPayment(
+ @Query("data")Map<String, dynamic> data,
+ @Header("X-API-KEY") String apiKey,
+ @Header("Content-Type") String contentType,
+ );
+
+ @POST("/Payment/v1/post-payments-details")
+ Future<dynamic> postPaymentsDetails(
+ @Body() Map<String, dynamic> data,
+ @Header("Content-Type") String contentType,
+ @Header("X-API-KEY") String apiKey,
+ );
+}
diff --git a/payment_plugin/lib/data/remote/models/api_response.dart b/payment_plugin/lib/data/remote/models/api_response.dart
new file mode 100644
index 00000000..2bf04114
--- /dev/null
+++ b/payment_plugin/lib/data/remote/models/api_response.dart
@@ -0,0 +1,13 @@
+import 'package:freezed_annotation/freezed_annotation.dart';
+import 'package:payment_plugin/utils/json.dart';
+
+part '../../../_generated/data/remote/models/api_response.g.dart';
+
+@JsonSerializable(genericArgumentFactories: true, createToJson: false)
+class ApiResponse<T> {
+ final T data;
+ ApiResponse({required this.data});
+
+ factory ApiResponse.fromJson(Json json, T Function(Object? json) fromJsonT) =>
+ _$ApiResponseFromJson(json, fromJsonT);
+}
diff --git a/payment_plugin/lib/data/remote/models/stored_payment_methods_response.dart b/payment_plugin/lib/data/remote/models/stored_payment_methods_response.dart
new file mode 100644
index 00000000..5dd7a6b4
--- /dev/null
+++ b/payment_plugin/lib/data/remote/models/stored_payment_methods_response.dart
@@ -0,0 +1,17 @@
+import 'package:json_annotation/json_annotation.dart';
+import 'package:payment_plugin/domain/models/stored_payment_method.dart';
+import 'package:payment_plugin/utils/json.dart';
+
+part '../../../_generated/data/remote/models/stored_payment_methods_response.g.dart';
+
+@JsonSerializable()
+class StoredPaymentsResponse {
+ final String merchantAccount;
+ final String shopperReference;
+ final Iterable<StoredPaymentMethod>? storedPaymentMethods;
+
+ StoredPaymentsResponse({required this.merchantAccount, required this.shopperReference, required this.storedPaymentMethods});
+
+ factory StoredPaymentsResponse.fromJson(Json json) => _$StoredPaymentsResponseFromJson(json);
+
+}
\ No newline at end of file
diff --git a/payment_plugin/lib/domain/models/add_card_payment_method.dart b/payment_plugin/lib/domain/models/add_card_payment_method.dart
new file mode 100644
index 00000000..d38283ca
--- /dev/null
+++ b/payment_plugin/lib/domain/models/add_card_payment_method.dart
@@ -0,0 +1,41 @@
+import 'package:json_annotation/json_annotation.dart';
+import 'package:payment_plugin/utils/json.dart';
+
+part '../../_generated/domain/models/add_card_payment_method.g.dart';
+
+enum CardType {
+ visa,
+ mastercard,
+ amexgooglepay,
+ maestro;
+
+ static CardType fromString(String type) {
+ switch (type.toLowerCase()) {
+ case 'visa':
+ return CardType.visa;
+ case 'mc':
+ return CardType.mastercard;
+ case 'maestro':
+ return CardType.maestro;
+ case 'amex_googlepay':
+ return CardType.amexgooglepay;
+ default:
+ throw Exception('Unknown card type: $type');
+ }
+ }
+}
+
+@JsonSerializable()
+class AddCardPaymentMethod {
+ final Iterable<CardType> brands;
+ final String name;
+ final String type;
+
+ AddCardPaymentMethod({
+ required this.brands,
+ required this.name,
+ required this.type,
+ });
+
+ Json toJson() => _$AddCardPaymentMethodToJson(this);
+}
diff --git a/payment_plugin/lib/domain/models/payment_configurations.dart b/payment_plugin/lib/domain/models/payment_configurations.dart
new file mode 100644
index 00000000..feeac7ba
--- /dev/null
+++ b/payment_plugin/lib/domain/models/payment_configurations.dart
@@ -0,0 +1,26 @@
+import 'package:adyen_checkout/adyen_checkout.dart';
+
+class PaymentConfigurations {
+ final SessionCheckout sessionCheckout;
+ final GooglePayComponentConfiguration googlePayConfiguration;
+ final DropInConfiguration dropInConfiguration;
+ final CardComponentConfiguration cardComponentConfiguration;
+ final ApplePayComponentConfiguration applePayConfiguration;
+ final bool isFullyPaidWithPoints;
+ final List<Map<String, dynamic>> availablePaymentMethods;
+
+ PaymentConfigurations({
+ required this.cardComponentConfiguration,
+ required this.sessionCheckout,
+ required this.googlePayConfiguration,
+ required this.dropInConfiguration,
+ required this.applePayConfiguration,
+ required this.isFullyPaidWithPoints,
+ required this.availablePaymentMethods,
+ });
+
+ @override
+ String toString() {
+ return 'PaymentConfigurations(sessionCheckout: $sessionCheckout, googlePayConfiguration: $googlePayConfiguration, dropInConfiguration: $dropInConfiguration, cardComponentConfiguration: $cardComponentConfiguration, applePayConfiguration: $applePayConfiguration, isFullyPaidWithPoints: $isFullyPaidWithPoints)';
+ }
+}
diff --git a/payment_plugin/lib/domain/models/payment_method.dart b/payment_plugin/lib/domain/models/payment_method.dart
new file mode 100644
index 00000000..7b087a4e
--- /dev/null
+++ b/payment_plugin/lib/domain/models/payment_method.dart
@@ -0,0 +1,39 @@
+import 'package:json_annotation/json_annotation.dart';
+import 'package:payment_plugin/utils/json.dart';
+
+part '../../_generated/domain/models/payment_method.g.dart';
+
+@JsonSerializable()
+class PaymentMethod {
+ final String? brand;
+ final String? cvc;
+ final String? encryptedCard;
+ final String? encryptedCardNumber;
+ final String? encryptedExpiryMonth;
+ final String? encryptedExpiryYear;
+ final String? encryptedSecurityCode;
+ final String? expiryMonth;
+ final String? expiryYear;
+ final String? holderName;
+ final String? number;
+ final String? type;
+
+ PaymentMethod({
+ this.brand,
+ this.cvc,
+ this.encryptedCard,
+ this.encryptedCardNumber,
+ this.encryptedExpiryMonth,
+ this.encryptedExpiryYear,
+ this.encryptedSecurityCode,
+ this.expiryMonth,
+ this.expiryYear,
+ this.holderName,
+ this.number,
+ this.type,
+ });
+
+ factory PaymentMethod.fromJson(Json json) => _$PaymentMethodFromJson(json);
+
+ Json toJson() => _$PaymentMethodToJson(this);
+}
\ No newline at end of file
diff --git a/payment_plugin/lib/domain/models/stored_payment_method.dart b/payment_plugin/lib/domain/models/stored_payment_method.dart
new file mode 100644
index 00000000..a0677bf8
--- /dev/null
+++ b/payment_plugin/lib/domain/models/stored_payment_method.dart
@@ -0,0 +1,37 @@
+import 'package:equatable/equatable.dart';
+import 'package:json_annotation/json_annotation.dart';
+import 'package:payment_plugin/utils/json.dart';
+
+part '../../_generated/domain/models/stored_payment_method.g.dart';
+
+@JsonSerializable()
+class StoredPaymentMethod extends Equatable {
+ final String brand;
+ final String expiryMonth;
+ final String expiryYear;
+ final String holderName;
+ final String id;
+ final String lastFour;
+
+ const StoredPaymentMethod({
+ required this.brand,
+ required this.expiryMonth,
+ required this.expiryYear,
+ required this.holderName,
+ required this.id,
+ required this.lastFour,
+ });
+
+ factory StoredPaymentMethod.fromJson(Json json) =>
+ _$StoredPaymentMethodFromJson(json);
+
+ Json toJson() => _$StoredPaymentMethodToJson(this);
+
+ @override
+ String toString() {
+ return 'StoredPaymentMethod(brand: $brand, expiryMonth: $expiryMonth, expiryYear: $expiryYear, holderName: $holderName, id: $id, lastFour: $lastFour)';
+ }
+
+ @override
+ List<Object?> get props => [id, brand, expiryMonth, expiryYear, holderName, lastFour];
+}
diff --git a/payment_plugin/lib/domain/repositories/adyen_repository.dart b/payment_plugin/lib/domain/repositories/adyen_repository.dart
new file mode 100644
index 00000000..4ed0da82
--- /dev/null
+++ b/payment_plugin/lib/domain/repositories/adyen_repository.dart
@@ -0,0 +1,275 @@
+import 'dart:convert';
+
+import 'package:adyen_checkout/adyen_checkout.dart';
+import 'package:dio/dio.dart';
+import 'package:easy_localization/easy_localization.dart';
+import 'package:flutter/foundation.dart';
+import 'package:flutter_dotenv/flutter_dotenv.dart';
+import 'package:payment_plugin/domain/models/payment_configurations.dart';
+import 'package:payment_plugin/domain/models/payment_method.dart';
+import 'package:payment_plugin/domain/models/stored_payment_method.dart';
+import 'package:payment_plugin/data/remote/api/api_client.dart';
+import 'package:payment_plugin/common/constants.dart';
+import 'package:payment_plugin/utils/json.dart';
+
+class AdyenRepository {
+ final ApiClient _api;
+
+ /// Creates an AdyenRepository with a Dio instance from the main app
+ ///
+ /// [dio] - The Dio instance from your main app (with all configured interceptors, baseUrl, etc.)
+ ///
+ /// Example:
+ /// ```dart
+ /// final repo = AdyenRepository(
+ /// dio: yourConfiguredDioInstance,
+ /// );
+ /// ```
+ AdyenRepository({
+ required Dio dio,
+ }) : _api = ApiClient(dio);
+
+ Future<Iterable<StoredPaymentMethod>> getCards() async {
+ final cards = await _api.getPaymentMethods();
+ return cards.storedPaymentMethods ?? [];
+ }
+
+ Future<void> removeCard(String cardId) async {
+ try {
+ await _api.removePaymentMethod(cardId);
+ } catch (e) {
+ throw Exception(e);
+ }
+ }
+
+ Future<dynamic> addCard(PaymentMethod paymentMethod) async {
+ final response = await _api.storePaymentMethod(paymentMethod);
+ return response;
+ }
+
+
+ Future<Json?> postPaymentsDetails(Json body, String apiKey) async {
+ final contentType = "application/json";
+ final Map<String, dynamic> data = {
+ "shopperReference": "Test reference",
+ "amount": {"value:": 0, "currency": "DKK"},
+ "countryCode": "DK",
+ "merchantAccount": "ComwellHotelsECOM",
+ "returnUrl": "comwell://",
+ "reference": "flutter-test_${DateTime.now().millisecondsSinceEpoch}",
+ "channel": "iOS",
+ };
+ data.addAll(body);
+ data.remove("storePaymentMethod");
+ final response = await _api.postPaymentsDetails(data, contentType, apiKey);
+ return response;
+ }
+
+ Future<Json> submitPayment(Json paymentResult, String shopperReference,
+ String merchantAccount, String apiKey) async {
+ final Map<String, dynamic> data = {
+ "shopperReference": shopperReference,
+ "amount": {"value:": 0, "currency": "DKK"},
+ "countryCode": "DK",
+ "merchantAccount": merchantAccount,
+ "returnUrl": "comwell://",
+ "reference": "flutter-test_${DateTime.now().millisecondsSinceEpoch}",
+ "channel": "iOS",
+ };
+ data.addAll(paymentResult);
+ data.remove("storePaymentMethod");
+ debugPrint("Data: $data");
+ final response = await _api.submitPayment(data, apiKey, "application/json");
+ return response;
+ }
+
+ Future<PaymentConfigurations?> sessionCheckout(
+ String hotelCode,
+ String bookingId,
+ bool usePoints,
+ ) async {
+ final body = {
+ "hotelCode": hotelCode,
+ "bookingConfirmationNumber": bookingId,
+ "returnUrl": returnUrl,
+ "usePoints": usePoints,
+ };
+ final json = jsonEncode(body);
+ final response = await _api.createAdyenSession(json) as Map<String, dynamic>;
+ final clientKey = response["clientKey"] as String;
+ final sessionResponse = response["sessionResponse"];
+ final payedWithPoints = response["isFullyPaidWithPoints"] as bool;
+ if (payedWithPoints) {
+ return null;
+ }
+ final id = sessionResponse["id"] as String;
+ final sessionData = sessionResponse["sessionData"] as String;
+ final availablePaymentMethods = await _api.listAvailablePaymentMethods() as Map<String, dynamic>;
+
+ final scheme = _findPaymentMethod(availablePaymentMethods, "scheme");
+ final googlePay = _findPaymentMethod(availablePaymentMethods, "googlePay");
+ final applePay = _findPaymentMethod(availablePaymentMethods, "applePay");
+
+ // Use the server response amount for consistency across all payment methods
+ final serverAmount = Amount(
+ value: sessionResponse["amount"]["value"] as int, currency: "DKK");
+
+ final session = await AdyenCheckout.session.create(
+ sessionId: id,
+ sessionData: sessionData,
+ configuration: dropInConfiguration(serverAmount, clientKey, false));
+
+ return PaymentConfigurations(
+ sessionCheckout: session,
+ googlePayConfiguration: _getGooglePayComponentConfig(clientKey),
+ dropInConfiguration:
+ dropInConfiguration(serverAmount, clientKey, false),
+ cardComponentConfiguration:
+ cardComponentConfiguration(serverAmount, clientKey),
+ applePayConfiguration:
+ _applePayComponentConfiguration(clientKey, serverAmount, false),
+ isFullyPaidWithPoints: payedWithPoints,
+ availablePaymentMethods: [scheme!, googlePay!, applePay!]);
+ }
+
+ Future<PaymentConfigurations?> fetchSessionForAddingCard() async {
+ final body = {
+ "returnUrl": returnUrl,
+ };
+ final json = jsonEncode(body);
+ final response = await _api.createAdyenSessionForCards(json) as Map<String, dynamic>;
+ final availablePaymentMethods = await _api.listAvailablePaymentMethods() as Map<String, dynamic>;
+
+ final clientKey = response["clientKey"] as String;
+ final sessionResponse = response["sessionResponse"];
+ final id = sessionResponse["id"] as String;
+ final sessionData = sessionResponse["sessionData"] as String;
+
+ final scheme = _findPaymentMethod(availablePaymentMethods, "scheme");
+ final googlePay = _findPaymentMethod(availablePaymentMethods, "googlePay");
+ final applePay = _findPaymentMethod(availablePaymentMethods, "applePay");
+
+ final session = await AdyenCheckout.session.create(
+ sessionId: id,
+ sessionData: sessionData,
+ configuration: cardComponentConfiguration(
+ Amount(value: 0, currency: "DKK"), clientKey),
+ );
+
+ return PaymentConfigurations(
+ sessionCheckout: session,
+ availablePaymentMethods: [scheme!, googlePay!, applePay!],
+ googlePayConfiguration: _getGooglePayComponentConfig(clientKey),
+ dropInConfiguration: dropInConfiguration(
+ Amount(value: 0, currency: "DKK"), clientKey, true),
+ cardComponentConfiguration: cardComponentConfiguration(
+ Amount(value: 0, currency: "DKK"), clientKey),
+ applePayConfiguration: _applePayComponentConfiguration(
+ clientKey, Amount(value: 0, currency: "DKK"), true),
+ isFullyPaidWithPoints: false);
+ }
+
+
+CardComponentConfiguration cardComponentConfiguration(
+ Amount amount,
+ String clientKey,
+ ) {
+ return CardComponentConfiguration(
+ environment: Environment.test,
+ amount: amount,
+ cardConfiguration: const CardConfiguration(showStorePaymentField: false),
+ clientKey: clientKey,
+ countryCode: "DK",
+ );
+ }
+
+ DropInConfiguration dropInConfiguration(
+ Amount amount, String clientKey, bool addCard) {
+ return DropInConfiguration(
+ environment: Environment.test,
+ clientKey: clientKey,
+ countryCode: "DK",
+ skipListWhenSinglePaymentMethod: true,
+ googlePayConfiguration: _googlePayConfiguration,
+ applePayConfiguration: _getApplePlayConfig(amount, addCard),
+ cardConfiguration: const CardConfiguration(showStorePaymentField: false),
+ paymentMethodNames: {
+ "scheme": "Credit card",
+ },
+ );
+ }
+
+ GooglePayConfiguration get _googlePayConfiguration =>
+ const GooglePayConfiguration(
+ googlePayEnvironment: GooglePayEnvironment.test,
+ );
+
+ GooglePayComponentConfiguration _getGooglePayComponentConfig(
+ String clientKey,
+ ) {
+ return GooglePayComponentConfiguration(
+ environment: Environment.test,
+ countryCode: "DK",
+ clientKey: clientKey,
+ googlePayConfiguration: _googlePayConfiguration,
+ );
+ }
+
+ ApplePayComponentConfiguration _applePayComponentConfiguration(
+ String clientKey,
+ Amount amount,
+ bool addCard,
+ ) {
+ return ApplePayComponentConfiguration(
+ environment: Environment.test,
+ countryCode: "DK",
+ clientKey: clientKey,
+ applePayConfiguration: _getApplePlayConfig(amount, addCard),
+ );
+ }
+
+ ApplePayConfiguration _getApplePlayConfig(Amount amount, bool addCard) {
+ return ApplePayConfiguration(
+ merchantId: merchantId,
+ merchantName: merchantName,
+ allowOnboarding: true,
+ allowShippingContactEditing: true,
+ applePaySummaryItems: [
+ ApplePaySummaryItem(
+ label: addCard ? "add_card".tr() : "total".tr(),
+ amount: Amount(value: amount.value, currency: amount.currency),
+ type: ApplePaySummaryItemType.definite)
+ ]);
+ }
+
+ static final Iterable<StoredPaymentMethod> mockStoredPaymentData = [
+ 1,
+ 2,
+ 3,
+ 4
+ ].map((i) => StoredPaymentMethod(
+ expiryMonth: "12",
+ expiryYear: "2035",
+ holderName: "holder name",
+ id: "id $i",
+ lastFour: "$i$i$i$i",
+ brand: "visa"));
+}
+
+Map<String, dynamic>? _findPaymentMethod(
+ Map<String, dynamic> pmResponse,
+ String type,
+) {
+ final list =
+ (pmResponse['paymentMethods'] as List?)?.cast<Map<String, dynamic>>() ??
+ const <Map<String, dynamic>>[];
+
+ for (final m in list) {
+ final t = (m['type'] as String?)?.toLowerCase();
+ if (t == type.toLowerCase()) {
+ return m; // e.g. 'scheme', 'applepay', 'googlepay'
+ }
+ }
+ return null;
+}
+
diff --git a/payment_plugin/lib/payment_config.dart b/payment_plugin/lib/payment_config.dart
new file mode 100644
index 00000000..382e0934
--- /dev/null
+++ b/payment_plugin/lib/payment_config.dart
@@ -0,0 +1,22 @@
+import 'package:dio/dio.dart';
+
+/// Configuration class for initializing the Payment Plugin
+class PaymentConfig {
+ /// Dio instance from the main app (with configured interceptors, headers, baseUrl, etc.)
+ final Dio dio;
+
+ /// Optional: Environment configuration (test/production)
+ final PaymentEnvironment environment;
+
+ const PaymentConfig({
+ required this.dio,
+ this.environment = PaymentEnvironment.test,
+ });
+}
+
+enum PaymentEnvironment {
+ test,
+ production,
+}
+
+
diff --git a/payment_plugin/lib/payment_plugin.dart b/payment_plugin/lib/payment_plugin.dart
new file mode 100644
index 00000000..7a743635
--- /dev/null
+++ b/payment_plugin/lib/payment_plugin.dart
@@ -0,0 +1,69 @@
+// Export the main classes that the main app will use
+export 'payment_plugin_core.dart';
+export 'payment_config.dart';
+export 'domain/models/payment_configurations.dart';
+export 'domain/models/stored_payment_method.dart';
+export 'domain/models/payment_method.dart';
+
+// For advanced usage, also export the repository
+export 'domain/repositories/adyen_repository.dart';
+
+// Export themes
+export 'themes/light_theme.dart' show lightTheme;
+export 'themes/dark_theme.dart' show darkTheme;
+export 'themes/comwell_colors.dart';
+
+// Export screens
+export 'presentation/screens/payment_cards_page.dart';
+export 'presentation/screens/payment_processing_page.dart';
+
+// Export cubits
+export 'presentation/app/bloc/payment_cubit.dart';
+export 'presentation/app/bloc/payment_cards_cubit.dart';
+
+// Export cubit states
+export 'presentation/app/bloc/payment_processing_state.dart';
+export 'presentation/app/bloc/payment_cards_state.dart';
+
+/// Example usage in your main app:
+///
+/// ```dart
+/// import 'package:payment_plugin/payment_plugin.dart';
+/// import 'package:dio/dio.dart';
+///
+/// // 1. Create and configure your Dio instance in the main app
+/// final dio = Dio(BaseOptions(
+/// baseUrl: 'https://your-api.com',
+/// ));
+///
+/// // Add your interceptors (auth, logging, etc.)
+/// dio.interceptors.add(
+/// InterceptorsWrapper(
+/// onRequest: (options, handler) async {
+/// final token = await authService.getToken();
+/// options.headers['Authorization'] = 'Bearer $token';
+/// return handler.next(options);
+/// },
+/// ),
+/// );
+///
+/// // 2. Initialize the plugin with your configured Dio instance
+/// await PaymentPlugin.initialize(
+/// config: PaymentConfig(
+/// dio: dio,
+/// ),
+/// );
+///
+/// // 3. Use it anywhere in your app via the singleton
+/// final cards = await PaymentPlugin.instance.getCards();
+///
+/// // 4. Create payment sessions
+/// final config = await PaymentPlugin.instance.sessionCheckout(
+/// 'hotel123',
+/// 'booking456',
+/// false, // usePoints
+/// );
+///
+/// // 5. Add cards
+/// final cardConfig = await PaymentPlugin.instance.fetchSessionForAddingCard();
+/// ```
diff --git a/payment_plugin/lib/payment_plugin_core.dart b/payment_plugin/lib/payment_plugin_core.dart
new file mode 100644
index 00000000..38586cda
--- /dev/null
+++ b/payment_plugin/lib/payment_plugin_core.dart
@@ -0,0 +1,85 @@
+import 'package:payment_plugin/domain/repositories/adyen_repository.dart';
+import 'package:payment_plugin/payment_config.dart';
+
+/// Main entry point for the Payment Plugin
+///
+/// Initialize once in your app:
+/// ```dart
+/// await PaymentPlugin.initialize(
+/// config: PaymentConfig(
+/// tokenProvider: () async => authService.getToken(),
+/// baseUrl: 'https://api.example.com',
+/// ),
+/// );
+/// ```
+///
+/// Then access anywhere:
+/// ```dart
+/// final cards = await PaymentPlugin.instance.getCards();
+/// ```
+class PaymentPlugin {
+ static PaymentPlugin? _instance;
+ late final AdyenRepository _repository;
+
+ PaymentPlugin._(PaymentConfig config) {
+ _repository = AdyenRepository(
+ dio: config.dio,
+ );
+ }
+
+ /// Initialize the payment plugin with configuration
+ ///
+ /// This should be called once, typically in your app's initialization
+ static Future<void> initialize({required PaymentConfig config}) async {
+ _instance = PaymentPlugin._(config);
+ }
+
+ /// Get the singleton instance of PaymentPlugin
+ ///
+ /// Throws [StateError] if [initialize] hasn't been called yet
+ static PaymentPlugin get instance {
+ if (_instance == null) {
+ throw StateError(
+ 'PaymentPlugin has not been initialized. '
+ 'Call PaymentPlugin.initialize() first.',
+ );
+ }
+ return _instance!;
+ }
+
+ /// Check if the plugin has been initialized
+ static bool get isInitialized => _instance != null;
+
+ /// Reset the instance (mainly for testing)
+ static void reset() {
+ _instance = null;
+ }
+
+ /// Access the Adyen repository for payment operations
+ AdyenRepository get repository => _repository;
+
+ // Convenience methods that delegate to the repository
+
+ /// Get all stored payment methods (cards)
+ Future<Iterable<dynamic>> getCards() => _repository.getCards();
+
+ /// Remove a stored payment method
+ Future<void> removeCard(String cardId) => _repository.removeCard(cardId);
+
+ /// Add a new payment method
+ Future<dynamic> addCard(dynamic paymentMethod) =>
+ _repository.addCard(paymentMethod);
+
+ /// Create a payment session for checkout
+ Future<dynamic> sessionCheckout(
+ String hotelCode,
+ String bookingId,
+ bool usePoints,
+ ) => _repository.sessionCheckout(hotelCode, bookingId, usePoints);
+
+ /// Create a payment session for adding cards
+ Future<dynamic> fetchSessionForAddingCard() =>
+ _repository.fetchSessionForAddingCard();
+}
+
+
diff --git a/payment_plugin/lib/presentation/app/bloc/payment_cards_cubit.dart b/payment_plugin/lib/presentation/app/bloc/payment_cards_cubit.dart
new file mode 100644
index 00000000..e64a379d
--- /dev/null
+++ b/payment_plugin/lib/presentation/app/bloc/payment_cards_cubit.dart
@@ -0,0 +1,181 @@
+import 'dart:io';
+
+import 'package:adyen_checkout/adyen_checkout.dart';
+import 'package:bloc/bloc.dart';
+
+import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
+import 'package:go_router/go_router.dart';
+import 'package:payment_plugin/_generated/data/remote/models/payment_event_handler.dart';
+import 'package:payment_plugin/domain/models/payment_configurations.dart';
+import 'package:payment_plugin/domain/models/payment_method.dart';
+import 'package:payment_plugin/domain/models/stored_payment_method.dart';
+import 'package:payment_plugin/domain/repositories/adyen_repository.dart';
+import 'package:payment_plugin/presentation/app/bloc/payment_cards_state.dart';
+
+class PaymentCardsCubit extends Cubit<PaymentCardsState> {
+ final AdyenRepository adyenRepository;
+
+
+ late PaymentConfigurations? paymentConfigurations;
+ void Function(StoredPaymentMethod)? onPaymentMethodSelected;
+ void Function()? onPaymentMethodValidationError;
+
+ PaymentCardsCubit({
+ required this.adyenRepository,
+ this.onPaymentMethodSelected,
+ this.onPaymentMethodValidationError,
+ StoredPaymentMethod? initialSelectedPaymentMethod,
+ }) : super(PaymentCardsState.initial()) {
+ if (initialSelectedPaymentMethod != null) {
+ emit(state.paymentMethodSelected(initialSelectedPaymentMethod));
+ }
+ init();
+ }
+
+ bool get isSelectedPaymentMethod => state.selectedPaymentMethod != null;
+
+ Future<PaymentConfigurations?> fetchPaymentConfiguration() async {
+ try {
+ final session =
+ await adyenRepository.fetchSessionForAddingCard();
+
+ if (session == null) {
+ return null;
+ }
+ return session;
+ } catch (e) {
+ debugPrint("error: $e");
+ return null;
+ }
+ }
+
+ void init() async {
+ try {
+ final cards = await adyenRepository.getCards();
+ paymentConfigurations = await fetchPaymentConfiguration();
+
+ if (cards.isEmpty) {
+ emit(state.cardsFetchedEmpty());
+ } else {
+ emit(state.cardsFetched(cards));
+ }
+ } catch (e) {
+ debugPrint("error: $e");
+ emit(state.cardsFetchedError());
+ }
+ }
+
+ Future<void> onPaymentResult(
+ PaymentResult result, BuildContext context) async {
+ switch (result) {
+ case PaymentAdvancedFinished():
+ debugPrint("Result advanced: ${result.resultCode}");
+
+ if (context.mounted) {
+ context.pop();
+ init();
+ }
+ break;
+ case PaymentSessionFinished():
+ debugPrint("Result session: ${result.sessionData}");
+ if (context.mounted) {
+ context.pop();
+ init();
+ }
+ break;
+ case PaymentCancelledByUser():
+ case PaymentError():
+ emit(state.cardsFetchedError());
+ }
+ }
+
+ Future<void> storePaymentMethod(PaymentMethod paymentMethod) async {
+ debugPrint("Payment method: $paymentMethod");
+ try {
+ final response = await adyenRepository.addCard(paymentMethod);
+
+ if (response != null) {
+ //emit(state.cardsFetched(response));
+ } else {
+ emit(state.cardsFetchedError());
+ }
+ } catch (e) {
+ debugPrint("error: $e");
+ }
+ }
+
+ SessionCheckout sessionCheckout() {
+ return paymentConfigurations?.sessionCheckout ??
+ SessionCheckout(
+ id: "123",
+ sessionData: "123",
+ paymentMethods: {
+ "scheme": {
+ "type": "scheme",
+ "brands": ["visa", "mc"],
+ },
+ },
+ );
+ }
+
+ Checkout advancedCheckout(String shopperReference, String apiKey) {
+ return AdvancedCheckout(
+ onSubmit: (data, [extra]) async {
+ debugPrint("Data: $data");
+ debugPrint("Extra: $extra");
+ try {
+ final merchantAccount =
+ paymentConfigurations?.sessionCheckout.id as String;
+ final response =
+ await adyenRepository.submitPayment(data, shopperReference, merchantAccount, apiKey);
+ return PaymentEventHandler().handleResponse(jsonResponse: response);
+ } catch (e) {
+ return Error(errorMessage: "$e");
+ }
+ },
+ onAdditionalDetails: (data) async {
+ final response = await adyenRepository.postPaymentsDetails(data, apiKey);
+
+ return PaymentEventHandler().handleResponse(jsonResponse: response!);
+ },
+ );
+ }
+
+ void disposeWebViewResources() {
+ // Force WebView cleanup
+ if (Platform.isAndroid) {
+ // Android-specific WebView cleanup
+ SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual,
+ overlays: SystemUiOverlay.values);
+ }
+ }
+
+ Future<void> onRemoveCard(String cardId) async {
+ try {
+ emit(state.loading(true));
+ await adyenRepository.removeCard(cardId);
+ // If the removed card was selected, clear the selection
+ if (state.selectedPaymentMethod?.id == cardId) {
+ emit(state.clearSelectedPaymentMethod());
+ }
+ init();
+ } catch (e) {
+ debugPrint("error: $e");
+ }
+ }
+
+ void selectPaymentMethod(StoredPaymentMethod paymentMethod) {
+ emit(state.paymentMethodSelected(paymentMethod).setMissingPaymentMethod(false));
+ onPaymentMethodSelected?.call(paymentMethod);
+ }
+
+ void setMissingPaymentMethodError() {
+ emit(state.setMissingPaymentMethod(true));
+ onPaymentMethodValidationError?.call();
+ }
+
+ void clearMissingPaymentMethodError() {
+ emit(state.setMissingPaymentMethod(false));
+ }
+}
diff --git a/payment_plugin/lib/presentation/app/bloc/payment_cards_state.dart b/payment_plugin/lib/presentation/app/bloc/payment_cards_state.dart
new file mode 100644
index 00000000..73742310
--- /dev/null
+++ b/payment_plugin/lib/presentation/app/bloc/payment_cards_state.dart
@@ -0,0 +1,73 @@
+import 'package:equatable/equatable.dart';
+import 'package:payment_plugin/domain/models/stored_payment_method.dart';
+
+class PaymentCardsState extends Equatable {
+ final bool isLoading;
+ final bool hasError;
+ final Iterable<StoredPaymentMethod> cards;
+ final StoredPaymentMethod? selectedPaymentMethod;
+ final bool missingPaymentMethod;
+
+ const PaymentCardsState._({
+ required this.cards,
+ required this.isLoading,
+ required this.hasError,
+ this.selectedPaymentMethod,
+ this.missingPaymentMethod = false,
+ });
+
+ PaymentCardsState.initial()
+ : cards = [],
+ isLoading = true,
+ hasError = false,
+ selectedPaymentMethod = null,
+ missingPaymentMethod = false;
+
+ PaymentCardsState cardsFetched(Iterable<StoredPaymentMethod> cards) {
+ return _copyWith(cards: cards, isLoading: false);
+ }
+
+ PaymentCardsState cardsFetchedEmpty() {
+ return _copyWith(cards: [], isLoading: false, hasCards: false);
+ }
+
+ PaymentCardsState cardsFetchedError() =>
+ _copyWith(isLoading: false, hasError: true);
+
+ PaymentCardsState loading(bool isLoading) {
+ return _copyWith(isLoading: isLoading);
+ }
+
+ PaymentCardsState _copyWith({
+ Iterable<StoredPaymentMethod>? cards,
+ bool? isLoading,
+ bool? hasError,
+ bool? hasCards,
+ StoredPaymentMethod? selectedPaymentMethod,
+ bool? missingPaymentMethod,
+ }) {
+ return PaymentCardsState._(
+ cards: cards ?? this.cards,
+ isLoading: isLoading ?? this.isLoading,
+ hasError: hasError ?? this.hasError,
+ selectedPaymentMethod: selectedPaymentMethod ?? this.selectedPaymentMethod,
+ missingPaymentMethod: missingPaymentMethod ?? this.missingPaymentMethod,
+ );
+ }
+
+ PaymentCardsState paymentMethodSelected(StoredPaymentMethod? paymentMethod) {
+ return _copyWith(selectedPaymentMethod: paymentMethod);
+ }
+
+ PaymentCardsState clearSelectedPaymentMethod() {
+ return _copyWith(selectedPaymentMethod: null);
+ }
+
+ PaymentCardsState setMissingPaymentMethod(bool value) {
+ return _copyWith(missingPaymentMethod: value);
+ }
+
+ @override
+ List<Object?> get props => [isLoading, hasError, cards, selectedPaymentMethod, missingPaymentMethod];
+
+}
diff --git a/payment_plugin/lib/presentation/app/bloc/payment_cubit.dart b/payment_plugin/lib/presentation/app/bloc/payment_cubit.dart
new file mode 100644
index 00000000..f2e96b82
--- /dev/null
+++ b/payment_plugin/lib/presentation/app/bloc/payment_cubit.dart
@@ -0,0 +1,50 @@
+import 'package:adyen_checkout/adyen_checkout.dart';
+
+import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:payment_plugin/domain/repositories/adyen_repository.dart';
+import 'package:payment_plugin/presentation/app/bloc/payment_processing_state.dart';
+
+class PaymentCubit extends Cubit<PaymentProcessingState> {
+ final AdyenRepository adyenRepository;
+
+ PaymentCubit({required this.adyenRepository})
+ : super(PaymentProcessingStateNotStarted());
+
+ Future<void> createSession(int price, String confirmationId,
+ bool applyClubPoints, String hotelCode) async {
+ try {
+
+ emit(PaymentProcessingStateProcessing());
+
+ final paymentConfigurations = await adyenRepository
+ .sessionCheckout(hotelCode, confirmationId, applyClubPoints);
+ if (paymentConfigurations == null) {
+ emit(PaymentProcessingStateConfirmed());
+ } else {
+ emit(PaymentProcessingStateSessionReceived(
+ paymentConfigurations: paymentConfigurations));
+ }
+ } catch (e) {
+ emit(PaymentProcessingStateError(message: "Error creating session"));
+ await Future<void>.delayed(const Duration(milliseconds: 1000));
+ }
+ }
+
+ Future<void> onPaymentResult(PaymentResult result) async {
+ switch (result) {
+ case PaymentAdvancedFinished():
+ case PaymentSessionFinished():
+ emit(PaymentProcessingStateConfirmed());
+ break;
+ case PaymentCancelledByUser():
+ case PaymentError():
+ emit(PaymentProcessingStateError(message: "Error processing payment"));
+ }
+ }
+
+ void onUserDismissPaymentPopup() {
+ emit(PaymentProcessingStateNotStarted());
+ }
+
+
+}
diff --git a/payment_plugin/lib/presentation/app/bloc/payment_processing_state.dart b/payment_plugin/lib/presentation/app/bloc/payment_processing_state.dart
new file mode 100644
index 00000000..a5209984
--- /dev/null
+++ b/payment_plugin/lib/presentation/app/bloc/payment_processing_state.dart
@@ -0,0 +1,23 @@
+import 'package:payment_plugin/domain/models/payment_configurations.dart';
+
+sealed class PaymentProcessingState {}
+
+class PaymentProcessingStateNotStarted extends PaymentProcessingState {}
+
+class PaymentProcessingStateProcessing extends PaymentProcessingState {}
+
+class PaymentProcessingStateSessionReceived extends PaymentProcessingState {
+ final PaymentConfigurations paymentConfigurations;
+
+ PaymentProcessingStateSessionReceived({required this.paymentConfigurations});
+}
+
+class PaymentProcessingPaymentStateSuccess extends PaymentProcessingState {}
+
+class PaymentProcessingStateConfirmed extends PaymentProcessingState {}
+
+class PaymentProcessingStateError extends PaymentProcessingState {
+ final String message;
+
+ PaymentProcessingStateError({required this.message});
+}
diff --git a/payment_plugin/lib/presentation/components/add_card.dart b/payment_plugin/lib/presentation/components/add_card.dart
new file mode 100644
index 00000000..d4fcb88d
--- /dev/null
+++ b/payment_plugin/lib/presentation/components/add_card.dart
@@ -0,0 +1,75 @@
+import 'package:adyen_checkout/adyen_checkout.dart';
+import 'package:easy_localization/easy_localization.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:flutter_svg/flutter_svg.dart';
+import 'package:payment_plugin/presentation/app/bloc/payment_cards_cubit.dart';
+import 'package:payment_plugin/themes/light_theme.dart';
+
+class AddCard extends StatelessWidget {
+ const AddCard({super.key});
+
+ Future<void> showPaymentDialog(BuildContext context) async {
+ final cubit = context.read<PaymentCardsCubit>();
+ //Extracting the scheme payment method config from the available payment methods
+ final paymentMethodConfig = cubit
+ .paymentConfigurations?.availablePaymentMethods
+ .firstWhere((element) => element["type"] == "scheme");
+
+ await showModalBottomSheet<void>(
+ isDismissible: true,
+ isScrollControlled: true,
+ context: context,
+ builder: (BuildContext context) {
+ return SafeArea(
+ child: Padding(
+ padding: MediaQuery.of(context).viewInsets,
+ child: AdyenCardComponent(
+ configuration:
+ cubit.paymentConfigurations?.cardComponentConfiguration ??
+ CardComponentConfiguration(
+ environment: Environment.test,
+ countryCode: "DK",
+ clientKey: "test",
+ ),
+ paymentMethod: paymentMethodConfig ?? const {"type": "", "name": ""},
+ checkout: cubit.sessionCheckout(),
+ onPaymentResult: (result) => cubit.onPaymentResult(result, context),
+ ),
+ ));
+ },
+ );
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return InkWell(
+ onTap: () async {
+ await showPaymentDialog(context);
+ },
+ child: Container(
+ decoration: BoxDecoration(
+ border: Border.all(color: colorDivider),
+ ),
+ padding: const EdgeInsets.all(12),
+ child: Row(
+ crossAxisAlignment: CrossAxisAlignment.center,
+ children: [
+ Text(
+ "preregistration_payment_add_card".tr(),
+ style: Theme.of(context).textTheme.bodyMedium,
+ ),
+ const SizedBox(width: 12),
+ SvgPicture.asset("assets/icons/visa.svg"),
+ const SizedBox(width: 4),
+ SvgPicture.asset("assets/icons/mastercard.svg"),
+ const SizedBox(width: 4),
+ SvgPicture.asset("assets/icons/maestro.svg"),
+ const Expanded(child: SizedBox()),
+ SvgPicture.asset("assets/icons/ic_plus.svg"),
+ ],
+ ),
+ ),
+ );
+ }
+}
diff --git a/payment_plugin/lib/presentation/components/card_item.dart b/payment_plugin/lib/presentation/components/card_item.dart
new file mode 100644
index 00000000..1b84da78
--- /dev/null
+++ b/payment_plugin/lib/presentation/components/card_item.dart
@@ -0,0 +1,85 @@
+
+import 'package:flutter/material.dart';
+import 'package:flutter_svg/flutter_svg.dart';
+import 'package:payment_plugin/domain/models/add_card_payment_method.dart';
+import 'package:payment_plugin/domain/models/stored_payment_method.dart';
+import 'package:payment_plugin/presentation/components/payment_card_image.dart';
+import 'package:payment_plugin/themes/light_theme.dart';
+
+class CardItem extends StatelessWidget {
+ final StoredPaymentMethod paymentMethod;
+ final bool isSelectedPaymentMethod;
+ final VoidCallback? onSelect;
+ final bool needsScaffold;
+
+ const CardItem({
+ super.key,
+ required this.paymentMethod,
+ required this.isSelectedPaymentMethod,
+ this.onSelect,
+ this.needsScaffold = false,
+ });
+
+ @override
+ Widget build(BuildContext context) {
+ return Row(
+ crossAxisAlignment: CrossAxisAlignment.center,
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ PaymentCardImage(cardType: CardType.fromString(paymentMethod.brand)),
+ const SizedBox(width: 12),
+ Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text(
+ paymentMethod.holderName,
+ style: Theme.of(context).textTheme.bodySmall,
+ ),
+ Row(
+ crossAxisAlignment: CrossAxisAlignment.center,
+ children: [
+ ..."**** **** **** ".characters.map((char) {
+ if (char == '*') {
+ return Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 1.0),
+ child: Container(
+ width: 5,
+ height: 5,
+ decoration: const BoxDecoration(
+ shape: BoxShape.circle, color: colorTertiary),
+ ),
+ );
+ }
+ return const SizedBox(width: 8);
+ }),
+ Text(
+ paymentMethod.lastFour,
+ style: Theme.of(context).textTheme.bodyMedium,
+ ),
+ ],
+ ),
+ ],
+ ),
+ const Expanded(child: SizedBox()),
+ !needsScaffold ? InkWell(
+ customBorder: const CircleBorder(),
+ radius: 18,
+ onTap: onSelect,
+ child: isSelectedPaymentMethod
+ ? SvgPicture.asset("assets/icons/ic_checkmark.svg", width: 24, height: 24)
+ : Container(
+ width: 24,
+ height: 24,
+ decoration: BoxDecoration(
+ shape: BoxShape.circle,
+ border: Border.all(
+ color: colorTertiary,
+ width: 1.0,
+ ),
+ ),
+ ),
+ ) : const Icon(Icons.arrow_forward_ios, color: colorTertiary, size: 24),
+ ],
+ );
+ }
+}
diff --git a/payment_plugin/lib/presentation/components/comwell_error_widget.dart b/payment_plugin/lib/presentation/components/comwell_error_widget.dart
new file mode 100644
index 00000000..66a060f9
--- /dev/null
+++ b/payment_plugin/lib/presentation/components/comwell_error_widget.dart
@@ -0,0 +1,60 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_svg/flutter_svg.dart';
+import 'package:payment_plugin/themes/light_theme.dart';
+
+class ComwellErrorWidget extends StatelessWidget {
+ final String title;
+ final String subtitle;
+ final bool border;
+ final bool small;
+ const ComwellErrorWidget(
+ {super.key,
+ required this.title,
+ required this.subtitle,
+ this.border = false,
+ this.small = false});
+
+ @override
+ Widget build(BuildContext context) {
+ final theme = Theme.of(context);
+ return Padding(
+ padding: const EdgeInsets.all(8.0),
+ child: Container(
+ width: double.infinity,
+ decoration: BoxDecoration(
+ color: Colors.white,
+ border: border ? Border.all(color: colorDivider) : null,
+ borderRadius: BorderRadius.circular(8),
+ ),
+ padding: const EdgeInsets.symmetric(vertical: 15.0),
+ child: Container(
+ padding: const EdgeInsets.symmetric(horizontal: 16.0),
+ child: small ? Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ SvgPicture.asset('assets/icons/ic_error.svg',
+ width: 24, height: 24),
+ const SizedBox(width: 12),
+ Text(subtitle, style: theme.textTheme.headlineSmall, textAlign: TextAlign.center),
+ ],
+ ) : Column(
+ crossAxisAlignment: CrossAxisAlignment.center,
+ mainAxisAlignment: MainAxisAlignment.start,
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ SvgPicture.asset('assets/icons/ic_error.svg',
+ width: 48, height: 48),
+ const SizedBox(height: 30),
+ Text(title,
+ style: theme.textTheme.headlineMedium,
+ textAlign: TextAlign.center),
+ const SizedBox(height: 12),
+ Text(subtitle,
+ style: theme.textTheme.bodySmall, textAlign: TextAlign.center),
+ ],
+ ),
+ ),
+ ),
+ );
+ }
+}
diff --git a/payment_plugin/lib/presentation/components/content_placeholder.dart b/payment_plugin/lib/presentation/components/content_placeholder.dart
new file mode 100644
index 00000000..fb655590
--- /dev/null
+++ b/payment_plugin/lib/presentation/components/content_placeholder.dart
@@ -0,0 +1,63 @@
+import 'package:flutter/material.dart';
+
+enum ContentLineType {
+ twoLines,
+ threeLines,
+}
+
+class ContentPlaceholder extends StatelessWidget {
+ final ContentLineType lineType;
+
+ const ContentPlaceholder({
+ super.key,
+ required this.lineType,
+ });
+
+ @override
+ Widget build(BuildContext context) {
+ return Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 16.0),
+ child: Row(
+ mainAxisSize: MainAxisSize.max,
+ crossAxisAlignment: CrossAxisAlignment.center,
+ children: [
+ Container(
+ width: 96.0,
+ height: 72.0,
+ decoration: BoxDecoration(
+ borderRadius: BorderRadius.circular(12.0),
+ color: Colors.white,
+ ),
+ ),
+ const SizedBox(width: 12.0),
+ Expanded(
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Container(
+ width: double.infinity,
+ height: 10.0,
+ color: Colors.white,
+ margin: const EdgeInsets.only(bottom: 8.0),
+ ),
+ if (lineType == ContentLineType.threeLines)
+ Container(
+ width: double.infinity,
+ height: 10.0,
+ color: Colors.white,
+ margin: const EdgeInsets.only(bottom: 8.0),
+ ),
+ Container(
+ width: 100.0,
+ height: 10.0,
+ color: Colors.white,
+ )
+ ],
+ ),
+ )
+ ],
+ ),
+ );
+ }
+}
\ No newline at end of file
diff --git a/payment_plugin/lib/presentation/components/edit_card_dialog.dart b/payment_plugin/lib/presentation/components/edit_card_dialog.dart
new file mode 100644
index 00000000..10594c73
--- /dev/null
+++ b/payment_plugin/lib/presentation/components/edit_card_dialog.dart
@@ -0,0 +1,201 @@
+import 'package:easy_localization/easy_localization.dart';
+import 'package:flutter/material.dart';
+import 'package:payment_plugin/domain/models/stored_payment_method.dart';
+import 'package:payment_plugin/presentation/components/remove_card_button.dart';
+import 'package:payment_plugin/themes/light_theme.dart';
+
+class EditCardDialog extends StatelessWidget {
+ final StoredPaymentMethod storedPaymentMethod;
+ final void Function() onRemoveCard;
+
+ const EditCardDialog({
+ super.key,
+ required this.storedPaymentMethod,
+ required this.onRemoveCard,
+ });
+
+ @override
+ Widget build(BuildContext context) {
+ final theme = Theme.of(context);
+ final expirationDate =
+ "${storedPaymentMethod.expiryMonth}/${storedPaymentMethod.expiryYear}";
+
+ return Column(
+ mainAxisAlignment: MainAxisAlignment.start,
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ const SizedBox(height: 12),
+ Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Text("payment_cards_edit_card_title".tr(),
+ style: Theme.of(context).textTheme.headlineLarge),
+ IconButton(
+ style: IconButton.styleFrom(
+ backgroundColor: sandColor[40],
+ ),
+ onPressed: () {
+ Navigator.of(context).pop();
+ },
+ icon: const Icon(Icons.close),
+ ),
+ ],
+ ),
+ const SizedBox(height: 36),
+ Text(storedPaymentMethod.holderName,
+ style: Theme.of(context).textTheme.headlineMedium),
+ const SizedBox(height: 20),
+ Container(
+ height: 62,
+ width: double.infinity,
+ decoration: BoxDecoration(
+ borderRadius: BorderRadius.circular(8),
+ border: Border.all(color: colorDivider),
+ ),
+ child: Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 10),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text("name_on_card_hint".tr(),
+ style: theme.textTheme.bodySmall
+ ?.copyWith(color: colorHeadlineText)),
+ Text(
+ storedPaymentMethod.holderName,
+ style: theme.textTheme.headlineSmall,
+ ),
+ ],
+ ),
+ ),
+ ),
+ const SizedBox(height: 8),
+ Container(
+ height: 62,
+ decoration: BoxDecoration(
+ borderRadius: BorderRadius.circular(8),
+ border: Border.all(color: colorDivider),
+ ),
+ child: Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 12.0, vertical: 10),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text("card_number_hint".tr(),
+ style: theme.textTheme.bodySmall
+ ?.copyWith(color: colorHeadlineText)),
+ Row(
+ crossAxisAlignment: CrossAxisAlignment.center,
+ children: [
+ ..."**** **** **** ".characters.map((char) {
+ if (char == '*') {
+ return Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 1.0),
+ child: Container(
+ width: 5,
+ height: 5,
+ decoration: const BoxDecoration(
+ shape: BoxShape.circle, color: colorTertiary),
+ ),
+ );
+ }
+ return const SizedBox(width: 8);
+ }),
+ Text(
+ storedPaymentMethod.lastFour,
+ style: theme.textTheme.headlineSmall,
+ )
+ ],
+ ),
+ ],
+ ),
+ ),
+ ),
+ const SizedBox(height: 4),
+ Row(
+ children: [
+ Expanded(
+ child: Container(
+ height: 62,
+ decoration: BoxDecoration(
+ borderRadius: BorderRadius.circular(8),
+ border: Border.all(color: colorDivider),
+ ),
+ child: Padding(
+ padding: const EdgeInsets.symmetric(
+ horizontal: 12.0, vertical: 10),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text("expiry_date_hint".tr(),
+ style: theme.textTheme.bodySmall
+ ?.copyWith(color: colorHeadlineText)),
+ Text(
+ expirationDate,
+ style: theme.textTheme.headlineSmall,
+ ),
+ ],
+ ),
+ ),
+ ),
+ ),
+ const SizedBox(width: 4),
+ Expanded(
+ child: Container(
+ height: 62,
+ decoration: BoxDecoration(
+ borderRadius: BorderRadius.circular(8),
+ border: Border.all(color: colorDivider),
+ ),
+ child: Padding(
+ padding: const EdgeInsets.symmetric(
+ horizontal: 12.0, vertical: 10),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Text("cvc_hint".tr(),
+ style: theme.textTheme.bodySmall
+ ?.copyWith(color: colorHeadlineText)),
+
+ Row(
+ crossAxisAlignment: CrossAxisAlignment.center,
+ children: [
+ ..."***".characters.map((char) {
+ if (char == '*') {
+ return Padding(
+ padding:
+ const EdgeInsets.symmetric(horizontal: 1.0),
+ child: Container(
+ width: 5,
+ height: 5,
+ decoration: const BoxDecoration(
+ shape: BoxShape.circle,
+ color: colorTertiary),
+ ),
+ );
+ }
+ return const SizedBox(width: 8);
+ }),
+ ],
+ ),
+ const SizedBox.shrink(),
+ ],
+ ),
+ ),
+ ),
+ ),
+ ],
+ ),
+ const SizedBox(height: 24),
+ Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ RemoveCardButton(onRemoveCard: () {
+ onRemoveCard();
+ }),
+ ],
+ ),
+ ],
+ );
+ }
+}
diff --git a/payment_plugin/lib/presentation/components/payment_card_image.dart b/payment_plugin/lib/presentation/components/payment_card_image.dart
new file mode 100644
index 00000000..12596f8b
--- /dev/null
+++ b/payment_plugin/lib/presentation/components/payment_card_image.dart
@@ -0,0 +1,25 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_svg/svg.dart';
+import 'package:payment_plugin/domain/models/add_card_payment_method.dart';
+
+
+
+class PaymentCardImage extends StatelessWidget {
+ final CardType cardType;
+
+ const PaymentCardImage({super.key, required this.cardType});
+
+ @override
+ Widget build(BuildContext context) {
+ switch (cardType) {
+ case CardType.visa:
+ return SvgPicture.asset('assets/icons/visa.svg');
+ case CardType.mastercard:
+ return SvgPicture.asset('assets/icons/mastercard.svg');
+ case CardType.maestro:
+ return SvgPicture.asset('assets/icons/maestro.svg');
+ case CardType.amexgooglepay:
+ return SvgPicture.asset('assets/icons/ic_google.svg' );
+ }
+ }
+}
diff --git a/payment_plugin/lib/presentation/components/payment_cards_shimmer_loader.dart b/payment_plugin/lib/presentation/components/payment_cards_shimmer_loader.dart
new file mode 100644
index 00000000..927c4dfc
--- /dev/null
+++ b/payment_plugin/lib/presentation/components/payment_cards_shimmer_loader.dart
@@ -0,0 +1,53 @@
+
+import 'package:flutter/material.dart';
+import 'package:payment_plugin/presentation/components/content_placeholder.dart';
+import 'package:payment_plugin/presentation/components/title_placeholder.dart';
+import 'package:payment_plugin/themes/light_theme.dart';
+import 'package:shimmer/shimmer.dart';
+
+class PaymentCardsShimmerLoader extends StatelessWidget {
+ const PaymentCardsShimmerLoader({super.key});
+
+ @override
+ Widget build(BuildContext context) {
+ return Shimmer.fromColors(
+ baseColor: sandColor[40]!,
+ highlightColor: sandColor[10]!,
+ enabled: true,
+ child: ListView(
+ children: [
+ const SizedBox(height: 40),
+ const TitlePlaceholder(width: 200),
+ const SizedBox(height: 12),
+ const TitlePlaceholder(width: 120),
+ const SizedBox(height: 40),
+ const TitlePlaceholder(width: 160),
+ const SizedBox(height: 20),
+ ...List.generate(
+ 3,
+ (index) => const Padding(
+ padding: EdgeInsets.symmetric(vertical: 8.0),
+ child:
+ ContentPlaceholder(lineType: ContentLineType.twoLines),
+ )),
+ const SizedBox(height: 12),
+ // Simulate Add Card button
+ const Padding(
+ padding: EdgeInsets.symmetric(horizontal: 32.0),
+ child: SizedBox(
+ width: double.infinity,
+ height: 48,
+ child: DecoratedBox(
+ decoration: BoxDecoration(
+ color: Colors.white,
+ borderRadius: BorderRadius.all(Radius.circular(8)),
+ ),
+ ),
+ ),
+ ),
+ const SizedBox(height: 24),
+ ],
+ ),
+ );
+ }
+}
diff --git a/payment_plugin/lib/presentation/components/payment_details.dart b/payment_plugin/lib/presentation/components/payment_details.dart
new file mode 100644
index 00000000..416203ba
--- /dev/null
+++ b/payment_plugin/lib/presentation/components/payment_details.dart
@@ -0,0 +1,54 @@
+import 'package:equatable/equatable.dart';
+import 'package:json_annotation/json_annotation.dart';
+import 'package:payment_plugin/utils/json.dart';
+
+part '../../_generated/presentation/components/payment_details.g.dart';
+
+@JsonSerializable()
+class PaymentDetails extends Equatable {
+ final String cardNumber;
+ final String cardHolder;
+ final String? expiryDate;
+ final String? cvc;
+ final CardType cardType;
+ final String? cardName;
+
+ const PaymentDetails({
+ required this.cardNumber,
+ required this.cardHolder,
+ required this.expiryDate,
+ required this.cvc,
+ required this.cardType,
+ this.cardName,
+ });
+
+ factory PaymentDetails.fromJson(Json json) => _$PaymentDetailsFromJson(json);
+
+ Map<String, dynamic> toJson() => _$PaymentDetailsToJson(this);
+
+
+ @override
+ List<Object?> get props => [cardNumber, cardHolder, expiryDate, cvc, cardType, cardName];
+}
+
+enum CardType {
+ visa,
+ mastercard,
+ amexgooglepay,
+ maestro;
+
+ static CardType fromString(String type) {
+ switch (type.toLowerCase()) {
+ case 'visa':
+ return CardType.visa;
+ case 'mc':
+ return CardType.mastercard;
+ case 'maestro':
+ return CardType.maestro;
+ case 'amex_googlepay':
+ return CardType.amexgooglepay;
+ default:
+ throw Exception('Unknown card type: $type');
+ }
+ }
+}
diff --git a/payment_plugin/lib/presentation/components/remove_card_button.dart b/payment_plugin/lib/presentation/components/remove_card_button.dart
new file mode 100644
index 00000000..610d1d5c
--- /dev/null
+++ b/payment_plugin/lib/presentation/components/remove_card_button.dart
@@ -0,0 +1,102 @@
+
+import 'package:easy_localization/easy_localization.dart';
+import 'package:flutter/material.dart';
+import 'package:go_router/go_router.dart';
+import 'package:payment_plugin/themes/light_theme.dart';
+
+class RemoveCardButton extends StatelessWidget {
+ final void Function() onRemoveCard;
+
+ const RemoveCardButton({super.key, required this.onRemoveCard});
+
+ @override
+ Widget build(BuildContext context) {
+ final theme = Theme.of(context);
+ return InkWell(
+ onTap: () async {
+ final response = await showDialog<dynamic>(
+ context: context,
+ builder: (context) {
+ return Dialog(
+ backgroundColor: colorBackground,
+ child: Padding(
+ padding: const EdgeInsets.all(24.0),
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ Text(
+ "payment_cards_confirm_remove_title".tr(),
+ textAlign: TextAlign.center,
+ style: theme.textTheme.headlineMedium,
+ ),
+ const SizedBox(height: 32),
+ Text(
+ "payment_cards_confirm_remove_subtitle".tr(),
+ style: theme.textTheme.bodySmall
+ ?.copyWith(color: colorHeadlineText),
+ textAlign: TextAlign.center,
+ ),
+ const SizedBox(height: 32),
+ ElevatedButton(
+ style: ButtonStyle(
+ minimumSize: const WidgetStatePropertyAll(
+ Size(double.infinity, 50)),
+ shape: WidgetStatePropertyAll(RoundedRectangleBorder(
+ borderRadius: BorderRadius.circular(30),
+ )),
+ ),
+ onPressed: () {
+ context.pop(true);
+ },
+ child: Text(
+ "generic_confirm".tr(),
+ style: theme.textTheme.bodyMedium?.copyWith(color: colorBackground),
+ ),
+ ),
+ const SizedBox(height: 8),
+ OutlinedButton(
+ style: ButtonStyle(
+ minimumSize: const WidgetStatePropertyAll(
+ Size(double.infinity, 50)),
+ shape:
+ WidgetStatePropertyAll(RoundedRectangleBorder(
+ borderRadius: BorderRadius.circular(30),
+ )),
+ ),
+ child: Text(
+ "cancel".tr(),
+ style: theme.textTheme.bodyMedium,
+ ),
+ onPressed: () {
+ context.pop(false);
+ }),
+ ],
+ ),
+ ),
+ );
+ });
+ if (response is bool && response == true) {
+ onRemoveCard();
+ if (context.mounted) {
+ context.pop();
+ }
+ }
+ },
+ child: Container(
+ decoration: BoxDecoration(
+ border: Border.all(
+ color: colorDivider,
+ ),
+ borderRadius: BorderRadius.circular(30),
+ ),
+ child: Padding(
+ padding: const EdgeInsets.symmetric(vertical: 10.0, horizontal: 32),
+ child: Text(
+ "payment_cards_remove_card_button".tr(),
+ style: theme.textTheme.bodySmall?.copyWith(color: Theme.of(context).colorScheme.error),
+ ),
+ ),
+ ),
+ );
+ }
+}
diff --git a/payment_plugin/lib/presentation/components/save_card_button.dart b/payment_plugin/lib/presentation/components/save_card_button.dart
new file mode 100644
index 00000000..ab1450c7
--- /dev/null
+++ b/payment_plugin/lib/presentation/components/save_card_button.dart
@@ -0,0 +1,33 @@
+import 'package:easy_localization/easy_localization.dart';
+import 'package:flutter/material.dart';
+import 'package:payment_plugin/themes/light_theme.dart';
+
+class SaveCardButton extends StatelessWidget {
+ final void Function() onSaveCard;
+
+ const SaveCardButton({super.key, required this.onSaveCard});
+
+ @override
+ Widget build(BuildContext context) {
+ return InkWell(
+ onTap: () async {},
+ child: Container(
+ decoration: BoxDecoration(
+ border: Border.all(
+ color: colorDivider,
+ ),
+ borderRadius: BorderRadius.circular(30),
+ ),
+ child: Padding(
+ padding: const EdgeInsets.symmetric(vertical: 10.0, horizontal: 32),
+ child: Text(
+ "payment_cards_remove_card_button".tr(),
+ style: Theme.of(context)
+ .textTheme
+ .bodySmall,
+ ),
+ ),
+ ),
+ );
+ }
+}
diff --git a/payment_plugin/lib/presentation/components/title_placeholder.dart b/payment_plugin/lib/presentation/components/title_placeholder.dart
new file mode 100644
index 00000000..00f12174
--- /dev/null
+++ b/payment_plugin/lib/presentation/components/title_placeholder.dart
@@ -0,0 +1,34 @@
+import 'package:flutter/material.dart';
+
+class TitlePlaceholder extends StatelessWidget {
+ final double width;
+
+ const TitlePlaceholder({
+ super.key,
+ required this.width,
+ });
+
+ @override
+ Widget build(BuildContext context) {
+ return Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 16.0),
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Container(
+ width: width,
+ height: 12.0,
+ color: Colors.white,
+ ),
+ const SizedBox(height: 8.0),
+ Container(
+ width: width,
+ height: 12.0,
+ color: Colors.white,
+ ),
+ ],
+ ),
+ );
+ }
+}
diff --git a/payment_plugin/lib/presentation/screens/payment_cards_page.dart b/payment_plugin/lib/presentation/screens/payment_cards_page.dart
new file mode 100644
index 00000000..ed16d64b
--- /dev/null
+++ b/payment_plugin/lib/presentation/screens/payment_cards_page.dart
@@ -0,0 +1,181 @@
+
+import 'package:easy_localization/easy_localization.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:payment_plugin/domain/models/stored_payment_method.dart';
+import 'package:payment_plugin/presentation/app/bloc/payment_cards_state.dart';
+import 'package:payment_plugin/presentation/components/add_card.dart';
+import 'package:payment_plugin/presentation/components/card_item.dart';
+import 'package:payment_plugin/presentation/components/comwell_error_widget.dart';
+import 'package:payment_plugin/presentation/components/edit_card_dialog.dart';
+import 'package:payment_plugin/presentation/components/payment_cards_shimmer_loader.dart';
+import '../app/bloc/payment_cards_cubit.dart';
+
+class PaymentCardsPage extends StatelessWidget {
+ final bool needScaffold;
+ final PreferredSizeWidget appBar;
+
+ const PaymentCardsPage({super.key, this.needScaffold = false, required this.appBar});
+
+ @override
+ Widget build(BuildContext context) {
+ if (needScaffold) {
+ return Scaffold(
+ backgroundColor: Theme.of(context).colorScheme.surface,
+ appBar: appBar,
+ body: BlocBuilder<PaymentCardsCubit, PaymentCardsState>(
+ builder: (context, state) {
+ final cubit = context.read<PaymentCardsCubit>();
+ if (cubit.state.isLoading) {
+ return const Center(child: PaymentCardsShimmerLoader());
+ }
+ if (cubit.state.hasError) {
+ return Column(
+ crossAxisAlignment: CrossAxisAlignment.center,
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ ComwellErrorWidget(
+ title: 'generic_error_title'.tr(),
+ subtitle: 'payment_cards_error_subtitle'.tr(),
+ border: false,
+ ),
+ ],
+ );
+ }
+ return _buildPaymentCards(context, state, cubit);
+ },
+ ),
+ );
+ } else {
+ return BlocBuilder<PaymentCardsCubit, PaymentCardsState>(
+ builder: (context, state) {
+ final cubit = context.read<PaymentCardsCubit>();
+ if (cubit.state.isLoading) {
+ return const Center(child: PaymentCardsShimmerLoader());
+ }
+ if (cubit.state.hasError) {
+ return Column(
+ crossAxisAlignment: CrossAxisAlignment.center,
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ ComwellErrorWidget(
+ title: 'generic_error_title'.tr(),
+ subtitle: 'payment_cards_error_subtitle'.tr(),
+ border: false,
+ ),
+ ],
+ );
+ }
+ return _buildPaymentCards(context, state, cubit);
+ },
+ );
+ }
+ }
+
+ Widget _buildPaymentCards(
+ BuildContext context, PaymentCardsState state, PaymentCardsCubit cubit) {
+ final cards = state.cards;
+ final theme = Theme.of(context);
+ final showPaymentError =
+ state.missingPaymentMethod && state.selectedPaymentMethod == null;
+
+ return _buildCardsList(context, theme, cards, cubit, showPaymentError);
+ }
+
+ Widget _buildCardsList(
+ BuildContext context,
+ ThemeData theme,
+ Iterable<StoredPaymentMethod> cards,
+ PaymentCardsCubit cubit,
+ bool showPaymentError,
+ ) {
+ return ListView(
+ children: [
+ Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 16.0),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ const SizedBox(height: 40),
+ Text(
+ "payment_cards_title".tr(),
+ style: theme.textTheme.headlineLarge,
+ ),
+ const SizedBox(height: 40),
+ Text("payment_cards_my_cards".tr(),
+ style: theme.textTheme.headlineMedium),
+ const SizedBox(height: 20),
+ if (showPaymentError) ...[
+ Container(
+ padding: const EdgeInsets.all(12),
+ margin: const EdgeInsets.only(bottom: 12),
+ decoration: BoxDecoration(
+ color: theme.colorScheme.error.withValues(alpha: 0.1),
+ border: Border.all(color: theme.colorScheme.error),
+ borderRadius: BorderRadius.circular(8),
+ ),
+ child: Row(
+ children: [
+ Icon(Icons.error_outline,
+ color: theme.colorScheme.error, size: 20),
+ const SizedBox(width: 8),
+ Expanded(
+ child: Text(
+ "payment_cards_missing_payment_method_subtitle".tr(),
+ style: theme.textTheme.bodyMedium?.copyWith(
+ color: theme.colorScheme.error,
+ ),
+ ),
+ ),
+ ],
+ ),
+ ),
+ ],
+ ...cards.map(
+ (card) => InkWell(
+ onTap: () {
+ showModalBottomSheet<void>(
+ context: context,
+ backgroundColor: Theme.of(context).colorScheme.surface,
+ builder: (context) {
+ return Padding(
+ padding: const EdgeInsets.all(16.0),
+ child: EditCardDialog(
+ storedPaymentMethod: card,
+ onRemoveCard: () {
+ cubit.onRemoveCard(card.id);
+ },
+ ),
+ );
+ },
+ );
+ },
+ child: Padding(
+ padding: const EdgeInsets.symmetric(vertical: 4.0),
+ child: Container(
+ decoration: BoxDecoration(
+ border: Border.all(color: theme.colorScheme.outline),
+ ),
+ padding: const EdgeInsets.all(12),
+ child: CardItem(
+ needsScaffold: needScaffold,
+ paymentMethod: card,
+ onSelect: () {
+ cubit.selectPaymentMethod(card);
+ },
+ isSelectedPaymentMethod:
+ cubit.state.selectedPaymentMethod?.id == card.id,
+ ),
+ ),
+ ),
+ ),
+ ),
+ const SizedBox(height: 12),
+ const AddCard(),
+ ],
+ ),
+ ),
+ ],
+ );
+ }
+}
diff --git a/payment_plugin/lib/presentation/screens/payment_processing_page.dart b/payment_plugin/lib/presentation/screens/payment_processing_page.dart
new file mode 100644
index 00000000..04fb5dc6
--- /dev/null
+++ b/payment_plugin/lib/presentation/screens/payment_processing_page.dart
@@ -0,0 +1,127 @@
+import 'package:adyen_checkout/adyen_checkout.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:lottie/lottie.dart';
+import 'package:payment_plugin/presentation/app/bloc/payment_cubit.dart';
+import 'package:payment_plugin/presentation/app/bloc/payment_processing_state.dart';
+import 'package:payment_plugin/themes/light_theme.dart';
+import 'package:payment_plugin/utils/lottie_utils.dart';
+
+class PaymentProcessingPage extends StatefulWidget {
+ const PaymentProcessingPage({super.key});
+
+ @override
+ State<PaymentProcessingPage> createState() => _PaymentProcessingPageState();
+}
+
+class _PaymentProcessingPageState extends State<PaymentProcessingPage>
+ with SingleTickerProviderStateMixin {
+ LottieComposition? loadingComposition;
+ late final AnimationController animationController;
+
+ @override
+ void initState() {
+ animationController = AnimationController(vsync: this);
+ super.initState();
+ }
+
+ @override
+ void dispose() {
+ animationController.dispose();
+ super.dispose();
+ }
+
+ void onAnimationEnd() {
+ playLoading();
+ }
+
+ void playLoading() {
+ loadingComposition?.playBetween(
+ animationController,
+ "spinner",
+ markerEnd: "success",
+ repeat: true,
+ );
+ }
+
+ void playError() async {
+ loadingComposition?.playBetween(
+ animationController,
+ "error",
+ repeat: true,
+ );
+ await Future<void>.delayed(const Duration(seconds: 1));
+ if (mounted) Navigator.of(context).pop();
+ }
+
+ void playSuccess() async {
+ await loadingComposition?.playBetween(
+ animationController,
+ "success",
+ markerEnd: "error",
+ );
+ await Future<void>.delayed(const Duration(seconds: 1));
+ if (mounted) Navigator.of(context).pop();
+ }
+
+ void showAdyenModal(BuildContext context) async {
+ final cubit = context.read<PaymentCubit>();
+ final processingState = cubit.state;
+ dynamic response;
+ if (processingState is! PaymentProcessingStateSessionReceived) return;
+ final paymentConfig = processingState.paymentConfigurations;
+ if (!mounted) return;
+
+ final dropInConfig = paymentConfig.dropInConfiguration;
+ response = await AdyenCheckout.session.startDropIn(
+ dropInConfiguration: dropInConfig,
+ checkout: paymentConfig.sessionCheckout,
+ );
+
+ if (!mounted) return;
+ if (response is PaymentResult) {
+ cubit.onPaymentResult(response);
+ } else {
+ cubit.onUserDismissPaymentPopup();
+ }
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ final cubit = context.read<PaymentCubit>();
+ return Scaffold(
+ body: Container(
+ alignment: Alignment.center,
+ color: sandColor[80],
+ child: BlocListener<PaymentCubit, PaymentProcessingState>(
+ listener: (context, state) {
+ if (state is PaymentProcessingStateSessionReceived) {
+ showAdyenModal(context);
+ }
+ },
+ child: Lottie.asset(
+ 'assets/animations/load_animation.json',
+ controller: animationController,
+ onLoaded: (composition) {
+ if (loadingComposition == null) {
+ loadingComposition = composition;
+ animationController.duration = composition.duration;
+ switch (cubit.state) {
+ case PaymentProcessingStateConfirmed _:
+ playSuccess();
+ case PaymentProcessingStateError _:
+ playError();
+ default:
+ playLoading();
+ }
+ }
+ },
+ fit: BoxFit.cover,
+ width: 64,
+ height: 64,
+ ),
+ ),
+ ),
+ );
+ }
+}
diff --git a/payment_plugin/lib/scripts/new_model.dart b/payment_plugin/lib/scripts/new_model.dart
new file mode 100644
index 00000000..59920cef
--- /dev/null
+++ b/payment_plugin/lib/scripts/new_model.dart
@@ -0,0 +1,143 @@
+import 'package:change_case/change_case.dart';
+import 'package:payment_plugin/scripts/utils.dart';
+
+Future<void> main(List<String> args) async {
+ // create files
+ if (args.isEmpty) throw Exception("Missing args");
+ if (args.length > 1) throw Exception("Too many arguments");
+ final modelName = args[0];
+ final className = modelName.toPascalCase();
+ final dbClassName = "${className}Db";
+ final dtoClassName = "${className}Dto";
+ final presentationFileName = className.toSnakeCase();
+ final dbFileName = dbClassName.toSnakeCase();
+ final dtoFileName = dtoClassName.toSnakeCase();
+ final tableClassName = "${className}Table";
+
+ final presentationFile =
+ await createFile("lib/presentation/models/$presentationFileName.dart");
+ final dbFile = await createFile("lib/data/local/tables/$dbFileName.dart");
+ final dtoFile = await createFile("lib/data/remote/models/$dtoFileName.dart");
+ final mapperFile = await createFile(
+ "lib/domain/mappers/${className.toSnakeCase()}_mapper.dart");
+
+ // write files
+ await writeToFile(presentationFile, presentationFileContent(className));
+ await writeToFile(dbFile, dbFileContent(dbClassName, tableClassName));
+ await writeToFile(dtoFile, dtoFileContent(dtoClassName, dtoFileName));
+ await writeToFile(
+ mapperFile,
+ mapperFileContent(
+ dbClassName: dbClassName,
+ dbFileName: dbFileName,
+ dtoClassName: dtoClassName,
+ dtoFileName: dtoFileName,
+ presClassName: className,
+ presentationFileName: presentationFileName,
+ ));
+ print("$className created successfully");
+ print("Please add $tableClassName to tables-list in @DriftDatabase(tables: [..., $tableClassName]) and the build runner");
+}
+
+String presentationFileContent(String className) {
+ return """
+class $className {
+ final String id;
+
+ $className({
+ required this.id,
+ });
+}
+ """
+ .trim();
+}
+
+String dtoFileContent(String className, String fileName) {
+ return """
+import 'package:freezed_annotation/freezed_annotation.dart';
+import 'package:luxplus_app/presentation/utils/json_utils.dart';
+
+part '../../../_generated/data/remote/models/$fileName.g.dart';
+
+@JsonSerializable()
+class $className {
+ final String id;
+
+ $className({
+ required this.id,
+ });
+
+ factory $className.fromJson(Json json) => _\$${className}FromJson(json);
+
+ Json toJson() => _\$${className}ToJson(this);
+}
+ """
+ .trim();
+}
+
+String dbFileContent(String className, String tableName) {
+ return """
+import 'package:drift/drift.dart';
+
+@DataClassName("$className")
+class $tableName extends Table {
+ TextColumn get uid => text().unique()();
+
+ TextColumn get json => text()();
+}
+ """
+ .trim();
+}
+
+String mapperFileContent({
+ required String presClassName,
+ required String dtoClassName,
+ required String dbClassName,
+ required String dbFileName,
+ required String dtoFileName,
+ required String presentationFileName,
+}) {
+ return """
+import 'dart:convert';
+
+import 'package:luxplus_app/data/local/database/luxplus_database.dart';
+import 'package:luxplus_app/data/remote/models/$dtoFileName.dart';
+import 'package:luxplus_app/presentation/models/$presentationFileName.dart';
+
+extension ${dtoClassName}Mapper on $dtoClassName {
+
+ $presClassName to$presClassName() {
+ return $presClassName(id: id);
+ }
+
+ $dbClassName toDb() {
+ final json = jsonEncode(toJson());
+ return $dbClassName(uid: id, json: json);
+ }
+}
+
+extension ${dtoClassName}ListMapper on Iterable<$dtoClassName> {
+ Iterable<$presClassName> to${presClassName}List() {
+ return map((productDto) => productDto.to$presClassName());
+ }
+
+ Iterable<$dbClassName> toDbList() {
+ return map((productDto) => productDto.toDb());
+ }
+}
+
+
+extension ${dbClassName}Mapper on $dbClassName {
+ $presClassName to$presClassName() {
+ return $dtoClassName.fromJson(jsonDecode(this.json)).to$presClassName();
+ }
+}
+
+extension ${dbClassName}ListMapper on Iterable<$dbClassName> {
+ Iterable<$presClassName> to${presClassName}List() {
+ return map((db) => db.to$presClassName());
+ }
+}
+ """
+ .trim();
+}
diff --git a/payment_plugin/lib/scripts/utils.dart b/payment_plugin/lib/scripts/utils.dart
new file mode 100644
index 00000000..77dae9b1
--- /dev/null
+++ b/payment_plugin/lib/scripts/utils.dart
@@ -0,0 +1,15 @@
+import 'dart:io';
+
+Future<File> createFile(String path) async {
+ final file = File(path);
+ if (file.existsSync()) throw Exception("File already exists");
+ await file.create(recursive: true);
+ return file;
+}
+
+Future<void> writeToFile(File file, String content) async {
+ var sink = file.openWrite();
+ sink.write(content);
+ await sink.flush();
+ await sink.close();
+}
\ No newline at end of file
diff --git a/payment_plugin/lib/themes/comwell_colors.dart b/payment_plugin/lib/themes/comwell_colors.dart
new file mode 100644
index 00000000..f09d20e4
--- /dev/null
+++ b/payment_plugin/lib/themes/comwell_colors.dart
@@ -0,0 +1,49 @@
+
+
+import 'package:flutter/material.dart';
+
+const colorPrimary = Color(0xFF677169);
+const colorSecondary = Color(0xffF0EAE2);
+const colorBackground = Color(0xFFFFFFFF);
+const colorPrimaryText = Color(0xFF000000);
+const colorSecondaryText = Color(0xFF000000);
+const colorOnPrimaryTextColor = Color(0xFFFFFFFF);
+const colorPrimarySystem = Color(0xFF000000);
+const colorSecondarySystem = Color.fromARGB(237, 227, 216, 1);
+const colorTertiary = Color(0xFF000000);
+const colorTertiaryText = Color(0xFF000000);
+const colorTertiarySystem = Color(0xFF000000);
+const colorShadow = Color(0xFF000000);
+const colorDivider = Color(0xFFE0E0E0);
+const disabledButtonColor = Color(0xFFF0F0F0);
+const colorError = Color(0xFFEB0026);
+
+const colorBlack = MaterialColor(0xFF000000, {
+ 75: Color(0xB3000000),
+ 65: Color(0xA6000000),
+});
+
+const int _earthColor = 0xFF677169;
+const earthColor = MaterialColor(_earthColor, <int, Color>{
+ 100: Color(_earthColor),
+ 80: Color.fromRGBO(128, 139, 130, 1.0),
+ 60: Color.fromRGBO(160, 171, 163, 1.0),
+ 40: Color.fromRGBO(192, 200, 194, 1.0),
+ 20: Color.fromRGBO(227, 230, 227, 1.0),
+ 10: Color.fromRGBO(238, 239, 238, 1.0),
+});
+
+const int _sandColor = 0xFFAA8D65;
+const sandColor = MaterialColor(_sandColor, <int, Color>{
+ 100: Color(_sandColor),
+ 80: Color.fromRGBO(190, 161, 121, 1.0),
+ 60: Color.fromRGBO(215, 201, 185, 1.0),
+ 40: Color.fromRGBO(237, 227, 216, 1.0),
+ 20: Color.fromRGBO(240, 234, 226, 1.0),
+ 10: Color.fromRGBO(249, 246, 242, 1.0),
+});
+
+extension CustomColors on ColorScheme {
+ Color get secondaryText => const Color(0xFF888888); // medium gray
+ Color get disabledText => const Color(0xFFCCCCCC); // light gray
+}
\ No newline at end of file
diff --git a/payment_plugin/lib/themes/dark_theme.dart b/payment_plugin/lib/themes/dark_theme.dart
new file mode 100644
index 00000000..d9977494
--- /dev/null
+++ b/payment_plugin/lib/themes/dark_theme.dart
@@ -0,0 +1,98 @@
+import 'package:flutter/material.dart';
+
+import 'comwell_colors.dart';
+
+
+ThemeData darkTheme = ThemeData(
+ fontFamily: 'Fellix',
+ brightness: Brightness.light,
+ appBarTheme: const AppBarTheme(
+ backgroundColor: colorPrimary,
+ foregroundColor: colorBackground,
+ ),
+ progressIndicatorTheme: const ProgressIndicatorThemeData(
+ color: Colors.lightBlueAccent,
+ linearTrackColor: Colors.grey,
+ circularTrackColor: Colors.grey,
+ ),
+ textTheme: const TextTheme(
+ displayLarge: TextStyle(fontSize: 36.0, fontWeight: FontWeight.w600),
+ displayMedium: TextStyle(fontSize: 34.0, fontWeight: FontWeight.w600),
+ headlineLarge: TextStyle(fontSize: 24.0, fontWeight: FontWeight.w600),
+ headlineMedium: TextStyle(fontSize: 18.0, fontWeight: FontWeight.w600),
+ headlineSmall: TextStyle(fontSize: 16.0, fontWeight: FontWeight.w600),
+ bodyMedium: TextStyle(fontSize: 16.0, fontWeight: FontWeight.w600),
+ bodySmall: TextStyle(fontSize: 14.0, fontWeight: FontWeight.w500)),
+ colorScheme: ColorScheme(
+ primary: colorPrimary,
+ onPrimary: colorOnPrimaryTextColor,
+ secondary: colorSecondary,
+ onSecondary: colorSecondaryText,
+ surface: colorBackground, // main backgrounds
+ onSurface: colorPrimaryText, // main text/icons
+ error: colorError,
+ onError: colorOnPrimaryTextColor,
+ outline: colorDivider,
+ shadow: colorShadow,
+ scrim: colorPrimarySystem,
+ brightness: Brightness.light,
+ // Material 3+ surface roles:
+ surfaceContainerLow:
+ colorCallReceivedBackground, // custom container background
+ tertiary: colorTertiary,
+ onTertiary: colorTertiaryText,
+ tertiaryContainer: colorTertiarySystem,
+ surfaceTint: colorHeadlineText,
+ ),
+ elevatedButtonTheme: ElevatedButtonThemeData(
+ style: ElevatedButton.styleFrom(
+ elevation: 0,
+ backgroundColor: sandColor[80],
+ minimumSize: const Size(double.infinity, 52),
+ textStyle: const TextStyle(
+ color: colorBackground,
+ fontSize: 16,
+ fontWeight: FontWeight.w600,
+ ),
+ shape: RoundedRectangleBorder(
+ borderRadius: BorderRadius.circular(30),
+ ),
+ ),
+ ),
+);
+
+const colorPrimary = Color(0xFF677169);
+const colorSecondary = Color(0xffF0EAE2);
+const colorBackground = Color(0xFFFFFFFF);
+const colorPrimaryText = Color(0xFF000000);
+const colorSecondaryText = Color(0xFF000000);
+const colorOnPrimaryTextColor = Color(0xFFFFFFFF);
+const colorPrimarySystem = Color(0xFF000000);
+const colorSecondarySystem = Color.fromARGB(237, 227, 216, 1);
+const colorTertiary = Color(0xFF000000);
+const colorTertiaryText = Color(0xFF000000);
+const colorTertiarySystem = Color(0xFF000000);
+const colorShadow = Color(0xFF000000);
+const colorError = Color(0xFFEB0026);
+final colorHeadlineText = colorPrimaryText.withValues(alpha: 0.65);
+const colorCallReceivedBackground = Color(0xFFF6F4F3);
+
+const int _earthColor = 0xFF677169;
+const earthColor = MaterialColor(_earthColor, <int, Color>{
+ 100: Color(_earthColor),
+ 80: Color.fromRGBO(128, 139, 130, 1.0),
+ 60: Color.fromRGBO(160, 171, 163, 1.0),
+ 40: Color.fromRGBO(192, 200, 194, 1.0),
+ 20: Color.fromRGBO(227, 230, 227, 1.0),
+ 10: Color.fromRGBO(238, 239, 238, 1.0),
+});
+
+const int _sandColor = 0xFFAA8D65;
+const sandColor = MaterialColor(_sandColor, <int, Color>{
+ 100: Color(_sandColor),
+ 80: Color.fromRGBO(190, 161, 121, 1.0),
+ 60: Color.fromRGBO(215, 201, 185, 1.0),
+ 40: Color.fromRGBO(237, 227, 216, 1.0),
+ 20: Color.fromRGBO(240, 234, 226, 1.0),
+ 10: Color(0xFFF9F6F2),
+});
diff --git a/payment_plugin/lib/themes/light_theme.dart b/payment_plugin/lib/themes/light_theme.dart
new file mode 100644
index 00000000..a248e928
--- /dev/null
+++ b/payment_plugin/lib/themes/light_theme.dart
@@ -0,0 +1,107 @@
+import 'package:flutter/material.dart';
+
+import 'comwell_colors.dart';
+
+
+ThemeData lightTheme = ThemeData(
+ fontFamily: 'Fellix',
+ brightness: Brightness.light,
+ appBarTheme: const AppBarTheme(
+ backgroundColor: colorPrimary,
+ foregroundColor: colorBackground,
+ ),
+ progressIndicatorTheme: const ProgressIndicatorThemeData(
+ color: Colors.lightBlueAccent,
+ linearTrackColor: Colors.grey,
+ circularTrackColor: Colors.grey,
+ ),
+ textTheme: const TextTheme(
+ displayLarge: TextStyle(fontSize: 36.0, fontWeight: FontWeight.w600),
+ displayMedium: TextStyle(fontSize: 34.0, fontWeight: FontWeight.w600),
+ displaySmall: TextStyle(fontSize: 32, fontWeight: FontWeight.w600),
+ headlineLarge: TextStyle(fontSize: 24.0, fontWeight: FontWeight.w600),
+ headlineMedium: TextStyle(fontSize: 18.0, fontWeight: FontWeight.w600),
+ headlineSmall: TextStyle(fontSize: 16.0, fontWeight: FontWeight.w600),
+ bodyMedium: TextStyle(fontSize: 16.0, fontWeight: FontWeight.w600),
+ bodySmall: TextStyle(fontSize: 14.0, fontWeight: FontWeight.w500),
+ labelLarge: TextStyle(fontSize: 14.0, fontWeight: FontWeight.w600),
+ titleLarge: TextStyle(fontSize: 28.0, fontWeight: FontWeight.w600),
+ ),
+ colorScheme: ColorScheme(
+ primary: colorPrimary,
+ onPrimary: colorOnPrimaryTextColor,
+ secondary: colorSecondary,
+ onSecondary: colorSecondaryText,
+ surface: colorBackground, // main backgrounds
+ onSurface: colorPrimaryText, // main text/icons
+ error: colorError,
+ onError: colorOnPrimaryTextColor,
+ outline: colorDivider,
+ shadow: colorShadow,
+ scrim: colorPrimarySystem,
+ brightness: Brightness.light,
+ // Material 3+ surface roles:
+ surfaceContainerLow: colorCallReceivedBackground, // custom container background
+ tertiary: colorTertiary,
+ onTertiary: colorTertiaryText,
+ tertiaryContainer: colorTertiarySystem,
+ surfaceTint: colorHeadlineText,
+ ),
+ elevatedButtonTheme: ElevatedButtonThemeData(
+ style: ElevatedButton.styleFrom(
+ elevation: 0,
+ backgroundColor: sandColor[80],
+ minimumSize: const Size(double.infinity, 52),
+ textStyle: const TextStyle(
+ color: colorBackground,
+ fontSize: 16,
+ fontWeight: FontWeight.w600,
+ ),
+ shape: RoundedRectangleBorder(
+ borderRadius: BorderRadius.circular(30),
+ ),
+ ),
+ ),
+);
+
+const colorPrimary = Color(0xFF677169);
+const colorSecondary = Color(0xffF0EAE2);
+const colorBackground = Color(0xFFFFFFFF);
+const colorPrimaryText = Color(0xFF000000);
+const colorSecondaryText = Color(0xFF000000);
+const colorOnPrimaryTextColor = Color(0xFFFFFFFF);
+const colorPrimarySystem = Color(0xFF000000);
+const colorSecondarySystem = Color.fromARGB(237, 227, 216, 1);
+const colorTertiary = Color(0xFF000000);
+const colorTertiaryText = Color(0xFF000000);
+const colorTertiarySystem = Color(0xFF000000);
+const colorShadow = Color(0xFF000000);
+const colorDivider = Color(0xFFE0E0E0);
+const disabledButtonColor = Color(0xFFF0F0F0);
+final colorHeadlineText = colorPrimaryText.withValues(alpha: 0.65);
+const colorCallReceivedBackground = Color(0xFFF6F4F3);
+const colorGrey = Color(0xFF757575);
+const colorBlack = MaterialColor(0xFF000000, {
+ 75: Color(0xB3000000),
+ 65: Color(0xA6000000),
+});
+
+const int _earthColor = 0xFF677169;
+const earthColor = MaterialColor(_earthColor, <int, Color>{
+ 100: Color(_earthColor),
+ 80: Color.fromRGBO(128, 139, 130, 1.0),
+ 60: Color.fromRGBO(160, 171, 163, 1.0),
+ 40: Color.fromRGBO(192, 200, 194, 1.0),
+ 20: Color.fromRGBO(227, 230, 227, 1.0),
+ 10: Color.fromRGBO(238, 239, 238, 1.0),
+});
+
+const int _sandColor = 0xFFAA8D65;
+const sandColor = MaterialColor(_sandColor, <int, Color>{
+ 100: Color(_sandColor),
+ 80: Color.fromRGBO(190, 161, 121, 1.0),
+ 60: Color.fromRGBO(215, 201, 185, 1.0),
+ 40: Color.fromRGBO(237, 227, 216, 1.0),
+ 20: Color.fromRGBO(240, 234, 226, 1.0),
+ 10: Color.fromRGBO(249, 246, 242, 1.0),
+});
diff --git a/payment_plugin/lib/utils/json.dart b/payment_plugin/lib/utils/json.dart
new file mode 100644
index 00000000..3e024144
--- /dev/null
+++ b/payment_plugin/lib/utils/json.dart
@@ -0,0 +1 @@
+typedef Json = Map<String, dynamic>;
\ No newline at end of file
diff --git a/payment_plugin/lib/utils/lottie_utils.dart b/payment_plugin/lib/utils/lottie_utils.dart
new file mode 100644
index 00000000..47d5eee9
--- /dev/null
+++ b/payment_plugin/lib/utils/lottie_utils.dart
@@ -0,0 +1,25 @@
+import 'package:flutter/animation.dart';
+import 'package:lottie/lottie.dart';
+
+extension LottieUtils on LottieComposition {
+ Future<void> playBetween(
+ AnimationController animationController,
+ String markerStart, {
+ String markerEnd = "",
+ bool repeat = false,
+ }) async {
+ final startMarker = getMarker(markerStart)!.start;
+ final endMarker = markerEnd == "" ? 1.0 : getMarker(markerEnd)!.start;
+ final duration = this.duration * (endMarker - startMarker).abs();
+ animationController.value = startMarker;
+ if (repeat) {
+ await animationController.repeat(
+ min: startMarker,
+ max: endMarker,
+ period: duration,
+ );
+ } else {
+ await animationController.animateTo(endMarker, duration: duration);
+ }
+ }
+}
diff --git a/payment_plugin/pubspec.yaml b/payment_plugin/pubspec.yaml
new file mode 100644
index 00000000..5907656f
--- /dev/null
+++ b/payment_plugin/pubspec.yaml
@@ -0,0 +1,97 @@
+name: payment_plugin
+description: "A new Flutter plugin project."
+version: 0.0.1
+
+environment:
+ sdk: ^3.8.1
+ flutter: '>=3.3.0'
+
+dependencies:
+ flutter:
+ sdk: flutter
+ flutter_web_plugins:
+ sdk: flutter
+ web: ^1.0.0
+ plugin_platform_interface: ^2.0.2
+ adyen_checkout: ^1.7.0
+ equatable: ^2.0.5
+ json_annotation: ^4.9.0
+ easy_localization: ^3.0.7
+ dio: ^5.8.0+1
+ retrofit: ^4.6.0
+ freezed_annotation: ^3.1.0
+ bloc: ^9.1.0
+ flutter_bloc: ^9.1.1
+ shimmer: ^3.0.0
+ lottie: ^3.1.2
+ flutter_dotenv: ^6.0.0
+ go_router: ^16.2.0
+ flutter_svg: ^2.0.10+1
+
+
+dev_dependencies:
+ flutter_test:
+ sdk: flutter
+ flutter_lints: ^5.0.0
+ build_runner: ^2.4.13
+ json_serializable: ^6.9.0
+ change_case: ^2.2.0
+ retrofit_generator: ^10.0.5
+ freezed: ^3.0.6
+
+# For information on the generic Dart part of this file, see the
+# following page: https://dart.dev/tools/pub/pubspec
+
+# The following section is specific to Flutter packages.
+flutter:
+ # This section identifies this Flutter project as a plugin project.
+ # The 'pluginClass' specifies the class (in Java, Kotlin, Swift, Objective-C, etc.)
+ # which should be registered in the plugin registry. This is required for
+ # using method channels.
+ # The Android 'package' specifies package in which the registered class is.
+ # This is required for using method channels on Android.
+ # The 'ffiPlugin' specifies that native code should be built and bundled.
+ # This is required for using `dart:ffi`.
+ # All these are used by the tooling to maintain consistency when
+ # adding or updating assets for this project.
+ plugin:
+ platforms:
+ android:
+ package: com.example.payment_plugin
+ pluginClass: PaymentPlugin
+ ios:
+ pluginClass: PaymentPlugin
+ web:
+ pluginClass: PaymentPluginWeb
+ fileName: payment_plugin_web.dart
+
+ # To add assets to your plugin package, add an assets section, like this:
+ # assets:
+ # - images/a_dot_burr.jpeg
+ # - images/a_dot_ham.jpeg
+ #
+ # For details regarding assets in packages, see
+ # https://flutter.dev/to/asset-from-package
+ #
+ # An image asset can refer to one or more resolution-specific "variants", see
+ # https://flutter.dev/to/resolution-aware-images
+
+ # To add custom fonts to your plugin package, add a fonts section here,
+ # in this "flutter" section. Each entry in this list should have a
+ # "family" key with the font family name, and a "fonts" key with a
+ # list giving the asset and other descriptors for the font. For
+ # example:
+ # fonts:
+ # - family: Schyler
+ # fonts:
+ # - asset: fonts/Schyler-Regular.ttf
+ # - asset: fonts/Schyler-Italic.ttf
+ # style: italic
+ # - family: Trajan Pro
+ # fonts:
+ # - asset: fonts/TrajanPro.ttf
+ # - asset: fonts/TrajanPro_Bold.ttf
+ # weight: 700
+ #
+ # For details regarding fonts in packages, see
+ # https://flutter.dev/to/font-from-package
diff --git a/payment_plugin/test/payment_plugin_method_channel_test.dart b/payment_plugin/test/payment_plugin_method_channel_test.dart
new file mode 100644
index 00000000..f5893bc6
--- /dev/null
+++ b/payment_plugin/test/payment_plugin_method_channel_test.dart
@@ -0,0 +1,27 @@
+import 'package:flutter/services.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:payment_plugin/payment_plugin_method_channel.dart';
+
+void main() {
+ TestWidgetsFlutterBinding.ensureInitialized();
+
+ MethodChannelPaymentPlugin platform = MethodChannelPaymentPlugin();
+ const MethodChannel channel = MethodChannel('payment_plugin');
+
+ setUp(() {
+ TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(
+ channel,
+ (MethodCall methodCall) async {
+ return '42';
+ },
+ );
+ });
+
+ tearDown(() {
+ TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null);
+ });
+
+ test('getPlatformVersion', () async {
+ expect(await platform.getPlatformVersion(), '42');
+ });
+}
diff --git a/payment_plugin/test/payment_plugin_test.dart b/payment_plugin/test/payment_plugin_test.dart
new file mode 100644
index 00000000..7283a117
--- /dev/null
+++ b/payment_plugin/test/payment_plugin_test.dart
@@ -0,0 +1,15 @@
+import 'package:flutter_test/flutter_test.dart';
+import 'package:payment_plugin/payment_plugin.dart';
+
+void main() {
+ test('AdyenRepository can be initialized', () {
+ final repo = AdyenRepository(
+ tokenProvider: () async => 'test-token',
+ baseUrl: 'https://test-api.com',
+ );
+
+ expect(repo, isNotNull);
+ });
+
+ // Add more tests for your payment logic here
+}