0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

FlutterでPAY.JPを導入してみた(カードフォームでトークン化 → サーバーで支払い作成まで)

Last updated at Posted at 2025-12-21

FlutterでPAY.JPを導入してみた(カードフォームでトークン化 → サーバーで支払い作成まで)

Gemini_Generated_Image_54ebbz54ebbz54eb.png

mizoiです。
今回はPAY.JP の Flutter プラグイン(payjp_flutter)を使って、Flutterサンプルアプリにクレジットカード決済を組み込むところまでを一通りやってみました。

PAY.JP の Flutter 連携は「アプリ側でカード情報を直接扱わず、SDKでトークン化して、トークンだけをサーバーへ送る」という設計になっていて、最短距離で“決済っぽい体験”を作れます。 (PAY.JP)


この記事でやること

  • Flutter に payjp_flutter を入れる
  • カードフォーム → トークン作成まで(アプリ側)
  • 受け取った トークンをサーバーへ送信
  • サーバー側で 支払い(charge)を作成してテスト決済完了

重要:Secret Key(秘密鍵)はアプリに絶対入れない(サーバー側だけで使用) (PAY.JP)


全体の流れ(ざっくり)

Flutterアプリ
  ↓(カードフォームで入力)
PAY.JP Mobile SDK(payjp_flutter)
  ↓(Token発行)
Flutterアプリ
  ↓(token.id を自サーバーへPOST)
自サーバー
  ↓(Secret Keyで /v1/charges)
PAY.JP API
  ↓
決済完了(テスト)

PAY.JP の Flutter プラグインは、カードフォームを表示してトークンを作る機能(+ iOS は Apple Pay)を提供しています。 (PAY.JP)


前提

  • PAY.JP のアカウント作成(テスト環境OK)

  • 管理画面から APIキー取得

    • 公開鍵(Public Key):アプリ(SDK初期化)に入れる
    • 秘密鍵(Secret Key):サーバーでのみ使用 (PAY.JP)

1. Flutter側:payjp_flutter を追加

pubspec.yaml

dependencies:
  flutter:
    sdk: flutter
  payjp_flutter:

公式の Flutter 利用ガイドでも、このプラグインを使う手順が案内されています。 (PAY.JP)
私は公式の手順を見ながら作業しました。

スクリーンショット 2025-12-21 23.35.16.png

2. Flutter側:SDK初期化(公開鍵)

import 'package:payjp_flutter/payjp_flutter.dart';

Future<void> initPayjp() async {
  await Payjp.init(publicKey: 'pk_test_xxxxxxxxxxxxxxxxxxxxxxxx');
}

Payjp.init(publicKey: ...) で公開鍵をセットします。 (PAY.JP)


3. Flutter側:カードフォームを開く → Tokenを受け取る

最小はこれです。

Future<void> startCardForm() async {
  await Payjp.startCardForm(
    cardFormType: CardFormType.cardDisplay,
    onCardFormProducedTokenCallback: onToken,
  );
}

Future<CallbackResult> onToken(Token token) async {
  try {
    // token.id を自サーバーへ送る
    await sendTokenToServer(token.id);
    return CallbackResultOk();
  } on ApiException catch (e) {
    // ここで返すとフォーム内にエラー表示される
    return CallbackResultError(e.message);
  }
}
  • Payjp.startCardForm() でカードフォームを起動 (PAY.JP)
  • onCardFormProducedTokenCallbackToken を受け取ってサーバー送信 (PAY.JP)
  • 成功時は CallbackResultOk()、失敗時は CallbackResultError() を返すと、フォーム側のUXが自然です (PAY.JP)

4. iOSの注意:カード読み取りを使うならカメラ許可

iOS ではカード読み取り機能のために Info.plistNSCameraUsageDescription が必要です(Android側ではFlutter経由のカード読み取りは現在利用不可の旨も明記されています)。 (PAY.JP)


5. サーバー側:Tokenで支払い(charge)を作成

PAY.JP では、トークン作成ができたら次はサーバー側で charges を作って支払いを行います。 (PAY.JP)
また、トークンは1回しか使えない点も要注意です。 (PAY.JP)

例:Node.js(Express)で最小実装

import express from "express";

const app = express();
app.use(express.json());

const PAYJP_SECRET_KEY = process.env.PAYJP_SECRET_KEY; // sk_test_...

app.post("/pay/charge", async (req, res) => {
  const { tokenId, amount } = req.body;

  // ここでは最小構成でfetchを使う(SDKを使ってもOK)
  const params = new URLSearchParams();
  params.append("amount", String(amount));
  params.append("currency", "jpy");
  params.append("card", tokenId);

  const resp = await fetch("https://api.pay.jp/v1/charges", {
    method: "POST",
    headers: {
      "Content-Type": "application/x-www-form-urlencoded",
      // Basic認証: username=SecretKey, passwordは空
      Authorization:
        "Basic " + Buffer.from(`${PAYJP_SECRET_KEY}:`).toString("base64"),
    },
    body: params.toString(),
  });

  const data = await resp.json();

  if (!resp.ok) {
    return res.status(resp.status).json({ error: data });
  }
  return res.json({ charge: data });
});

app.listen(3000, () => console.log("listening on 3000"));

PAY.JP の API は Secret Key を使った認証(Basic認証)で操作でき、Secret Key は全API操作が可能な重要情報なので取り扱い注意、という前提が公式に書かれています。 (PAY.JP)
(なので アプリに Secret Key を入れないのが絶対条件)

Flutter側は /pay/chargetokenIdamount をPOSTするだけにすると安全です。


6. 3Dセキュアを使う場合(必要なときだけ読む)

payjp_flutterPayjp.startCardForm() の引数で useThreeDSecure を指定できます(デフォルトは false)。 (PAY.JP)

モバイルSDKで3Dセキュアを使う場合、管理画面で リダイレクトURL を設定し、認証後にアプリへ戻るための導線(Universal Links / App Links / カスタムスキーム等)を準備します。 (PAY.JP)

また、管理画面側で「トークン作成時の3Dセキュアを常に必須」にするモードもあり、その場合はSDK側の指定に関わらず実施される挙動も明記されています。 (PAY.JP)

まずは「テストで動かす」なら、3DSなしでトークン → チャージ作成まで通してから、必要に応じて3DSを足すのが早いです。


7. 実装してみて良かったところ / ハマりどころ

良かった

  • Flutterからカードフォームを呼び出すだけでトークン化まで完了する(自前UI・自前バリデーション地獄になりにくい) (PAY.JP)
  • コールバックの成否でフォーム側が自然に完了/エラー表示してくれる(CallbackResultOk/Error) (PAY.JP)
  • スマホ新法で今後も使うことが多そうだと感じた。

ハマりどころ

  • 「決済」はサーバー側が必須(Secret Keyを安全に扱うため)
  • 3Dセキュアを入れるなら、リダイレクトURLやアプリ復帰の導線づくりが必要 (PAY.JP)
  • トークンは使い捨て(再利用前提の設計にすると詰む) (PAY.JP)
  • リポジトリの注意として、Android Emulatorで3DS画面が出ないケース(Chrome初期設定が原因のことがある)も言及があります (GitHub)

まとめ

PAY.JP × Flutter は、

  • アプリ:カード入力 → トークン発行
  • サーバー:トークンで支払い作成

という王道パターンを、payjp_flutter のカードフォームでかなり短い実装で作れました。 (PAY.JP)

今後も決済でぜひ使っていきたいサービスだと思いました。
いいねやコメント待ってます!!

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?