Skip to content

Commit f4e03de

Browse files
authored
feat: stripe js (flutter-stripe#1052)
* feat: stripe js library * test: add tests for api and endpoints * fix: path workflow * fix: publish stripe_js * ci: fix workflow * fix: add stripe_js docs * fix: change license * fix: update examples * fix: analyze warnings
1 parent 43635ec commit f4e03de

File tree

214 files changed

+39485
-4040
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

214 files changed

+39485
-4040
lines changed

.github/workflows/stripe_js.yaml

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
name: stripe_js
2+
3+
on:
4+
push:
5+
branches: [master]
6+
paths-ignore:
7+
- "docs/**"
8+
- "website/**"
9+
- "**.md"
10+
pull_request:
11+
branches: ['**']
12+
paths-ignore:
13+
- "docs/**"
14+
- "website/**"
15+
- "**.md"
16+
17+
jobs:
18+
build:
19+
defaults:
20+
run:
21+
working-directory: packages/stripe_js
22+
23+
runs-on: ubuntu-latest
24+
25+
steps:
26+
- name: 📚 Git Checkout
27+
uses: actions/checkout@v3
28+
29+
- name: 🎯 Setup Dart
30+
uses: dart-lang/setup-dart@v1
31+
32+
- name: 📦 Install Dependencies
33+
run: dart pub get
34+
35+
- name: "Set env keys"
36+
env:
37+
STRIPE_PUBLISHABLE_KEY: ${{ secrets.STRIPE_PUBLISHABLE_KEY }}
38+
run: |
39+
cd ../../
40+
./.github/workflows/scripts/env-files.sh
41+
42+
- name: ✨ Check Formatting
43+
run: dart format --set-exit-if-changed .
44+
45+
- name: 🕵️ Analyze
46+
run: dart analyze --fatal-infos --fatal-warnings lib test
47+
48+
- name: 🧪 Run Tests
49+
run: |
50+
dart pub global activate coverage
51+
dart test -j 4 --coverage=coverage --platform=chrome && dart pub global run coverage:format_coverage --lcov --in=coverage --out=coverage/lcov.info --packages=.dart_tool/package_config.json --report-on=lib
52+
53+
- name: 📊 Check Code Coverage
54+
uses: VeryGoodOpenSource/very_good_coverage@v2
55+
with:
56+
path: packages/stripe_js/coverage/lcov.info
57+
exclude: "lib/**/*.g.dart lib/**/*.freezed.dart"
58+
min_coverage: 0

.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,4 +60,10 @@ build/
6060
lcov.info
6161

6262
**/pubspec.lock
63+
6364
**/server/package-lock.json
65+
66+
**/node_modules/
67+
/test-results/
68+
/playwright-report/
69+
/playwright/.cache/

.vscode/launch.json

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,27 @@
1010
"request": "launch",
1111
"type": "dart",
1212
},
13+
{
14+
"name": "Stripe.js web tests",
15+
"request": "launch",
16+
"type": "dart",
17+
"program": "packages/stripe_js/test",
18+
"args": [
19+
"-p",
20+
"chrome",
21+
"-t",
22+
"browser"
23+
], // Additional args to pass
24+
"codeLens": {
25+
"for": [
26+
"run-test",
27+
"run-test-file",
28+
"debug-test",
29+
"debug-test-file"
30+
],
31+
"path": "packages/stripe_js/test",
32+
"title": "${debugType} (chrome)"
33+
},
34+
},
1335
]
1436
}

example/lib/screens/checkout/checkout_screen.dart

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import 'package:flutter/material.dart';
1111
import 'package:stripe_checkout/stripe_checkout.dart';
1212
import 'package:http/http.dart' as http;
1313

14-
1514
class CheckoutScreenExample extends StatefulWidget {
1615
CheckoutScreenExample({
1716
Key? key,

example/lib/screens/financial_connections.dart/financial_connections_session_screen.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ class _FinancialConnectionsScreenState
7171
}
7272
}
7373
}
74+
7475
Future<void> _collectBankToken(BuildContext context) async {
7576
// Precondition:
7677
// 1. Make sure to create a financial connection session on the backend and
@@ -117,7 +118,6 @@ class _FinancialConnectionsScreenState
117118
},
118119
text: 'Collect financial account',
119120
),
120-
121121
LoadingButton(
122122
onPressed: () async {
123123
await _collectBankToken(context);

example/lib/screens/others/cvc_re_collection_screen.dart

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -65,12 +65,13 @@ class _CVCReCollectionScreenState extends State<CVCReCollectionScreen> {
6565
final paymentMethod = await _fetchPaymentIntentWithPaymentMethod();
6666

6767
if (paymentMethod['error'] != null) {
68-
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('Error code: ${paymentMethod['error']}')));
68+
ScaffoldMessenger.of(context).showSnackBar(
69+
SnackBar(content: Text('Error code: ${paymentMethod['error']}')));
6970
return;
7071
}
7172

72-
ScaffoldMessenger.of(context)
73-
.showSnackBar(SnackBar(content: Text('Success!: The payment was confirmed successfully!')));
73+
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
74+
content: Text('Success!: The payment was confirmed successfully!')));
7475
}
7576

7677
Future<void> _paySynchronously() async {
@@ -83,10 +84,11 @@ class _CVCReCollectionScreenState extends State<CVCReCollectionScreen> {
8384
);
8485
log('paymentIntent $paymentIntent');
8586
if (paymentIntent['error'] != null) {
86-
ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('Error code: ${paymentIntent['error']}')));
87+
ScaffoldMessenger.of(context).showSnackBar(
88+
SnackBar(content: Text('Error code: ${paymentIntent['error']}')));
8789
} else if (paymentIntent['succeeded'] == true) {
88-
ScaffoldMessenger.of(context)
89-
.showSnackBar(SnackBar(content: Text('Success!: The payment was confirmed successfully!')));
90+
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
91+
content: Text('Success!: The payment was confirmed successfully!')));
9092
} else {
9193
// Handle other statuses accordingly
9294
throw UnimplementedError();

example/lib/screens/others/legacy_token_bank_screen.dart

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,11 @@ class _LegacyTokenBankScreenState extends State<LegacyTokenBankScreen> {
5858
try {
5959
// 1. Gather customer billing information (ex. email)
6060
final params = BankAccountTokenParams(
61-
currency: 'EUR',
62-
country: 'DE',
63-
accountNumber: _controller.text,
64-
accountHolderName: 'Dash Flutter',
65-
accountHolderType: BankAccountHolderType.Individual,
61+
currency: 'EUR',
62+
country: 'DE',
63+
accountNumber: _controller.text,
64+
accountHolderName: 'Dash Flutter',
65+
accountHolderType: BankAccountHolderType.Individual,
6666
); // mocked data for tests
6767

6868
// 2. Create payment method

example/lib/screens/others/legacy_token_card_screen.dart

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -61,12 +61,9 @@ class _LegacyTokenCardScreenState extends State<LegacyTokenCardScreen> {
6161
); // mocked data for tests
6262

6363
// 2. Create payment method
64-
final tokenData =
65-
await Stripe.instance.createToken(CreateTokenParams.card(
66-
params: CardTokenParams(
67-
address: address,
68-
currency: 'USD'
69-
)));
64+
final tokenData = await Stripe.instance.createToken(
65+
CreateTokenParams.card(
66+
params: CardTokenParams(address: address, currency: 'USD')));
7067
setState(() {
7168
this.tokenData = tokenData;
7269
});

example/lib/screens/others/setup_future_payment_screen.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ class _SetupFuturePaymentScreenState extends State<SetupFuturePaymentScreen> {
127127
// 3. Confirm setup intent
128128

129129
final setupIntentResult = await Stripe.instance.confirmSetupIntent(
130-
paymentIntentClientSecret: clientSecret,
130+
paymentIntentClientSecret: clientSecret,
131131
params: PaymentMethodParams.card(
132132
paymentMethodData: PaymentMethodData(
133133
billingDetails: billingDetails,

example/lib/screens/web/src/payment_element.dart renamed to example/lib/screens/payment_sheet/payment_element/payment_element.dart

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ import 'package:flutter/material.dart';
44
import 'package:stripe_example/config.dart';
55

66
import 'package:http/http.dart' as http;
7+
import 'platforms/payment_element.dart'
8+
if (dart.library.js) 'platforms/payment_element_web.dart';
9+
import 'package:stripe_example/widgets/loading_button.dart';
710

811
class PaymentElementExample extends StatefulWidget {
912
@override
@@ -44,32 +47,29 @@ class _ThemeCardExampleState extends State<PaymentElementExample> {
4447
),
4548
body: Column(
4649
children: [
47-
// Container(
48-
// height: 150,
49-
// alignment: Alignment.center,
50-
// padding: EdgeInsets.symmetric(vertical: 20, horizontal: 20),
51-
// child: clientSecret != null ? PaymentElement(
52-
// autofocus: true,
53-
// enablePostalCode: true,
54-
// onCardChanged: (_) {},
55-
// clientSecret: clientSecret ?? '',
56-
// ) : Center(
57-
// child: CircularProgressIndicator(),
58-
// )
59-
// )
50+
Container(
51+
child: clientSecret != null
52+
? PlatformPaymentElement(clientSecret)
53+
: Center(child: CircularProgressIndicator())),
54+
LoadingButton(onPressed: pay, text: 'Pay'),
6055
],
6156
),
6257
);
6358
}
6459

6560
Future<String> createPaymentIntent() async {
66-
final url = Uri.parse('$kApiUrl/universal-payment');
61+
final url = Uri.parse('$kApiUrl/create-payment-intent');
6762
final response = await http.post(
6863
url,
6964
headers: {
7065
'Content-Type': 'application/json',
7166
},
72-
body: json.encode({}),
67+
body: json.encode({
68+
'currency': 'usd',
69+
'amount': 1099,
70+
'payment_method_types': ['card'],
71+
'request_three_d_secure': 'any',
72+
}),
7373
);
7474
return json.decode(response.body)['clientSecret'];
7575
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import 'package:flutter/widgets.dart';
2+
3+
Future<void> pay() async {
4+
throw UnimplementedError();
5+
}
6+
7+
class PlatformPaymentElement extends StatelessWidget {
8+
const PlatformPaymentElement(this.clientSecret);
9+
10+
final String? clientSecret;
11+
12+
@override
13+
Widget build(BuildContext context) {
14+
return Container();
15+
}
16+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import 'package:flutter/widgets.dart';
2+
import 'package:flutter_stripe_web/flutter_stripe_web.dart';
3+
4+
import '../../../checkout/platforms/stripe_checkout_web.dart';
5+
6+
Future<void> pay() async {
7+
await WebStripe().confirmPaymentElement(
8+
ConfirmPaymentElementOptions(
9+
confirmParams: ConfirmPaymentParams(return_url: getReturnUrl()),
10+
),
11+
);
12+
}
13+
14+
class PlatformPaymentElement extends StatelessWidget {
15+
const PlatformPaymentElement(this.clientSecret);
16+
17+
final String? clientSecret;
18+
19+
@override
20+
Widget build(BuildContext context) {
21+
return PaymentElement(
22+
autofocus: true,
23+
enablePostalCode: true,
24+
onCardChanged: (_) {},
25+
clientSecret: clientSecret ?? '',
26+
);
27+
}
28+
}

example/lib/screens/regional_payment_methods/ali_pay_screen.dart

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,9 @@ class AliPayScreen extends StatelessWidget {
4040
// 2. use the client secret to confirm the payment and handle the result.
4141
try {
4242
await Stripe.instance.confirmPayment(
43-
paymentIntentClientSecret: clientSecret,
44-
data: PaymentMethodParams.alipay(
45-
paymentMethodData: const PaymentMethodData(),
43+
paymentIntentClientSecret: clientSecret,
44+
data: PaymentMethodParams.alipay(
45+
paymentMethodData: const PaymentMethodData(),
4646
),
4747
);
4848

example/lib/screens/regional_payment_methods/aubecs_debit.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,8 +100,8 @@ class _AubecsExampleState extends State<AubecsExample> {
100100
// 2. use the client secret to confirm the payment and handle the result.
101101
try {
102102
await Stripe.instance.confirmPayment(
103-
paymentIntentClientSecret: clientSecret,
104-
data: PaymentMethodParams.aubecs(
103+
paymentIntentClientSecret: clientSecret,
104+
data: PaymentMethodParams.aubecs(
105105
paymentMethodData: PaymentMethodDataAubecs(formDetails: _details!),
106106
),
107107
);

example/lib/screens/regional_payment_methods/fpx_screen.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,8 @@ class FpxScreen extends StatelessWidget {
4040
// 2. use the client secret to confirm the payment and handle the result.
4141
try {
4242
await Stripe.instance.confirmPayment(
43-
paymentIntentClientSecret: clientSecret,
44-
data: PaymentMethodParams.fpx(
43+
paymentIntentClientSecret: clientSecret,
44+
data: PaymentMethodParams.fpx(
4545
paymentMethodData: PaymentMethodDataFpx(
4646
testOfflineBank: false,
4747
),

example/lib/screens/regional_payment_methods/grab_pay_screen.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,8 @@ class GrabPayScreen extends StatelessWidget {
5454
// 3. use the client secret to confirm the payment and handle the result.
5555
try {
5656
await Stripe.instance.confirmPayment(
57-
paymentIntentClientSecret: clientSecret,
58-
data: PaymentMethodParams.grabPay(
57+
paymentIntentClientSecret: clientSecret,
58+
data: PaymentMethodParams.grabPay(
5959
paymentMethodData: PaymentMethodData(
6060
billingDetails: billingDetails,
6161
),

example/lib/screens/regional_payment_methods/ideal_screen.dart

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,10 @@ class IdealScreen extends StatelessWidget {
4141
// 2. use the client secret to confirm the payment and handle the result.
4242
try {
4343
await Stripe.instance.confirmPayment(
44-
paymentIntentClientSecret: clientSecret,
45-
data: PaymentMethodParams.ideal(
46-
paymentMethodData: PaymentMethodDataIdeal(bankName: kIsWeb ? 'revolut' : null),
44+
paymentIntentClientSecret: clientSecret,
45+
data: PaymentMethodParams.ideal(
46+
paymentMethodData:
47+
PaymentMethodDataIdeal(bankName: kIsWeb ? 'revolut' : null),
4748
),
4849
);
4950

@@ -56,7 +57,8 @@ class IdealScreen extends StatelessWidget {
5657
if (e is StripeException) {
5758
ScaffoldMessenger.of(context).showSnackBar(
5859
SnackBar(
59-
content: Text('Error from Stripe: ${e.error.localizedMessage ?? e.error.code}'),
60+
content: Text(
61+
'Error from Stripe: ${e.error.localizedMessage ?? e.error.code}'),
6062
),
6163
);
6264
} else {

0 commit comments

Comments
 (0)