Untitled
unknown
dart
2 years ago
16 kB
20
Indexable
import 'dart:async';
import 'package:BlinkDocs/app_state.dart';
import 'package:BlinkDocs/backend/stripe/StripeAddress.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:dio/dio.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/foundation.dart' show kIsWeb;
import 'package:flutter/material.dart';
import 'package:flutter_stripe/flutter_stripe.dart';
import 'StripeCustomer.dart';
import 'StripeResponse.dart';
const _isProd = false;
// Stripe Credentials
const _kProdStripePublishableKey =
'pk_live_51LNt0dSJESo9WpI9EK7gV4gR2ziZP41Y91sLIpnRc4mCOKQxLCzq88fBhVUYtVstk6FXOOpUiwIje94OAdOccRxU005cOw8IdW';
const _kTestStripePublishableKey =
'pk_test_51LNt0dSJESo9WpI9zXhDLotxJ8FA1n2p4Q5h9tcpmfAQWwmAE1TWMO6xQtaUPFvNQLpLl3wsMSKPi4oniiirFCyd008XdMg6vv';
const _kAppleMerchantId = 'BLINKVIS';
const _kProdStripeSecretKey =
"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" ;
const _kTestStripeSecretKey =
"sk_test_51LNt0dSJESo9WpI9kZxaIS4eUd14FtO2i8tB8MNvbPIhEnC874Hn5DM8sTK9skqmK8KvluvOEXNM5mrIUzVJfwdu00XTYt4rm4";
String stripePublishableKey() =>
_isProd ? _kProdStripePublishableKey : _kTestStripePublishableKey;
// check if the user is android or ios
Future initializeStripe() async {
Stripe.publishableKey = stripePublishableKey();
await Stripe.instance.applySettings();
}
class StripePaymentResponse {
const StripePaymentResponse({this.paymentId, this.errorMessage});
final String? paymentId;
final String? errorMessage;
}
// call Stripe Create Customer Api. This will return a customer id.
Future createCustomer( StripeAddressData address, String customerName
, String customerEmail, String description) async {
var customerData = StripeCustomer(
name: customerName,
email: customerEmail,
description: description,
address: address
);
Response response = await Dio().post('https://api.stripe.com/v1/customers',
data: customerData.toJson(),
options: Options(contentType:Headers.formUrlEncodedContentType,
headers: {
'Authorization': "Bearer ${_isProd ? _kProdStripeSecretKey : _kTestStripeSecretKey}"
}
),
);
// convert response to StripeResponse model
var stripeResponseObj = StripeResponse.fromJson(response.data);
return stripeResponseObj;
}
Future<StripePaymentResponse> showWebPaymentSheet(
BuildContext context, {
required String name,
required String paymentId,
required String paymentIntentSecret,
required num amount,
required String currency,
required String description,
Color? buttonColor,
Color? buttonTextColor,
ThemeMode? themeStyle,
}) async {
final isDarkMode = themeStyle == null
? Theme.of(context).brightness == Brightness.dark
: themeStyle == ThemeMode.dark;
buttonColor = buttonColor ?? Theme.of(context).primaryColor;
final screenWidth = MediaQuery.of(context).size.width;
// ignore: prefer_function_declarations_over_variables
final buildPaymentSheet = (BuildContext context, double width) => WebStripePayment(
paymentId: paymentId,
paymentIntentSecret: paymentIntentSecret,
amount: amount,
currency: currency,
description: description,
buttonColor: buttonColor,
buttonTextColor: buttonTextColor,
themeStyle: themeStyle,
darkMode: isDarkMode,
width: width,
);
StripePaymentResponse? response;
// If on mobile for web, display the payment sheet as a bottom sheet.
if (screenWidth < 429) {
response = await showModalBottomSheet<StripePaymentResponse>(
context: context,
builder: (context) => buildPaymentSheet(context, screenWidth),
);
}
// Otherwise, show a dialog, which is better for Tablet and Web/Desktop.
else {
response = await showDialog<StripePaymentResponse>(
context: context,
builder: (context) => AlertDialog(
backgroundColor: Colors.transparent,
contentPadding: EdgeInsets.zero,
content: buildPaymentSheet(context, 420),
),
);
}
// Return the payment response, or an empty response if the user canceled.
return response ?? StripePaymentResponse();
}
// create Stateful widget for StripePaymentResponse
class WebStripePayment extends StatefulWidget {
final String paymentId;
final String paymentIntentSecret;
final num amount;
final String currency;
final String description;
final Color? buttonColor;
final Color? buttonTextColor;
final ThemeMode? themeStyle;
final bool darkMode;
final double width;
const WebStripePayment({
Key? key,
required this.paymentId,
required this.paymentIntentSecret,
required this.amount,
required this.currency,
required this.description,
this.buttonColor,
this.buttonTextColor,
this.themeStyle, required this.darkMode, required this.width,
}) : super(key: key);
@override
WebStripePaymentState createState() => WebStripePaymentState();
}
class WebStripePaymentState extends State<WebStripePayment> {
ThemeMode themeStyle = ThemeMode.system;
bool isLoading = false;
@override
void initState() {
// TODO: implement initState
super.initState();
}
@override
Widget build(BuildContext context) {
return Container(
width: 600,
child: Column(
mainAxisSize: MainAxisSize.max,
children: [
ClipRRect(
borderRadius: BorderRadius.circular(8.0),
child: Material(
color: Colors.transparent,
child: Container(
padding: const EdgeInsets.fromLTRB(24.0, 14.0, 24.0, 24.0),
color: widget.darkMode ? Color(0xFF101213) : Colors.white,
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
const Expanded(
child: Text(
'Payment Information',
),
),
InkWell(
onTap: () => Navigator.pop(context),
child: Padding(
padding: EdgeInsets.all(4.0),
child: Icon(
Icons.close_rounded,
size: 22,
color: widget.darkMode
? const Color(0xFF95A1AC)
: const Color(0xFF57636C),
),
),
),
],
),
if (widget.description.isNotEmpty) ...[
const SizedBox(height: 8.0),
Text(
widget.description,
),
],
],
),
const SizedBox(height: 16.0),
CardField(
decoration: InputDecoration(
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: widget.buttonColor!,
width: 2.0,
),
borderRadius: BorderRadius.circular(8.0),
),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color: widget.darkMode
? const Color(0xFF22282F)
: const Color(0xFFE0E3E7),
width: 2.0,
),
borderRadius: BorderRadius.circular(8.0),
),
fillColor: const Color(0xFFF1F4F8),
filled: widget.darkMode,
),
// enablePostalCode: true,
),
const SizedBox(height: 20.0),
GestureDetector(
onTap: () async {
setState(() {
isLoading = true;
});
try {
final response = await Stripe.instance.confirmPayment(
paymentIntentClientSecret: widget.paymentIntentSecret,
data: const PaymentMethodParams.card(
paymentMethodData: PaymentMethodData(),
),
);
if (response.status == PaymentIntentsStatus.Succeeded) {
setState(() {
isLoading = false;
});
Navigator.pop(
context,
StripePaymentResponse(paymentId: widget.paymentId),
);
}
}catch ( error){
setState(() {
isLoading = false;
});
if (error is StripeError && error.message != null) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
backgroundColor: Colors.blue,
content:
SizedBox(
height: 40,
child: Text(error.message!
,style: TextStyle(color: Colors.white,
fontWeight: FontWeight.w600, fontSize: 16,fontFamily: 'Gilroy'
) ),
),
),
);
}
}
},
child: Container(
width: kIsWeb ?203:103,
height: 56,
decoration: BoxDecoration(
color: const Color(0xff1F75EC),
borderRadius: BorderRadius.circular(14),
),
alignment: Alignment.center,
child: isLoading ? const CircularProgressIndicator(
color: Colors.white,
) : Text(
'Pay ${widget.currency} ${(widget.amount/100).toString()}',
style: const TextStyle(
color: Colors.white,
fontSize: 16,
fontWeight: FontWeight.w600,
),
),
),
),
],
),
),
),
),
],
),
);
}
}
// call Stripe Create Payment Method Api. This will return a payment method id.
Future<StripePaymentResponse> processStripePayment(
BuildContext context, {
required num amount,
required String currency,
required String customerEmail,
required StripeAddressData address,
required customerName,
required description,
bool allowGooglePay = false,
bool allowApplePay = false,
ThemeMode themeStyle = ThemeMode.system,
Color? buttonColor,
Color? buttonTextColor,
}) async {
var paymentID = '';
try {
if ( FFAppState().stripeCustomerId.isEmpty){
StripeResponse stripeResponseObj = await createCustomer(address, customerName, customerEmail, description);
FFAppState().stripeCustomerId = stripeResponseObj.id;
FirebaseFirestore.instance.collection("customerData").
doc(FirebaseAuth.instance.currentUser?.uid).update({"stripeCustID":FFAppState().stripeCustomerId});
}
var paymentItentData = {
"currency": currency,
"amount": amount.round(),
"description": description,
"customer": FFAppState().stripeCustomerId,
};
Response response = await Dio().post(
'https://api.stripe.com/v1/payment_intents',
data: paymentItentData,
options: Options(contentType: Headers.formUrlEncodedContentType,
headers: {
'Authorization': "Bearer ${_isProd
? _kProdStripeSecretKey
: _kTestStripeSecretKey}",
"HttpHeaders.contentTypeHeader": "application/x-www-form-urlencoded"
}
),
).onError((error, stackTrace) {
print(error);
print(stackTrace);
throw stackTrace.toString();
} );
/// For web, display a payment sheet with a credit card form.
if (kIsWeb) {
StripePaymentResponse resp = await showWebPaymentSheet(
context,
paymentId: response.data['id'],
paymentIntentSecret: response.data['client_secret'],
amount: amount,
currency: currency,
description: description,
buttonColor: buttonColor,
buttonTextColor: buttonTextColor,
themeStyle: themeStyle, name: customerName,
);
paymentID = resp.paymentId ?? '';
return resp;
}
await Stripe.instance.initPaymentSheet(
paymentSheetParameters: SetupPaymentSheetParameters(
customFlow: false,
paymentIntentClientSecret: response.data['client_secret'],
customerId: response.data['customer'],
merchantDisplayName: 'BLINKVISA',
style: themeStyle,
appearance: PaymentSheetAppearance(
primaryButton: PaymentSheetPrimaryButtonAppearance(
colors: PaymentSheetPrimaryButtonTheme(
light: PaymentSheetPrimaryButtonThemeColors(
background: buttonColor,
text: buttonTextColor,
),
dark: PaymentSheetPrimaryButtonThemeColors(
background: buttonColor,
text: buttonTextColor
),
),
),
),
),
);
/// Then show the payment sheet and confirm payment.
try {
await Stripe.instance.presentPaymentSheet().then((value) {
Stripe.instance.confirmPaymentSheetPayment();
});
} catch (e) {
// Show snackbar if the payment was cancelled
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
backgroundColor: Colors.blue,
content:
Text('Payment Failed. Please try again to continue'
,style: TextStyle(color: Colors.white,
fontWeight: FontWeight.bold
, fontSize: 16
) ),
),
);
return StripePaymentResponse(paymentId: "", errorMessage: e.toString());
}
paymentID = response.data['id'];
return StripePaymentResponse(paymentId: paymentID);
}
catch (e) {
if (e is StripeException && e.error.code == FailureCode.Canceled) {
return StripePaymentResponse();
}
return StripePaymentResponse(errorMessage: '$e');
}
}Editor is loading...