【実践ガイド】Flutter×Stripeでスムーズな決済システムを簡単に導入する方法
はじめに
株式会社メンバーズ Cross Applicationカンパニーの朝山です。
FlutterアプリにStripeのクレジットカード決済を導入する機会があったのでその方法を公開します。
この記事を読めば、FlutterアプリにStripeを導入し、実際に決済を試せるようになります。
※本記事の情報は2025年3月時点のものです。最新の仕様は公式ドキュメントをご参照ください。
Stripeとは
APIを利用してさまざまな種類の決済を受け付けることができるプラットフォームです。
料金体系もシンプルで、初期費用や月額費用は無料で決済手数料のみが一律かかります。
Stripeの利用開始準備
Stripeの利用開始はとても簡単です。
まずは、今すぐ始める からアカウント登録をします。
Stripeのダッシュボードでは、テスト環境と本番環境を簡単に切り替えることができるので、実際に決済の動作確認をすることができます。
まずは試したいという方は、登録すればテスト環境が自動的に有効になり、すぐに試せます。
本番環境を利用するには、Stripeに銀行口座情報の登録と本人確認を行う必要があります。(本記事では省略します)
ログイン後、テスト環境のダッシュボードから公開キーと秘密キーを取得します。
- 公開キー →Flutterアプリ(フロント)で使用
- 秘密キー →サーバー(バックエンド)で使用
必要なパッケージをインストール
まずは必要なパッケージをインストールします。
flutter pub add flutter_stripe
flutter pub add http
flutter_stripe はStripeの公式パッケージで、http はAPI通信に使用します。
Stripeの初期化
main.dartでStripeを初期化します。
先程ダッシュボードで取得した公開キーを使用して初期化します。
import 'package:flutter_stripe/flutter_stripe.dart';
import 'package:flutter/material.dart';
void main() {
WidgetsFlutterBinding.ensureInitialized();
Stripe.publishableKey = 'pk_test_XXXXXXXXXXXX'; // 公開キー
runApp(MyApp());
}
PaymentIntentを作成(サーバー処理)
Flutterアプリは直接Stripeの秘密キーを扱えないため、バックエンドを用意して決済を処理します。
サーバーで行う処理は、clientSecretを作成することです。
PaymentIntent は、決済を処理するために必要な情報(決済金額、通貨、支払い方法など)をStripe に伝え、クライアントが決済を確定させるためのclientSecretを取得するための仕組みです。
生成されたclientSecretをフロントに返し、決済を実行することができます。
サーバー側はfirebaseやawsなどを使用してエンドポイントを作成します。
以下はコード例です。(Node.jsのExpressを使用)
const stripe = require('stripe')('sk_test_XXXXXXXXXXXX'); // 秘密キー
const express = require('express');
const app = express();
app.use(express.json());
app.post('/create-payment-intent', async (req, res) => {
const paymentIntent = await stripe.paymentIntents.create({
amount: 1000, // 金額
currency: 'jpy', // 通貨
payment_method_types: ['card'],
});
res.json({ clientSecret: paymentIntent.client_secret });
});
app.listen(3000, () => console.log("Server running on port 3000"));
PaymentSheetで決済処理(フロント処理)
Flutterアプリからサーバーへリクエストを送り、clientSecret を取得して決済を行います。
今回はPaymentSheetというStripeが用意しているカード情報入力ページを立ち上げるメソッドを使用します。
▼PaymentSheetを使用したコード例
import 'package:flutter/material.dart';
import 'package:flutter_stripe/flutter_stripe.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
class PaymentScreen extends StatefulWidget {
@override
PaymentScreenState createState() => PaymentScreenState();
}
class _PaymentScreenState extends State<PaymentScreen> {
Map<String, dynamic>? paymentIntent;
Future<void> createPayment(int amount) async {
try {
// 1. サーバーからPaymentIntentを取得
final response = await http.post(
Uri.parse('https://your-server.com/create-payment-intent'),
headers: {'Content-Type': 'application/json'},
body: json.encode({'amount': amount}),
);
final jsonResponse = json.decode(response.body);
paymentIntent = jsonResponse;
// 2. クライアント側でカード情報を入力
await Stripe.instance.initPaymentSheet(
paymentSheetParameters: SetupPaymentSheetParameters(
paymentIntentClientSecret: paymentIntent!['clientSecret'],
merchantDisplayName: 'MyApp',
),
);
// 3. 決済を実行
await Stripe.instance.presentPaymentSheet();
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('決済に成功しました')),
);
} catch (error) {
print('決済に失敗しました: $error');
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('決済に失敗しました')),
);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('カード決済')),
body: Center(
child: ElevatedButton(
onPressed: createPayment,
child: Text('決済する'),
),
),
);
}
}
決済の確認
テスト環境では、Stripeが用意しているテストカードを使用して動作を確認することができます。
以下のカードを使用すれば決済が成功します。
カード番号:42424 4242 4242 4242 (Visa)
有効期限 :任意の未来日
cvc :任意の値
他にも失敗するパターンなども検証することができるので公式に記載されているカードを試してください。
テストカード
※テストカードはテスト環境のみで使用可能です
決済が完了すると、Stripeのダッシュボードの【支払い】に反映されます。
ダッシュボードでは、詳細なログも確認することができ、返金なども可能です。
(返金の場合も決済手数料はアカウントにかかるので注意です)
その他
- アプリにカードフォームを埋め込みたい
PaymentSheetはStripeが構築した決済ページを表示してくれますが、アプリ内にカードフォームを埋め込みたい場合もあると思います。
例えばStripeへ顧客登録(カード)登録を行って決済処理とは分離したいサービスの場合は、CardFormを使用すると実現できます。
Stripeではカード情報を暗号化してサーバーに送信することが重要となっているため、暗号化をするにはPaymentSheetかCardFormを使用するかどちらかの選択肢となります。
CardFormはカード番号・有効期限・CVC の3つのフィールドを含み、Stripe によって自動バリデーションが行われます。
▼Flutterアプリ内にCardFormを実装するコード例
import 'package:flutter/material.dart';import 'package:flutter_stripe/flutter_stripe.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
class CardPaymentScreen extends StatefulWidget {
@override
CardPaymentScreenState createState() => CardPaymentScreenState();
}
class _CardPaymentScreenState extends State<CardPaymentScreen> {
Map<String, dynamic>? paymentIntentData; // PaymentIntentのデータを格納する変数
// サーバーにリクエストを送信し、PaymentIntentを作成する関数
Future<void> createPaymentIntent() async {
try {
final response = await http.post(
Uri.parse('http://your-backend.com/create-payment-intent'), // バックエンドのエンドポイント
body: jsonEncode({'amount': 1000}), // 決済金額
headers: {'Content-Type': 'application/json'}, // JSONリクエストを送信
);
paymentIntentData = jsonDecode(response.body); // レスポンスからPaymentIntentの情報を取得
} catch (e) {
print("Error creating payment intent: $e"); // エラーハンドリング
}
}
/// ユーザーが入力したカード情報を使用して決済を確定する関数
Future<void> confirmPayment() async {
try {
if (paymentIntentData == null) {
await createPaymentIntent(); // PaymentIntentがない場合は新しく作成
}
// ユーザーの入力したカード情報を使って決済を確定
await Stripe.instance.confirmPayment(
paymentIntentClientSecret: paymentIntentData!['clientSecret'], // サーバーから受け取ったクライアントシークレット
data: PaymentMethodParams.card(
paymentMethodData: PaymentMethodData(), // カード情報を取得
),
);
// 決済成功時の処理
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text("Payment Successful")), // 成功メッセージを表示
);
} catch (e) {
// 決済失敗時の処理
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text("決済に失敗しました: $e")), // エラーメッセージを表示
);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("カード決済")),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// カード情報を入力するフォーム
CardFormField(
controller: CardFormEditController(), // フォームのコントローラー
style: CardFormStyle(
backgroundColor: Colors.white,
borderColor: Colors.grey,
borderRadius: 8,
cursorColor: Colors.black,
),
),
SizedBox(height: 20),
// 決済ボタン
ElevatedButton(
onPressed: confirmPayment, // ボタンが押されたら決済を実行
child: Text("決済する"),
),
],
),
),
);
}
}
- オーソリとキャプチャーを分けたい
Stripeの決済ではオーソリ(与信)とキャプチャー(決済)に分けて処理を実行することもできます。
すぐに決済を完了させないが、与信枠をとっておきたい場合などに利用します。
例えば、ホテル予約やレンタカーの支払いでは、予約時に与信(オーソリ)を取得し、実際の利用が確定した時点でキャプチャー(決済確定)を行います。
オーソリには保留期間があり、デフォルトで7日ですが最大30日です。
▼オーソリのみを行うサーバーの例
const paymentIntent = await stripe.paymentIntents.create({
amount: 1000,
currency: 'jpy',
payment_method_types: ['card'],
capture_method: 'manual', // オーソリのみ
});
capture_method: 'manual' を指定すると、あとで手動で決済を確定できます。
- アカウントを連結させた決済を実現したい
Stripe Connectというサービスがあります。
これを利用することで、親アカウント配下に子アカウントを連結し、決済を管理することが可能です。
ショッピングモールのようなイメージで、親アカウントはショッピングモールを管理している人で、子アカウントは店舗のようなものです。
- 3Dセキュアに対応したい
Stripeは2025年3月末までに全ての対象取引に3D セキュアを適用することになっており、義務化されます。
それまでに3D セキュアに対応した実装にする必要があります。
3D セキュアは、不正利用防止のために 一部の決済に対して追加の認証を求める仕組みです。
例えば、高額取引や初回決済などで適用される可能性があります。
3D セキュアに対応するためには、具体的にはpaymentIntentやsetupIntent時にnextアクションが必要かを確認し、必要であればクライアント側で3DS認証画面を立ち上げるように対応します。
3DS認証画面を作る必要はなく、カード会社指定の画面が立ち上がるようにアクションを繋げるだけで良いです。
具体的には、Stripe.instance.handleNextActionを実行し、そこにclientSecretを渡します。
clientSecretは決済作成時に生成される決済情報idで、サーバからクライアントに返してあげます。
handleNextAction を実行すると、カード会社指定の認証画面が表示され、ユーザーが認証を完了すると決済が確定します。
まとめ
今回は、Flutter × Stripe で決済を導入する方法 を紹介しました。
PaymentSheet or CardForm を使えば、簡単にカード決済を実装できます。3Dセキュア対応 や オーソリ・キャプチャー分離、アカウント連結などの高度な実装も可能です。Stripeはテスト環境が充実していて、すぐに試せますので是非トライしてみてください!
関連記事
- 【Flutter】VSCodeで効率よく開発するためのおすす...
Nicolas Christopher
Advent Calendar!
Advent Calendar 2024