Untitled

 avatar
unknown
dart
a year ago
16 kB
16
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...