Skip to content

Commit d2f08cd

Browse files
Implement aubecs platformview (#481)
* implement aubecs debit formview on android * create initial widget for aubecs form * add styling for the form * create controller that supplies the data entered by the user * add keys for aubecs payment to example env file * feat: add ios AuBECSDebitFormFactory * refactor: autoformat files * make screen look bit better on android Co-authored-by: Jaime Blasco <[email protected]>
1 parent f507836 commit d2f08cd

25 files changed

+1777
-24
lines changed

example/ios/Podfile.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,4 +45,4 @@ SPEC CHECKSUMS:
4545

4646
PODFILE CHECKSUM: 4e8f8b2be68aeea4c0d5beb6ff1e79fface1d048
4747

48-
COCOAPODS: 1.10.1
48+
COCOAPODS: 1.11.2

example/lib/screens/card_payments/no_webhook_payment_cardform_screen.dart

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,6 @@ class _NoWebhookPaymentCardFormScreenState
6565
onPressed: () => controller.blur(),
6666
child: Text('Blur'),
6767
),
68-
6968
],
7069
),
7170
),
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
import 'dart:convert';
2+
3+
import 'package:flutter/material.dart';
4+
import 'package:flutter_stripe/flutter_stripe.dart';
5+
import 'package:http/http.dart' as http;
6+
import 'package:stripe_example/widgets/example_scaffold.dart';
7+
import 'package:stripe_example/widgets/loading_button.dart';
8+
9+
import '../../config.dart';
10+
11+
class AubecsExample extends StatefulWidget {
12+
const AubecsExample({Key? key}) : super(key: key);
13+
14+
@override
15+
State<AubecsExample> createState() => _AubecsExampleState();
16+
}
17+
18+
class _AubecsExampleState extends State<AubecsExample> {
19+
late AubecsEditFormController _controller;
20+
bool isCompleted = false;
21+
AubecsFormInputDetails? _details;
22+
23+
@override
24+
void initState() {
25+
_controller = AubecsEditFormController()
26+
..addListener(() {
27+
setState(() {
28+
isCompleted = _controller.isComplete;
29+
_details = _controller.data;
30+
});
31+
});
32+
super.initState();
33+
}
34+
35+
@override
36+
void dispose() {
37+
_controller.dispose();
38+
super.dispose();
39+
}
40+
41+
@override
42+
Widget build(BuildContext context) {
43+
return ExampleScaffold(
44+
title: 'Aubecs',
45+
tags: ['Aubecs'],
46+
padding: EdgeInsets.symmetric(horizontal: 16),
47+
children: [
48+
AubecsFormField(
49+
controller: _controller,
50+
style: AubecsFormStyle(
51+
textColor: Colors.black,
52+
placeholderColor: Colors.blueAccent,
53+
backgroundColor: Colors.grey[400],
54+
borderColor: Colors.green,
55+
textErrorColor: Colors.red,
56+
borderWidth: 3,
57+
borderRadius: 8,
58+
fontSize: 16,
59+
),
60+
companyName: 'Flutter stripe',
61+
),
62+
LoadingButton(
63+
text: 'Pay',
64+
onPressed: _details != null
65+
? () async {
66+
await _pay(context);
67+
}
68+
: null,
69+
),
70+
],
71+
);
72+
}
73+
74+
Future<Map<String, dynamic>> _createPaymentIntent() async {
75+
final url = Uri.parse('$kApiUrl/create-payment-intent');
76+
final response = await http.post(
77+
url,
78+
headers: {
79+
'Content-Type': 'application/json',
80+
},
81+
body: json.encode({
82+
'currency': 'aud',
83+
'payment_method_types': ['au_becs_debit'],
84+
'amount': 100
85+
}),
86+
);
87+
88+
return json.decode(response.body);
89+
}
90+
91+
Future<void> _pay(BuildContext context) async {
92+
// Precondition:
93+
//Make sure to have set a custom URI scheme in your app and add it to Stripe SDK
94+
// see file main.dart in this example app.
95+
// 1. on the backend create a payment intent for payment method and save the
96+
// client secret.
97+
final result = await _createPaymentIntent();
98+
final clientSecret = await result['clientSecret'];
99+
100+
// 2. use the client secret to confirm the payment and handle the result.
101+
try {
102+
await Stripe.instance.confirmPayment(
103+
clientSecret,
104+
PaymentMethodParams.aubecs(formDetails: _details!),
105+
);
106+
107+
ScaffoldMessenger.of(context).showSnackBar(
108+
SnackBar(
109+
content: Text('Payment succesfully completed'),
110+
),
111+
);
112+
} on Exception catch (e) {
113+
if (e is StripeException) {
114+
ScaffoldMessenger.of(context).showSnackBar(
115+
SnackBar(
116+
content: Text('Error from Stripe: ${e.error.localizedMessage}'),
117+
),
118+
);
119+
} else {
120+
ScaffoldMessenger.of(context).showSnackBar(
121+
SnackBar(
122+
content: Text('Unforeseen error: ${e}'),
123+
),
124+
);
125+
}
126+
}
127+
}
128+
}

example/lib/screens/regional_payment_methods/wechat_pay_screen.dart

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
1-
21
import 'package:flutter/material.dart';
32
import 'package:flutter_stripe/flutter_stripe.dart';
43
import 'package:stripe_example/widgets/example_scaffold.dart';
54
import 'package:stripe_example/widgets/loading_button.dart';
65

7-
86
class WeChatPayScreen extends StatelessWidget {
97
const WeChatPayScreen({Key? key}) : super(key: key);
108

@@ -42,8 +40,8 @@ class WeChatPayScreen extends StatelessWidget {
4240
// final clientSecret = await result['clientSecret'];
4341

4442
// 3. use the client secret to confirm the payment and handle the result.
45-
46-
// TODO: uncomment when wechat is enabled again
43+
44+
// TODO: uncomment when wechat is enabled again
4745

4846
// await Stripe.instance.confirmPayment(
4947
// clientSecret,

example/lib/screens/screens.dart

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
22
import 'package:stripe_example/screens/payment_sheet/payment_sheet_screen.dart';
33
import 'package:stripe_example/screens/payment_sheet/payment_sheet_screen_custom_flow.dart';
44
import 'package:stripe_example/screens/regional_payment_methods/ali_pay_screen.dart';
5+
import 'package:stripe_example/screens/regional_payment_methods/aubecs_debit.dart';
56
import 'package:stripe_example/screens/regional_payment_methods/ideal_screen.dart';
67
import 'package:stripe_example/screens/wallets/apple_pay_screen.dart';
78
import 'package:stripe_example/screens/wallets/apple_pay_screen_plugin.dart';
@@ -175,6 +176,10 @@ class Example extends StatelessWidget {
175176
),
176177
builder: (context) => IdealScreen(),
177178
),
179+
Example(
180+
title: 'Aubecs',
181+
builder: (context) => AubecsExample(),
182+
)
178183
// TODO: uncomment when we can re-enable wechat pay
179184
// Example(
180185
// title: 'WeChat Pay',

example/lib/screens/wallets/apple_pay_screen_plugin.dart

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,5 +112,3 @@ class _ApplePayExternalPluginScreenState
112112
return json.decode(response.body);
113113
}
114114
}
115-
116-

example/server/.env.example

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22
STRIPE_PUBLISHABLE_KEY=pk_test
33
STRIPE_SECRET_KEY=sk_test
44

5+
#keys for aubecs_payment
6+
STRIPE_PUBLISHABLE_KEY_AU=pk_test
7+
STRIPE_SECRET_KEY_AU=sk_test
8+
59
# Required to run webhook
610
# See README on how to use the Stripe CLI to setup
711
# Ignore when running `without-webhooks` samples

packages/stripe/lib/flutter_stripe.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ export 'package:stripe_platform_interface/stripe_platform_interface.dart';
33
export 'src/model/apple_pay_button.dart';
44
export 'src/stripe.dart';
55
export 'src/widgets/apple_pay_button.dart';
6+
export 'src/widgets/aubecs_debit_form.dart';
67
export 'src/widgets/card_field.dart';
78
export 'src/widgets/card_form_field.dart';
89
export 'src/widgets/google_pay_button.dart';
Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
import 'package:flutter/foundation.dart';
2+
import 'package:flutter/gestures.dart';
3+
import 'package:flutter/rendering.dart';
4+
import 'package:flutter/services.dart';
5+
import 'package:flutter/widgets.dart';
6+
import 'package:stripe_platform_interface/stripe_platform_interface.dart';
7+
8+
class AubecsFormField extends StatelessWidget {
9+
const AubecsFormField({
10+
this.height = 300,
11+
this.style,
12+
this.companyName,
13+
this.controller,
14+
Key? key,
15+
}) : super(key: key);
16+
17+
@override
18+
Widget build(BuildContext context) {
19+
return _AubecsFormField(
20+
height: height,
21+
style: style,
22+
controller: controller,
23+
companyName: companyName,
24+
);
25+
}
26+
27+
final AubecsFormStyle? style;
28+
final AubecsEditFormController? controller;
29+
final String? companyName;
30+
final double height;
31+
}
32+
33+
class _AubecsFormField extends StatefulWidget {
34+
const _AubecsFormField({
35+
required this.height,
36+
this.style,
37+
this.companyName,
38+
this.controller,
39+
Key? key,
40+
}) : super(key: key);
41+
42+
@override
43+
_AubecsFormFieldState createState() => _AubecsFormFieldState();
44+
45+
final AubecsFormStyle? style;
46+
final AubecsEditFormController? controller;
47+
final String? companyName;
48+
final double height;
49+
}
50+
51+
class _AubecsFormFieldState extends State<_AubecsFormField> {
52+
static const _viewType = 'flutter.stripe/aubecs_form_field';
53+
54+
MethodChannel? _methodChannel;
55+
AubecsFormStyle? _lastStyle;
56+
57+
AubecsEditFormController? _fallbackContoller;
58+
AubecsEditFormController get controller {
59+
if (widget.controller != null) return widget.controller!;
60+
_fallbackContoller ??= AubecsEditFormController();
61+
return _fallbackContoller!;
62+
}
63+
64+
@override
65+
void initState() {
66+
super.initState();
67+
}
68+
69+
@override
70+
void didChangeDependencies() {
71+
_lastStyle ??= widget.style;
72+
final style = widget.style;
73+
74+
if (style != _lastStyle && style != null) {
75+
_methodChannel?.invokeMethod('onStyleChanged', {
76+
'formStyle': style.toJson(),
77+
});
78+
}
79+
80+
_lastStyle = style;
81+
super.didChangeDependencies();
82+
}
83+
84+
@override
85+
void didUpdateWidget(covariant _AubecsFormField oldWidget) {
86+
if (widget.style != null && widget.style != oldWidget.style) {
87+
_methodChannel?.invokeMethod('onStyleChanged', {
88+
'formStyle': widget.style!.toJson(),
89+
});
90+
}
91+
if (widget.companyName != oldWidget.companyName) {
92+
_methodChannel?.invokeMethod('onCompanyNameChanged', {
93+
'companyName': widget.companyName ?? '',
94+
});
95+
}
96+
97+
_lastStyle = widget.style;
98+
super.didUpdateWidget(oldWidget);
99+
}
100+
101+
void onPlatformViewCreated(int viewId) {
102+
_methodChannel = MethodChannel('flutter.stripe/aubecs_form_field/$viewId');
103+
_methodChannel!.setMethodCallHandler((call) async {
104+
if (call.method == 'onCompleteAction') {
105+
final tmp = _createData(call.arguments);
106+
controller.data = tmp;
107+
}
108+
});
109+
}
110+
111+
AubecsFormInputDetails _createData(dynamic rawData) {
112+
final map = Map<String, dynamic>.from(rawData);
113+
return AubecsFormInputDetails.fromJson(map);
114+
}
115+
116+
@override
117+
Widget build(BuildContext context) {
118+
final creationParams = <String, Object>{
119+
'formStyle': widget.style?.toJson() ?? const AubecsFormStyle(),
120+
'companyName': widget.companyName ?? '',
121+
};
122+
123+
return SizedBox(
124+
height: widget.height,
125+
child: defaultTargetPlatform == TargetPlatform.iOS
126+
? UiKitView(
127+
viewType: _viewType,
128+
creationParamsCodec: const StandardMessageCodec(),
129+
creationParams: creationParams,
130+
onPlatformViewCreated: onPlatformViewCreated,
131+
)
132+
: PlatformViewLink(
133+
surfaceFactory: (context, controller) {
134+
return AndroidViewSurface(
135+
controller: controller as AndroidViewController,
136+
hitTestBehavior: PlatformViewHitTestBehavior.opaque,
137+
gestureRecognizers: const <
138+
Factory<OneSequenceGestureRecognizer>>{},
139+
);
140+
},
141+
onCreatePlatformView: (params) {
142+
onPlatformViewCreated(params.id);
143+
return PlatformViewsService.initSurfaceAndroidView(
144+
id: params.id,
145+
viewType: _viewType,
146+
layoutDirection: TextDirection.ltr,
147+
creationParams: creationParams,
148+
creationParamsCodec: const StandardMessageCodec(),
149+
)
150+
..addOnPlatformViewCreatedListener(
151+
params.onPlatformViewCreated)
152+
..create();
153+
},
154+
viewType: _viewType,
155+
),
156+
);
157+
}
158+
}
159+
160+
class AubecsEditFormController extends ChangeNotifier {
161+
set data(AubecsFormInputDetails? newData) {
162+
if (data == newData) {
163+
return;
164+
} else {
165+
_data = newData;
166+
notifyListeners();
167+
}
168+
}
169+
170+
bool get isComplete =>
171+
_data?.bsbNumber != null &&
172+
_data?.accountNumber != null &&
173+
_data?.email != null &&
174+
_data?.name != null;
175+
176+
AubecsFormInputDetails? get data => _data;
177+
178+
AubecsFormInputDetails? _data;
179+
}

0 commit comments

Comments
 (0)