[Ruby] Pay.jpでクレジットカード決済を簡単実装

概要

クレジットカード決済代行サービスのPay.jpを導入することで、Webサービスなどにクレジットカード決済機能を簡単に導入することができる。

本記事では、PayjpをRubyから利用するための公式gemである、’payjp’を用いて、Rubyスクリプトから顧客、カード、プラン、支払い、定期課金を作成し、利用する。

前提

以下の環境で動作確認済み

要素 バージョン
debian 8.6
ruby 2.3.1
payjp 0.0.5

また、Payjpのアカウントを保有していて、テストモードで動いてる状態を想定する。

テストモードについて

Pay.jpには、実際に支払いが行われる本番運用の「ライブモード」と、動作確認用で、実際の支払いは行われない「テストモード」があるが、本記事ではテストモードを使って、テスト用のクレジットカードを登録して一連のチュートリアルを行う。

クレジットカード登録フォームについて

Payjpでは、Webやネイティブアプリに埋め込む「チェックアウト」と呼ばれる、最初から完成されたクレジットカード登録フォームが提供される。これを用いると、クレジットカード情報の入力UIから、カード情報の検証、支払い用カードトークンの発行までを自動で行ってくれる。

実装においては事実上必須ではあるが、本記事ではRubyで、サーバサイドだけで完結させることを目的としているので、これを用いずに同様のことを行う。

用語

実装の前に、Payjpにおける各種用語を簡単に確認する。
詳細は公式ドキュメントを参照するとして、ここでは用語の概要を数行でまとめる。

ダッシュボード

管理者が売り上げ、顧客、プラン、定額課金などの確認を行ったり、各種設定を行う場所

売上

クレッジトカードによる支払い。1回の支払いにつき1個の売上データが生成される。

カード(トークン)

入力されたクレジットカード情報が正しかった場合に、カードトークンが生成される。Payjpではこのカードトークンを用いて支払いを行うことができる。しかし、カードトークンはセキュリティの観点から一度しか使用することができない。そこで、後述の「顧客」と紐付けることで永続化する。

顧客

Payjp上に登録するユーザ。顧客にはクレジットカードを紐付けることができ、紐付けておくことで毎回クレジットカード情報を送信しなくても顧客IDを指定するだけで支払いを行える

プラン

月額費500円、年会費3000円のような、定期的に自動で支払いを行う課金パターン。金額、支払い間隔(毎月or毎年)、支払日の組で事前に登録することで定額課金を作成できるようになる

定額課金

プランと顧客を紐付けたもの。プランIDと顧客IDを指定することで、定額課金を作成することができ、対象の顧客から毎月(or毎年)一定額自動で課金させることができる

その他

プロダクト、入金、PayIDといった概念には本記事では触れないので、必要に応じて公式ドキュメントを参照すること

実装

ライブラリのインストール

PayjpはRest風のAPIを通じて各種支払い処理を行うことができる。
APIを直接呼び出しても良いが、Rubyを含む各種主要言語について公式ライブラリが提供されているので、本記事ではRubyからPayJPのAPIを利用する。

Ruby用のライブラリはgemで提供されているので、インストールする。

gem install payjp

あるいはGemfileに以下を追加してbundle installする

gem 'payjp'

APIキーの設定

Payjpの管理画面の設定メニューにて、以下のように各種APIキーを確認することができる。

APIキーは、ライブモードとテストモードで異なるものを利用するようになっているので、テストモード用については最悪流出しても問題はない。また、それぞれについて公開鍵と秘密鍵があるが、公開鍵はWebに埋め込んだりするものなので、今回は秘密鍵のみ利用する。

よって、本記事では一番上のテスト用秘密鍵のみ利用する。ソースコード中にキーを埋め込んでも良いが、今回は環境変数に埋め込むようにする。

$ export PAYJP_PRIVATE_KEY="sk_test_dummydummydummydummydummydummydummydummydummy"

これで下準備が完了した。

下準備

先程インストールしたgemをrequireし、PayjpモジュールにAPIキーを環境変数から設定する。

require 'payjp'

class MyPayjp
  Payjp::api_key = ENV['PAYJP_PRIVATE_KEY']
end

これでPayjpを利用する準備はすべて完了だ。
本記事では、上記MypPayjpクラスに対してクラスメソッドを順に定義し、それぞれの実行結果を確認しながら進める。

カードトークンを作成する

カードトークンは、Token.createで作成することができる。カード情報として、以下を指定する。

  • カード番号
  • CVC(セキュリティコード)
  • 有効期限(月)
  • 有効期限(年)

以上を引数と取るメソッドを実装すると以下のようになる

#
# カードトークンを生成する
#
def self.create_token(number, cvc, exp_month, exp_year)
  token = Payjp::Token.create(
    card: {
      number:    number,
      cvc:       cvc,
      exp_year:  exp_year,
      exp_month: exp_month,
    }
  )
  return token.id
end

カード情報を指定して実行すると、以下のようにトークンを取得することができる。ちなみに下記カード情報はVISAのテスト用カードなので流出しても問題ない。

irb(main):027:0* MyPayjp.create_token('4242424242424242', '123', '2', '2020')
=> "tok_d0b6e7ddf1735d3ddc77e28aa59f"

トークンを用いて支払う

前項で取得したトークンを用いて、支払いを行う。支払いにはCharge.createを用いる。
この時、利用する通貨単位を指定するが、現状JPYしか対応していないのに必須なので、ここは固定でラップしておく。

#
# カードトークンを用いて支払いを作成する
#
def self.create_charge_by_token(token, amount)
  Payjp::Charge.create(
    amount:   amount,
    card:     token,
    currency: 'jpy'
  )
end

前項で取得したカードトークンと、支払金額を指定することで支払いを行うことができる

irb(main):011:0* MyPayjp.create_charge_by_token('tok_d0b6e7ddf1735d3ddc77e28aa59f', 3000)

支払い結果は管理画面で確認することができる。とりあえずダッシュボードに売上が3000円と入っているので支払いに成功したことがわかる。

なお、同じトークンを用いてもう一度支払いを行おうとすると、以下のように例外が発生するため、トークンは使い捨てであることがわかる。

irb(main):012:0* MyPayjp.create_charge_by_token('tok_5edfae0f38b7b1352e7c249a4124', 3000)
Payjp::InvalidRequestError: (Status 400) Token "tok_5edfae0f38b7b1352e7c249a4124" has already been used.

顧客の作成

前項の、カードトークンを用いた支払いだと、トークンが使い捨てであることもあって、匿名での課金になってしまう。誰が、いつ、いくら課金したかをシステムで管理できるように、通常は顧客を作成する必要がある。

顧客を作成して後からトークンと紐付けることもできるが、顧客情報と同時にカード情報を指定することで、トークン化を経由すること無く顧客とカードを紐付けることができる。今回は後者のパターンで実装する。

なお、顧客登録時にIDやメールアドレス、備考、メタデータなど柔軟に登録することができるが、デフォルトではいずれも不要で、自動で割り振られるIDで顧客を特定することができるので、今回は最小限の構成にする。

#
# 顧客を登録する
#
def self.create_customer(number, cvc, exp_month, exp_year)
  token = self.create_token(number, cvc, exp_month, exp_year)
  Payjp::Customer.create(card: token)
end

カードトークンの生成については作成済みのcreate_tokenメソッドを用いた。顧客情報に他に必須パラメータはないので、これだけで顧客を作成することができる。

irb(main):032:0* MyPayjp.create_customer('4242424242424242', '123', '2', '2020')

管理画面の顧客一覧を確認すると、顧客が作成され、カードも登録済みになっていることがわかる。

以降では、この作成した顧客を用いて各種支払を行う。

顧客を指定して支払いを行う

前項で作成した顧客を用いて支払いを行う。基本はトークンを用いた支払いと同じだが、使い捨てのトークンと違い、顧客を用いることで何度でも繰り返し支払いを行うことができる。利用するメソッドはトークン利用時と同様に、Charges.createだが、tokenを指定していた部分をcustomerに変更している。

 #
 # 顧客を用いて支払いを作成する
 #
 def self.create_charge_by_customer(customer, amount)
   Payjp::Charge.create(
     amount:   amount,
     customer: customer,
     currency: 'jpy'
   )
 end

せっかくなので同一顧客、同一金額で3回支払いを行ってみる。

irb(main):042:0> MyPayjp.create_charge_by_customer('cus_3a374fa5b6c5b7dabf0778dadff2', 500)
irb(main):043:0> MyPayjp.create_charge_by_customer('cus_3a374fa5b6c5b7dabf0778dadff2', 500)
irb(main):044:0> MyPayjp.create_charge_by_customer('cus_3a374fa5b6c5b7dabf0778dadff2', 500)

管理画面の顧客詳細ページを見ると、3回分支払いが行われていることが確認できる。

プランを作成

「月額500円」のような定期課金を行わせたい場合に、前項の支払いを月1回、cronで自動実行するなども考えられるが、Payjpには定額課金の機能も導入されている。
プランにも、任意のIDやプラン名を指定する他、支払日やトライアル期間(登録後3ヶ月無料など)の設定が行えるが、ここでは最小構成として、金額と支払間隔のみを指定する。

なお、支払日を指定しない場合、デフォルトで定額課金を開始した日が支払日になる。そのため、例えばユーザAは7日に、ユーザBは15日に定額課金を開始したとすると、それぞれが毎月7日と15日に支払いが行われる。よって、ユーザごとに支払日を分散したい場合(負荷を軽減したい場合)には、支払日を指定しないという方法もある。

実装については概ねこれまでと同じ流れなので結果だけ

#
# プランを作成する
#
def self.create_plan(amount, interval = 'month')
  Payjp::Plan.create(
    amount:   amount,
    interval: interval,
    currency: 'jpy'
  )
end
irb(main):063:0* MyPayjp.create_plan(500, 'month')

月額500円のプランが出来上がる

顧客に定額課金をさせる

顧客とプランを紐付けた「定額課金」を作成することで、プランの内容に応じた定額課金を顧客に行わせることができる。
ここでは、前項で作成した月額500円のプランを、作成済みの顧客に適用する。

なお、作成したプランは「支払日」を作成していないので、定額課金を開始した日が初回引き落とし日になるので、定額課金作成と同タイミングで初回分の支払いが作成される。(一応日割りでの支払いも可能だがここでは割愛する)

#
# 定額課金を作成する
#
def self.create_subscription(customer, plan)
  Payjp::Subscription.create(
    customer: customer,
    plan:     plan,
  )
end
irb(main):075:0* MyPayjp.create_subscription('cus_3a374fa5b6c5b7dabf0778dadff2', 'pln_5c30684cd1afda400e7693a8a1e5')

定額課金が作成され、初回分の支払いが行われていることが確認できる。

ソースコード

今回作成したソースコード全文は以下の通り

require 'payjp'

class MyPayjp

  Payjp::api_key = ENV['PAYJP_PRIVATE_KEY']

  #
  # カードトークンを生成する
  #
  def self.create_token(number, cvc, exp_month, exp_year)
    token = Payjp::Token.create(
      card: {
        number:    number,
        cvc:       cvc,
        exp_year:  exp_year,
        exp_month: exp_month,
      }
    )
    return token.id
  end

  #
  # カードトークンを用いて支払いを作成する
  #
  def self.create_charge_by_token(token, amount)
    Payjp::Charge.create(
      amount:   amount,
      card:     token,
      currency: 'jpy'
    )
  end

  #
  # 顧客を登録する
  #
  def self.create_customer(number, cvc, exp_month, exp_year)
    token = self.create_token(number, cvc, exp_month, exp_year)
    Payjp::Customer.create(card: token)
  end

  #
  # 顧客を用いて支払いを作成する
  #
  def self.create_charge_by_customer(customer, amount)
    Payjp::Charge.create(
      amount:   amount,
      customer: customer,
      currency: 'jpy'
    )
  end

  #
  # プランを作成する
  #
  def self.create_plan(amount, interval = 'month')
    Payjp::Plan.create(
      amount:   amount,
      interval: interval,
      currency: 'jpy'
    )
  end

  #
  # 定額課金を作成する
  #
  def self.create_subscription(customer, plan)
    Payjp::Subscription.create(
      customer: customer,
      plan:     plan,
    )
  end

end

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です