Настройка интеграции Stripe Payments с Flutter
13 января 2024 г.Привет! В этой статье я хочу поделиться своим опытом настройки платежей Stripe с помощью Flutter.
Я работаю над мобильным приложением, которое должно позволить пользователям создавать задачи (например, уборка, выгул собаки и другие), а другим пользователям — отвечать на задачи и получать оплату, когда задача будет завершена. Короче говоря, между пользователями должен осуществляться денежный перевод.
Самый популярный и хорошо документированный способ совершать платежи — использовать Stripe. Но статей, в которых показано, как сохранить банковскую карту в Stripe, использовать ее в дальнейшем или перевести деньги со своей банковской карты на карту другого человека, я не нашел. Вся информация, которую я нашел, была связана с библиотеками со скидкой или просто небезопасна.
В этой статье я хочу показать, как я решил эту проблему и совершил платежи с помощью Stripe. В конце на GitHub не будет проекта-примера, но все методы работают, и вы можете просто скопировать их и использовать, если вам нужно. Конечно, вы можете лучше реализовать мои идеи, и если да, то напишите, пожалуйста, свой код в комментариях. Итак, начнем!
Аккаунт Stripe
Чтобы иметь возможность работать с Stripe, первое, что вам нужно сделать, это, очевидно, создать учетную запись. Платформа проведет вас через процесс заполнения различной информации о том, кто вы, но вы можете пока пропустить это и сделать это позже.
На панели управления выберите раздел Разработчик и найдите Ключи API.
Надежно храните в своем приложении как Publishable, так и Secret ключи (я использую dotenv), чтобы позже связаться с Stripe API. Вы также можете заметить, что пока работаете в тестовом режиме, поэтому можете безопасно работать с транзакциями.
Интеграция Flutter
Следующий шаг — добавить Stripe в ваше приложение. Вы можете сделать это, добавив пакет flutter_stripe в свои зависимости.
Затем нам нужно подготовить Stripe для использования в нашем приложении. В main.dart
добавьте следующий код:
// The main function that kicks off the execution of the Flutter application.
Future<void> main() async {
// Ensure that the Flutter app's widgets are initialized properly before running the app.
WidgetsFlutterBinding.ensureInitialized();
// Load environment variables from a .env file.
await dotenv.load();
// Set the Stripe publishable key from the loaded environment variables.
Stripe.publishableKey = dotenv.env[Keys.stripePublicKey]!;
// Set the Stripe merchant identifier for the app.
Stripe.merchantIdentifier = 'merchant.flutter.stripe.appTitle';
// Set the URL scheme for handling deep links related to Stripe payments.
Stripe.urlScheme = 'flutterstripe';
// Apply settings for the Stripe SDK.
await Stripe.instance.applySettings();
// Run the Flutter app with the provided App widget.
runApp(App());
}
Теперь мы готовы использовать Stripe. Но в некоторых случаях мы не будем использовать методы SDK, а напишем свои.
Понимание процесса оплаты Stripe
Дело в том, что нельзя просто получить данные банковской карты и передать их на биллинговый сервер без шифрования. Это небезопасно, и вы можете потерять деньги. Так как же Stripe нам в этом поможет…
Короче говоря, ваша учетная запись Stripe — это точка, через которую ваши пользователи будут переводить деньги и платить некоторую комиссию за предоставляемую вами услугу. Пользователей вашего приложения в Stripe называют Клиенты. Подробнее об этом можно узнать здесь.
Чтобы создать объект Клиент, мы напишем собственный метод, использующий http-пакет:
// A function to create a customer in the Stripe payment system using the provided email.
Future<void> createCustomer({
required String email,
}) async {
try {
// Prepare the request body with the customer’s email.
final body = <String, dynamic>{
‘email’: email,
};
// Make a POST request to the Stripe API to create a customer.
final response = await http.post(
Uri.parse(‘https://api.stripe.com/v1/customers’), // Stripe API endpoint for creating customers.
headers: {
‘Authorization’: ‘Bearer ${dotenv.env[Keys.stripeSecretKey]}’, // Set the API key for authentication.
‘Content-Type’: ‘application/x-www-form-urlencoded’ // Set the request body content type.
},
body: body, // Include the request body in the POST request.
);
// Parse the response JSON and store the created customer data in ‘stripeCustomer’ variable.
// 'stripeCustomer' is declared earlier.
stripeCustomer = StripeCustomer.fromJson(
json: json.decode(response.body),
);
} catch (err) {
// If an error occurs during the process, throw an exception with the error message.
throw Exception(err.toString());
}
}
Ответ официального API инкапсулирован в нашем собственном классе StripeCustomer. Код самого класса следующий:
/// Represents a customer entity in the Stripe payment system.
class StripeCustomer {
/// Unique identifier for the customer.
final String id;
/// Customer's balance, represented in cents.
final int balance;
/// Date and time when the customer was created.
final DateTime created;
/// Email address associated with the customer.
final String email;
/// Constructor to create a [StripeCustomer] object.
StripeCustomer({
required this.id,
required this.balance,
required this.created,
required this.email,
});
/// Factory method to create a [StripeCustomer] object from JSON data.
///
/// [json] - A Map containing the customer information in JSON format.
/// Throws an exception if the required fields are missing or invalid in the [json].
factory StripeCustomer.fromJson({
required Map<String, dynamic> json,
}) =>
StripeCustomer(
id: json['id'].toString(),
balance: json['balance'].toString().toInt ~/ 100,
created: DateTime.fromMicrosecondsSinceEpoch(int.parse(json['created'].toString())),
email: json['email'].toString(),
);
}
На этом этапе вы можете проверить своего Клиента на панели инструментов Stripe.
На данный момент мы подготовили Stripe в нашем приложении и метод для создания объекта Customer. Теперь определимся со сценариями платежей в приложении.
1. Пополните баланс клиента.
В нашем приложении платеж пользователя может быть произведен только в тот момент, когда пользователю необходимо пополнить свой баланс Stripe. У Клиента в Stripe есть свойство баланса, в котором хранится сумма средств. И для этого мы воспользуемся методами пакета flutter_stripe.
Чтобы пополнить баланс, нам необходимо создать объект Payment Intent. Это можно сделать с помощью следующего кода:
/// Creates a payment intent.
Future<Map<String, dynamic>> createPaymentIntent({
required String amount,
}) async {
try {
// Request body
final body = <String, dynamic>{
// The amount should be in cents
'amount': (int.parse(amount) * 100).toString(),
'currency': 'THB',
// The Customer ID, which balance will be toped-up
'customer': stripeCustomer!.id,
'receipt_email': stripeCustomer!.email,
};
// Make post request to Stripe
final response = await http.post(
Uri.parse('https://api.stripe.com/v1/payment_intents'),
headers: {
'Authorization': 'Bearer ${dotenv.env[Keys.stripeSecretKey]}',
'Content-Type': 'application/x-www-form-urlencoded'
},
body: body,
);
// Return the Map object
return json.decode(response.body);
} catch (err) {
throw Exception(err.toString());
}
}
Далее нам нужно инициализировать платежный лист Stripe, чтобы завершить созданное Платежное намерение. Сделайте это с помощью следующего кода:
Future<void> initStripePayment({
required int amount,
}) async {
// Create a payment intent, the method described above
final paymentIntent = await createPaymentIntent(
amount: (amount).toString(),
);
try {
// Initialize Stripe payment sheet
await Stripe.instance.initPaymentSheet(
paymentSheetParameters: SetupPaymentSheetParameters(
customerId: stripeCustomer!.id,
paymentIntentClientSecret: paymentIntent['client_secret'], // Gotten from payment intent
merchantDisplayName: 'MyCoolApp',
),
);
} on Exception catch (e) {
log('initStripePayment() exception in PaymentStore: $e');
topUpError = e.toString();
paymentSheetInitialized = false;
}
}
И теперь нам нужно показать пользователю форму Stripe для ввода данных карты и продолжения оплаты. Это можно сделать с помощью следующего кода:
await Stripe.instance.presentPaymentSheet();
Появится форма для оплаты.
После завершения платежа баланс Клиента останется неизменным. Это связано с тем, что Клиент добавил средства на счет Stripe нашего приложения, и теперь нам нужно создать Транзакцию баланса клиента, чтобы добавить средства конкретному Клиенту. . Это будет сделано с помощью следующего кода:
Future<void> topUpBalance({
required int amount,
}) async {
try {
//Request body
final body = <String, dynamic>{
'amount': (amount * 100).toInt().toString(),
'currency': 'thb',
};
//Make post request to Stripe
final response = await http.post(
Uri.parse(
'https://api.stripe.com/v1/customers/${stripeCustomer?.id}/balance_transactions',
),
headers: {
'Authorization': 'Bearer ${dotenv.env[Keys.stripeSecretKey]}',
'Content-Type': 'application/x-www-form-urlencoded'
},
body: body,
);
} catch (err) {
log('topUpBalance() exception in PaymentStore: $err');
throw Exception(err.toString());
}
}
После выполнения этого действия баланс Клиента будет изменен.
2. Вывод средств за обслуживание приложения.
Теперь на балансе нашего пользователя достаточно средств для оплаты в нашем приложении ответов на задачи.
Исполнитель выбирает в приложении задачу, на которую хочет ответить. И для этого ему необходимо иметь достаточно средств на балансе Stripe. Если средств достаточно, после того, как он ответит на задание, с его баланса будет снята определенная сумма денег. Если средств недостаточно, пользователь будет переведен на страницу пополнения (и снова будет показана форма Stripe).
Для вывода средств с баланса Клиента мы будем использовать следующий метод:
Future<void> withdrawCustomerBalance({
required int amount,
}) async {
try {
// Request body
final body = <String, dynamic>{
// Before this operation, we hae to be sure
// that the `amount` is less or equal to `stripeCustomer!.balance`
'balance': ((stripeCustomer!.balance - amount) * 100).toInt().toString(),
};
// Make post request to Stripe
final response = await http.post(
Uri.parse(
'https://api.stripe.com/v1/customers/${stripeCustomer?.id}',
),
headers: {
'Authorization': 'Bearer ${dotenv.env[Keys.stripeSecretKey]}',
'Content-Type': 'application/x-www-form-urlencoded'
},
body: body,
);
if (response.statusCode == 402) {
withdrawError = json.decode(response.body)['error']['decline_code'].toString();
}
// Refresh the Stripe customer data
await getStripeCustomer(
email: stripeCustomer!.email,
);
} catch (err) {
log('withdrawCustomerBalance() exception in PaymentStore: $err');
throw Exception(err.toString());
}
}
Другими словами, мы просто вручную меняем баланс Клиента в Stripe.
Сводка
В итоге у нас есть метод пополнения баланса пользователя на платформе Stripe и метод вывода средств за сервис приложения.
Спасибо за внимание, надеюсь, эта статья кому-нибудь поможет. Любые рекомендации будут оценены. Если вы обнаружили, что я сделал что-то неправильно, сообщите мне об этом.
:::информация Также опубликовано здесь.
:::
Оригинал