From f5587db73cbd74c41a8353235ee9c01350db6990 Mon Sep 17 00:00:00 2001 From: cedvandenbosch Date: Sat, 12 Oct 2024 13:27:40 +0200 Subject: [PATCH 01/16] wip --- .../src/js/setup_intents/confirm_setup.dart | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 packages/stripe_js/lib/src/js/setup_intents/confirm_setup.dart diff --git a/packages/stripe_js/lib/src/js/setup_intents/confirm_setup.dart b/packages/stripe_js/lib/src/js/setup_intents/confirm_setup.dart new file mode 100644 index 000000000..95857ba58 --- /dev/null +++ b/packages/stripe_js/lib/src/js/setup_intents/confirm_setup.dart @@ -0,0 +1,25 @@ +import 'package:stripe_js/stripe_api.dart'; +import 'package:stripe_js/stripe_js.dart'; +import '../utils/utils.dart'; +import 'dart:js_interop'; + +extension ExtensionConfirmCardSetup on Stripe { + Future confirmSetup( + String clientSecret, { + ConfirmCardSetupData? data, + ConfirmCardSetupOptions? options, + }) { + final jsData = (data?.toJson() ?? {}).jsify(); + final jsOptions = (options?.toJson() ?? {}).jsify(); + return _confirmCardSetup(clientSecret, jsData, jsOptions) + .toDart + .then((response) => response.toDart); + } + + @JS('confirmSetup') + external JSPromise _confirmCardSetup( + String clientSecret, [ + JSAny? data, + JSAny? options, + ]); +} From 47f9ce1e0cb9994e6738fecb7c9b93d65d1e5295 Mon Sep 17 00:00:00 2001 From: cedvandenbosch Date: Sat, 12 Oct 2024 23:28:20 +0200 Subject: [PATCH 02/16] add confirm setup --- ...ress_checkout_element_options.freezed.dart | 3 + .../setup_intents/confirm_setup_options.dart | 62 +++ .../confirm_setup_options.freezed.dart | 483 ++++++++++++++++++ .../confirm_setup_options.g.dart | 62 +++ .../src/js/setup_intents/confirm_setup.dart | 42 +- 5 files changed, 634 insertions(+), 18 deletions(-) create mode 100644 packages/stripe_js/lib/src/api/setup_intents/confirm_setup_options.dart create mode 100644 packages/stripe_js/lib/src/api/setup_intents/confirm_setup_options.freezed.dart create mode 100644 packages/stripe_js/lib/src/api/setup_intents/confirm_setup_options.g.dart diff --git a/packages/stripe_js/lib/src/api/elements/express_checkout_element_options.freezed.dart b/packages/stripe_js/lib/src/api/elements/express_checkout_element_options.freezed.dart index f09569aad..7c51340fe 100644 --- a/packages/stripe_js/lib/src/api/elements/express_checkout_element_options.freezed.dart +++ b/packages/stripe_js/lib/src/api/elements/express_checkout_element_options.freezed.dart @@ -368,6 +368,7 @@ ExpressCheckoutConfirmEvent _$ExpressCheckoutConfirmEventFromJson( /// @nodoc mixin _$ExpressCheckoutConfirmEvent { + /// The method that was used to pay. String get expressPaymentType => throw _privateConstructorUsedError; BillingDetails? get billingDetails => throw _privateConstructorUsedError; @@ -497,6 +498,7 @@ class _$ExpressCheckoutConfirmEventImpl Map json) => _$$ExpressCheckoutConfirmEventImplFromJson(json); + /// The method that was used to pay. @override final String expressPaymentType; @override @@ -550,6 +552,7 @@ abstract class _ExpressCheckoutConfirmEvent factory _ExpressCheckoutConfirmEvent.fromJson(Map json) = _$ExpressCheckoutConfirmEventImpl.fromJson; + /// The method that was used to pay. @override String get expressPaymentType; @override diff --git a/packages/stripe_js/lib/src/api/setup_intents/confirm_setup_options.dart b/packages/stripe_js/lib/src/api/setup_intents/confirm_setup_options.dart new file mode 100644 index 000000000..f5e30deb5 --- /dev/null +++ b/packages/stripe_js/lib/src/api/setup_intents/confirm_setup_options.dart @@ -0,0 +1,62 @@ +import 'package:stripe_js/src/api/converters/js_converter.dart'; +import 'package:stripe_js/stripe_api.dart'; + +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'confirm_setup_options.freezed.dart'; +part 'confirm_setup_options.g.dart'; + +@freezed +class ConfirmSetupOptions with _$ConfirmSetupOptions { + const factory ConfirmSetupOptions({ + /// Required if you collect payment details before creating an Intent. It's always required if you don't provide a clientSecret. + @ElementsConverter() required Elements elements, + + /// Required if you collect payment details before creating an Intent. + /// It's always required if you don't provide an elements instance containing a client secret. + String? clientSecret, + + /// Parameters that will be passed on to the Stripe API. + /// Refer to the Payment Intents API for a full list of parameters. + required ConfirmSetupParams confirmParams, + + /// By default, stripe.confirmSetup will always redirect to your return_url + /// after a successful confirmation. If you set redirect: "if_required", + /// then stripe.confirmSetup will only redirect if your user chooses a + /// redirect-based payment method. + + /// Note: Setting if_required requires that you handle successful confirmations + /// for redirect-based and non-redirect based payment methods separately. + /// When a non-redirect based payment method is successfully confirmed, + /// stripe.confirmSetup will resolve with a {setupIntent} object. + SetupConfirmationRedirect? redirect, + }) = _ConfirmSetupOptions; + + factory ConfirmSetupOptions.fromJson(Map json) => + _$ConfirmSetupOptionsFromJson(json); +} + +@freezed +class ConfirmSetupParams with _$ConfirmSetupParams { + const factory ConfirmSetupParams({ + /// The url your customer will be directed to after they complete authentication. + required String return_url, + + /// If collected previously, the ID of the ConfirmationToken to use to confirm this SetupIntent. + /// This is mutually exclusive with the elements parameter. + String? confirmation_token, + }) = _ConfirmSetupParams; + + factory ConfirmSetupParams.fromJson(Map json) => + _$ConfirmSetupParamsFromJson(json); +} + +/// By default, stripe.confirmPayment will always redirect to +/// your return_url after a successful confirmation. +/// If you set redirect: "if_required", then stripe.confirmPayment +/// will only redirect if your user chooses a redirect-based payment method. +enum SetupConfirmationRedirect { + always, + @JsonValue('if_required') + ifRequired, +} diff --git a/packages/stripe_js/lib/src/api/setup_intents/confirm_setup_options.freezed.dart b/packages/stripe_js/lib/src/api/setup_intents/confirm_setup_options.freezed.dart new file mode 100644 index 000000000..946b81e53 --- /dev/null +++ b/packages/stripe_js/lib/src/api/setup_intents/confirm_setup_options.freezed.dart @@ -0,0 +1,483 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'confirm_setup_options.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); + +ConfirmSetupOptions _$ConfirmSetupOptionsFromJson(Map json) { + return _ConfirmSetupOptions.fromJson(json); +} + +/// @nodoc +mixin _$ConfirmSetupOptions { + /// Required if you collect payment details before creating an Intent. It's always required if you don't provide a clientSecret. + @ElementsConverter() + Elements get elements => throw _privateConstructorUsedError; + + /// Required if you collect payment details before creating an Intent. + /// It's always required if you don't provide an elements instance containing a client secret. + String? get clientSecret => throw _privateConstructorUsedError; + + /// Parameters that will be passed on to the Stripe API. + /// Refer to the Payment Intents API for a full list of parameters. + ConfirmSetupParams get confirmParams => throw _privateConstructorUsedError; + + /// By default, stripe.confirmSetup will always redirect to your return_url + /// after a successful confirmation. If you set redirect: "if_required", + /// then stripe.confirmSetup will only redirect if your user chooses a + /// redirect-based payment method. + /// Note: Setting if_required requires that you handle successful confirmations + /// for redirect-based and non-redirect based payment methods separately. + /// When a non-redirect based payment method is successfully confirmed, + /// stripe.confirmSetup will resolve with a {setupIntent} object. + SetupConfirmationRedirect? get redirect => throw _privateConstructorUsedError; + + /// Serializes this ConfirmSetupOptions to a JSON map. + Map toJson() => throw _privateConstructorUsedError; + + /// Create a copy of ConfirmSetupOptions + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + $ConfirmSetupOptionsCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $ConfirmSetupOptionsCopyWith<$Res> { + factory $ConfirmSetupOptionsCopyWith( + ConfirmSetupOptions value, $Res Function(ConfirmSetupOptions) then) = + _$ConfirmSetupOptionsCopyWithImpl<$Res, ConfirmSetupOptions>; + @useResult + $Res call( + {@ElementsConverter() Elements elements, + String? clientSecret, + ConfirmSetupParams confirmParams, + SetupConfirmationRedirect? redirect}); + + $ConfirmSetupParamsCopyWith<$Res> get confirmParams; +} + +/// @nodoc +class _$ConfirmSetupOptionsCopyWithImpl<$Res, $Val extends ConfirmSetupOptions> + implements $ConfirmSetupOptionsCopyWith<$Res> { + _$ConfirmSetupOptionsCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + /// Create a copy of ConfirmSetupOptions + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? elements = null, + Object? clientSecret = freezed, + Object? confirmParams = null, + Object? redirect = freezed, + }) { + return _then(_value.copyWith( + elements: null == elements + ? _value.elements + : elements // ignore: cast_nullable_to_non_nullable + as Elements, + clientSecret: freezed == clientSecret + ? _value.clientSecret + : clientSecret // ignore: cast_nullable_to_non_nullable + as String?, + confirmParams: null == confirmParams + ? _value.confirmParams + : confirmParams // ignore: cast_nullable_to_non_nullable + as ConfirmSetupParams, + redirect: freezed == redirect + ? _value.redirect + : redirect // ignore: cast_nullable_to_non_nullable + as SetupConfirmationRedirect?, + ) as $Val); + } + + /// Create a copy of ConfirmSetupOptions + /// with the given fields replaced by the non-null parameter values. + @override + @pragma('vm:prefer-inline') + $ConfirmSetupParamsCopyWith<$Res> get confirmParams { + return $ConfirmSetupParamsCopyWith<$Res>(_value.confirmParams, (value) { + return _then(_value.copyWith(confirmParams: value) as $Val); + }); + } +} + +/// @nodoc +abstract class _$$ConfirmSetupOptionsImplCopyWith<$Res> + implements $ConfirmSetupOptionsCopyWith<$Res> { + factory _$$ConfirmSetupOptionsImplCopyWith(_$ConfirmSetupOptionsImpl value, + $Res Function(_$ConfirmSetupOptionsImpl) then) = + __$$ConfirmSetupOptionsImplCopyWithImpl<$Res>; + @override + @useResult + $Res call( + {@ElementsConverter() Elements elements, + String? clientSecret, + ConfirmSetupParams confirmParams, + SetupConfirmationRedirect? redirect}); + + @override + $ConfirmSetupParamsCopyWith<$Res> get confirmParams; +} + +/// @nodoc +class __$$ConfirmSetupOptionsImplCopyWithImpl<$Res> + extends _$ConfirmSetupOptionsCopyWithImpl<$Res, _$ConfirmSetupOptionsImpl> + implements _$$ConfirmSetupOptionsImplCopyWith<$Res> { + __$$ConfirmSetupOptionsImplCopyWithImpl(_$ConfirmSetupOptionsImpl _value, + $Res Function(_$ConfirmSetupOptionsImpl) _then) + : super(_value, _then); + + /// Create a copy of ConfirmSetupOptions + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? elements = null, + Object? clientSecret = freezed, + Object? confirmParams = null, + Object? redirect = freezed, + }) { + return _then(_$ConfirmSetupOptionsImpl( + elements: null == elements + ? _value.elements + : elements // ignore: cast_nullable_to_non_nullable + as Elements, + clientSecret: freezed == clientSecret + ? _value.clientSecret + : clientSecret // ignore: cast_nullable_to_non_nullable + as String?, + confirmParams: null == confirmParams + ? _value.confirmParams + : confirmParams // ignore: cast_nullable_to_non_nullable + as ConfirmSetupParams, + redirect: freezed == redirect + ? _value.redirect + : redirect // ignore: cast_nullable_to_non_nullable + as SetupConfirmationRedirect?, + )); + } +} + +/// @nodoc +@JsonSerializable() +class _$ConfirmSetupOptionsImpl implements _ConfirmSetupOptions { + const _$ConfirmSetupOptionsImpl( + {@ElementsConverter() required this.elements, + this.clientSecret, + required this.confirmParams, + this.redirect}); + + factory _$ConfirmSetupOptionsImpl.fromJson(Map json) => + _$$ConfirmSetupOptionsImplFromJson(json); + + /// Required if you collect payment details before creating an Intent. It's always required if you don't provide a clientSecret. + @override + @ElementsConverter() + final Elements elements; + + /// Required if you collect payment details before creating an Intent. + /// It's always required if you don't provide an elements instance containing a client secret. + @override + final String? clientSecret; + + /// Parameters that will be passed on to the Stripe API. + /// Refer to the Payment Intents API for a full list of parameters. + @override + final ConfirmSetupParams confirmParams; + + /// By default, stripe.confirmSetup will always redirect to your return_url + /// after a successful confirmation. If you set redirect: "if_required", + /// then stripe.confirmSetup will only redirect if your user chooses a + /// redirect-based payment method. + /// Note: Setting if_required requires that you handle successful confirmations + /// for redirect-based and non-redirect based payment methods separately. + /// When a non-redirect based payment method is successfully confirmed, + /// stripe.confirmSetup will resolve with a {setupIntent} object. + @override + final SetupConfirmationRedirect? redirect; + + @override + String toString() { + return 'ConfirmSetupOptions(elements: $elements, clientSecret: $clientSecret, confirmParams: $confirmParams, redirect: $redirect)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$ConfirmSetupOptionsImpl && + (identical(other.elements, elements) || + other.elements == elements) && + (identical(other.clientSecret, clientSecret) || + other.clientSecret == clientSecret) && + (identical(other.confirmParams, confirmParams) || + other.confirmParams == confirmParams) && + (identical(other.redirect, redirect) || + other.redirect == redirect)); + } + + @JsonKey(includeFromJson: false, includeToJson: false) + @override + int get hashCode => + Object.hash(runtimeType, elements, clientSecret, confirmParams, redirect); + + /// Create a copy of ConfirmSetupOptions + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + @override + @pragma('vm:prefer-inline') + _$$ConfirmSetupOptionsImplCopyWith<_$ConfirmSetupOptionsImpl> get copyWith => + __$$ConfirmSetupOptionsImplCopyWithImpl<_$ConfirmSetupOptionsImpl>( + this, _$identity); + + @override + Map toJson() { + return _$$ConfirmSetupOptionsImplToJson( + this, + ); + } +} + +abstract class _ConfirmSetupOptions implements ConfirmSetupOptions { + const factory _ConfirmSetupOptions( + {@ElementsConverter() required final Elements elements, + final String? clientSecret, + required final ConfirmSetupParams confirmParams, + final SetupConfirmationRedirect? redirect}) = _$ConfirmSetupOptionsImpl; + + factory _ConfirmSetupOptions.fromJson(Map json) = + _$ConfirmSetupOptionsImpl.fromJson; + + /// Required if you collect payment details before creating an Intent. It's always required if you don't provide a clientSecret. + @override + @ElementsConverter() + Elements get elements; + + /// Required if you collect payment details before creating an Intent. + /// It's always required if you don't provide an elements instance containing a client secret. + @override + String? get clientSecret; + + /// Parameters that will be passed on to the Stripe API. + /// Refer to the Payment Intents API for a full list of parameters. + @override + ConfirmSetupParams get confirmParams; + + /// By default, stripe.confirmSetup will always redirect to your return_url + /// after a successful confirmation. If you set redirect: "if_required", + /// then stripe.confirmSetup will only redirect if your user chooses a + /// redirect-based payment method. + /// Note: Setting if_required requires that you handle successful confirmations + /// for redirect-based and non-redirect based payment methods separately. + /// When a non-redirect based payment method is successfully confirmed, + /// stripe.confirmSetup will resolve with a {setupIntent} object. + @override + SetupConfirmationRedirect? get redirect; + + /// Create a copy of ConfirmSetupOptions + /// with the given fields replaced by the non-null parameter values. + @override + @JsonKey(includeFromJson: false, includeToJson: false) + _$$ConfirmSetupOptionsImplCopyWith<_$ConfirmSetupOptionsImpl> get copyWith => + throw _privateConstructorUsedError; +} + +ConfirmSetupParams _$ConfirmSetupParamsFromJson(Map json) { + return _ConfirmSetupParams.fromJson(json); +} + +/// @nodoc +mixin _$ConfirmSetupParams { + /// The url your customer will be directed to after they complete authentication. + String get return_url => throw _privateConstructorUsedError; + + /// If collected previously, the ID of the ConfirmationToken to use to confirm this SetupIntent. + /// This is mutually exclusive with the elements parameter. + String? get confirmation_token => throw _privateConstructorUsedError; + + /// Serializes this ConfirmSetupParams to a JSON map. + Map toJson() => throw _privateConstructorUsedError; + + /// Create a copy of ConfirmSetupParams + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + $ConfirmSetupParamsCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $ConfirmSetupParamsCopyWith<$Res> { + factory $ConfirmSetupParamsCopyWith( + ConfirmSetupParams value, $Res Function(ConfirmSetupParams) then) = + _$ConfirmSetupParamsCopyWithImpl<$Res, ConfirmSetupParams>; + @useResult + $Res call({String return_url, String? confirmation_token}); +} + +/// @nodoc +class _$ConfirmSetupParamsCopyWithImpl<$Res, $Val extends ConfirmSetupParams> + implements $ConfirmSetupParamsCopyWith<$Res> { + _$ConfirmSetupParamsCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + /// Create a copy of ConfirmSetupParams + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? return_url = null, + Object? confirmation_token = freezed, + }) { + return _then(_value.copyWith( + return_url: null == return_url + ? _value.return_url + : return_url // ignore: cast_nullable_to_non_nullable + as String, + confirmation_token: freezed == confirmation_token + ? _value.confirmation_token + : confirmation_token // ignore: cast_nullable_to_non_nullable + as String?, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$ConfirmSetupParamsImplCopyWith<$Res> + implements $ConfirmSetupParamsCopyWith<$Res> { + factory _$$ConfirmSetupParamsImplCopyWith(_$ConfirmSetupParamsImpl value, + $Res Function(_$ConfirmSetupParamsImpl) then) = + __$$ConfirmSetupParamsImplCopyWithImpl<$Res>; + @override + @useResult + $Res call({String return_url, String? confirmation_token}); +} + +/// @nodoc +class __$$ConfirmSetupParamsImplCopyWithImpl<$Res> + extends _$ConfirmSetupParamsCopyWithImpl<$Res, _$ConfirmSetupParamsImpl> + implements _$$ConfirmSetupParamsImplCopyWith<$Res> { + __$$ConfirmSetupParamsImplCopyWithImpl(_$ConfirmSetupParamsImpl _value, + $Res Function(_$ConfirmSetupParamsImpl) _then) + : super(_value, _then); + + /// Create a copy of ConfirmSetupParams + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? return_url = null, + Object? confirmation_token = freezed, + }) { + return _then(_$ConfirmSetupParamsImpl( + return_url: null == return_url + ? _value.return_url + : return_url // ignore: cast_nullable_to_non_nullable + as String, + confirmation_token: freezed == confirmation_token + ? _value.confirmation_token + : confirmation_token // ignore: cast_nullable_to_non_nullable + as String?, + )); + } +} + +/// @nodoc +@JsonSerializable() +class _$ConfirmSetupParamsImpl implements _ConfirmSetupParams { + const _$ConfirmSetupParamsImpl( + {required this.return_url, this.confirmation_token}); + + factory _$ConfirmSetupParamsImpl.fromJson(Map json) => + _$$ConfirmSetupParamsImplFromJson(json); + + /// The url your customer will be directed to after they complete authentication. + @override + final String return_url; + + /// If collected previously, the ID of the ConfirmationToken to use to confirm this SetupIntent. + /// This is mutually exclusive with the elements parameter. + @override + final String? confirmation_token; + + @override + String toString() { + return 'ConfirmSetupParams(return_url: $return_url, confirmation_token: $confirmation_token)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$ConfirmSetupParamsImpl && + (identical(other.return_url, return_url) || + other.return_url == return_url) && + (identical(other.confirmation_token, confirmation_token) || + other.confirmation_token == confirmation_token)); + } + + @JsonKey(includeFromJson: false, includeToJson: false) + @override + int get hashCode => Object.hash(runtimeType, return_url, confirmation_token); + + /// Create a copy of ConfirmSetupParams + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + @override + @pragma('vm:prefer-inline') + _$$ConfirmSetupParamsImplCopyWith<_$ConfirmSetupParamsImpl> get copyWith => + __$$ConfirmSetupParamsImplCopyWithImpl<_$ConfirmSetupParamsImpl>( + this, _$identity); + + @override + Map toJson() { + return _$$ConfirmSetupParamsImplToJson( + this, + ); + } +} + +abstract class _ConfirmSetupParams implements ConfirmSetupParams { + const factory _ConfirmSetupParams( + {required final String return_url, + final String? confirmation_token}) = _$ConfirmSetupParamsImpl; + + factory _ConfirmSetupParams.fromJson(Map json) = + _$ConfirmSetupParamsImpl.fromJson; + + /// The url your customer will be directed to after they complete authentication. + @override + String get return_url; + + /// If collected previously, the ID of the ConfirmationToken to use to confirm this SetupIntent. + /// This is mutually exclusive with the elements parameter. + @override + String? get confirmation_token; + + /// Create a copy of ConfirmSetupParams + /// with the given fields replaced by the non-null parameter values. + @override + @JsonKey(includeFromJson: false, includeToJson: false) + _$$ConfirmSetupParamsImplCopyWith<_$ConfirmSetupParamsImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/packages/stripe_js/lib/src/api/setup_intents/confirm_setup_options.g.dart b/packages/stripe_js/lib/src/api/setup_intents/confirm_setup_options.g.dart new file mode 100644 index 000000000..fde890904 --- /dev/null +++ b/packages/stripe_js/lib/src/api/setup_intents/confirm_setup_options.g.dart @@ -0,0 +1,62 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'confirm_setup_options.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_$ConfirmSetupOptionsImpl _$$ConfirmSetupOptionsImplFromJson(Map json) => + _$ConfirmSetupOptionsImpl( + elements: const ElementsConverter().fromJson(json['elements']), + clientSecret: json['clientSecret'] as String?, + confirmParams: ConfirmSetupParams.fromJson( + Map.from(json['confirmParams'] as Map)), + redirect: $enumDecodeNullable( + _$SetupConfirmationRedirectEnumMap, json['redirect']), + ); + +Map _$$ConfirmSetupOptionsImplToJson( + _$ConfirmSetupOptionsImpl instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('elements', const ElementsConverter().toJson(instance.elements)); + writeNotNull('clientSecret', instance.clientSecret); + val['confirmParams'] = instance.confirmParams.toJson(); + writeNotNull( + 'redirect', _$SetupConfirmationRedirectEnumMap[instance.redirect]); + return val; +} + +const _$SetupConfirmationRedirectEnumMap = { + SetupConfirmationRedirect.always: 'always', + SetupConfirmationRedirect.ifRequired: 'if_required', +}; + +_$ConfirmSetupParamsImpl _$$ConfirmSetupParamsImplFromJson(Map json) => + _$ConfirmSetupParamsImpl( + return_url: json['return_url'] as String, + confirmation_token: json['confirmation_token'] as String?, + ); + +Map _$$ConfirmSetupParamsImplToJson( + _$ConfirmSetupParamsImpl instance) { + final val = { + 'return_url': instance.return_url, + }; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('confirmation_token', instance.confirmation_token); + return val; +} diff --git a/packages/stripe_js/lib/src/js/setup_intents/confirm_setup.dart b/packages/stripe_js/lib/src/js/setup_intents/confirm_setup.dart index 95857ba58..dc20fe5cb 100644 --- a/packages/stripe_js/lib/src/js/setup_intents/confirm_setup.dart +++ b/packages/stripe_js/lib/src/js/setup_intents/confirm_setup.dart @@ -1,25 +1,31 @@ -import 'package:stripe_js/stripe_api.dart'; -import 'package:stripe_js/stripe_js.dart'; -import '../utils/utils.dart'; +import 'dart:async'; import 'dart:js_interop'; -extension ExtensionConfirmCardSetup on Stripe { - Future confirmSetup( - String clientSecret, { - ConfirmCardSetupData? data, - ConfirmCardSetupOptions? options, - }) { - final jsData = (data?.toJson() ?? {}).jsify(); - final jsOptions = (options?.toJson() ?? {}).jsify(); - return _confirmCardSetup(clientSecret, jsData, jsOptions) +import 'package:stripe_js/src/api/setup_intents/confirm_setup_options.dart'; +import 'package:stripe_js/stripe_js.dart'; + +extension ExtensionConfirmPayment on Stripe { + /// Use stripe.confirmSetup to confirm a SetupIntent using data collected + /// by the Payment Element, or with manually provided data via confirmParams. + /// When called, stripe.confirmSetup will attempt to complete any required actions, + /// such as authenticating your user by displaying a 3DS dialog or redirecting + /// them to a bank authorization page. Your user will be redirected to the + /// return_url you pass once the authorization is complete. + /// + /// [confirmSetup] will return a Future. Upon a successful authorization, + /// your user will be redirected to the return_url you provide + /// before the Future ever resolves. + /// see: https://docs.stripe.com/js/setup_intents/confirm_setup + Future confirmSetup( + ConfirmSetupOptions options, + ) async { + final completer = Completer(); + _confirmSetup(options.toJson().jsify()) .toDart - .then((response) => response.toDart); + .then((response) => completer.complete()); + return completer.future; } @JS('confirmSetup') - external JSPromise _confirmCardSetup( - String clientSecret, [ - JSAny? data, - JSAny? options, - ]); + external JSPromise _confirmSetup(JSAny? options); } From 3f5f32a52094935d2721fba542d48c1be0f91e3f Mon Sep 17 00:00:00 2001 From: cedvandenbosch Date: Sun, 13 Oct 2024 00:06:01 +0200 Subject: [PATCH 03/16] confirm setup --- packages/stripe_js/lib/src/js/setup_intents/setup_intents.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/stripe_js/lib/src/js/setup_intents/setup_intents.dart b/packages/stripe_js/lib/src/js/setup_intents/setup_intents.dart index d36c015cd..a51b8c88b 100644 --- a/packages/stripe_js/lib/src/js/setup_intents/setup_intents.dart +++ b/packages/stripe_js/lib/src/js/setup_intents/setup_intents.dart @@ -1,3 +1,4 @@ export 'confirm_card_setup.dart'; export 'confirm_sepa_debit_setup.dart'; export 'retrieve_setup_intent.dart'; +export 'confirm_setup.dart'; From cb707ec7e989d4bfa35c5e15f9c57f0e59818c79 Mon Sep 17 00:00:00 2001 From: cedvandenbosch Date: Sun, 13 Oct 2024 13:23:22 +0200 Subject: [PATCH 04/16] add confirm to api --- .../src/api/setup_intents/setup_intents.dart | 1 + .../src/js/setup_intents/confirm_setup.dart | 15 +- .../confirm_payment_options.freezed.dart | 32 ++- .../lib/src/models/confirm_setup_options.dart | 25 ++ .../models/confirm_setup_options.freezed.dart | 251 ++++++++++++++++++ .../src/models/confirm_setup_options.g.dart | 38 +++ .../stripe_web/lib/src/models/models.dart | 1 + packages/stripe_web/lib/src/web_stripe.dart | 17 ++ 8 files changed, 363 insertions(+), 17 deletions(-) create mode 100644 packages/stripe_web/lib/src/models/confirm_setup_options.dart create mode 100644 packages/stripe_web/lib/src/models/confirm_setup_options.freezed.dart create mode 100644 packages/stripe_web/lib/src/models/confirm_setup_options.g.dart diff --git a/packages/stripe_js/lib/src/api/setup_intents/setup_intents.dart b/packages/stripe_js/lib/src/api/setup_intents/setup_intents.dart index 88bc759a7..5db90e391 100644 --- a/packages/stripe_js/lib/src/api/setup_intents/setup_intents.dart +++ b/packages/stripe_js/lib/src/api/setup_intents/setup_intents.dart @@ -4,3 +4,4 @@ export 'confirm_sepa_debit_setup_data.dart'; export 'setup_intent.dart'; export 'setup_intent_response.dart'; export 'setup_intent_status.dart'; +export 'confirm_setup_options.dart'; diff --git a/packages/stripe_js/lib/src/js/setup_intents/confirm_setup.dart b/packages/stripe_js/lib/src/js/setup_intents/confirm_setup.dart index dc20fe5cb..c66ec511c 100644 --- a/packages/stripe_js/lib/src/js/setup_intents/confirm_setup.dart +++ b/packages/stripe_js/lib/src/js/setup_intents/confirm_setup.dart @@ -1,10 +1,11 @@ import 'dart:async'; import 'dart:js_interop'; -import 'package:stripe_js/src/api/setup_intents/confirm_setup_options.dart'; +import 'package:stripe_js/src/js/utils/utils.dart'; +import 'package:stripe_js/stripe_api.dart'; import 'package:stripe_js/stripe_js.dart'; -extension ExtensionConfirmPayment on Stripe { +extension ExtensionConfirmSetup on Stripe { /// Use stripe.confirmSetup to confirm a SetupIntent using data collected /// by the Payment Element, or with manually provided data via confirmParams. /// When called, stripe.confirmSetup will attempt to complete any required actions, @@ -16,16 +17,14 @@ extension ExtensionConfirmPayment on Stripe { /// your user will be redirected to the return_url you provide /// before the Future ever resolves. /// see: https://docs.stripe.com/js/setup_intents/confirm_setup - Future confirmSetup( + Future confirmSetup( ConfirmSetupOptions options, ) async { - final completer = Completer(); - _confirmSetup(options.toJson().jsify()) + return _confirmSetup(options.toJson().jsify()) .toDart - .then((response) => completer.complete()); - return completer.future; + .then((response) => response.toDart); } @JS('confirmSetup') - external JSPromise _confirmSetup(JSAny? options); + external JSPromise _confirmSetup(JSAny? options); } diff --git a/packages/stripe_web/lib/src/models/confirm_payment_options.freezed.dart b/packages/stripe_web/lib/src/models/confirm_payment_options.freezed.dart index 8f3cca8be..fcbf0a786 100644 --- a/packages/stripe_web/lib/src/models/confirm_payment_options.freezed.dart +++ b/packages/stripe_web/lib/src/models/confirm_payment_options.freezed.dart @@ -12,7 +12,7 @@ part of 'confirm_payment_options.dart'; T _$identity(T value) => value; final _privateConstructorUsedError = UnsupportedError( - 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); ConfirmPaymentElementOptions _$ConfirmPaymentElementOptionsFromJson( Map json) { @@ -38,8 +38,12 @@ mixin _$ConfirmPaymentElementOptions { PaymentConfirmationRedirect? get redirect => throw _privateConstructorUsedError; + /// Serializes this ConfirmPaymentElementOptions to a JSON map. Map toJson() => throw _privateConstructorUsedError; - @JsonKey(ignore: true) + + /// Create a copy of ConfirmPaymentElementOptions + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) $ConfirmPaymentElementOptionsCopyWith get copyWith => throw _privateConstructorUsedError; } @@ -70,6 +74,8 @@ class _$ConfirmPaymentElementOptionsCopyWithImpl<$Res, // ignore: unused_field final $Res Function($Val) _then; + /// Create a copy of ConfirmPaymentElementOptions + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -88,6 +94,8 @@ class _$ConfirmPaymentElementOptionsCopyWithImpl<$Res, ) as $Val); } + /// Create a copy of ConfirmPaymentElementOptions + /// with the given fields replaced by the non-null parameter values. @override @pragma('vm:prefer-inline') $ConfirmPaymentParamsCopyWith<$Res> get confirmParams { @@ -124,6 +132,8 @@ class __$$ConfirmPaymentElementOptionsImplCopyWithImpl<$Res> $Res Function(_$ConfirmPaymentElementOptionsImpl) _then) : super(_value, _then); + /// Create a copy of ConfirmPaymentElementOptions + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -178,7 +188,7 @@ class _$ConfirmPaymentElementOptionsImpl } @override - bool operator ==(dynamic other) { + bool operator ==(Object other) { return identical(this, other) || (other.runtimeType == runtimeType && other is _$ConfirmPaymentElementOptionsImpl && @@ -188,11 +198,13 @@ class _$ConfirmPaymentElementOptionsImpl other.redirect == redirect)); } - @JsonKey(ignore: true) + @JsonKey(includeFromJson: false, includeToJson: false) @override int get hashCode => Object.hash(runtimeType, confirmParams, redirect); - @JsonKey(ignore: true) + /// Create a copy of ConfirmPaymentElementOptions + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) @override @pragma('vm:prefer-inline') _$$ConfirmPaymentElementOptionsImplCopyWith< @@ -218,12 +230,10 @@ abstract class _ConfirmPaymentElementOptions factory _ConfirmPaymentElementOptions.fromJson(Map json) = _$ConfirmPaymentElementOptionsImpl.fromJson; - @override - /// Parameters that will be passed on to the Stripe API. /// Refer to the Payment Intents API for a full list of parameters. - ConfirmPaymentParams get confirmParams; @override + ConfirmPaymentParams get confirmParams; /// By default, stripe.confirmPayment will always redirect to your /// return_url after a successful confirmation. @@ -235,9 +245,13 @@ abstract class _ConfirmPaymentElementOptions /// methods separately. When a non-redirect based payment method is /// successfully confirmed, stripe.confirmPayment will resolve with a /// {paymentIntent} object. + @override PaymentConfirmationRedirect? get redirect; + + /// Create a copy of ConfirmPaymentElementOptions + /// with the given fields replaced by the non-null parameter values. @override - @JsonKey(ignore: true) + @JsonKey(includeFromJson: false, includeToJson: false) _$$ConfirmPaymentElementOptionsImplCopyWith< _$ConfirmPaymentElementOptionsImpl> get copyWith => throw _privateConstructorUsedError; diff --git a/packages/stripe_web/lib/src/models/confirm_setup_options.dart b/packages/stripe_web/lib/src/models/confirm_setup_options.dart new file mode 100644 index 000000000..ade2b98e2 --- /dev/null +++ b/packages/stripe_web/lib/src/models/confirm_setup_options.dart @@ -0,0 +1,25 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; +import 'package:stripe_js/stripe_api.dart'; +export 'package:stripe_js/stripe_api.dart' + show PaymentConfirmationRedirect, ConfirmPaymentParams; + +part 'confirm_setup_options.freezed.dart'; +part 'confirm_setup_options.g.dart'; + +@freezed +class ConfirmSetupElementOptions with _$ConfirmSetupElementOptions { + const factory ConfirmSetupElementOptions({ + /// Parameters that will be passed on to the Stripe API. + /// Refer to the Payment Intents API for a full list of parameters. + required ConfirmSetupParams confirmParams, + + /// By default, stripe.confirmPayment will always redirect to + /// your return_url after a successful confirmation. + /// If you set redirect: "if_required", then stripe.confirmPayment + /// will only redirect if your user chooses a redirect-based payment method. + SetupConfirmationRedirect? redirect, + }) = _SetupPaymentElementOptions; + + factory ConfirmSetupElementOptions.fromJson(Map json) => + _$ConfirmSetupElementOptionsFromJson(json); +} diff --git a/packages/stripe_web/lib/src/models/confirm_setup_options.freezed.dart b/packages/stripe_web/lib/src/models/confirm_setup_options.freezed.dart new file mode 100644 index 000000000..a5fb605bd --- /dev/null +++ b/packages/stripe_web/lib/src/models/confirm_setup_options.freezed.dart @@ -0,0 +1,251 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'confirm_setup_options.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); + +ConfirmSetupElementOptions _$ConfirmSetupElementOptionsFromJson( + Map json) { + return _SetupPaymentElementOptions.fromJson(json); +} + +/// @nodoc +mixin _$ConfirmSetupElementOptions { + /// Parameters that will be passed on to the Stripe API. + /// Refer to the Setup Intents API for a full list of parameters. + ConfirmSetupParams get confirmParams => throw _privateConstructorUsedError; + + /// By default, stripe.confirmPayment will always redirect to your + /// return_url after a successful confirmation. + /// If you set redirect: "if_required", then stripe.confirmPayment will + /// only redirect if your user chooses a redirect-based payment method. + /// + /// Note: Setting if_required requires that you handle successful + /// confirmations for redirect-based and non-redirect based payment + /// methods separately. When a non-redirect based payment method is + /// successfully confirmed, stripe.confirmPayment will resolve with a + /// {paymentIntent} object. + SetupConfirmationRedirect? get redirect => throw _privateConstructorUsedError; + + /// Serializes this ConfirmSetupElementOptions to a JSON map. + Map toJson() => throw _privateConstructorUsedError; + + /// Create a copy of ConfirmSetupElementOptions + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + $ConfirmSetupElementOptionsCopyWith + get copyWith => throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $ConfirmSetupElementOptionsCopyWith<$Res> { + factory $ConfirmSetupElementOptionsCopyWith(ConfirmSetupElementOptions value, + $Res Function(ConfirmSetupElementOptions) then) = + _$ConfirmSetupElementOptionsCopyWithImpl<$Res, + ConfirmSetupElementOptions>; + @useResult + $Res call( + {ConfirmSetupParams confirmParams, SetupConfirmationRedirect? redirect}); + + $ConfirmSetupParamsCopyWith<$Res> get confirmParams; +} + +/// @nodoc +class _$ConfirmSetupElementOptionsCopyWithImpl<$Res, + $Val extends ConfirmSetupElementOptions> + implements $ConfirmSetupElementOptionsCopyWith<$Res> { + _$ConfirmSetupElementOptionsCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + /// Create a copy of ConfirmSetupElementOptions + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? confirmParams = null, + Object? redirect = freezed, + }) { + return _then(_value.copyWith( + confirmParams: null == confirmParams + ? _value.confirmParams + : confirmParams // ignore: cast_nullable_to_non_nullable + as ConfirmSetupParams, + redirect: freezed == redirect + ? _value.redirect + : redirect // ignore: cast_nullable_to_non_nullable + as SetupConfirmationRedirect?, + ) as $Val); + } + + /// Create a copy of ConfirmSetupElementOptions + /// with the given fields replaced by the non-null parameter values. + @override + @pragma('vm:prefer-inline') + $ConfirmSetupParamsCopyWith<$Res> get confirmParams { + return $ConfirmSetupParamsCopyWith<$Res>(_value.confirmParams, (value) { + return _then(_value.copyWith(confirmParams: value) as $Val); + }); + } +} + +/// @nodoc +abstract class _$$SetupPaymentElementOptionsImplCopyWith<$Res> + implements $ConfirmSetupElementOptionsCopyWith<$Res> { + factory _$$SetupPaymentElementOptionsImplCopyWith( + _$SetupPaymentElementOptionsImpl value, + $Res Function(_$SetupPaymentElementOptionsImpl) then) = + __$$SetupPaymentElementOptionsImplCopyWithImpl<$Res>; + @override + @useResult + $Res call( + {ConfirmSetupParams confirmParams, SetupConfirmationRedirect? redirect}); + + @override + $ConfirmSetupParamsCopyWith<$Res> get confirmParams; +} + +/// @nodoc +class __$$SetupPaymentElementOptionsImplCopyWithImpl<$Res> + extends _$ConfirmSetupElementOptionsCopyWithImpl<$Res, + _$SetupPaymentElementOptionsImpl> + implements _$$SetupPaymentElementOptionsImplCopyWith<$Res> { + __$$SetupPaymentElementOptionsImplCopyWithImpl( + _$SetupPaymentElementOptionsImpl _value, + $Res Function(_$SetupPaymentElementOptionsImpl) _then) + : super(_value, _then); + + /// Create a copy of ConfirmSetupElementOptions + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? confirmParams = null, + Object? redirect = freezed, + }) { + return _then(_$SetupPaymentElementOptionsImpl( + confirmParams: null == confirmParams + ? _value.confirmParams + : confirmParams // ignore: cast_nullable_to_non_nullable + as ConfirmSetupParams, + redirect: freezed == redirect + ? _value.redirect + : redirect // ignore: cast_nullable_to_non_nullable + as SetupConfirmationRedirect?, + )); + } +} + +/// @nodoc +@JsonSerializable() +class _$SetupPaymentElementOptionsImpl implements _SetupPaymentElementOptions { + const _$SetupPaymentElementOptionsImpl( + {required this.confirmParams, this.redirect}); + + factory _$SetupPaymentElementOptionsImpl.fromJson( + Map json) => + _$$SetupPaymentElementOptionsImplFromJson(json); + + /// Parameters that will be passed on to the Stripe API. + /// Refer to the Setup Intents API for a full list of parameters. + @override + final ConfirmSetupParams confirmParams; + + /// By default, stripe.confirmPayment will always redirect to your + /// return_url after a successful confirmation. + /// If you set redirect: "if_required", then stripe.confirmPayment will + /// only redirect if your user chooses a redirect-based payment method. + /// + /// Note: Setting if_required requires that you handle successful + /// confirmations for redirect-based and non-redirect based payment + /// methods separately. When a non-redirect based payment method is + /// successfully confirmed, stripe.confirmPayment will resolve with a + /// {paymentIntent} object. + @override + final SetupConfirmationRedirect? redirect; + + @override + String toString() { + return 'ConfirmSetupElementOptions(confirmParams: $confirmParams, redirect: $redirect)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$SetupPaymentElementOptionsImpl && + (identical(other.confirmParams, confirmParams) || + other.confirmParams == confirmParams) && + (identical(other.redirect, redirect) || + other.redirect == redirect)); + } + + @JsonKey(includeFromJson: false, includeToJson: false) + @override + int get hashCode => Object.hash(runtimeType, confirmParams, redirect); + + /// Create a copy of ConfirmSetupElementOptions + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + @override + @pragma('vm:prefer-inline') + _$$SetupPaymentElementOptionsImplCopyWith<_$SetupPaymentElementOptionsImpl> + get copyWith => __$$SetupPaymentElementOptionsImplCopyWithImpl< + _$SetupPaymentElementOptionsImpl>(this, _$identity); + + @override + Map toJson() { + return _$$SetupPaymentElementOptionsImplToJson( + this, + ); + } +} + +abstract class _SetupPaymentElementOptions + implements ConfirmSetupElementOptions { + const factory _SetupPaymentElementOptions( + {required final ConfirmSetupParams confirmParams, + final SetupConfirmationRedirect? redirect}) = + _$SetupPaymentElementOptionsImpl; + + factory _SetupPaymentElementOptions.fromJson(Map json) = + _$SetupPaymentElementOptionsImpl.fromJson; + + /// Parameters that will be passed on to the Stripe API. + /// Refer to the Setup Intents API for a full list of parameters. + @override + ConfirmSetupParams get confirmParams; + + /// By default, stripe.confirmPayment will always redirect to your + /// return_url after a successful confirmation. + /// If you set redirect: "if_required", then stripe.confirmPayment will + /// only redirect if your user chooses a redirect-based payment method. + /// + /// Note: Setting if_required requires that you handle successful + /// confirmations for redirect-based and non-redirect based payment + /// methods separately. When a non-redirect based payment method is + /// successfully confirmed, stripe.confirmPayment will resolve with a + /// {paymentIntent} object. + @override + SetupConfirmationRedirect? get redirect; + + /// Create a copy of ConfirmSetupElementOptions + /// with the given fields replaced by the non-null parameter values. + @override + @JsonKey(includeFromJson: false, includeToJson: false) + _$$SetupPaymentElementOptionsImplCopyWith<_$SetupPaymentElementOptionsImpl> + get copyWith => throw _privateConstructorUsedError; +} diff --git a/packages/stripe_web/lib/src/models/confirm_setup_options.g.dart b/packages/stripe_web/lib/src/models/confirm_setup_options.g.dart new file mode 100644 index 000000000..39911f9d0 --- /dev/null +++ b/packages/stripe_web/lib/src/models/confirm_setup_options.g.dart @@ -0,0 +1,38 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'confirm_setup_options.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_$SetupPaymentElementOptionsImpl _$$SetupPaymentElementOptionsImplFromJson( + Map json) => + _$SetupPaymentElementOptionsImpl( + confirmParams: ConfirmSetupParams.fromJson( + Map.from(json['confirmParams'] as Map)), + redirect: $enumDecodeNullable( + _$SetupConfirmationRedirectEnumMap, json['redirect']), + ); + +Map _$$SetupPaymentElementOptionsImplToJson( + _$SetupPaymentElementOptionsImpl instance) { + final val = { + 'confirmParams': instance.confirmParams.toJson(), + }; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull( + 'redirect', _$SetupConfirmationRedirectEnumMap[instance.redirect]); + return val; +} + +const _$SetupConfirmationRedirectEnumMap = { + SetupConfirmationRedirect.always: 'always', + SetupConfirmationRedirect.ifRequired: 'if_required', +}; diff --git a/packages/stripe_web/lib/src/models/models.dart b/packages/stripe_web/lib/src/models/models.dart index 7b2dbb387..9c28e7d19 100644 --- a/packages/stripe_web/lib/src/models/models.dart +++ b/packages/stripe_web/lib/src/models/models.dart @@ -1 +1,2 @@ export 'confirm_payment_options.dart'; +export 'confirm_setup_options.dart'; diff --git a/packages/stripe_web/lib/src/web_stripe.dart b/packages/stripe_web/lib/src/web_stripe.dart index 2679d7dd9..51f0dfee7 100644 --- a/packages/stripe_web/lib/src/web_stripe.dart +++ b/packages/stripe_web/lib/src/web_stripe.dart @@ -409,6 +409,23 @@ class WebStripe extends StripePlatform { } } + Future confirmSetupElement( + ConfirmSetupElementOptions options, + ) async { + final response = await js.confirmSetup( + stripe_js.ConfirmSetupOptions( + elements: elements!, + confirmParams: options.confirmParams, + redirect: options.redirect, + ), + ); + if (response.error != null) { + throw response.error!; + } else { + return; + } + } + @override Widget buildCard({ Key? key, From f94365370646326b9bb595068e6d9bde92de8813 Mon Sep 17 00:00:00 2001 From: cedvandenbosch Date: Sun, 13 Oct 2024 13:34:47 +0200 Subject: [PATCH 05/16] wip --- packages/stripe_web/lib/src/models/confirm_setup_options.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/stripe_web/lib/src/models/confirm_setup_options.dart b/packages/stripe_web/lib/src/models/confirm_setup_options.dart index ade2b98e2..21ef60c9d 100644 --- a/packages/stripe_web/lib/src/models/confirm_setup_options.dart +++ b/packages/stripe_web/lib/src/models/confirm_setup_options.dart @@ -1,7 +1,7 @@ import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:stripe_js/stripe_api.dart'; export 'package:stripe_js/stripe_api.dart' - show PaymentConfirmationRedirect, ConfirmPaymentParams; + show SetupConfirmationRedirect, ConfirmSetupParams; part 'confirm_setup_options.freezed.dart'; part 'confirm_setup_options.g.dart'; From 869d1ee3951d2720ac6b0e7aed07c9a40dedd2cb Mon Sep 17 00:00:00 2001 From: cedvandenbosch Date: Sun, 13 Oct 2024 19:49:45 +0200 Subject: [PATCH 06/16] exports --- packages/stripe_web/lib/flutter_stripe_web.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/stripe_web/lib/flutter_stripe_web.dart b/packages/stripe_web/lib/flutter_stripe_web.dart index e975c0cc4..c4213e690 100644 --- a/packages/stripe_web/lib/flutter_stripe_web.dart +++ b/packages/stripe_web/lib/flutter_stripe_web.dart @@ -1,6 +1,7 @@ library stripe_web; -export 'package:stripe_js/stripe_api.dart' show ConfirmPaymentParams; +export 'package:stripe_js/stripe_api.dart' + show ConfirmPaymentParams, ConfirmSetupParams; export 'package:stripe_platform_interface/stripe_platform_interface.dart'; export 'src/models/models.dart'; From 9c3ad4d6945c0b4788049b26a2bc6431e671fc0a Mon Sep 17 00:00:00 2001 From: cedvandenbosch Date: Sun, 13 Oct 2024 21:19:26 +0200 Subject: [PATCH 07/16] wip --- packages/stripe_web/pubspec.yaml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/stripe_web/pubspec.yaml b/packages/stripe_web/pubspec.yaml index e0b7c2a86..370913499 100644 --- a/packages/stripe_web/pubspec.yaml +++ b/packages/stripe_web/pubspec.yaml @@ -1,6 +1,6 @@ name: flutter_stripe_web description: Stripe sdk bindings for the Flutter web platform. This package contains the implementation of the platform interface for web. -version: 6.2.0 +version: 6.2.1 homepage: https://github.com/flutter-stripe/flutter_stripe environment: @@ -14,7 +14,11 @@ dependencies: sdk: flutter freezed_annotation: ^2.0.3 stripe_platform_interface: ^11.2.0 - stripe_js: ^6.2.0 + stripe_js: + git: + url: git@github.com:cedvdb/flutter_stripe.git + path: packages/stripe_js + ref: setup_confirm web: ^1.0.0 dev_dependencies: From 38720d140383209ac5cae2210699b2ec941e214e Mon Sep 17 00:00:00 2001 From: cedvandenbosch Date: Mon, 14 Oct 2024 01:23:41 +0200 Subject: [PATCH 08/16] add_confirm_setup --- ...ress_checkout_element_options.freezed.dart | 3 + .../setup_intents/confirm_setup_options.dart | 62 +++ .../confirm_setup_options.freezed.dart | 483 ++++++++++++++++++ .../confirm_setup_options.g.dart | 62 +++ .../src/api/setup_intents/setup_intents.dart | 1 + .../src/js/setup_intents/confirm_setup.dart | 30 ++ .../src/js/setup_intents/setup_intents.dart | 1 + 7 files changed, 642 insertions(+) create mode 100644 packages/stripe_js/lib/src/api/setup_intents/confirm_setup_options.dart create mode 100644 packages/stripe_js/lib/src/api/setup_intents/confirm_setup_options.freezed.dart create mode 100644 packages/stripe_js/lib/src/api/setup_intents/confirm_setup_options.g.dart create mode 100644 packages/stripe_js/lib/src/js/setup_intents/confirm_setup.dart diff --git a/packages/stripe_js/lib/src/api/elements/express_checkout_element_options.freezed.dart b/packages/stripe_js/lib/src/api/elements/express_checkout_element_options.freezed.dart index f09569aad..7c51340fe 100644 --- a/packages/stripe_js/lib/src/api/elements/express_checkout_element_options.freezed.dart +++ b/packages/stripe_js/lib/src/api/elements/express_checkout_element_options.freezed.dart @@ -368,6 +368,7 @@ ExpressCheckoutConfirmEvent _$ExpressCheckoutConfirmEventFromJson( /// @nodoc mixin _$ExpressCheckoutConfirmEvent { + /// The method that was used to pay. String get expressPaymentType => throw _privateConstructorUsedError; BillingDetails? get billingDetails => throw _privateConstructorUsedError; @@ -497,6 +498,7 @@ class _$ExpressCheckoutConfirmEventImpl Map json) => _$$ExpressCheckoutConfirmEventImplFromJson(json); + /// The method that was used to pay. @override final String expressPaymentType; @override @@ -550,6 +552,7 @@ abstract class _ExpressCheckoutConfirmEvent factory _ExpressCheckoutConfirmEvent.fromJson(Map json) = _$ExpressCheckoutConfirmEventImpl.fromJson; + /// The method that was used to pay. @override String get expressPaymentType; @override diff --git a/packages/stripe_js/lib/src/api/setup_intents/confirm_setup_options.dart b/packages/stripe_js/lib/src/api/setup_intents/confirm_setup_options.dart new file mode 100644 index 000000000..f5e30deb5 --- /dev/null +++ b/packages/stripe_js/lib/src/api/setup_intents/confirm_setup_options.dart @@ -0,0 +1,62 @@ +import 'package:stripe_js/src/api/converters/js_converter.dart'; +import 'package:stripe_js/stripe_api.dart'; + +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'confirm_setup_options.freezed.dart'; +part 'confirm_setup_options.g.dart'; + +@freezed +class ConfirmSetupOptions with _$ConfirmSetupOptions { + const factory ConfirmSetupOptions({ + /// Required if you collect payment details before creating an Intent. It's always required if you don't provide a clientSecret. + @ElementsConverter() required Elements elements, + + /// Required if you collect payment details before creating an Intent. + /// It's always required if you don't provide an elements instance containing a client secret. + String? clientSecret, + + /// Parameters that will be passed on to the Stripe API. + /// Refer to the Payment Intents API for a full list of parameters. + required ConfirmSetupParams confirmParams, + + /// By default, stripe.confirmSetup will always redirect to your return_url + /// after a successful confirmation. If you set redirect: "if_required", + /// then stripe.confirmSetup will only redirect if your user chooses a + /// redirect-based payment method. + + /// Note: Setting if_required requires that you handle successful confirmations + /// for redirect-based and non-redirect based payment methods separately. + /// When a non-redirect based payment method is successfully confirmed, + /// stripe.confirmSetup will resolve with a {setupIntent} object. + SetupConfirmationRedirect? redirect, + }) = _ConfirmSetupOptions; + + factory ConfirmSetupOptions.fromJson(Map json) => + _$ConfirmSetupOptionsFromJson(json); +} + +@freezed +class ConfirmSetupParams with _$ConfirmSetupParams { + const factory ConfirmSetupParams({ + /// The url your customer will be directed to after they complete authentication. + required String return_url, + + /// If collected previously, the ID of the ConfirmationToken to use to confirm this SetupIntent. + /// This is mutually exclusive with the elements parameter. + String? confirmation_token, + }) = _ConfirmSetupParams; + + factory ConfirmSetupParams.fromJson(Map json) => + _$ConfirmSetupParamsFromJson(json); +} + +/// By default, stripe.confirmPayment will always redirect to +/// your return_url after a successful confirmation. +/// If you set redirect: "if_required", then stripe.confirmPayment +/// will only redirect if your user chooses a redirect-based payment method. +enum SetupConfirmationRedirect { + always, + @JsonValue('if_required') + ifRequired, +} diff --git a/packages/stripe_js/lib/src/api/setup_intents/confirm_setup_options.freezed.dart b/packages/stripe_js/lib/src/api/setup_intents/confirm_setup_options.freezed.dart new file mode 100644 index 000000000..946b81e53 --- /dev/null +++ b/packages/stripe_js/lib/src/api/setup_intents/confirm_setup_options.freezed.dart @@ -0,0 +1,483 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'confirm_setup_options.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); + +ConfirmSetupOptions _$ConfirmSetupOptionsFromJson(Map json) { + return _ConfirmSetupOptions.fromJson(json); +} + +/// @nodoc +mixin _$ConfirmSetupOptions { + /// Required if you collect payment details before creating an Intent. It's always required if you don't provide a clientSecret. + @ElementsConverter() + Elements get elements => throw _privateConstructorUsedError; + + /// Required if you collect payment details before creating an Intent. + /// It's always required if you don't provide an elements instance containing a client secret. + String? get clientSecret => throw _privateConstructorUsedError; + + /// Parameters that will be passed on to the Stripe API. + /// Refer to the Payment Intents API for a full list of parameters. + ConfirmSetupParams get confirmParams => throw _privateConstructorUsedError; + + /// By default, stripe.confirmSetup will always redirect to your return_url + /// after a successful confirmation. If you set redirect: "if_required", + /// then stripe.confirmSetup will only redirect if your user chooses a + /// redirect-based payment method. + /// Note: Setting if_required requires that you handle successful confirmations + /// for redirect-based and non-redirect based payment methods separately. + /// When a non-redirect based payment method is successfully confirmed, + /// stripe.confirmSetup will resolve with a {setupIntent} object. + SetupConfirmationRedirect? get redirect => throw _privateConstructorUsedError; + + /// Serializes this ConfirmSetupOptions to a JSON map. + Map toJson() => throw _privateConstructorUsedError; + + /// Create a copy of ConfirmSetupOptions + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + $ConfirmSetupOptionsCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $ConfirmSetupOptionsCopyWith<$Res> { + factory $ConfirmSetupOptionsCopyWith( + ConfirmSetupOptions value, $Res Function(ConfirmSetupOptions) then) = + _$ConfirmSetupOptionsCopyWithImpl<$Res, ConfirmSetupOptions>; + @useResult + $Res call( + {@ElementsConverter() Elements elements, + String? clientSecret, + ConfirmSetupParams confirmParams, + SetupConfirmationRedirect? redirect}); + + $ConfirmSetupParamsCopyWith<$Res> get confirmParams; +} + +/// @nodoc +class _$ConfirmSetupOptionsCopyWithImpl<$Res, $Val extends ConfirmSetupOptions> + implements $ConfirmSetupOptionsCopyWith<$Res> { + _$ConfirmSetupOptionsCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + /// Create a copy of ConfirmSetupOptions + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? elements = null, + Object? clientSecret = freezed, + Object? confirmParams = null, + Object? redirect = freezed, + }) { + return _then(_value.copyWith( + elements: null == elements + ? _value.elements + : elements // ignore: cast_nullable_to_non_nullable + as Elements, + clientSecret: freezed == clientSecret + ? _value.clientSecret + : clientSecret // ignore: cast_nullable_to_non_nullable + as String?, + confirmParams: null == confirmParams + ? _value.confirmParams + : confirmParams // ignore: cast_nullable_to_non_nullable + as ConfirmSetupParams, + redirect: freezed == redirect + ? _value.redirect + : redirect // ignore: cast_nullable_to_non_nullable + as SetupConfirmationRedirect?, + ) as $Val); + } + + /// Create a copy of ConfirmSetupOptions + /// with the given fields replaced by the non-null parameter values. + @override + @pragma('vm:prefer-inline') + $ConfirmSetupParamsCopyWith<$Res> get confirmParams { + return $ConfirmSetupParamsCopyWith<$Res>(_value.confirmParams, (value) { + return _then(_value.copyWith(confirmParams: value) as $Val); + }); + } +} + +/// @nodoc +abstract class _$$ConfirmSetupOptionsImplCopyWith<$Res> + implements $ConfirmSetupOptionsCopyWith<$Res> { + factory _$$ConfirmSetupOptionsImplCopyWith(_$ConfirmSetupOptionsImpl value, + $Res Function(_$ConfirmSetupOptionsImpl) then) = + __$$ConfirmSetupOptionsImplCopyWithImpl<$Res>; + @override + @useResult + $Res call( + {@ElementsConverter() Elements elements, + String? clientSecret, + ConfirmSetupParams confirmParams, + SetupConfirmationRedirect? redirect}); + + @override + $ConfirmSetupParamsCopyWith<$Res> get confirmParams; +} + +/// @nodoc +class __$$ConfirmSetupOptionsImplCopyWithImpl<$Res> + extends _$ConfirmSetupOptionsCopyWithImpl<$Res, _$ConfirmSetupOptionsImpl> + implements _$$ConfirmSetupOptionsImplCopyWith<$Res> { + __$$ConfirmSetupOptionsImplCopyWithImpl(_$ConfirmSetupOptionsImpl _value, + $Res Function(_$ConfirmSetupOptionsImpl) _then) + : super(_value, _then); + + /// Create a copy of ConfirmSetupOptions + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? elements = null, + Object? clientSecret = freezed, + Object? confirmParams = null, + Object? redirect = freezed, + }) { + return _then(_$ConfirmSetupOptionsImpl( + elements: null == elements + ? _value.elements + : elements // ignore: cast_nullable_to_non_nullable + as Elements, + clientSecret: freezed == clientSecret + ? _value.clientSecret + : clientSecret // ignore: cast_nullable_to_non_nullable + as String?, + confirmParams: null == confirmParams + ? _value.confirmParams + : confirmParams // ignore: cast_nullable_to_non_nullable + as ConfirmSetupParams, + redirect: freezed == redirect + ? _value.redirect + : redirect // ignore: cast_nullable_to_non_nullable + as SetupConfirmationRedirect?, + )); + } +} + +/// @nodoc +@JsonSerializable() +class _$ConfirmSetupOptionsImpl implements _ConfirmSetupOptions { + const _$ConfirmSetupOptionsImpl( + {@ElementsConverter() required this.elements, + this.clientSecret, + required this.confirmParams, + this.redirect}); + + factory _$ConfirmSetupOptionsImpl.fromJson(Map json) => + _$$ConfirmSetupOptionsImplFromJson(json); + + /// Required if you collect payment details before creating an Intent. It's always required if you don't provide a clientSecret. + @override + @ElementsConverter() + final Elements elements; + + /// Required if you collect payment details before creating an Intent. + /// It's always required if you don't provide an elements instance containing a client secret. + @override + final String? clientSecret; + + /// Parameters that will be passed on to the Stripe API. + /// Refer to the Payment Intents API for a full list of parameters. + @override + final ConfirmSetupParams confirmParams; + + /// By default, stripe.confirmSetup will always redirect to your return_url + /// after a successful confirmation. If you set redirect: "if_required", + /// then stripe.confirmSetup will only redirect if your user chooses a + /// redirect-based payment method. + /// Note: Setting if_required requires that you handle successful confirmations + /// for redirect-based and non-redirect based payment methods separately. + /// When a non-redirect based payment method is successfully confirmed, + /// stripe.confirmSetup will resolve with a {setupIntent} object. + @override + final SetupConfirmationRedirect? redirect; + + @override + String toString() { + return 'ConfirmSetupOptions(elements: $elements, clientSecret: $clientSecret, confirmParams: $confirmParams, redirect: $redirect)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$ConfirmSetupOptionsImpl && + (identical(other.elements, elements) || + other.elements == elements) && + (identical(other.clientSecret, clientSecret) || + other.clientSecret == clientSecret) && + (identical(other.confirmParams, confirmParams) || + other.confirmParams == confirmParams) && + (identical(other.redirect, redirect) || + other.redirect == redirect)); + } + + @JsonKey(includeFromJson: false, includeToJson: false) + @override + int get hashCode => + Object.hash(runtimeType, elements, clientSecret, confirmParams, redirect); + + /// Create a copy of ConfirmSetupOptions + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + @override + @pragma('vm:prefer-inline') + _$$ConfirmSetupOptionsImplCopyWith<_$ConfirmSetupOptionsImpl> get copyWith => + __$$ConfirmSetupOptionsImplCopyWithImpl<_$ConfirmSetupOptionsImpl>( + this, _$identity); + + @override + Map toJson() { + return _$$ConfirmSetupOptionsImplToJson( + this, + ); + } +} + +abstract class _ConfirmSetupOptions implements ConfirmSetupOptions { + const factory _ConfirmSetupOptions( + {@ElementsConverter() required final Elements elements, + final String? clientSecret, + required final ConfirmSetupParams confirmParams, + final SetupConfirmationRedirect? redirect}) = _$ConfirmSetupOptionsImpl; + + factory _ConfirmSetupOptions.fromJson(Map json) = + _$ConfirmSetupOptionsImpl.fromJson; + + /// Required if you collect payment details before creating an Intent. It's always required if you don't provide a clientSecret. + @override + @ElementsConverter() + Elements get elements; + + /// Required if you collect payment details before creating an Intent. + /// It's always required if you don't provide an elements instance containing a client secret. + @override + String? get clientSecret; + + /// Parameters that will be passed on to the Stripe API. + /// Refer to the Payment Intents API for a full list of parameters. + @override + ConfirmSetupParams get confirmParams; + + /// By default, stripe.confirmSetup will always redirect to your return_url + /// after a successful confirmation. If you set redirect: "if_required", + /// then stripe.confirmSetup will only redirect if your user chooses a + /// redirect-based payment method. + /// Note: Setting if_required requires that you handle successful confirmations + /// for redirect-based and non-redirect based payment methods separately. + /// When a non-redirect based payment method is successfully confirmed, + /// stripe.confirmSetup will resolve with a {setupIntent} object. + @override + SetupConfirmationRedirect? get redirect; + + /// Create a copy of ConfirmSetupOptions + /// with the given fields replaced by the non-null parameter values. + @override + @JsonKey(includeFromJson: false, includeToJson: false) + _$$ConfirmSetupOptionsImplCopyWith<_$ConfirmSetupOptionsImpl> get copyWith => + throw _privateConstructorUsedError; +} + +ConfirmSetupParams _$ConfirmSetupParamsFromJson(Map json) { + return _ConfirmSetupParams.fromJson(json); +} + +/// @nodoc +mixin _$ConfirmSetupParams { + /// The url your customer will be directed to after they complete authentication. + String get return_url => throw _privateConstructorUsedError; + + /// If collected previously, the ID of the ConfirmationToken to use to confirm this SetupIntent. + /// This is mutually exclusive with the elements parameter. + String? get confirmation_token => throw _privateConstructorUsedError; + + /// Serializes this ConfirmSetupParams to a JSON map. + Map toJson() => throw _privateConstructorUsedError; + + /// Create a copy of ConfirmSetupParams + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + $ConfirmSetupParamsCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $ConfirmSetupParamsCopyWith<$Res> { + factory $ConfirmSetupParamsCopyWith( + ConfirmSetupParams value, $Res Function(ConfirmSetupParams) then) = + _$ConfirmSetupParamsCopyWithImpl<$Res, ConfirmSetupParams>; + @useResult + $Res call({String return_url, String? confirmation_token}); +} + +/// @nodoc +class _$ConfirmSetupParamsCopyWithImpl<$Res, $Val extends ConfirmSetupParams> + implements $ConfirmSetupParamsCopyWith<$Res> { + _$ConfirmSetupParamsCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + /// Create a copy of ConfirmSetupParams + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? return_url = null, + Object? confirmation_token = freezed, + }) { + return _then(_value.copyWith( + return_url: null == return_url + ? _value.return_url + : return_url // ignore: cast_nullable_to_non_nullable + as String, + confirmation_token: freezed == confirmation_token + ? _value.confirmation_token + : confirmation_token // ignore: cast_nullable_to_non_nullable + as String?, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$ConfirmSetupParamsImplCopyWith<$Res> + implements $ConfirmSetupParamsCopyWith<$Res> { + factory _$$ConfirmSetupParamsImplCopyWith(_$ConfirmSetupParamsImpl value, + $Res Function(_$ConfirmSetupParamsImpl) then) = + __$$ConfirmSetupParamsImplCopyWithImpl<$Res>; + @override + @useResult + $Res call({String return_url, String? confirmation_token}); +} + +/// @nodoc +class __$$ConfirmSetupParamsImplCopyWithImpl<$Res> + extends _$ConfirmSetupParamsCopyWithImpl<$Res, _$ConfirmSetupParamsImpl> + implements _$$ConfirmSetupParamsImplCopyWith<$Res> { + __$$ConfirmSetupParamsImplCopyWithImpl(_$ConfirmSetupParamsImpl _value, + $Res Function(_$ConfirmSetupParamsImpl) _then) + : super(_value, _then); + + /// Create a copy of ConfirmSetupParams + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? return_url = null, + Object? confirmation_token = freezed, + }) { + return _then(_$ConfirmSetupParamsImpl( + return_url: null == return_url + ? _value.return_url + : return_url // ignore: cast_nullable_to_non_nullable + as String, + confirmation_token: freezed == confirmation_token + ? _value.confirmation_token + : confirmation_token // ignore: cast_nullable_to_non_nullable + as String?, + )); + } +} + +/// @nodoc +@JsonSerializable() +class _$ConfirmSetupParamsImpl implements _ConfirmSetupParams { + const _$ConfirmSetupParamsImpl( + {required this.return_url, this.confirmation_token}); + + factory _$ConfirmSetupParamsImpl.fromJson(Map json) => + _$$ConfirmSetupParamsImplFromJson(json); + + /// The url your customer will be directed to after they complete authentication. + @override + final String return_url; + + /// If collected previously, the ID of the ConfirmationToken to use to confirm this SetupIntent. + /// This is mutually exclusive with the elements parameter. + @override + final String? confirmation_token; + + @override + String toString() { + return 'ConfirmSetupParams(return_url: $return_url, confirmation_token: $confirmation_token)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$ConfirmSetupParamsImpl && + (identical(other.return_url, return_url) || + other.return_url == return_url) && + (identical(other.confirmation_token, confirmation_token) || + other.confirmation_token == confirmation_token)); + } + + @JsonKey(includeFromJson: false, includeToJson: false) + @override + int get hashCode => Object.hash(runtimeType, return_url, confirmation_token); + + /// Create a copy of ConfirmSetupParams + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + @override + @pragma('vm:prefer-inline') + _$$ConfirmSetupParamsImplCopyWith<_$ConfirmSetupParamsImpl> get copyWith => + __$$ConfirmSetupParamsImplCopyWithImpl<_$ConfirmSetupParamsImpl>( + this, _$identity); + + @override + Map toJson() { + return _$$ConfirmSetupParamsImplToJson( + this, + ); + } +} + +abstract class _ConfirmSetupParams implements ConfirmSetupParams { + const factory _ConfirmSetupParams( + {required final String return_url, + final String? confirmation_token}) = _$ConfirmSetupParamsImpl; + + factory _ConfirmSetupParams.fromJson(Map json) = + _$ConfirmSetupParamsImpl.fromJson; + + /// The url your customer will be directed to after they complete authentication. + @override + String get return_url; + + /// If collected previously, the ID of the ConfirmationToken to use to confirm this SetupIntent. + /// This is mutually exclusive with the elements parameter. + @override + String? get confirmation_token; + + /// Create a copy of ConfirmSetupParams + /// with the given fields replaced by the non-null parameter values. + @override + @JsonKey(includeFromJson: false, includeToJson: false) + _$$ConfirmSetupParamsImplCopyWith<_$ConfirmSetupParamsImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/packages/stripe_js/lib/src/api/setup_intents/confirm_setup_options.g.dart b/packages/stripe_js/lib/src/api/setup_intents/confirm_setup_options.g.dart new file mode 100644 index 000000000..fde890904 --- /dev/null +++ b/packages/stripe_js/lib/src/api/setup_intents/confirm_setup_options.g.dart @@ -0,0 +1,62 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'confirm_setup_options.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_$ConfirmSetupOptionsImpl _$$ConfirmSetupOptionsImplFromJson(Map json) => + _$ConfirmSetupOptionsImpl( + elements: const ElementsConverter().fromJson(json['elements']), + clientSecret: json['clientSecret'] as String?, + confirmParams: ConfirmSetupParams.fromJson( + Map.from(json['confirmParams'] as Map)), + redirect: $enumDecodeNullable( + _$SetupConfirmationRedirectEnumMap, json['redirect']), + ); + +Map _$$ConfirmSetupOptionsImplToJson( + _$ConfirmSetupOptionsImpl instance) { + final val = {}; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('elements', const ElementsConverter().toJson(instance.elements)); + writeNotNull('clientSecret', instance.clientSecret); + val['confirmParams'] = instance.confirmParams.toJson(); + writeNotNull( + 'redirect', _$SetupConfirmationRedirectEnumMap[instance.redirect]); + return val; +} + +const _$SetupConfirmationRedirectEnumMap = { + SetupConfirmationRedirect.always: 'always', + SetupConfirmationRedirect.ifRequired: 'if_required', +}; + +_$ConfirmSetupParamsImpl _$$ConfirmSetupParamsImplFromJson(Map json) => + _$ConfirmSetupParamsImpl( + return_url: json['return_url'] as String, + confirmation_token: json['confirmation_token'] as String?, + ); + +Map _$$ConfirmSetupParamsImplToJson( + _$ConfirmSetupParamsImpl instance) { + final val = { + 'return_url': instance.return_url, + }; + + void writeNotNull(String key, dynamic value) { + if (value != null) { + val[key] = value; + } + } + + writeNotNull('confirmation_token', instance.confirmation_token); + return val; +} diff --git a/packages/stripe_js/lib/src/api/setup_intents/setup_intents.dart b/packages/stripe_js/lib/src/api/setup_intents/setup_intents.dart index 88bc759a7..5db90e391 100644 --- a/packages/stripe_js/lib/src/api/setup_intents/setup_intents.dart +++ b/packages/stripe_js/lib/src/api/setup_intents/setup_intents.dart @@ -4,3 +4,4 @@ export 'confirm_sepa_debit_setup_data.dart'; export 'setup_intent.dart'; export 'setup_intent_response.dart'; export 'setup_intent_status.dart'; +export 'confirm_setup_options.dart'; diff --git a/packages/stripe_js/lib/src/js/setup_intents/confirm_setup.dart b/packages/stripe_js/lib/src/js/setup_intents/confirm_setup.dart new file mode 100644 index 000000000..c66ec511c --- /dev/null +++ b/packages/stripe_js/lib/src/js/setup_intents/confirm_setup.dart @@ -0,0 +1,30 @@ +import 'dart:async'; +import 'dart:js_interop'; + +import 'package:stripe_js/src/js/utils/utils.dart'; +import 'package:stripe_js/stripe_api.dart'; +import 'package:stripe_js/stripe_js.dart'; + +extension ExtensionConfirmSetup on Stripe { + /// Use stripe.confirmSetup to confirm a SetupIntent using data collected + /// by the Payment Element, or with manually provided data via confirmParams. + /// When called, stripe.confirmSetup will attempt to complete any required actions, + /// such as authenticating your user by displaying a 3DS dialog or redirecting + /// them to a bank authorization page. Your user will be redirected to the + /// return_url you pass once the authorization is complete. + /// + /// [confirmSetup] will return a Future. Upon a successful authorization, + /// your user will be redirected to the return_url you provide + /// before the Future ever resolves. + /// see: https://docs.stripe.com/js/setup_intents/confirm_setup + Future confirmSetup( + ConfirmSetupOptions options, + ) async { + return _confirmSetup(options.toJson().jsify()) + .toDart + .then((response) => response.toDart); + } + + @JS('confirmSetup') + external JSPromise _confirmSetup(JSAny? options); +} diff --git a/packages/stripe_js/lib/src/js/setup_intents/setup_intents.dart b/packages/stripe_js/lib/src/js/setup_intents/setup_intents.dart index d36c015cd..a51b8c88b 100644 --- a/packages/stripe_js/lib/src/js/setup_intents/setup_intents.dart +++ b/packages/stripe_js/lib/src/js/setup_intents/setup_intents.dart @@ -1,3 +1,4 @@ export 'confirm_card_setup.dart'; export 'confirm_sepa_debit_setup.dart'; export 'retrieve_setup_intent.dart'; +export 'confirm_setup.dart'; From 1d178d1a72e365da5dee7eeab2f372cc4e8a3487 Mon Sep 17 00:00:00 2001 From: cedvandenbosch Date: Mon, 14 Oct 2024 01:25:59 +0200 Subject: [PATCH 09/16] changelog & versioning" --- packages/stripe_js/CHANGELOG.md | 4 ++++ packages/stripe_js/pubspec.yaml | 6 ++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/stripe_js/CHANGELOG.md b/packages/stripe_js/CHANGELOG.md index 26c20d5f1..2b86a167f 100644 --- a/packages/stripe_js/CHANGELOG.md +++ b/packages/stripe_js/CHANGELOG.md @@ -1,3 +1,7 @@ +## 6.2.1 +**Features** +- Add support for any kind of payment method in setup intent with [confirmSetup] + ## 6.2.0 **Features** - Add basic support for Expresscheckout on the web diff --git a/packages/stripe_js/pubspec.yaml b/packages/stripe_js/pubspec.yaml index fa7029cc7..f37802fd2 100644 --- a/packages/stripe_js/pubspec.yaml +++ b/packages/stripe_js/pubspec.yaml @@ -1,12 +1,11 @@ name: stripe_js description: Stripe.js bindings for dart. This package is used by Stripe web so that the Stripe js sdk can be invoked directly. -version: 6.2.0 +version: 6.2.1 homepage: https://github.com/flutter-stripe/flutter_stripe environment: sdk: ">=3.3.0 <4.0.0" - dependencies: freezed_annotation: ^2.2.0 json_annotation: ^4.8.1 @@ -18,7 +17,6 @@ dev_dependencies: freezed: ^2.3.5 json_serializable: ^6.5.4 test: ^1.22.0 - + false_secrets: - /tests/** - From b13b0fcd8bd8f294fe682fd29118d09a4e43e6d4 Mon Sep 17 00:00:00 2001 From: cedvandenbosch Date: Mon, 14 Oct 2024 01:33:55 +0200 Subject: [PATCH 10/16] changelog and versioning --- packages/stripe_web/CHANGELOG.md | 4 ++++ packages/stripe_web/pubspec.yaml | 6 +----- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/stripe_web/CHANGELOG.md b/packages/stripe_web/CHANGELOG.md index 5804518e8..411088617 100644 --- a/packages/stripe_web/CHANGELOG.md +++ b/packages/stripe_web/CHANGELOG.md @@ -1,3 +1,7 @@ +## 6.2.1 +**Features** +- Add support for any kind of payment method in setup intent with [confirmSetup] + ## 6.2.0 **Features** - Add basic support for Expresscheckout on the web diff --git a/packages/stripe_web/pubspec.yaml b/packages/stripe_web/pubspec.yaml index 370913499..f18c87373 100644 --- a/packages/stripe_web/pubspec.yaml +++ b/packages/stripe_web/pubspec.yaml @@ -14,11 +14,7 @@ dependencies: sdk: flutter freezed_annotation: ^2.0.3 stripe_platform_interface: ^11.2.0 - stripe_js: - git: - url: git@github.com:cedvdb/flutter_stripe.git - path: packages/stripe_js - ref: setup_confirm + stripe_js: ^6.2.1 web: ^1.0.0 dev_dependencies: From e08a6f408c1f7d6e1732b37dd9c0b8d8ab53216c Mon Sep 17 00:00:00 2001 From: cedvandenbosch Date: Thu, 7 Nov 2024 15:05:27 +0100 Subject: [PATCH 11/16] add example --- .../others/setup_future_payment_screen.dart | 239 ------------------ example/lib/screens/screens.dart | 15 +- .../_create_setup_intent.dart | 30 +++ .../add_payment_method_button.dart | 42 +++ .../add_payment_method_screen_loader.dart | 11 + ...payment_method_screen_loader_abstract.dart | 8 + ...d_payment_method_screen_loader_mobile.dart | 128 ++++++++++ .../add_payment_method_screen_loader_web.dart | 143 +++++++++++ .../setup_future_payments_screen.dart | 17 ++ example/server/src/index.ts | 9 +- 10 files changed, 396 insertions(+), 246 deletions(-) delete mode 100644 example/lib/screens/others/setup_future_payment_screen.dart create mode 100644 example/lib/screens/setup_future_payments/_create_setup_intent.dart create mode 100644 example/lib/screens/setup_future_payments/add_payment_method_button.dart create mode 100644 example/lib/screens/setup_future_payments/add_payment_method_screen_loader.dart create mode 100644 example/lib/screens/setup_future_payments/add_payment_method_screen_loader_abstract.dart create mode 100644 example/lib/screens/setup_future_payments/add_payment_method_screen_loader_mobile.dart create mode 100644 example/lib/screens/setup_future_payments/add_payment_method_screen_loader_web.dart create mode 100644 example/lib/screens/setup_future_payments/setup_future_payments_screen.dart diff --git a/example/lib/screens/others/setup_future_payment_screen.dart b/example/lib/screens/others/setup_future_payment_screen.dart deleted file mode 100644 index 45be5d61a..000000000 --- a/example/lib/screens/others/setup_future_payment_screen.dart +++ /dev/null @@ -1,239 +0,0 @@ -import 'dart:convert'; -import 'dart:developer'; - -import 'package:flutter/material.dart' hide Card; -import 'package:flutter_stripe/flutter_stripe.dart'; -import 'package:http/http.dart' as http; -import 'package:stripe_example/config.dart'; -import 'package:stripe_example/screens/payment_sheet/payment_sheet_screen_custom_flow.dart'; -import 'package:stripe_example/utils.dart'; -import 'package:stripe_example/widgets/example_scaffold.dart'; -import 'package:stripe_example/widgets/loading_button.dart'; -import 'package:stripe_example/widgets/response_card.dart'; - -class SetupFuturePaymentScreen extends StatefulWidget { - @override - _SetupFuturePaymentScreenState createState() => - _SetupFuturePaymentScreenState(); -} - -class _SetupFuturePaymentScreenState extends State { - PaymentIntent? _retrievedPaymentIntent; - CardFieldInputDetails? _card; - SetupIntent? _setupIntentResult; - String _email = 'email@stripe.com'; - - int step = 0; - - @override - Widget build(BuildContext context) { - return ExampleScaffold( - title: 'Setup Future Payment', - children: [ - Padding( - padding: EdgeInsets.all(16), - child: TextFormField( - initialValue: _email, - decoration: InputDecoration(hintText: 'Email', labelText: 'Email'), - onChanged: (value) { - setState(() { - _email = value; - }); - }, - ), - ), - Padding( - padding: EdgeInsets.all(16), - child: CardField( - onCardChanged: (card) { - setState(() { - _card = card; - }); - }, - ), - ), - Stepper( - controlsBuilder: emptyControlBuilder, - currentStep: step, - steps: [ - Step( - title: Text('Save card'), - content: LoadingButton( - onPressed: _card?.complete == true ? _handleSavePress : null, - text: 'Save', - ), - ), - Step( - title: Text('Pay with saved card'), - content: LoadingButton( - onPressed: _setupIntentResult != null - ? _handleOffSessionPayment - : null, - text: 'Pay with saved card off-session', - ), - ), - Step( - title: Text('[Extra] Recovery Flow - Authenticate payment'), - content: Column( - children: [ - Text( - 'If the payment did not succeed. Notify your customer to return to your application to complete the payment. We recommend creating a recovery flow in your app that shows why the payment failed initially and lets your customer retry.'), - SizedBox(height: 8), - LoadingButton( - onPressed: _retrievedPaymentIntent != null - ? _handleRecoveryFlow - : null, - text: 'Authenticate payment (recovery flow)', - ), - ], - ), - ), - ], - ), - if (_setupIntentResult != null) - Padding( - padding: EdgeInsets.all(16), - child: ResponseCard( - response: _setupIntentResult!.toJson().toPrettyString(), - ), - ), - ], - ); - } - - Future _handleSavePress() async { - if (_card == null) { - return; - } - try { - // 1. Create setup intent on backend - final clientSecret = await _createSetupIntentOnBackend(_email); - - // 2. Gather customer billing information (ex. email) - final billingDetails = BillingDetails( - name: "Test User", - email: 'email@stripe.com', - phone: '+48888000888', - address: Address( - city: 'Houston', - country: 'US', - line1: '1459 Circle Drive', - line2: '', - state: 'Texas', - postalCode: '77063', - ), - ); // mo/ mocked data for tests - - // 3. Confirm setup intent - - final setupIntentResult = await Stripe.instance.confirmSetupIntent( - paymentIntentClientSecret: clientSecret, - params: PaymentMethodParams.card( - paymentMethodData: PaymentMethodData( - billingDetails: billingDetails, - ), - ), - ); - log('Setup Intent created $setupIntentResult'); - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text( - 'Success: Setup intent created.', - ), - ), - ); - setState(() { - step = 1; - _setupIntentResult = setupIntentResult; - }); - } catch (error) { - ScaffoldMessenger.of(context) - .showSnackBar(SnackBar(content: Text('Error code: $error'))); - rethrow; - } - } - - Future _handleOffSessionPayment() async { - final res = await _chargeCardOffSession(); - if (res['error'] != null) { - // If the PaymentIntent has any other status, the payment did not succeed and the request fails. - // Notify your customer e.g., by email, text, push notification) to complete the payment. - // We recommend creating a recovery flow in your app that shows why the payment failed initially and lets your customer retry. - ScaffoldMessenger.of(context).showSnackBar(SnackBar( - content: Text( - 'Error!: The payment could not be completed! ${res['error']}'))); - await _handleRetrievePaymentIntent(res['clientSecret']); - } else { - ScaffoldMessenger.of(context).showSnackBar(SnackBar( - content: Text('Success!: The payment was confirmed successfully!'))); - setState(() { - step = 2; - }); - } - - log('charge off session result: $res'); - } - - // When customer back to the App to complete the payment, retrieve the PaymentIntent via clientSecret. - // Check the PaymentIntent’s lastPaymentError to inspect why the payment attempt failed. - // For card errors, you can show the user the last payment error’s message. Otherwise, you can show a generic failure message. - Future _handleRetrievePaymentIntent(String clientSecret) async { - final paymentIntent = - await Stripe.instance.retrievePaymentIntent(clientSecret); - - final paymentMethodId = paymentIntent.paymentMethodId == null - ? _setupIntentResult?.paymentMethodId - : paymentIntent.paymentMethodId; - - setState(() { - _retrievedPaymentIntent = - paymentIntent.copyWith(paymentMethodId: paymentMethodId); - }); - } - - // https://stripe.com/docs/payments/save-and-reuse?platform=ios#start-a-recovery-flow - Future _handleRecoveryFlow() async { - // TODO lastPaymentError - if (_retrievedPaymentIntent?.paymentMethodId != null && _card != null) { - await Stripe.instance.confirmPayment( - paymentIntentClientSecret: _retrievedPaymentIntent!.clientSecret, - data: PaymentMethodParams.cardFromMethodId( - paymentMethodData: PaymentMethodDataCardFromMethod( - paymentMethodId: _retrievedPaymentIntent!.paymentMethodId!), - ), - ); - } - ScaffoldMessenger.of(context).showSnackBar(SnackBar( - content: Text('Success!: The payment was confirmed successfully!'))); - } - - Future _createSetupIntentOnBackend(String email) async { - final url = Uri.parse('$kApiUrl/create-setup-intent'); - final response = await http.post( - url, - headers: { - 'Content-Type': 'application/json', - }, - body: json.encode({ - 'email': email, - }), - ); - final Map bodyResponse = json.decode(response.body); - final clientSecret = bodyResponse['clientSecret'] as String; - log('Client token $clientSecret'); - - return clientSecret; - } - - Future> _chargeCardOffSession() async { - final url = Uri.parse('$kApiUrl/charge-card-off-session'); - final response = await http.post( - url, - headers: { - 'Content-Type': 'application/json', - }, - body: json.encode({'email': _email}), - ); - return json.decode(response.body); - } -} diff --git a/example/lib/screens/screens.dart b/example/lib/screens/screens.dart index d71b1a9d0..6de0dc1b3 100644 --- a/example/lib/screens/screens.dart +++ b/example/lib/screens/screens.dart @@ -17,6 +17,7 @@ import 'package:stripe_example/screens/regional_payment_methods/paypal_screen.da import 'package:stripe_example/screens/regional_payment_methods/revolutpay_screen.dart'; import 'package:stripe_example/screens/regional_payment_methods/sofort_screen.dart'; import 'package:stripe_example/screens/regional_payment_methods/us_bank_account.dart'; +import 'package:stripe_example/screens/setup_future_payments/setup_future_payments_screen.dart'; import 'package:stripe_example/screens/wallets/apple_pay_screen.dart'; import 'package:stripe_example/screens/wallets/apple_pay_screen_plugin.dart'; import 'package:stripe_example/screens/wallets/google_pay_screen.dart'; @@ -33,7 +34,6 @@ import 'financial_connections.dart/financial_connections_session_screen.dart'; import 'others/cvc_re_collection_screen.dart'; import 'others/legacy_token_bank_screen.dart'; import 'others/legacy_token_card_screen.dart'; -import 'others/setup_future_payment_screen.dart'; import 'regional_payment_methods/grab_pay_screen.dart'; import 'themes.dart'; import 'wallets/apple_pay_create_payment_method.dart'; @@ -131,6 +131,15 @@ class Example extends StatelessWidget { DevicePlatform.web, ], ), + Example( + title: 'Setup future payments', + builder: (_) => SetupFuturePaymentsScreen(), + platformsSupported: [ + DevicePlatform.android, + DevicePlatform.ios, + DevicePlatform.web, + ], + ) ], expanded: true, ), @@ -356,10 +365,6 @@ class Example extends StatelessWidget { ], ), ExampleSection(title: 'Others', children: [ - Example( - title: 'Setup Future Payment', - builder: (c) => SetupFuturePaymentScreen(), - ), Example( title: 'Re-collect CVC', builder: (c) => CVCReCollectionScreen(), diff --git a/example/lib/screens/setup_future_payments/_create_setup_intent.dart b/example/lib/screens/setup_future_payments/_create_setup_intent.dart new file mode 100644 index 000000000..310f295bd --- /dev/null +++ b/example/lib/screens/setup_future_payments/_create_setup_intent.dart @@ -0,0 +1,30 @@ +import 'dart:convert'; + +import 'package:http/http.dart' as http; +import 'package:stripe_example/config.dart'; + +typedef SetupKeys = ({ + String clientSecret, + String customerId, +}); + +Future createSetupIntent() async { + final url = Uri.parse('$kApiUrl/create-setup-intent'); + final response = await http.post( + url, + headers: { + 'Content-Type': 'application/json', + }, + body: json.encode({ + 'payment_method_types': ['cards', 'sepa_debit'], + }), + ); + final body = json.decode(response.body); + if (body['error'] != null) { + throw Exception(body['error']); + } + return ( + clientSecret: body['clientSecret'] as String, + customerId: body['customerId'] as String + ); +} diff --git a/example/lib/screens/setup_future_payments/add_payment_method_button.dart b/example/lib/screens/setup_future_payments/add_payment_method_button.dart new file mode 100644 index 000000000..f17976946 --- /dev/null +++ b/example/lib/screens/setup_future_payments/add_payment_method_button.dart @@ -0,0 +1,42 @@ +import 'package:flutter/material.dart'; + +import 'add_payment_method_screen_loader.dart' + if (dart.library.js) 'add_payment_method_screen_loader_web.dart' + if (dart.library.io) 'add_payment_method_screen_loader_mobile.dart'; + +class AddPaymentMethodButton extends StatefulWidget { + const AddPaymentMethodButton({ + super.key, + }); + + @override + State createState() => _AddPaymentMethodButtonState(); +} + +class _AddPaymentMethodButtonState extends State { + bool isLoading = false; + + void _onAddPaymentMethodPressed() async { + setState(() => isLoading = true); + try { + AddPaymentMethodScreenLoader().display( + context: context, + ); + } finally { + setState(() => isLoading = false); + } + } + + @override + Widget build(BuildContext context) { + return ListTile( + leading: isLoading + ? SizedBox( + height: 16, width: 16, child: const CircularProgressIndicator()) + : const Icon(Icons.credit_card), + title: Text('Add payment method'), + trailing: const Icon(Icons.chevron_right), + onTap: isLoading ? null : _onAddPaymentMethodPressed, + ); + } +} diff --git a/example/lib/screens/setup_future_payments/add_payment_method_screen_loader.dart b/example/lib/screens/setup_future_payments/add_payment_method_screen_loader.dart new file mode 100644 index 000000000..83a03b34a --- /dev/null +++ b/example/lib/screens/setup_future_payments/add_payment_method_screen_loader.dart @@ -0,0 +1,11 @@ +import 'package:flutter/material.dart'; + +class AddPaymentMethodScreenLoader { + const AddPaymentMethodScreenLoader(); + + Future display({ + required BuildContext context, + }) { + throw UnimplementedError(); + } +} diff --git a/example/lib/screens/setup_future_payments/add_payment_method_screen_loader_abstract.dart b/example/lib/screens/setup_future_payments/add_payment_method_screen_loader_abstract.dart new file mode 100644 index 000000000..33e377e92 --- /dev/null +++ b/example/lib/screens/setup_future_payments/add_payment_method_screen_loader_abstract.dart @@ -0,0 +1,8 @@ +import 'package:flutter/material.dart'; + +/// abstract class so both mobile and web use the same constructor +abstract class AddPaymentMethodScreenLoaderAbstract { + const AddPaymentMethodScreenLoaderAbstract(); + + Future display({required BuildContext context}); +} diff --git a/example/lib/screens/setup_future_payments/add_payment_method_screen_loader_mobile.dart b/example/lib/screens/setup_future_payments/add_payment_method_screen_loader_mobile.dart new file mode 100644 index 000000000..f641d217d --- /dev/null +++ b/example/lib/screens/setup_future_payments/add_payment_method_screen_loader_mobile.dart @@ -0,0 +1,128 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_stripe/flutter_stripe.dart' as stripe; +import 'package:flutter_stripe/flutter_stripe.dart'; + +import '_create_setup_intent.dart'; + +class AddPaymentMethodScreenLoader { + const AddPaymentMethodScreenLoader(); + + Future display({ + required BuildContext context, + }) async { + final setupKeys = await createSetupIntent(); + + await Stripe.instance.initPaymentSheet( + paymentSheetParameters: SetupPaymentSheetParameters( + // Main params + setupIntentClientSecret: setupKeys.clientSecret, + customerId: setupKeys.customerId, + merchantDisplayName: 'Comover', + returnURL: 'flutterstripe://redirect', + allowsDelayedPaymentMethods: true, + allowsRemovalOfLastSavedPaymentMethod: false, + intentConfiguration: IntentConfiguration( + mode: IntentMode.setupMode( + currencyCode: 'EUR', + setupFutureUsage: IntentFutureUsage.OffSession, + ), + paymentMethodTypes: ['card', 'bancontact'], + ), + billingDetails: BillingDetails( + name: 'Flutter Stripe', + email: 'email@stripe.com', + phone: '+48888000888', + address: Address( + city: 'Houston', + country: 'US', + line1: '1459 Circle Drive', + line2: '', + state: 'Texas', + postalCode: '77063', + ), + ), + billingDetailsCollectionConfiguration: + BillingDetailsCollectionConfiguration( + address: AddressCollectionMode.full, + ), + // Customer params + // Extra params + // applePay: const PaymentSheetApplePay( + // merchantCountryCode: 'BE', + // ), + googlePay: PaymentSheetGooglePay( + merchantCountryCode: 'BE', + buttonType: stripe.PlatformButtonType.book, + label: 'ADD', + testEnv: kDebugMode), + primaryButtonLabel: 'confirm', + style: Theme.of(context).brightness == Brightness.dark + ? ThemeMode.dark + : ThemeMode.light, + appearance: _buildSheetAppearance(context), + ), + ); + try { + await Stripe.instance.presentPaymentSheet(); + } catch (e) { + if (e is StripeException) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text('Error from Stripe: ${e.error.localizedMessage}'), + ), + ); + } else { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text('Unforeseen error: ${e}'), + ), + ); + } + } + } + + PaymentSheetAppearance _buildSheetAppearance(BuildContext context) { + final colors = Theme.of(context).colorScheme; + return PaymentSheetAppearance( + colors: PaymentSheetAppearanceColors( + background: ElevationOverlay.applySurfaceTint( + colors.surface, + colors.surfaceTint, + 3, + ), + primary: colors.primary, + primaryText: colors.onSurface, + secondaryText: colors.onSurfaceVariant, + error: colors.error, + placeholderText: colors.onSurfaceVariant, + componentBackground: colors.secondaryContainer, + componentText: colors.onSecondaryContainer, + componentDivider: colors.outline, + componentBorder: colors.outline, + icon: colors.onSurface, + ), + shapes: PaymentSheetShape( + borderWidth: 0, + shadow: stripe.PaymentSheetShadowParams( + color: Colors.transparent, + opacity: 0, + ), + ), + primaryButton: PaymentSheetPrimaryButtonAppearance( + colors: PaymentSheetPrimaryButtonTheme( + light: PaymentSheetPrimaryButtonThemeColors( + background: colors.primary, + text: colors.onPrimary, + border: null, + ), + dark: PaymentSheetPrimaryButtonThemeColors( + background: colors.primary, + text: colors.onPrimary, + border: null, + ), + ), + ), + ); + } +} diff --git a/example/lib/screens/setup_future_payments/add_payment_method_screen_loader_web.dart b/example/lib/screens/setup_future_payments/add_payment_method_screen_loader_web.dart new file mode 100644 index 000000000..513ceb667 --- /dev/null +++ b/example/lib/screens/setup_future_payments/add_payment_method_screen_loader_web.dart @@ -0,0 +1,143 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_stripe_web/flutter_stripe_web.dart'; + +import '_create_setup_intent.dart'; + +extension ToHex on Color { + String toRgb() => 'rgb($r, $g, $b)'; +} + +class AddPaymentMethodScreenLoader { + const AddPaymentMethodScreenLoader(); + + Future display({ + required BuildContext context, + }) async { + final setupKeys = await createSetupIntent(); + await Navigator.of(context).push( + MaterialPageRoute( + builder: (context) => _AddPaymentMethodScreenWeb( + setupKeys: setupKeys, + ), + ), + ); + } +} + +class _AddPaymentMethodScreenWeb extends StatefulWidget { + final SetupKeys setupKeys; + + const _AddPaymentMethodScreenWeb({ + required this.setupKeys, + }); + + @override + State<_AddPaymentMethodScreenWeb> createState() => + _AddPaymentMethodScreenPlatformState(); +} + +class _AddPaymentMethodScreenPlatformState + extends State<_AddPaymentMethodScreenWeb> { + bool isSubmitting = false; + bool isComplete = false; + + void _addCard() async { + setState(() => isSubmitting = true); + // will redirect so the next setState should not happen + // unless there is an error + try { + await WebStripe.instance.confirmSetupElement( + ConfirmSetupElementOptions( + confirmParams: ConfirmSetupParams(return_url: Uri.base.toString()), + ), + ); + } finally { + setState(() => isSubmitting = false); + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text('Add payment method'), + ), + body: Column( + children: [ + PaymentElement( + clientSecret: widget.setupKeys.clientSecret, + onCardChanged: (c) { + setState(() => isComplete = c?.complete ?? false); + }, + layout: PaymentElementLayout.tabs, + appearance: buildAppearance(context), + ), + const SizedBox(height: 8), + ElevatedButton( + onPressed: isComplete ? _addCard : null, + child: Text('Add'), + ), + ], + ), + ); + } + + ElementAppearance buildAppearance(BuildContext context) { + final theme = Theme.of(context); + final isDark = theme.brightness == Brightness.dark; + + return ElementAppearance( + theme: isDark ? ElementTheme.night : ElementTheme.none, + variables: { + 'fontFamily': 'roboto, system-ui, sans-serif', + 'colorBackground': theme.colorScheme.surface.toRgb(), + 'colorPrimary': theme.colorScheme.onSurface.toRgb(), + 'colorText': theme.colorScheme.onSurface.toRgb(), + 'colorTextSecondary': theme.colorScheme.onSurfaceVariant.toRgb(), + 'colorTextPlaceholder': theme.colorScheme.onSurfaceVariant.toRgb(), + 'colorSuccess': theme.colorScheme.tertiary.toRgb(), + 'colorDanger': theme.colorScheme.error.toRgb(), + }, + rules: { + '.AccordionItem': { + 'backgroundColor': theme.colorScheme.surface.toRgb(), + 'border': '0', + 'boxShadow': 'none', + }, + '.Input': { + 'backgroundColor': theme.colorScheme.surface.toRgb(), + 'color': theme.colorScheme.onSurface.toRgb(), + 'borderColor': '#a08d85', + 'borderRadius': '0', + 'borderTop': '0', + 'borderLeft': '0', + 'borderRight': '0', + 'boxShadow': '0', + }, + '.Input:focus': { + 'backgroundColor': theme.colorScheme.surface.toRgb(), + 'borderColor': theme.colorScheme.primary.toRgb(), + 'borderRadius': '0', + 'borderTop': '0', + 'borderLeft': '0', + 'borderRight': '0', + 'boxShadow': '0', + }, + '.Input--invalid': { + 'backgroundColor': theme.colorScheme.surface.toRgb(), + 'borderColor': theme.colorScheme.error.toRgb(), + 'borderRadius': '0', + 'borderTop': '0', + 'borderLeft': '0', + 'borderRight': '0', + 'boxShadow': '0', + }, + '.Block': { + 'backgroundColor': theme.colorScheme.surface.toRgb(), + 'boxShadow': 'none', + 'border': '1px solid ${theme.colorScheme.outline.toRgb()}', + }, + }, + ); + } +} diff --git a/example/lib/screens/setup_future_payments/setup_future_payments_screen.dart b/example/lib/screens/setup_future_payments/setup_future_payments_screen.dart new file mode 100644 index 000000000..f037218ef --- /dev/null +++ b/example/lib/screens/setup_future_payments/setup_future_payments_screen.dart @@ -0,0 +1,17 @@ +import 'package:flutter/material.dart'; +import 'package:stripe_example/screens/setup_future_payments/add_payment_method_button.dart'; +import 'package:stripe_example/widgets/example_scaffold.dart'; + +class SetupFuturePaymentsScreen extends StatelessWidget { + const SetupFuturePaymentsScreen({super.key}); + + @override + Widget build(BuildContext context) { + return ExampleScaffold( + title: 'Setup future payments', + children: [ + AddPaymentMethodButton(), + ], + ); + } +} diff --git a/example/server/src/index.ts b/example/server/src/index.ts index 2828d8c00..10ea8728d 100644 --- a/example/server/src/index.ts +++ b/example/server/src/index.ts @@ -369,7 +369,11 @@ app.post('/create-setup-intent', async (req, res) => { //@ts-ignore const setupIntent = await stripe.setupIntents.create({ - ...{ customer: customer.id, payment_method_types }, + ...{ + customer: customer.id, + payment_method_types, + usage: ['off_session'], + }, ...(payment_method_types?.includes('paypal') ? payPalIntentPayload : {}), }); @@ -377,6 +381,7 @@ app.post('/create-setup-intent', async (req, res) => { return res.send({ publishableKey: process.env.STRIPE_PUBLISHABLE_KEY, clientSecret: setupIntent.client_secret, + customerId: customer.id, }); }); @@ -619,7 +624,7 @@ app.post('/payment-sheet-subscription', async (_, res) => { } else { throw new Error( 'Expected response type string, but received: ' + - typeof subscription.pending_setup_intent + typeof subscription.pending_setup_intent ); } }); From a9650ef020a9cda7ad50207f0716efc8fdcf5353 Mon Sep 17 00:00:00 2001 From: cedvandenbosch Date: Thu, 7 Nov 2024 15:09:16 +0100 Subject: [PATCH 12/16] fix return body of create setup --- example/server/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/server/src/index.ts b/example/server/src/index.ts index 10ea8728d..b12d33801 100644 --- a/example/server/src/index.ts +++ b/example/server/src/index.ts @@ -370,7 +370,7 @@ app.post('/create-setup-intent', async (req, res) => { //@ts-ignore const setupIntent = await stripe.setupIntents.create({ ...{ - customer: customer.id, + customerId: customer.id, payment_method_types, usage: ['off_session'], }, From a2e24ff740f86c49d7eedf6e53ffa08d3df6c667 Mon Sep 17 00:00:00 2001 From: cedvandenbosch Date: Thu, 7 Nov 2024 15:18:12 +0100 Subject: [PATCH 13/16] fix web example --- .../_create_setup_intent.dart | 2 +- .../add_payment_method_screen_loader_web.dart | 23 +++++++++++-------- example/server/src/index.ts | 8 +++---- 3 files changed, 18 insertions(+), 15 deletions(-) diff --git a/example/lib/screens/setup_future_payments/_create_setup_intent.dart b/example/lib/screens/setup_future_payments/_create_setup_intent.dart index 310f295bd..5f005eb03 100644 --- a/example/lib/screens/setup_future_payments/_create_setup_intent.dart +++ b/example/lib/screens/setup_future_payments/_create_setup_intent.dart @@ -16,7 +16,7 @@ Future createSetupIntent() async { 'Content-Type': 'application/json', }, body: json.encode({ - 'payment_method_types': ['cards', 'sepa_debit'], + 'payment_method_types': ['card', 'sepa_debit'], }), ); final body = json.decode(response.body); diff --git a/example/lib/screens/setup_future_payments/add_payment_method_screen_loader_web.dart b/example/lib/screens/setup_future_payments/add_payment_method_screen_loader_web.dart index 513ceb667..9c926b78c 100644 --- a/example/lib/screens/setup_future_payments/add_payment_method_screen_loader_web.dart +++ b/example/lib/screens/setup_future_payments/add_payment_method_screen_loader_web.dart @@ -4,7 +4,7 @@ import 'package:flutter_stripe_web/flutter_stripe_web.dart'; import '_create_setup_intent.dart'; extension ToHex on Color { - String toRgb() => 'rgb($r, $g, $b)'; + String toRgb() => 'rgb($red, $green, $blue)'; } class AddPaymentMethodScreenLoader { @@ -64,13 +64,16 @@ class _AddPaymentMethodScreenPlatformState ), body: Column( children: [ - PaymentElement( - clientSecret: widget.setupKeys.clientSecret, - onCardChanged: (c) { - setState(() => isComplete = c?.complete ?? false); - }, - layout: PaymentElementLayout.tabs, - appearance: buildAppearance(context), + Padding( + padding: const EdgeInsets.all(24), + child: PaymentElement( + clientSecret: widget.setupKeys.clientSecret, + onCardChanged: (c) { + setState(() => isComplete = c?.complete ?? false); + }, + layout: PaymentElementLayout.tabs, + appearance: buildAppearance(context), + ), ), const SizedBox(height: 8), ElevatedButton( @@ -86,8 +89,10 @@ class _AddPaymentMethodScreenPlatformState final theme = Theme.of(context); final isDark = theme.brightness == Brightness.dark; + print(theme.colorScheme.surface.toRgb()); + return ElementAppearance( - theme: isDark ? ElementTheme.night : ElementTheme.none, + theme: isDark ? ElementTheme.night : ElementTheme.stripe, variables: { 'fontFamily': 'roboto, system-ui, sans-serif', 'colorBackground': theme.colorScheme.surface.toRgb(), diff --git a/example/server/src/index.ts b/example/server/src/index.ts index b12d33801..86e78295e 100644 --- a/example/server/src/index.ts +++ b/example/server/src/index.ts @@ -369,11 +369,9 @@ app.post('/create-setup-intent', async (req, res) => { //@ts-ignore const setupIntent = await stripe.setupIntents.create({ - ...{ - customerId: customer.id, - payment_method_types, - usage: ['off_session'], - }, + customer: customer.id, + payment_method_types, + usage: 'off_session', ...(payment_method_types?.includes('paypal') ? payPalIntentPayload : {}), }); From 623bdf2fae1fdcdd79c4b98325d0fdd4afc7246a Mon Sep 17 00:00:00 2001 From: cedvandenbosch Date: Thu, 7 Nov 2024 15:37:01 +0100 Subject: [PATCH 14/16] fix example --- .../add_payment_method_screen_loader_mobile.dart | 10 +++++----- .../add_payment_method_screen_loader_web.dart | 4 ++++ 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/example/lib/screens/setup_future_payments/add_payment_method_screen_loader_mobile.dart b/example/lib/screens/setup_future_payments/add_payment_method_screen_loader_mobile.dart index f641d217d..e77f90741 100644 --- a/example/lib/screens/setup_future_payments/add_payment_method_screen_loader_mobile.dart +++ b/example/lib/screens/setup_future_payments/add_payment_method_screen_loader_mobile.dart @@ -51,11 +51,11 @@ class AddPaymentMethodScreenLoader { // applePay: const PaymentSheetApplePay( // merchantCountryCode: 'BE', // ), - googlePay: PaymentSheetGooglePay( - merchantCountryCode: 'BE', - buttonType: stripe.PlatformButtonType.book, - label: 'ADD', - testEnv: kDebugMode), + // googlePay: PaymentSheetGooglePay( + // merchantCountryCode: 'BE', + // label: 'ADD', + // testEnv: kDebugMode, + // ), primaryButtonLabel: 'confirm', style: Theme.of(context).brightness == Brightness.dark ? ThemeMode.dark diff --git a/example/lib/screens/setup_future_payments/add_payment_method_screen_loader_web.dart b/example/lib/screens/setup_future_payments/add_payment_method_screen_loader_web.dart index 9c926b78c..8497d1994 100644 --- a/example/lib/screens/setup_future_payments/add_payment_method_screen_loader_web.dart +++ b/example/lib/screens/setup_future_payments/add_payment_method_screen_loader_web.dart @@ -64,6 +64,10 @@ class _AddPaymentMethodScreenPlatformState ), body: Column( children: [ + SizedBox(height: 12), + Text( + 'This will redirect to the bank in production. In the example it\'s going to redirect to home with the result in the url'), + SizedBox(height: 12), Padding( padding: const EdgeInsets.all(24), child: PaymentElement( From 294489d0b7f94dd1594c54181524de2e0e021435 Mon Sep 17 00:00:00 2001 From: cedvandenbosch Date: Mon, 11 Nov 2024 19:52:44 +0100 Subject: [PATCH 15/16] fix comments --- example/server/src/index.ts | 4 +--- packages/stripe_web/CHANGELOG.md | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/example/server/src/index.ts b/example/server/src/index.ts index 86e78295e..c6c01059d 100644 --- a/example/server/src/index.ts +++ b/example/server/src/index.ts @@ -369,9 +369,7 @@ app.post('/create-setup-intent', async (req, res) => { //@ts-ignore const setupIntent = await stripe.setupIntents.create({ - customer: customer.id, - payment_method_types, - usage: 'off_session', + ...{ customer: customer.id, payment_method_types }, ...(payment_method_types?.includes('paypal') ? payPalIntentPayload : {}), }); diff --git a/packages/stripe_web/CHANGELOG.md b/packages/stripe_web/CHANGELOG.md index 411088617..e41aa4b5c 100644 --- a/packages/stripe_web/CHANGELOG.md +++ b/packages/stripe_web/CHANGELOG.md @@ -1,4 +1,4 @@ -## 6.2.1 +## 6.3.0 **Features** - Add support for any kind of payment method in setup intent with [confirmSetup] From fb1545fbfee05026867523c592fd4ef8250ae390 Mon Sep 17 00:00:00 2001 From: cedvandenbosch Date: Mon, 11 Nov 2024 19:54:18 +0100 Subject: [PATCH 16/16] version --- packages/stripe_web/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/stripe_web/pubspec.yaml b/packages/stripe_web/pubspec.yaml index f18c87373..5f29bcd8f 100644 --- a/packages/stripe_web/pubspec.yaml +++ b/packages/stripe_web/pubspec.yaml @@ -1,6 +1,6 @@ name: flutter_stripe_web description: Stripe sdk bindings for the Flutter web platform. This package contains the implementation of the platform interface for web. -version: 6.2.1 +version: 6.3.0 homepage: https://github.com/flutter-stripe/flutter_stripe environment: