Ruby on Rails製のECシステムであるSolidusは非常に拡張性に富んだ設計がされているものの、デフォルトのままだと文字通り必要最低限の機能しかないうえに、海外の商習慣に基いて作られているため日本国内で使う場合には多くの箇所を自力でカスタマイズするしかありません。
この記事でテーマとしている決済手数料も、日本国内では未だ少なくないECサイトが「代金引換決済の場合324円発生します」とかしているにも関わらず標準のSolidusではそれを実現することができません。
エクステンションを用いて実現することも可能ですが古いバージョンのまま更新が止まってしまっていたり、複数通貨にも対応していて多機能過ぎたりします。もっとシンプルに実装したい場合には今回紹介する方法を用いると良いかもしれません。
最初にコードを記します。
- 動作環境 Solidus 2.3系
Spree::PaymentMethod.class_eval do
preference :payment_method_fee, :integer
end
Spree::CheckoutController.class_eval do
before_action :update_payment_method_fee, only: :update
private
def update_payment_method_fee
return unless params['state'] == "payment"
if params[:order][:wallet_payment_source_id].present?
# solidusはparams[:wallet_payment_source_id]の有無でwalletの使用/不使用を判定している
# カードの場合0円にしてます。適時書き換えてください。
payment_method_fee_is = 0
else
payment_method_fee_is = Spree::PaymentMethod.find(params['order']['payments_attributes'][0]['payment_method_id']).preferred_payment_method_fee
end
payment_method_fee_was = @order.adjustments.eligible.where(label: Spree.t(:payment_method_fee)).sum(:amount).to_i
if payment_method_fee_is != payment_method_fee_was
@order.adjustments.eligible.where(label: Spree.t(:payment_method_fee)).update_all(eligible: false)
Spree::Adjustment.create(label: Spree.t(:payment_method_fee), adjustable: @order, order: @order, amount: payment_method_fee_is) if payment_method_fee_is != 0
@order.adjustments.reload
@order.updater.update # @order.totalや@order.adjustment_totalを再計算する
end
end
end
これら2つのファイルをプロジェクトに追加するだけで実現できてしまいます。簡単ですよね。
仕組み
PREFERENCE
決済手数料の金額は、SolidusのPreferenceを使用して保存しています。Preferenceについて詳しくは下記の記事をご覧いただければと思いますが、簡単に説明するとDBにカラムを追加せずに任意のキーの変数をActiveRecordインスタンスやアプリケーション全体に対して保存できる仕組みのことです。
[日本語訳] PREFERENCES - DEVELOPER GUIDE | SPREE COMMERCE
なおSpree::PaymentMethodクラスはデフォルトでPreferenceを使用できる設計になっています。新しくPreferenceを定義すると自動でadmin/payment_methods/:id/edit
に型に応じたフォームが現れるのでビューをいじる必要もなく任意の変数を保存することができます。あとはこのフォームから金額を設定してあげるだけですね。
あとはcheckout_controller_decorator.rb
が決済方法に応じた手数料を注文に付与してくれます。解説についてはコード内にコメントで書いた通りです。
ちなみにSpree::Payment
のコールバックとかで決済手数料を計算しようともしてみたんですがうまく行きませんでした。adjustmentを作るだけと思いきや、order.totalやpayment.amount辺りも値を更新する必要があるのでタイミングが結構シビアです。色々試した結果checkout_controllerで計算するのが一番という結論に至りました。
ついでに
Solidusを使ってECサイトを作る場合、コアとなるようなメソッドに手を加えてしまうとSolidus本体のアップデートの際にシステムが壊れてしまったり、コードの見通しが悪くなったりとあまり良いことがありません。
カスタマイズする場合は、今回みたいにコントローラのbefore_actionやコールバック等を活用してSolidusのコアと疎結合になるよう組み立ててゆきましょう。