payjp.js v1 移行ガイド

ここではpayjp.js v1をご利用の方向けに、v1の提供終了に伴うマイグレーションについて説明します。

移行先の選定

v1をご利用の場合、クレジットカード情報入力フォームは加盟店様が実装しているかと思います。

フォームのデザインを変えたくない場合はpayjp.js v2の分割フォームを利用して再実装いただく必要があります。

弊社が用意したデザインへの変更を許容できる場合は、WebであればCheckout、または、payjp.js v2を type=card でご利用ください。 モバイルであればモバイルSDKの利用を検討ください。 モバイルSDKはiOSAndroidFlutterReact Nativeを用意しております。

移行先 Checkout v2 type=card v2 分割フォーム iOS/Android Flutter/React Native
デザイン 弊社提供 弊社提供 自由 弊社提供 弊社提供
工数の目安 中〜大 中〜大
JSの知識 不要 必要 必要 不要 必要

移行によりできなくなること

v1廃止の目的でもありますが、いずれの移行先においても、カード番号・有効期限・CVCの値は取得できなくなります。

これに伴い、

  1. カード番号からブランド情報を自身で判定すること
  2. カード番号・有効期限・CVCを自身でバリデーションすること
  3. カード番号を取得してカード会社情報を判定すること

などができなくなります。

その代わりに、移行先ライブラリが1や2の処理を行います。 ただし3についてはセンシティブ情報に区分されるため、今後情報を取得することはできなくなります。

例えば1について、Checkoutでは、入力されたカード番号からブランド判定をリアルタイムでUIに反映し、 利用できないブランドでリクエストされた場合はエラーを表示します。 payjp.js v2ではJavaScript(以降JSと表記)を介してカードブランド情報をリアルタイムに通知し、さらに type=card の場合はリアルタイムでブランドロゴを表示します。

v2への移行について

以下ではpayjp.js v2へ移行される場合の説明を行います。 その他の移行先については、各ドキュメントをご参考ください。

デザインの制限

v2を利用される場合、デザイン面にておいて、以下のことができなくなります。

  1. 有効期限フォームにselectタグを使うこと
  2. inputタグのrequired属性によるUI表現
  3. CVCなどのinputタグにtype=password属性を付与すること
  4. 入力フォーム(inputタグ)に利用可能ではないCSSプロパティを適用すること
  5. inputタグへのWeb Fontの利用(近日実装予定)

1については、最近の決済フォームデザインの流行や利便性を鑑みての判断となります。 例えば02/22222と入力すればよく、スラッシュ・0埋めなどの整形は自動で行われます。 また、多くのモバイルではデフォルトで数字キーが立ち上がります。

2の属性があると、空欄でform.submit()が行われた際に「このフィールドを入力してください」というブラウザ実装の警告が出ます。 しかしiframe化により利用できなくなります。

3は、PCI-DSSにおける要件ではありませんが、モバイルSDKではデフォルトで表示が隠れる仕様となっております。

4について、利用可能なCSSプロパティについてはこちらを確認ください。

できるようになること

  1. 3D secure
  2. Apple Pay on the Webの最新バージョンの利用(近日実装予定)

移行の要点

・inputタグはiframe内に作られる

現在お使いのCSSやJS、外部ライブラリが、フォームのinputタグを参照している場合、適用されなくなるため改修が必要となります。 特にライブラリ側でワークアラウンドがなく利用できない場合には、自身でライブラリ相当の内容を実装し直す必要があるかもしれません。 本ページの移行例ではそうしたケースを取り扱っております。

なお、v2が生成するinputタグへ、CSSやイベント検知を適用することは可能です。

また、名義などの任意入力項目についてはご自身で用意いただくため、従来の実装をそのままご利用可能です。

・ライブラリの初期化方法の変更

payjp.jsではライブラリを読み込むことで使用可能になる Payjp という変数を利用します。

v1ではこの変数の型はobjectのため、Payjp.setPublicKey(key)Payjp.createToken() のように呼び出します。

一方v2では変数の型は関数であり、初めに var payjp = Payjp(key) とインスタンスを生成して、payjp.createToken() などを呼び出します。 この初期化処理における引数のオプションなどはAPIドキュメントを参考ください。

・Payjp Element

Payjp Elementとは、入力フォームを管理するJSオブジェクトです(文脈によって、入力フォーム自体を指す場合もあります)。 これを自身のDOM上に mount() して、入力フォームを持つiframeを生成します。

var elements = payjp.elements()
var cardElement = elements.create('card') // Payjp Elementを生成
cardElement.mount('#id') // 入力フォームを配置

Payjp Elementは大きく2種類存在します。

  1. type=card: カード番号・有効期限・CVCの入力フォームが一体となった固定デザイン
  2. type=cardNumber,cardExpiry,cardCvc: それぞれを独立したフォームとして扱う分割デザイン

既存フォームのデザインを維持したい場合は2をご利用ください。

移行例

ここではpayjp.js v1を使ってマテリアルデザイン調に作られた決済フォームをv2へ移行する例について解説します。

v1 v2
デザイン
フォームURL https://payjp.github.io/sample/payjp-js/v1.html https://payjp.github.io/sample/payjp-js/index.html

フォームのソースコードはこちらを確認ください。 ソースコードはMITライセンスでご利用いただけます。

このフォームは、カード番号・有効期限(月)・有効期限(年)・CVC・カード名義を入力後にボタンを押すことでトークンを生成します。 これを基本的なデザインは変えずにv2を利用したフォームへと移行させます。

例外として、v2の有効期限入力フォームは月と年でまとまったもののみを提供しているため、移行後のフォームではそのデザインに従っています。

注. 例は事前ビルドを必要としない構成で記述されてます。

1. 課題の確認

このv1決済フォームは、マテリアルデザインのライブラリであるMaterial Component Web(以下MDCライブラリと表記)を使って作られています。 このライブラリは指定のclass名を当てることで、マテリアルデザイン準拠の入力フォームを簡単に実現できます。 以下は mdc- という接頭辞のclass名を使ってText fieldsコンポーネントを実現しています。

<script type="text/javascript" src="https://js.pay.jp/v1/"></script>
<script src="https://unpkg.com/material-components-web@latest/dist/material-components-web.min.js"></script>
<link href="https://unpkg.com/material-components-web@latest/dist/material-components-web.min.css" rel="stylesheet">
<!-- 中略、以下カード番号の入力フォーム -->
<label class="field mdc-text-field mdc-text-field--filled">
  <span class="mdc-text-field__ripple"></span>
  <span class="mdc-floating-label">カード番号</span>
  <input id="v1-card-number"
         class="mdc-text-field__input"
         autocomplete="cc-number"
         autocorrect="off"
         spellcheck="false"
         inputmode="numeric"
         placeholder="4242 4242 4242 4242"
         required
  >
  <span class="mdc-line-ripple"></span>
</label>
<!-- カード番号の入力フォームここまで、以下中略 -->
<script src="mdc.js"></script>
<script type="text/javascript">
  setUpMDC() // MDCライブラリの初期化処理
</script>

v2移行を行うと、inputタグが別ドメインのiframe内へ移るため、class名を当てたりJSやCSSを直接適用することができず、Text fieldsコンポーネントをそのまま使うことができません(※注)。

次項から元のデザインを実現するために必要な作業を見ていきましょう。

注. iframe化されるフォームはカード番号・有効期限・CVCのみとなります。 カード名義のフォームは移行不要なので、Text fieldsコンポーネントをそのまま使うことができ、この例においてはそのまま残しています。

2. Payjp Elementの利用

まずは、元デザインのカード情報入力フォームであるinputタグを、v2の入力フォームであるPayjp Elementに置き換えましょう。

Payjp Elementの基本的な使い方は簡単です。

HTMLの変更としては、カード番号・有効期限・CVCのinputタグをdivタグに変更するだけです。 divタグにはユニークなidを必ず付けてください。 また今回のようにlabelタグを利用している場合、for 属性にそのidを指定することで、labelタグの領域をclick時に自動的にPayjp Elementへfocusを移すことが可能です。

<script type="text/javascript" src="https://js.pay.jp/v2/"></script>
<!-- 中略 -->
<label class="field mdc-text-field mdc-text-field--filled" for="v2-mdc-card-number">
  <span class="mdc-text-field__ripple"></span>
  <span class="mdc-floating-label">カード番号</span>
  <div id="v2-mdc-card-number" class="mdc-text-field__input"></div>
  <span class="mdc-line-ripple"></span>
</label>
<!-- 中略。以下、有効期限についてはも同様 -->
<label class="field mdc-text-field mdc-text-field--filled" for="v2-mdc-card-expiry">
  <span class="mdc-text-field__ripple"></span>
  <span class="mdc-floating-label">有効期限</span>
  <div id="v2-mdc-card-expiry" class="mdc-text-field__input"></div>
  <span class="mdc-line-ripple"></span>
</label>
<!-- 以下、CVCについても同様 -->

そして以下のJSを実行し、divタグにPayjp Elementをmount()しましょう。

// setUpMDC()以外の処理は削除やコメントアウトして以下を記述
var payjp = Payjp('pk_test_0383a1b8f91e8a6e3ea0e2a9')
var elements = payjp.elements()

// 入力フォーム毎にElementオブジェクトを生成する
var numberElement = elements.create('cardNumber')
var expiryElement = elements.create('cardExpiry')
var cvcElement = elements.create('cardCvc')
numberElement.mount('#v2-mdc-card-number')
expiryElement.mount('#v2-mdc-card-expiry')
cvcElement.mount('#v2-mdc-card-cvc')

ここまでの変更でinputタグを弊社ドメインのiframe内へと移せたため、本質的にはv2移行が完了できたことになります。

しかし現状では、下の図のように一部デザインが適用できていなかったり、カード番号・有効期限・CVCの入力欄はクリックできず、コンソールではMDCライブラリからの警告が出てしまっています。 MDCライブラリのデザイン生成にはinputタグの存在が必要であり、それらがiframeに隠れてしまったため、正しく機能していないためです。

3. Elementコンテナの利用

この例のように、デザインライブラリがinputタグに依存しており、iframe型決済用のワークアラウンドがない場合では、デザインライブラリ相当のJSやCSSを自身で用意する必要があります。

MDCライブラリのText fieldsコンポーネントが適用しているCSSを調べて、 そのうちinputタグ以外のCSSを最小限にまとめたものがmdc-on-v2.cssです。 (MDCライブラリのclass名と被らないよう、 mdc- の接頭字を除いた命名でclass名を新しく作っています。その際、 mdc-text-field--filled 相当のCSSは text-field にまとめています)

<link rel="stylesheet" type="text/css" href="mdc-on-v2.css" />
<!-- 上の読み込みを足して、各label内のclass名を修正 -->
<label class="field text-field" for="v2-mdc-card-number">
  <span class="text-field__ripple"></span>
  <span class="floating-label">カード番号</span>
  <div id="v2-mdc-card-number" class="text-field__input"></div>
  <span class="line-ripple"></span>
</label>

MDCライブラリを使用しなくなったのでコンソールエラーは消えます。 しかし現状だと、入力フォームにfocusした際のラベルのフローティングが移行前のように動作しません。 以下のようにラベルとプレースホルダーが被ってしまいます。

MDCライブラリは、inputタグへのfocusやinputイベントに応じて、labelタグに状態を表すclass名を足し、適用するCSSを変えることでラベルのフローティングを実現していました。

label.mdc-text-field--focus .mdc-floating-label {
  /* focus時にラベルをフローティング */
}

しかしv2を利用する場合、直接にはinputタグのイベント検知はできません。

解決法として後述するv2でのイベント検知を使った方法もありますが、CSSへの活用に限ればElementコンテナが便利です。

Elementコンテナとは、先述のMDCライブラリが行っているような、inputタグの状態を表すclass名をマウント先に付与する仕組みです。 入力フォームがfocus状態の時は PayjpElement--focus というクラス名が自動的にdivタグにつくので、それに合わせてCSSを変更しましょう。

div.PayjpElement--focus ~ .floating-label {}

なお、この例では先述のCSSと比べて兄弟関係を指定するセレクタ(~)を利用しています。 labelタグにclass名が付与される場合であればフローティングラベル要素( (mdc-)floating-label クラス)は子孫コンビネーターを使って実現できますが、 Elementコンテナが働くdivタグはフローティングラベル要素と同じ階層の要素のため、指定には工夫が必要になります。 この例ではdivタグの位置をlabelタグ直下に移動し、兄弟関係を指定するセレクタを利用しました。

<label class="field text-field" for="v2-mdc-card-number">
  <div id="v2-mdc-card-number" class="text-field__input"></div><!-- Payjp Elementを上に -->
  <span class="text-field__ripple"></span>
  <span class="floating-label">カード番号</span><!-- フローティングラベル要素を下に -->
  <span class="line-ripple"></span>
</label>

次項では、残るデザイン上の差異である、inputタグへのCSSとプレイスホルダーの指定について見てみましょう。

4. inputタグへのデザイン適用

iframe内のinputタグへのCSSやプレースホルダーの適用は、Payjp Elementの初期化時、及び更新時の引数でJSを介して行います。

2のPayjp Element初期化時のコードに引数styleやplaceholderを指定してみましょう。

var payjp = Payjp('pk_test_0383a1b8f91e8a6e3ea0e2a9')
var elements = payjp.elements()

// styleオブジェクトの詳細は以下を参照
// https://pay.jp/docs/payjs#style
var elementStyle = {
  base: { // 常に適用したいCSSを以下に記述
    fontFamily: '\'Noto Sans Japanese\', sans-serif', // CSSプロパティ名はキャメルケースで指定
    '::placeholder': { // 疑似クラスや疑似要素も指定可能 
      color: 'rgba(0, 0, 0, 0.54)',
    },
    caretColor: '#198FCC',
    lineHeight: '28px', // inputタグのheightを定義
  },
  invalid: { // フォームに無効な文字列が入っている場合、baseの値を継承しつつ、以下の値のみ上書きする
    color: 'rgba(0, 0, 0, 0.87)',
  },
}

var numberElement = elements.create('cardNumber', {
  style: elementStyle,
  placeholder: '4242 4242 4242 4242'
})
var expiryElement = elements.create('cardExpiry', {
  style: elementStyle,
})
var cvcElement = elements.create('cardCvc', {
  style: elementStyle,
})
numberElement.mount('#v2-mdc-card-number')
expiryElement.mount('#v2-mdc-card-expiry')
cvcElement.mount('#v2-mdc-card-cvc')

5. イベント検知

フォームの内容が不正であればエラーメッセージを出し、完了していればトークン生成ボタンを押下可能にする、といった判定処理を自身で実装する必要はありません。 Payjp Elementはユーザーの入力状況に応じてバリデーションを行い、結果をイベントとして通知します。

element.on() または element.addEventlistener() を使って、イベント検知を行いましょう。 検知可能なイベントは以下となります。

  • focus : フォームがフォーカスされたときに通知
  • blur : フォームがフォーカスを失ったときに通知
  • ready : フォームのレンダリングが完了したときに通知
  • change : フォームの値が変更されたときに通知

最も情報量が多いイベントは change イベントです。 入力が完了したかどうか、エラーはないか、カードブランドは何か、などを検知できます。

// 注. 以下、実装は一部省略や簡略化しています
numberElement.on('change', function(event) { // Element毎にイベントリスナーを用意
  /*
    (eventの値の例) = {
      brand: 'Visa', // <- expiryElement, cvcElementの場合はこの情報はない
      complete: false,
      elementType: 'cardNumber',
      empty: false,
      error: {
        message: '入力が完了していません。',
        code: 'incomplete_error'
      }
    }
  */
  if (event.error) { // error=nullでなければ入力値は不正
    displayErrorMsg(event.error.message) // メッセージはerror.messageをそのまま使うかerorr.codeから自作する
  } else {
    if (errors) {
      displayErrorMsg(errorMsg)
    } else  { // 全Elementでエラーがなければ
      errorElm.classList.remove('visible') // エラーメッセージを隠す
      if (completes) { // 全Elementでcomplete=trueになったら
        buttonElm.removeAttribute('disabled') // ボタンを押下可能に
      }
    }
  }
})

なお、MDCライブラリでは、required属性のついたinputタグがfocusされた後で入力せずにblurするとエラー扱いとなります。 v2ではデフォルトではエラー扱いとはなりません。 この例では実装していませんが、focus 及び blur イベントと element.update()などを組み合わせて挙動を合わせることができます。

6. トークン生成

トークン生成にはv1と同じ関数名のpayjp.createToken()を利用します。

第一引数にはPayjp Elementをどれか一つ渡します。どのElementを渡しても大丈夫です。

第二引数にはカード名義などの任意引数や、Platformをご利用の場合は tenant を渡します。

// 注. 以下、実装は一部省略や簡略化しています
form.addEventListener('submit', function(e) {
  e.preventDefault()

  var options = {
    card: {
      name: nameInput.value || undefined // カード名義フォームから値を取得
    }
  }

  payjp.createToken(numberElement, options).then(function(result) {
    // resultはToken objectまたはError object
    // APIドキュメントを参照: https://pay.jp/docs/api/ 
    if (result.id) { // トークンIDがあれば成功、resultはToken object
      section.querySelector('.token').innerText = result.id
    } else { // resultはError object
      displayErrorMsg(result.error.message)
    }
  })
})

トークン生成後、入力内容をクリアしたい場合、form.reset() ではiframe内の入力内容を初期化することはできません。 element.clear() をご利用ください。 その際Elementコンテナも初期化されるため、クリーンアップ処理ではMDCライブラリが付与するclass名の初期化処理を記述していますが、Payjp Elementに対しては不要です。

v2の機能とは直接関係しない部分については一部説明を省略させていただきましたが、移行の解説は以上となります。

FAQ

Ionicなどのモバイルフレームワークで利用できますか

ionic/capacitor 環境においてpayjp.js v2は利用可能です。 ionic/cordova 環境におけるpayjp.js v2の動作は未確認となっております。

なお、専用で用意しているモバイルSDKはiOSAndroidFlutterReact Nativeとなります。

モバイルの主要ブラウザ上ではpayjp.js v2は正常に動作いたします。 上記にないモバイルフレームワークにおきましては、決済フォーム部分を外部ブラウザへの遷移を誘導したり、アプリ内ブラウザなどを検討ください。

モバイルのWebView内でv2を利用できますか

基本的な動作は確認できております。

ただしモバイルのWebViewにおいて 3D Secure サブウィンドウ型が動作しないケースを確認しております。 代替案としてリダイレクト型を用意しておりますので、そちらをご利用ください。

React.jsやVue.jsなどのWebフレームワークにv2を組み込めますか

問題なくご利用いただけます。 よくあるご質問として、payjp.jsはnpm installしてご利用はできません。 ライブラリの性質上、今後もnpmなどのパッケージマネージャでのご提供は致しません。 必ずscriptタグから読み込んでください。

また、payjp.js v2の各APIの実行タイミングとフレームワークのライフサイクルについてはご注意ください。 例えばelement.mount(selector: string)は、引数を内部で document.querySelector() に渡しているため、実行時に対象の要素がレンダリング済みでないとエラーが発生します。 そのため、Vue.jsのcreated()のタイミングだとエラーが発生します。 mounted()など、要素がレンダリングされた後での実行が必要となります。

分割デザインでカード番号の入力が完了したら自動で有効期限へfocusを移せますか

ご自身でelement.focus()element.blur()を活用して実装いただく必要があります。

例. カード番号のElementをイベント監視し(numberElement.on('change', fn))、complete=true となったら有効期限のElementをfocusする(expiryElement.focus())。

なお、type=card ではデフォルトでその挙動をいたします。