自分の作っているミニ掲示板サイトに「クリエイターを支援する機能(投げ銭)」を追加したくて、PAY.JP を使ってクレジットカード決済を実装しました。
PAY.JP はドキュメントがシンプルで導入しやすいのですが、実際に実装してみると
- カード入力フォームが初期化されない
- カード番号欄にフォーカスできない
といったトラブルにも遭遇しました。
この記事では、実際に体験した内容とハマりポイントをまとめていきます。
実装した内容
前提として下記で作成している掲示板サイトに支援機能ボタンを追加しました。
- フロントエンド:Vue.js
- バックエンド:Flask
支援ボタンを追加
支援ボタンを押すと金額選択の画面に遷移します。
金額を選び、クレジットカード情報を入力すると支払いが完了します。
管理画面で支援金額の合計が確認できる
ダッシュボードで決済履歴も一覧できる
PAY.JP 側が決済処理の複雑な部分をほぼ吸収してくれているため、
フロントとバックエンドから API を呼ぶだけで実装できました。
実装中に発生したトラブル
最もハマったのがこのエラーです。
カード入力フォームの初期化に失敗しました
カード番号入力欄にフォーカスできない
Vue(SPA)環境で PAY.JP を使うと起きやすい症状のようで、
原因は Payjp を複数回 new してしまうこと でした。
ダメな例(mounted 内で毎回 new される)
mounted() {
const payjp = Payjp("pk_test_xxx"); // ← ページ遷移で毎回 new される
const elements = payjp.elements();
this.card = elements.create("card");
this.card.mount("#card-element");
}
Vue はコンポーネントが再描画されるたびに mounted() が実行されるため、
そのたびに new Payjp() が走ってしまいます。
PAY.JP は 1ページにつき1インスタンスのみ許可 のため、
2回目の new で即エラー → Elements が壊れる → フォーカス不可 という流れでした。
解決策:Payjp のインスタンスは 1 回だけ作る!
最終的に、以下のように シングルトン化 して解決しました。
// payjp.js
let payjpInstance = null;
export function getPayjp() {
if (!payjpInstance) {
payjpInstance = Payjp("pk_test_xxxxx");
}
return payjpInstance;
}
Vue 側ではこれを呼び出すだけです。
import { getPayjp } from "@/utils/payjp";
mounted() {
const payjp = getPayjp(); // ← これで二重初期化しない
const elements = payjp.elements();
this.card = elements.create("card");
this.card.mount("#card-element");
}
この変更を行った瞬間、
カード入力フォームの初期化エラーもフォーカス問題も完全に解決 しました。
まとめ
PAY.JP はとても使いやすいサービスでしたが、
SPA(Vue / React)で利用するときは インスタンスの二重生成に注意 が必要だと学びました。
実装してみて感じたのは、
サイトを運営する = 小さくても立派なビジネスであり
その導線を簡単に用意できる PAY.JP は非常に扱いやすい
ということです。
投げ銭機能や支援機能を導入したい人には、
PAY.JP はかなりオススメできるサービスだと思います。




