Konifar's WIP

親方!空からどらえもんが!

Androidでクレジットカードの入力フォームの実装を楽にするライブラリを公開しました

作りました。簡単に説明を書いておきます。

github.com

f:id:konifar:20200217214457g:plain

モチベーション

クレジットカードの入力フォームは、ちゃんと作ろうとすると意外とめんどくさいものです。 すでにGitHubには先人がいくつかのライブラリを公開していますが、下記を満たせるものがなかったので自分で作ることにしました。

  1. カードブランドごとのカード番号フォーマットに対応している
  2. サポートするカードブランドを設定できる
  3. 有効期限の年月の単一フォームに対応している
  4. バリデーションのエラーが細かい
  5. CustomViewではなくロジックを提供している
  6. テストコードがある

以下は補足説明です。使い方はリポジトリの方を見てください。

カードブランドごとのフォーマットとバリデーションに対応

カード番号のフォーマットは4桁 * 4 = 16桁が多いですが、実はカードブランドごとに異なります。

Brand PAN format Security code
Visa 4242 4242 4242 4242 3桁
Mastercard 5555 5555 5555 4444 3桁
Diners Club 3622 720627 1667 3桁
American Express 3782 822463 10005 4桁

American Expressはどうしてこうなってしまったんでしょうか。

これらのカードブランドはカード番号のprefixによって判断できます。

Brand Prefix
Visa 4
Mastercard 5
American Express 34, 37
Diners Club 300-305, 3095, 36, 38-39
JCB 3528-3589
Discover 60110, 60112-60114, 601174-601179, 601186-601199, 644-649, 65

このライブラリではCardNumberTextWatcherというTextWatcherを提供していて、入力された文字列からパターンマッチで現在のカードブランドを判定できます。

editText.addTextChangedListener(object : CardNumberTextWatcher(
    separatorType = CardNumberSeparatorType.SPACE, // 区切り文字はスペースかハイフンに対応
    supportedCardBrand = arrayOf(
        Visa(), Amex(), Mastercard()
    ) // サービスでサポートしているカードブランドのリストをセット
) {
    override fun onCardBrandChanged(cardBrand: CardBrand) {
        // カードブランドが決まった
    }

    override fun onCardNumberErrorChanged(error: CardNumberError) {
        // エラーステータスが変わった(エラーじゃなければCardNumberError.None)
    }

    override fun onCardNumberCompleted(cardBrand: CardBrand) {
        // 入力完了した
    }
})

番号がサポート外のブランドだった、Luhnアルゴリズムに沿っていないといった考えられうるエラーをEnumで定義し、Validatorクラスで判定できるようにしています。詳しくはサンプルのMainActivityにベタッと書いてあるので見てみてください。

有効期限の年月の単一フォームに対応

Google PayやPayPayもそうですが、有効期限のフォームは年と月で分けるのではなく次のように同じフォームで入力させるものが多いです。

f:id:konifar:20200217215851g:plain

月を入力するとスラッシュを自動挿入するという制御が必要なんですが、これをちゃんとやろうとすると結構めんどくさいんですよね。

たとえば 1 を入力した場合は 1/ のようにスラッシュを挿入しますが、カーソルの位置はスラッシュの前であるべきです。 12 を入力する可能性があるからです。 一方、 2 を入力した場合は 2/ のようになった上でカーソルはスラッシュの後に移動させるべきです。 20 以上を月に入力する可能性はないからです。

数字の入力だけではなく、スラッシュの入力、削除、コピペなども考慮すると意外と実装はめんどくさいです。

このライブラリでは、めちゃくちゃ愚直なCardMonthYearTextWatcherの実装とテストコードでゴリゴリやっています。もっといいやり方があると思うので、見かねた人がいればPull Requestもらえるとありがたいです。

CustomViewではなくフォーマットとバリデーションのロジックを提供

このライブラリが提供しているのはEditTextやTextInputLayoutを拡張したCustomViewではなくシンプルなTextWatcherです。

なぜCustomViewにしなかったかというと、CustomViewとして提供しようとすると色々なニーズに対応するために設定項目が多くなりがちだからです。例えば inputTypephonepassword か、 autofillHints には設定するのかしないのか、といった具合に色々と選択肢が多くなります。

それよりも、シンプルにフォーマットとバリデーションのロジックのみを提供するようにした方がいいんじゃないかと考えて、今はCustomViewの提供はしていません。そのぶん使う側が自分で addTextChangedListener() しないといけなかったり記述する量は増えるんですが、まあそれはいいかなという気持ちです。

と言いつつ、今後やっぱりめんどくさいと自分が感じたらEditTextの拡張クラスも提供するかもしれません。


もしカードフォームを自前で実装する機会があったら、このライブラリのことを思い出してみてください。そのまま使わなかったとしても実装は参考になるかもしれません。

github.com