Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sync with React Native 0.17.0 #874

Merged
merged 17 commits into from
Sep 4, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
implement financial connection methods in the plugin
  • Loading branch information
remonh87 committed Aug 23, 2022
commit 8fdcd66a9255c75b77645842ad5c538581430d4c
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:flutter_stripe/flutter_stripe.dart';
import 'package:http/http.dart' as http;
import 'package:stripe_example/widgets/example_scaffold.dart';
import 'package:stripe_example/widgets/loading_button.dart';

import '../../config.dart';
import '../../widgets/response_card.dart';

class FinancialConnectionsScreen extends StatefulWidget {
const FinancialConnectionsScreen({Key? key}) : super(key: key);

@override
State<FinancialConnectionsScreen> createState() =>
_FinancialConnectionsScreenState();
}

class _FinancialConnectionsScreenState
extends State<FinancialConnectionsScreen> {
late String response;

@override
void initState() {
response = '';
super.initState();
}

Future<Map<String, dynamic>> _financialConnectionsSheet() async {
final url = Uri.parse('$kApiUrl/financial-connections-sheet');
final response = await http.post(
url,
headers: {
'Content-Type': 'application/json',
},
);

return json.decode(response.body);
}

Future<void> _collectAccount(BuildContext context) async {
// Precondition:
// 1. Make sure to create a financial connection session on the backend and
// forward the client secret of the session to the app.
final result = await _financialConnectionsSheet();
final clientSecret = await result['clientSecret'];

// 2. use the client secret to confirm the payment and handle the result.
try {
final result = await Stripe.instance.collectFinancialConnectionsAccounts(
clientSecret: clientSecret,
);

setState(() {
response = result.toString();
});
} on Exception 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}'),
),
);
}
}
}
Future<void> _collectBankToken(BuildContext context) async {
// Precondition:
// 1. Make sure to create a financial connection session on the backend and
// forward the client secret of the session to the app.
final result = await _financialConnectionsSheet();
final clientSecret = await result['clientSecret'];

// 2. use the client secret to confirm the payment and handle the result.
try {
final result = await Stripe.instance.collectBankAccountToken(
clientSecret: clientSecret,
);

setState(() {
response = result.toString();
});
} on Exception 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}'),
),
);
}
}
}

@override
Widget build(BuildContext context) {
return ExampleScaffold(
title: 'Financial connections',
tags: ['Financial connections'],
padding: EdgeInsets.all(16),
children: [
LoadingButton(
onPressed: () async {
await _collectAccount(context);
},
text: 'Collect financial account',
),

LoadingButton(
onPressed: () async {
await _collectBankToken(context);
},
text: 'Collect banktoken',
),
Divider(),
SizedBox(height: 20),
ResponseCard(response: response),
],
);
}
}
14 changes: 14 additions & 0 deletions example/lib/screens/screens.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import 'card_payments/custom_card_payment_screen.dart';
import 'card_payments/no_webhook_payment_cardform_screen.dart';
import 'card_payments/no_webhook_payment_screen.dart';
import 'card_payments/webhook_payment_screen.dart';
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';
Expand Down Expand Up @@ -251,6 +252,19 @@ class Example extends StatelessWidget {
// builder: (context) => WeChatPayScreen(),
// ),
]),
ExampleSection(
title: 'Financial connections',
children: [
Example(
title: 'Financial connection sessions',
builder: (_)=> FinancialConnectionsScreen(),
platformsSupported: [
DevicePlatform.android,
DevicePlatform.ios,
],
),
],
),
ExampleSection(title: 'Others', children: [
Example(
title: 'Setup Future Payment',
Expand Down
33 changes: 33 additions & 0 deletions packages/stripe/lib/src/stripe.dart
Original file line number Diff line number Diff line change
Expand Up @@ -443,6 +443,39 @@ class Stripe {
return await _platform.canAddToWallet(last4);
}

/// Call the financial connections authentication flow in order to collect a US bank account to enhance payouts.
///
/// Needs `clientSecret` of the stripe financial connections sessions.
/// For more info see [Add a Financial Connections Account to a US Custom Connect](https://stripe.com/docs/financial-connections/connect-payouts).
///
/// Throws [StripeError] in case creating the token fails.

Future<FinancialConnectionTokenResult> collectBankAccountToken(
{required String clientSecret}) async {
try {
return _platform.collectBankAccountToken(clientSecret: clientSecret);
} on StripeError {
rethrow;
}
}

/// Call the financial connections authentication flow in order to collect the user account data.
///
/// Needs `clientSecret` of the stripe financial connections sessions.
/// For more info see: [Collect an account to build data-powered products](https://stripe.com/docs/financial-connections/other-data-powered-products)
///
/// Throws [StripeError] in case creating the token fails.

Future<FinancialConnectionSessionResult> collectFinancialConnectionsAccounts(
{required String clientSecret}) async {
try {
return _platform.collectFinancialConnectionsAccounts(
clientSecret: clientSecret);
} on StripeError {
rethrow;
}
}

FutureOr<void> _awaitForSettings() {
if (_needsSettings) {
_settingsFuture = applySettings();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import 'dart:io';
import 'package:flutter/services.dart';
import 'package:stripe_platform_interface/src/models/ach_params.dart';
import 'package:stripe_platform_interface/src/models/create_token_data.dart';
import 'package:stripe_platform_interface/src/models/financial_connections.dart';
import 'package:stripe_platform_interface/src/models/google_pay.dart';
import 'package:stripe_platform_interface/src/models/wallet.dart';
import 'package:stripe_platform_interface/src/result_parser.dart';
Expand Down Expand Up @@ -381,6 +382,36 @@ class MethodChannelStripe extends StripePlatform {
),
);
}

@override
Future<FinancialConnectionTokenResult> collectBankAccountToken(
{required String clientSecret}) async {
final result = await _methodChannel
.invokeMapMethod<String, dynamic>('collectBankAccountToken', {
'clientSecret': clientSecret,
});

if (result!.containsKey('error')) {
throw ResultParser<void>(parseJson: (json) => {}).parseError(result);
}

return FinancialConnectionTokenResult.fromJson(result);
}

@override
Future<FinancialConnectionSessionResult> collectFinancialConnectionsAccounts(
{required String clientSecret}) async {
final result = await _methodChannel.invokeMapMethod<String, dynamic>(
'collectFinancialConnectionsAccounts', {
'clientSecret': clientSecret,
});

if (result!.containsKey('error')) {
throw ResultParser<void>(parseJson: (json) => {}).parseError(result);
}

return FinancialConnectionSessionResult.fromJson(result);
}
}

class MethodChannelStripeFactory {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@ class CardStyle with _$CardStyle {
Color? placeholderColor,
}) = _CardStyleConstructor;

factory CardStyle.fromJson(Map<String, dynamic> json) => _$CardStyleFromJson(json);
factory CardStyle.fromJson(Map<String, dynamic> json) =>
_$CardStyleFromJson(json);

CardStyle._();

Expand Down Expand Up @@ -114,7 +115,8 @@ class CardFormStyle with _$CardFormStyle {
Color? placeholderColor,
}) = _CardFormStyleConstructor;

factory CardFormStyle.fromJson(Map<String, dynamic> json) => _$CardFormStyleFromJson(json);
factory CardFormStyle.fromJson(Map<String, dynamic> json) =>
_$CardFormStyleFromJson(json);

CardFormStyle._();

Expand Down Expand Up @@ -150,7 +152,8 @@ class CardPlaceholder with _$CardPlaceholder {
String? postalCode,
}) = _CardPlaceholderConstructor;

factory CardPlaceholder.fromJson(Map<String, dynamic> json) => _$CardPlaceholderFromJson(json);
factory CardPlaceholder.fromJson(Map<String, dynamic> json) =>
_$CardPlaceholderFromJson(json);

CardPlaceholder._();

Expand Down Expand Up @@ -209,16 +212,19 @@ class CardFieldInputDetails with _$CardFieldInputDetails {
@Default(CardValidationState.Unknown) CardValidationState validNumber,
}) = _CardFieldInputDetails;

factory CardFieldInputDetails.fromJson(Map<String, dynamic> json) => _$CardFieldInputDetailsFromJson(json);
factory CardFieldInputDetails.fromJson(Map<String, dynamic> json) =>
_$CardFieldInputDetailsFromJson(json);
}

/// Used to communicate with the card handler on the native platform side when focus changes.
@freezed
class CardFieldFocusName with _$CardFieldFocusName {
@JsonSerializable(explicitToJson: true)
factory CardFieldFocusName({CardFieldName? focusedField}) = _CardFieldFocusName;
factory CardFieldFocusName({CardFieldName? focusedField}) =
_CardFieldFocusName;

factory CardFieldFocusName.fromJson(Map<String, dynamic> json) => _$CardFieldFocusNameFromJson(json);
factory CardFieldFocusName.fromJson(Map<String, dynamic> json) =>
_$CardFieldFocusNameFromJson(json);
}

/// Enum representing the different fiels on the card field.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ class BankAccount with _$BankAccount {
BankAccountStatus? status,

/// Uniquely identifies the particular bank account.
///
///
/// You can use this to check whether or not two bank accounts are the same.
String? fingerprint,

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ class FinancialConnectionSession with _$FinancialConnectionSession {
required String clientSecret,

/// When `true` the object exists in livemode and when false the object exists in test mode.
required String livemode,
required bool livemode,

/// The accounts that were collected as part of this session
required List<FinancialConnectionAccount> accounts,
Expand All @@ -64,7 +64,7 @@ class FinancialConnectionBankAccountToken
BankAccount? bankAccount,

/// When `true` the object exists in livemode and when false the object exists in test mode.
required String livemode,
required bool livemode,

/// Unique id for this token.
String? id,
Expand Down Expand Up @@ -184,24 +184,24 @@ enum AccountCategory { cash, credit, investment, other }

enum AccountSubcategory {
checking,
credit_card,
line_of_credit,
creditCard,
lineOfCredit,
mortgage,
other,
savings,
}

enum FinancialConnectionsPaymentMethodType {
us_bank_account,
usBankAccount,
link,
}

enum AccountPermission {
balances,
ownership,
payment_method,
paymentMethod,
transactions,
account_numbers,
accountNumbers,
}

enum BalanceRefreshStatus { failed, pending, succeeded }
Expand Down
Loading