ユカシカド エンジニアブログ

体の栄養状態を把握する検査サービス VitaNoteを開発するエンジニアのブログ

Spreeにカード決済を導入する方法

Spreeざんまいの毎日です。SpreeでECを構築するのにあたって次に私が大いに、盛大に躓いたのがカード決済の導入。 自分が探した限り、新たにカード決済を導入する方法が説明されているようなドキュメントが見つけられませんでした。
少なくとも公式ドキュメントにはそっけなく、

In order to implement a new gateway in the spree_gateway project,
please refer to the other gateways within app/models/spree/gateway inside that project.

とあるだけでした。ツライ。

こうなったら調べるしかないのでそのメモです。あまり自信がないのでツッコミ大歓迎です。

カード決済を自前で導入

デフォルトではカード決済はできないので多様な支払い方法を導入するSpree Gatewayというgemが用意されていますが、ほとんど海外の決済プロバイダのgatewayしか用意されていないので、Spreeで国内向けECを構築する場合、少なくとも現時点では自前で用意することになると思います。
※ 試してはいませんがPayPalであればサポートされているようなのですぐに運用できるかもしれません。

前提として、当社ではご縁があった決済プロバイダーのサービスを利用することになったので、まずはそのAPIにアクセスしてレスポンスを受け取って整形するだけの基本的なモジュールを書いて、オレオレgemをつくりました。オレオレとはいえ初じめてのgemで感動しましたが、このお話は省略します。

Spree::Gateway::PaymentMethod

SpreeではSpree::PaymentMethodが支払い方法を司っていますが、外部の決済ゲートウェイ(?)を使用して、つまりカード決済をおこなう場合はSpree::Gatewayを継承したクラスを用意する必要があります。
元々用意されているカード決済デモのクラス、Spree::Gateway::Bogusを参考にそこで最低限必要となるAPIがどのようなものか知る必要があるのでそれを調べてみました。

#provider_class

ActiveMerchantに登録されているGatewayであれば、そのクラス返す。なければ以下のように自クラスを返すようにしておく。

def provider_class
  self.class
end

#payment_profiles_supported?

値がtrueの場合、支払い情報入力後に#create_profileメソッドが実行される。恐らく、決済プロバイダの会員情報保持サービスを利用するかどうかを決める値かも。

#create_profile

#payment_profiles_supported? がtrueの場合、Spree::Paymentのインスタンスのsave後(クレジットカード情報入力送信後)に実行され、Spree::CreditCardのインスタンスに:gateway_customer_profile_idが保存される。

#authorize

#payment_profiles_supported? がtrueの場合、注文確定送信後に実行される。
#payment_profiles_supported? がfalseの場合、クレジットカード情報入力送信後と注文確定送信後に実行される。
与信取得を実行して、ActiveMerchant::Billing::Responseのインスタンスを返す。

#purchase

Spree::Config[:auto_capture] = true

か、もしくはSpree::Gateway::PaymentMethod.auto_capture?がtrueを返す場合、クレジットカード情報入力後、#authorizeメソッドではなく、#purchageメソッドが実行される。 この場合、#authorizeメソッドは実行されない 恐らく、オーソリと売上計上の連続処理を想定したメソッドではないかと思われる。ActiveMerchant::Billing::Responseのインスタンスを返す。

#capture

管理画面の注文詳細から'入金申請’(caputuring)をクリックすると実行される売上計上メソッド。ActiveMerchant::Billing::Responseのインスタンスを返す。
#payment_profiles_supported? がtrueかfalseかで、パラメータの渡され方が変わる。 https://github.com/spree/spree/blob/v2.1.1/core/app/models/spree/payment/processing.rb#L41-L50

#void

管理画面の注文一覧から注文を選択。注文の支払い一覧から任意の支払いの”無効”の×ボタンをクリックすると実行される。
stateがvoidになる。stateがfailedやinvalidの場合は失敗する。
ActiveMerchant::Billing::Responseのインスタンスを返す。

#preferences

preferenceメソッドで定義した設定のうち、管理画面から変更できる属性と初期値を設定する

例)

preference :currency, :string, :default => 'JPY'

の場合、

def preferences
  {:currency => 'JPY'}
end    

管理画面で変更できないようにする(コード内でのみの定義でとどめる)場合は空のハッシュにしておく

def preferences
  {}
end    

まとめ

以上が最低限必要なインターフェースだと思います(たぶん)が、決済プロバイダの仕様や、運用上でどういうタイミングで与信ととって売上を計上するかなどで必要な実装は大きく変わってくると思います。
今回調べてみて初めて知りましたがActiveMerchantというライブラリをサポートしているゲートウェイが追加されていっているのでActiveMerchantについてもある程度知っておく必要があると思います。

でもやっぱり面倒

ECのロジックの開発に集中したいのが本音です。とても興味があった(今も諦めきれていない)最近話題の開発者向けクレジットカード決済サービスWebPayさんがActiveMerchantに対応されているので、そこから検討するのが開発者には一番楽かもしれません。