Created
December 14, 2021 09:40
-
-
Save takumade/b159f2b18e887357dd3f41c027f2e37e to your computer and use it in GitHub Desktop.
Flutter Stripe checkout
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'package:flutter_stripe/flutter_stripe.dart'; | |
// Initialize Stripe: Either in main or another file | |
Stripe.publishableKey = "YOUR KEY HERE"; | |
Stripe.merchantIdentifier = "MERCHANT ID HERE"; | |
// Stripe file, for payments | |
class StripePayments { | |
String itemDesc = ""; | |
int amount = 0; | |
double totalCost = 0; | |
int finalAmount = 0; | |
double tip = 1.0; | |
double tax = 0.0; | |
double taxPercent = 0.15; | |
final paymentType; | |
String gateway = "stripe"; | |
PaymentResponse? paymentResponse; | |
PaymentMethod? paymentMethod; | |
StripeSettings? stripeSettings; | |
final paymentsRepo = PaymentsRepo(); | |
final dynamic productId; | |
final purchaseType; | |
final settingsBox = Hive.box("settings"); | |
final auth = Get.find<AuthController>(); | |
String invoiceId = | |
"Invoice " + DateTime.now().millisecondsSinceEpoch.toString(); | |
String customerId = | |
"customer-" + DateTime.now().millisecondsSinceEpoch.toString(); | |
List<BuyItem>? _items; | |
StripePayments({this.paymentType, this.productId, this.purchaseType}); | |
Future<PaymentResponse> payWithCurrentCard( | |
CardFieldInputDetails? _card) async { | |
if (_card == null) { | |
print("Stop 1"); | |
return PaymentResponse( | |
status: false, | |
message: "Please add card!", | |
errorCode: "payment_failed"); | |
} | |
try { | |
print("Stop 2"); | |
// 1. Gather customer billing information (ex. email) | |
final billingDetails = | |
await this.getBillingAddress(); // mocked data for tests | |
// 2. Create payment method | |
final paymentMethod = | |
await Stripe.instance.createPaymentMethod(PaymentMethodParams.card( | |
billingDetails: billingDetails, | |
)); | |
print("Stop 3"); | |
// 3. call API to create PaymentIntent | |
final paymentIntentResult = await remoteCreatePaymentIntent( | |
useStripeSdk: true, | |
paymentMethodId: paymentMethod.id, | |
currency: 'usd', // mocked data | |
items: [ | |
{ | |
'amount': this.finalAmount, | |
'name': 'A movie', | |
'desc': 'A description' | |
} | |
], | |
); | |
print("Stop 4"); | |
if (paymentIntentResult['error'] != null) { | |
// Error during creating or confirming Intent | |
return PaymentResponse( | |
errorCode: "payment_failed", | |
message: 'Error: ${paymentIntentResult['error']}', | |
status: false); | |
} | |
print("Stop 5"); | |
if (paymentIntentResult['clientSecret'] != null && | |
paymentIntentResult['requiresAction'] == null) { | |
// Payment succedeed | |
await this.saveTranscaction(); | |
return PaymentResponse( | |
errorCode: "payment_success", | |
message: 'The payment was confirmed successfully!', | |
status: true); | |
} | |
if (paymentIntentResult['clientSecret'] != null && | |
paymentIntentResult['requiresAction'] == true) { | |
// 4. if payment requires action calling handleCardAction | |
final paymentIntent = await Stripe.instance | |
.handleCardAction(paymentIntentResult['clientSecret']); | |
// todo handle error | |
/*if (cardActionError) { | |
Alert.alert( | |
`Error code: ${cardActionError.code}`, | |
cardActionError.message | |
); | |
} else*/ | |
if (paymentIntent.status == PaymentIntentsStatus.RequiresConfirmation) { | |
// 5. Call API to confirm intent | |
return await confirmIntent(paymentIntent.id); | |
} else { | |
// Payment succedeed | |
return PaymentResponse( | |
errorCode: "payment_failed", | |
message: 'Error: ${paymentIntentResult['error']}', | |
status: false); | |
} | |
} | |
} catch (e) { | |
print(e); | |
String? errorMessage = ""; | |
if (e is StripeException) { | |
errorMessage = e.error.message; | |
} | |
if (e is PlatformException) { | |
errorMessage = e.message; | |
} | |
return PaymentResponse( | |
errorCode: "payment_failed", message: errorMessage, status: false); | |
} | |
return PaymentResponse( | |
status: false, | |
message: "Payment Failed, Unknown Error!", | |
errorCode: "payment_failed"); | |
} | |
Future<PaymentResponse> confirmIntent(String paymentIntentId) async { | |
final result = | |
await remoteConfirmPaymentIntent(paymentIntentId: paymentIntentId); | |
if (result['error'] != null) { | |
return PaymentResponse( | |
status: false, | |
message: 'Error: ${result['error']}', | |
errorCode: 'payment_failed'); | |
} else { | |
await this.saveTranscaction(); | |
return PaymentResponse( | |
status: true, | |
message: 'Success!: The payment was confirmed successfully!', | |
errorCode: 'payment_success'); | |
} | |
} | |
Future<Map<String, dynamic>> remoteConfirmPaymentIntent({ | |
String? paymentIntentId, | |
}) async { | |
final result = await this.paymentsRepo.stripeChargeCard(paymentIntentId); | |
return result; | |
} | |
Future<Map<String, dynamic>> remoteCreatePaymentIntent({ | |
bool? useStripeSdk, | |
String? paymentMethodId, | |
String? currency, | |
List<Map<String, dynamic>>? items, | |
}) async { | |
final result = await paymentsRepo.stripeCreatePaymentIntent( | |
useStripeSdk, paymentMethodId, currency, items); | |
return result!; | |
} | |
Future<void> saveTranscaction() async { | |
final transactionBox = Hive.box('transactions'); | |
final paymentRepo = PaymentsRepo(); | |
final transcation = Transcation( | |
auth.currentUser!.userId, | |
this.productId, | |
this.purchaseType, | |
this.gateway, | |
this.invoiceId, | |
this.customerId, | |
this.totalCost); | |
await transactionBox.add(transcation.toMap()); | |
await paymentRepo.setUserPayment(transcation); | |
} | |
Future<BillingDetails> getBillingAddress() async { | |
AppBillingDetails billingDetails; | |
var results = settingsBox.get("billing_details"); | |
if (results == null) { | |
billingDetails = new AppBillingDetails( | |
auth.currentUser!.userName, | |
"35 Coronation Avenue", | |
"Greendale, Harare", | |
"Harare", | |
"Mashonaland East", | |
"000000", | |
auth.currentUser!.userEmail, | |
"077777777", // TODO: Put real user number here | |
"Zimbabwe"); | |
} else { | |
billingDetails = AppBillingDetails.fromJson(results); | |
} | |
return BillingDetails( | |
email: billingDetails.email, | |
phone: billingDetails.phone, | |
address: Address( | |
city: billingDetails.city, | |
country: billingDetails.country, | |
line1: billingDetails.address1, | |
line2: billingDetails.address2, | |
state: billingDetails.state, | |
postalCode: billingDetails.zip, | |
), | |
); | |
} | |
Future<void> createPayment(List<BuyItem>? items) async { | |
this._items = items; | |
for (BuyItem i in _items!) { | |
itemDesc += i.name!; | |
totalCost += i.price; | |
} | |
// Calculate final amount | |
final stripeCharge = (2.9 / 100) * this.totalCost + 0.30; | |
this.totalCost = (this.totalCost + stripeCharge) * 100; | |
this.finalAmount = int.parse(this.totalCost.toStringAsFixed(0)); | |
} | |
} | |
// | |
Future<Map<String, dynamic>> remoteConfirmPaymentIntent({ | |
String? paymentIntentId, | |
}) async { | |
final result = await this.paymentsRepo.stripeChargeCard(paymentIntentId); | |
return result; | |
} | |
/////////////////////////////////////////////////////////// | |
// | |
//. PAYMENTS REPO | |
// | |
/////////////////////////////////////////////////////////// | |
Future<Map<String, dynamic>> remoteCreatePaymentIntent({ | |
bool? useStripeSdk, | |
String? paymentMethodId, | |
String? currency, | |
List<Map<String, dynamic>>? items, | |
}) async { | |
final result = await paymentsRepo.stripeCreatePaymentIntent( | |
useStripeSdk, paymentMethodId, currency, items); | |
return result!; | |
} | |
Future<Map<String, dynamic>> stripeChargeCard(String? paymentIntentId) async { | |
final url = '$kApiUrl/charge-card-off-session'; | |
final response = await Dio().post( | |
url, | |
queryParameters: { | |
'paymentIntentId': paymentIntentId, | |
}, | |
); | |
Future<Map<String, dynamic>?> stripeCreatePaymentIntent( | |
bool? useStripeSdk, | |
String? paymentMethodId, | |
String? currency, | |
List<Map<String, dynamic>>? items) async { | |
final url = '$kApiUrl/pay-without-webhooks'; | |
try { | |
final response = await Dio().post( | |
url, | |
options: Options(headers: { | |
'Content-Type': 'application/json', | |
}), | |
data: jsonEncode({ | |
'useStripeSdk': useStripeSdk, | |
'paymentMethodId': paymentMethodId, | |
'currency': currency, | |
'items': items | |
}), | |
); | |
final result = Map<String, dynamic>.from(response.data); | |
return result; | |
} catch (err) { | |
if (err is DioError) { | |
print(err.response!.data); | |
} | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment