💰
FlutterとNode.jsを連携したPayPay決済の実装方法
FlutterからNode.jsを使ってPayPay決済を実装する方法
はじめに
こんにちは!個人アプリ開発者の のす です。
私は趣味でFlutterを使ったモバイルアプリ開発を行っており、PayPay決済を導入しようとした際に、Flutter向けの公式SDKが存在しないという課題に直面しました。そこで、Node.jsバックエンドとの連携による実装方法を紹介します。
なぜNode.jsとの連携が必要なのか?
PayPayの決済処理をFlutterアプリに実装する際の最大の課題は、Flutter向けの公式PayPayライブラリが存在しない点です。これにより、バックエンドサーバー(Node.js)を介した連携が必要になります:
- APIキーのセキュリティ保護: APIキーをクライアント側に保存すると漏洩リスクがある
- サーバーサイドでの決済処理: 認証と決済処理をサーバー側で行うことでセキュリティを確保
- PayPayの公式Node.js SDKの活用: 公式のSDKを使うことで実装が容易になる
PayPay決済の実装フロー
PayPay決済を実装するための全体的なフローは以下の通りです:
1. 認証フェーズ
- Flutterアプリで「PayPayユーザー認証」ボタンをタップ
- Node.jsバックエンドへリクエストを送信
- バックエンドがPayPay APIから認証用QRコードURLを取得
- Flutterアプリ内のWebViewでQRコードを表示
- ユーザーがPayPayアプリで認証
- 認証完了後、コールバックで
userAuthorizationId
を取得
2. 決済フェーズ
- 取得した
userAuthorizationId
を使って決済画面に遷移 - 決済ボタンをタップするとNode.jsバックエンドに決済リクエスト送信
- バックエンドがPayPay APIで決済処理を実行
- 結果をFlutterアプリに返し、ユーザーに成功/失敗を通知
Flutter側の実装
1. PayPay認証画面の実装
// WebViewでPayPay認証ページを表示
Widget _buildInAppWebView(String url) {
return InAppWebView(
initialUrlRequest: URLRequest(url: Uri.parse(url)),
initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
javaScriptEnabled: true,
useShouldOverrideUrlLoading: true,
),
),
onLoadStop: (controller, url) async {
if (url != null && url.queryParameters['userAuthorizationId'] != null) {
userAuthorizationId = url.queryParameters['userAuthorizationId'];
// 認証が完了したら、次のページに自動的に遷移する
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) => CreatePaymentPage(userAuthorizationId),
),
);
}
},
);
}
// PayPay認証処理を開始
Future<void> getAuthorization() async {
final url = 'あなたのバックエンドURL/auth';
try {
// Node.jsバックエンドにリクエスト
final response = await http.post(
Uri.parse(url),
headers: {
'Content-Type': 'application/json',
},
);
// レスポンス処理
if (response.statusCode == 200) {
final responseBody = json.decode(response.body);
final jsonData = responseBody['BODY'];
final resultInfo = jsonData['resultInfo'];
final data = jsonData['data'];
if (resultInfo['code'] == "SUCCESS") {
String linkQRCodeURL = data['linkQRCodeURL'];
// WebViewでPayPay認証画面を表示
_launchURL(linkQRCodeURL);
}
}
} catch (e) {
print('Error occurred: $e');
}
}
2. 決済実行の実装
// 決済処理の実行
Future<Map<String, dynamic>?> createPayment(
BuildContext context, int amount) async {
final paymentUrl = 'あなたのバックエンドURL/createpayment?userAuthorizationId=${widget.authorizationId}';
try {
final response = await http.post(
Uri.parse(paymentUrl),
headers: {'Content-Type': 'application/json'},
body: json.encode({
'amount': amount
}),
);
if (response.statusCode == 200) {
final responseBody = json.decode(response.body);
// 決済成功時の処理
if (responseBody['status'] == 200 &&
responseBody['res_body']['resultInfo']['code'] == "SUCCESS") {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text('決済成功'),
content: Text('¥$amountの決済が正常に完了しました。'),
actions: [
TextButton(
child: Text('閉じる'),
onPressed: () {
Navigator.of(context).pop();
},
),
],
);
},
);
}
return responseBody;
}
return null;
} catch (e) {
// エラー処理
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text('決済失敗'),
content: Text('決済が完了できませんでした。もう一度お試しください。'),
actions: [
TextButton(
child: Text('閉じる'),
onPressed: () => Navigator.of(context).pop(),
),
],
);
},
);
return null;
}
}
3. ユーザーインターフェース実装例
class PayPayPaymentPage extends StatefulWidget {
_PaymentScreenState createState() => _PaymentScreenState();
}
class _PaymentScreenState extends State<PayPayPaymentPage> {
String? userAuthorizationId;
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('PayPay決済'),
backgroundColor: Colors.black,
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('PayPay決済を始める', style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold)),
SizedBox(height: 30),
ElevatedButton.icon(
onPressed: getAuthorization,
icon: Icon(Icons.payment, size: 24),
label: Text('PayPayユーザー認証'),
style: ElevatedButton.styleFrom(
primary: Color.fromARGB(255, 100, 154, 248),
padding: EdgeInsets.symmetric(horizontal: 32, vertical: 16),
textStyle: TextStyle(fontSize: 20),
),
),
],
),
),
);
}
}
バックエンドとの連携
バックエンドの詳細な実装はここでは省略しますが、Flutter側からは以下の2つの主要なエンドポイントを呼び出します:
-
認証エンドポイント (
/auth
):- PayPay SDK の
AccountLinkQRCodeCreate
メソッドを使用 - QRコードURLを生成して返す
- PayPay SDK の
-
決済エンドポイント (
/createpayment
):-
userAuthorizationId
と金額を受け取る - PayPay SDK の
CreatePayment
メソッドで決済処理 - 決済結果をFlutterに返す
-
サーバーレス実装のヒント
個人開発では、Firebase Cloud FunctionsやVercel Serverless Functionsなどのサーバーレスアーキテクチャを活用するとインフラ管理の負担が軽減されます。基本的な実装フローは変わりませんが、デプロイやAPIキー管理が容易になります。
まとめ
Flutter向けの公式PayPay SDKがない現状では、Node.jsバックエンドを介した連携が最も堅実な実装方法です。このアプローチにより、セキュリティを確保しながらPayPay決済機能をFlutterアプリに組み込むことができます。
重要なポイント:
- ユーザー認証と決済処理の2つのフェーズに分けて実装
- センシティブな情報や処理はすべてサーバーサイドで行う
- WebViewを使用してPayPay認証画面を表示
-
userAuthorizationId
を使って決済処理を行う
この記事が、PayPay決済を導入したいFlutterアプリ開発者の参考になれば幸いです。
Discussion