Skip to content

Commit 9732880

Browse files
Plaid api first commit
0 parents  commit 9732880

9 files changed

Lines changed: 533 additions & 0 deletions

File tree

flutter_plaid/.gitignore

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
# Miscellaneous
2+
*.class
3+
*.log
4+
*.pyc
5+
*.swp
6+
.DS_Store
7+
.atom/
8+
.buildlog/
9+
.history
10+
.svn/
11+
12+
# IntelliJ related
13+
*.iml
14+
*.ipr
15+
*.iws
16+
.idea/
17+
18+
# Visual Studio Code related
19+
.vscode/
20+
21+
# Flutter/Dart/Pub related
22+
**/doc/api/
23+
.dart_tool/
24+
.flutter-plugins
25+
.packages
26+
.pub-cache/
27+
.pub/
28+
build/
29+
30+
# Android related
31+
**/android/**/gradle-wrapper.jar
32+
**/android/.gradle
33+
**/android/captures/
34+
**/android/gradlew
35+
**/android/gradlew.bat
36+
**/android/local.properties
37+
**/android/**/GeneratedPluginRegistrant.java
38+
39+
# iOS/XCode related
40+
**/ios/**/*.mode1v3
41+
**/ios/**/*.mode2v3
42+
**/ios/**/*.moved-aside
43+
**/ios/**/*.pbxuser
44+
**/ios/**/*.perspectivev3
45+
**/ios/**/*sync/
46+
**/ios/**/.sconsign.dblite
47+
**/ios/**/.tags*
48+
**/ios/**/.vagrant/
49+
**/ios/**/DerivedData/
50+
**/ios/**/Icon?
51+
**/ios/**/Pods/
52+
**/ios/**/.symlinks/
53+
**/ios/**/profile
54+
**/ios/**/xcuserdata
55+
**/ios/.generated/
56+
**/ios/Flutter/App.framework
57+
**/ios/Flutter/Flutter.framework
58+
**/ios/Flutter/Generated.xcconfig
59+
**/ios/Flutter/app.flx
60+
**/ios/Flutter/app.zip
61+
**/ios/Flutter/flutter_assets/
62+
**/ios/ServiceDefinitions.json
63+
**/ios/Runner/GeneratedPluginRegistrant.*
64+
65+
# Exceptions to above rules.
66+
!**/ios/**/default.mode1v3
67+
!**/ios/**/default.mode2v3
68+
!**/ios/**/default.pbxuser
69+
!**/ios/**/default.perspectivev3
70+
!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages

flutter_plaid/.metadata

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# This file tracks properties of this Flutter project.
2+
# Used by Flutter tool to assess capabilities and perform upgrades etc.
3+
#
4+
# This file should be version controlled and should not be manually edited.
5+
6+
version:
7+
revision: 7a4c33425ddd78c54aba07d86f3f9a4a0051769b
8+
channel: beta
9+
10+
project_type: package

flutter_plaid/CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
## [0.0.1] - TODO: Add release date.
2+
3+
* TODO: Describe initial release.

flutter_plaid/LICENSE

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
TODO: Add your license here.

flutter_plaid/README.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
Flutter for Plaid Link
2+
3+
## Getting Started
4+
5+
```dart
6+
class _Example extends State {
7+
8+
showPlaidView() {
9+
bool plaidSandbox = false;
10+
11+
Configuration configuration = Configuration(
12+
plaidPublicKey: 'yourPublicKey',
13+
plaidBaseUrl: 'https://cdn.plaid.com/link/v2/stable/link.html',
14+
plaidEnvironment: plaidSandbox ? 'sandbox' : 'production',
15+
environmentPlaidPathAccessToken:
16+
'https://sandbox.plaid.com/item/public_token/exchange',
17+
environmentPlaidPathStripeToken:
18+
'https://sandbox.plaid.com/processor/stripe/bank_account_token/create',
19+
plaidClientId: 'yourPlaidClientId',
20+
secret: plaidSandbox ? 'yourSecret' : '');
21+
22+
FlutterPlaidApi flutterPlaidApi = FlutterPlaidApi(configuration);
23+
flutterPlaidApi.launch(context, (Result result) {
24+
///handle result
25+
}, stripeToken: true);
26+
}
27+
28+
@override
29+
Widget build(BuildContext context) {
30+
return Container();
31+
}
32+
}
33+
34+
```

flutter_plaid/lib/example.dart

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import 'package:flutter/widgets.dart';
2+
import 'package:flutter_plaid/flutter_plaid.dart';
3+
4+
class _Example extends State {
5+
6+
showPlaidView() {
7+
bool plaidSandbox = false;
8+
9+
Configuration configuration = Configuration(
10+
plaidPublicKey: 'yourPublicKey',
11+
plaidBaseUrl: 'https://cdn.plaid.com/link/v2/stable/link.html',
12+
plaidEnvironment: plaidSandbox ? 'sandbox' : 'production',
13+
environmentPlaidPathAccessToken:
14+
'https://sandbox.plaid.com/item/public_token/exchange',
15+
environmentPlaidPathStripeToken:
16+
'https://sandbox.plaid.com/processor/stripe/bank_account_token/create',
17+
plaidClientId: 'yourPlaidClientId',
18+
secret: plaidSandbox ? 'yourSecret' : '');
19+
20+
FlutterPlaidApi flutterPlaidApi = FlutterPlaidApi(configuration);
21+
flutterPlaidApi.launch(context, (Result result) {
22+
///handle result
23+
}, stripeToken: true);
24+
}
25+
26+
@override
27+
Widget build(BuildContext context) {
28+
return Container();
29+
}
30+
}
Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
library flutter_plaid;
2+
3+
import 'dart:convert';
4+
5+
import 'package:flutter/material.dart';
6+
import 'package:flutter/widgets.dart';
7+
import 'package:http/http.dart';
8+
import 'package:webview_flutter/webview_flutter.dart';
9+
10+
class FlutterPlaidApi {
11+
Configuration _configuration;
12+
13+
FlutterPlaidApi(Configuration configuration) {
14+
this._configuration = configuration;
15+
}
16+
17+
/// stripeToken = false use for get plaid token and accountId
18+
/// stripeToken = true: use for add the new payment method, returns stripe_token
19+
launch(BuildContext context, success(Result result),
20+
{bool stripeToken = false}) {
21+
_WebViewPage _webViewPage = new _WebViewPage();
22+
_webViewPage._init(this._configuration, success, stripeToken, context);
23+
24+
Navigator.push(context, MaterialPageRoute(builder: (BuildContext context) {
25+
return _webViewPage.build(context);
26+
}));
27+
}
28+
}
29+
30+
class _WebViewPage {
31+
String _url;
32+
Function(Result result) _success;
33+
Configuration _config;
34+
bool _stripeToken;
35+
BuildContext _context;
36+
37+
_init(Configuration config, success(Result result), bool stripeToken,
38+
BuildContext context) {
39+
this._success = success;
40+
this._config = config;
41+
this._stripeToken = stripeToken;
42+
this._context = context;
43+
_url = config.plaidBaseUrl +
44+
'?key=' +
45+
config.plaidPublicKey +
46+
'&isWebview=true' +
47+
'&product=auth' +
48+
'&isMobile=true' +
49+
'&apiVersion=v2' +
50+
'&selectAccount=true' +
51+
'&webhook=https://requestb.in' +
52+
'&env=' +
53+
config.plaidEnvironment;
54+
debugPrint('init plaid: ' + _url);
55+
}
56+
57+
_parseUrl(String url) {
58+
if (url?.isNotEmpty != null) {
59+
Uri uri = Uri.parse(url);
60+
debugPrint('PLAID uri: ' + uri.toString());
61+
Map<String, String> queryParams = uri.queryParameters;
62+
List<String> segments = uri.pathSegments;
63+
debugPrint('queryParams: ' + queryParams?.toString());
64+
debugPrint('segments: ' + segments?.toString());
65+
_processParams(queryParams, url);
66+
}
67+
}
68+
69+
_processParams(Map<String, String> queryParams, String url) async {
70+
if (queryParams != null) {
71+
String eventName = queryParams['event_name'] ?? 'unknow';
72+
debugPrint("PLAID Event name: " + eventName);
73+
74+
if (eventName == 'EXIT' || (url?.contains('/exit?') ?? false)) {
75+
this._closeWebView();
76+
} else if (eventName == 'HANDOFF') {
77+
this._closeWebView();
78+
}
79+
dynamic token = queryParams['public_token'];
80+
dynamic accountId = queryParams['account_id'];
81+
if (token != null && accountId != null) {
82+
if (!_stripeToken) {
83+
this._success(Result(token, accountId, queryParams));
84+
} else {
85+
await this._fetchStripeToken(token, accountId);
86+
}
87+
}
88+
}
89+
}
90+
91+
_fetchStripeToken(String token, String accountId) async {
92+
var headers = {'Content-Type': 'application/json'};
93+
94+
Response responseAccessToken =
95+
await post(_config.environmentPlaidPathAccessToken,
96+
body: json.encode({
97+
'public_token': token,
98+
'client_id': this._config.plaidClientId,
99+
'secret': this._config.secret
100+
}),
101+
headers: headers);
102+
var accessTokenData =
103+
json.decode(utf8.decode(responseAccessToken.bodyBytes));
104+
String accessToken = accessTokenData['access_token'];
105+
106+
Response responseStripeToken =
107+
await post(_config.environmentPlaidPathStripeToken,
108+
body: json.encode({
109+
'client_id': this._config.plaidClientId,
110+
'secret': this._config.secret,
111+
'access_token': accessToken,
112+
'account_id': accountId
113+
}),
114+
headers: headers);
115+
116+
var stripeTokenData =
117+
json.decode(utf8.decode(responseStripeToken.bodyBytes));
118+
_success(Result(
119+
stripeTokenData['stripe_bank_account_token'], null, stripeTokenData));
120+
}
121+
122+
_closeWebView() {
123+
if (_context != null && Navigator.canPop(_context)) {
124+
Navigator.pop(_context);
125+
}
126+
}
127+
128+
Widget build(BuildContext context) {
129+
var webView = new WebView(
130+
initialUrl: _url,
131+
javascriptMode: JavascriptMode.unrestricted,
132+
navigationDelegate: (NavigationRequest navigation) {
133+
if (navigation.url.contains('plaidlink://')) {
134+
this._parseUrl(navigation.url);
135+
return NavigationDecision.prevent;
136+
}
137+
return NavigationDecision.navigate;
138+
},
139+
);
140+
return Scaffold(body: webView);
141+
}
142+
}
143+
144+
class Configuration {
145+
String plaidPublicKey;
146+
String plaidBaseUrl;
147+
String plaidEnvironment;
148+
String environmentPlaidPathAccessToken;
149+
String environmentPlaidPathStripeToken;
150+
String plaidClientId;
151+
String secret;
152+
153+
Configuration(
154+
{@required this.plaidPublicKey,
155+
@required this.plaidBaseUrl,
156+
@required this.plaidEnvironment,
157+
@required this.environmentPlaidPathAccessToken,
158+
@required this.environmentPlaidPathStripeToken,
159+
@required this.plaidClientId,
160+
@required this.secret});
161+
}
162+
163+
class Result {
164+
String token;
165+
String accountId;
166+
dynamic response;
167+
168+
Result(this.token, this.accountId, this.response);
169+
}

0 commit comments

Comments
 (0)