-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
/
payment_method.rb
281 lines (245 loc) · 10.1 KB
/
payment_method.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
# frozen_string_literal: true
require 'discard'
require 'spree/preferences/statically_configurable'
module Spree
# A base class which is used for implementing payment methods.
#
# Uses STI (single table inheritance) to store all implemented payment methods
# in one table (+spree_payment_methods+).
#
# This class is not meant to be instantiated. Please create instances of concrete payment methods.
#
class PaymentMethod < Spree::Base
preference :server, :string, default: 'test'
preference :test_mode, :boolean, default: true
acts_as_paranoid
include Spree::ParanoiaDeprecations
include Discard::Model
self.discard_column = :deleted_at
acts_as_list
# @private
def self.const_missing(name)
if name == :DISPLAY
const_set(:DISPLAY, [:both, :front_end, :back_end])
else
super
end
end
validates :name, :type, presence: true
has_many :payments, class_name: "Spree::Payment", inverse_of: :payment_method
has_many :credit_cards, class_name: "Spree::CreditCard"
has_many :store_payment_methods, inverse_of: :payment_method
has_many :stores, through: :store_payment_methods
scope :ordered_by_position, -> { order(:position) }
scope :active, -> { where(active: true) }
scope :available_to_users, -> { where(available_to_users: true) }
scope :available_to_admin, -> { where(available_to_admin: true) }
scope :available_to_store, ->(store) do
raise ArgumentError, "You must provide a store" if store.nil?
store.payment_methods.empty? ? all : where(id: store.payment_method_ids)
end
delegate :authorize, :purchase, :capture, :void, :credit, to: :gateway
include Spree::Preferences::StaticallyConfigurable
# Custom ModelName#human implementation to ensure we don't refer to
# subclasses as just "PaymentMethod"
class ModelName < ActiveModel::Name
# Similar to ActiveModel::Name#human, but skips lookup_ancestors
def human(options = {})
defaults = [
i18n_key,
options[:default],
@human
].compact
options = { scope: [:activerecord, :models], count: 1, default: defaults }.merge!(options.except(:default))
I18n.translate(defaults.shift, options)
end
end
class << self
# @deprecated Use Spree::Config.environment.payment_methods instead
def providers
Spree::Deprecation.warn 'Spree::PaymentMethod.providers is deprecated and will be deleted in Solidus 3.0. ' \
'Please use Rails.application.config.spree.payment_methods instead'
Spree::Config.environment.payment_methods
end
# @deprecated Use {.active}, {.available_to_users}, and {.available_to_admin} scopes instead.
def available(display_on = nil, store: nil)
Spree::Deprecation.warn "Spree::PaymentMethod.available is deprecated."\
"Please use .active, .available_to_users, and .available_to_admin scopes instead."\
"For payment methods associated with a specific store, use Spree::PaymentMethod.available_to_store(your_store)"\
" as the base applying any further filtering"
display_on = display_on.to_s
available_payment_methods =
case display_on
when 'front_end'
active.available_to_users
when 'back_end'
active.available_to_admin
else
active.available_to_users.available_to_admin
end
available_payment_methods.select do |p|
store.nil? || store.payment_methods.empty? || store.payment_methods.include?(p)
end
end
def model_name
ModelName.new(self, Spree)
end
# @deprecated Use .active.any? instead
def active?
Spree::Deprecation.warn "#{self}.active? is deprecated. Use #{self}.active.any? instead"
where(type: to_s, active: true).count > 0
end
# @deprecated Use .with_deleted.find instead
def find_with_destroyed(*args)
Spree::Deprecation.warn "#{self}.find_with_destroyed is deprecated. Use #{self}.with_deleted.find instead"
unscoped { find(*args) }
end
end
# Represents the gateway of this payment method
#
# The gateway is responsible for communicating with the providers API.
#
# It implements methods for:
#
# - authorize
# - purchase
# - capture
# - void
# - credit
#
def gateway
gateway_options = options
gateway_options.delete :login if gateway_options.key?(:login) && gateway_options[:login].nil?
if gateway_options[:server]
ActiveMerchant::Billing::Base.mode = gateway_options[:server].to_sym
end
@gateway ||= gateway_class.new(gateway_options)
end
alias_method :provider, :gateway
deprecate provider: :gateway, deprecator: Spree::Deprecation
# Represents all preferences as a Hash
#
# Each preference is a key holding the value(s) and gets passed to the gateway via +gateway_options+
#
# @return Hash
def options
preferences.to_hash
end
# The class that will store payment sources (re)usable with this payment method
#
# Used by Spree::Payment as source (e.g. Spree::CreditCard in the case of a credit card payment method).
#
# Returning nil means the payment method doesn't support storing sources (e.g. Spree::PaymentMethod::Check)
def payment_source_class
raise ::NotImplementedError, "You must implement payment_source_class method for #{self.class}."
end
# @deprecated Use {Spree::PaymentMethod#available_to_users=} and {Spree::PaymentMethod#available_to_admin=} instead
def display_on=(value)
Spree::Deprecation.warn "Spree::PaymentMethod#display_on= is deprecated."\
"Please use #available_to_users= and #available_to_admin= instead."
self.available_to_users = value.blank? || value == 'front_end'
self.available_to_admin = value.blank? || value == 'back_end'
end
# @deprecated Use {Spree::PaymentMethod#available_to_users} and {Spree::PaymentMethod#available_to_admin} instead
def display_on
Spree::Deprecation.warn "Spree::PaymentMethod#display_on is deprecated."\
"Please use #available_to_users and #available_to_admin instead."
if available_to_users? && available_to_admin?
''
elsif available_to_users?
'front_end'
elsif available_to_admin?
'back_end'
else
'none'
end
end
# Used as partial name for your payment method
#
# Currently your payment method needs to provide these partials:
#
# 1. app/views/spree/checkout/payment/_{partial_name}.html.erb
# The form your customer enters the payment information in during checkout
#
# 2. app/views/spree/checkout/existing_payment/_{partial_name}.html.erb
# The payment information of your customers reusable sources during checkout
#
# 3. app/views/spree/admin/payments/source_forms/_{partial_name}.html.erb
# The form an admin enters payment information in when creating orders in the backend
#
# 4. app/views/spree/admin/payments/source_views/_{partial_name}.html.erb
# The view that represents your payment method on orders in the backend
#
def partial_name
deprecated_method_type_override || type.demodulize.downcase
end
# :nodoc:
# If method_type has been overridden, call it and return the value, otherwise return nil
def deprecated_method_type_override
if method(:method_type).owner != Spree::PaymentMethod
Spree::Deprecation.warn "#{method(:method_type).owner} is overriding PaymentMethod#method_type. This is deprecated and will be removed from Solidus 3.0 (override partial_name instead).", caller[1..-1]
method_type
end
end
def method_type
Spree::Deprecation.warn "method_type is deprecated and will be removed from Solidus 3.0 (use partial_name instead)", caller
partial_name
end
def payment_profiles_supported?
false
end
def source_required?
true
end
# Custom gateways can redefine this method to return reusable sources for an order.
# See {Spree::PaymentMethod::CreditCard#reusable_sources} as an example
def reusable_sources(_order)
[]
end
def auto_capture?
auto_capture.nil? ? Spree::Config[:auto_capture] : auto_capture
end
# Check if given source is supported by this payment method
#
# Please implement validation logic in your payment method implementation
#
# @see Spree::PaymentMethod::CreditCard#supports?
def supports?(_source)
true
end
# Used by Spree::Payment#cancel!
#
# Implement `try_void` on your payment method implementation to handle void attempts.
# In that method return a ActiveMerchant::Billing::Response object if the void succeeds.
# Return +false+ or +nil+ if the void is not possible anymore - because it was already processed by the bank.
# Solidus will refund the amount of the payment in this case.
#
# @return [ActiveMerchant::Billing::Response] with +true+ if the void succeeded
# @return [ActiveMerchant::Billing::Response] with +false+ if the void failed
# @return [false] if it can't be voided at this time
#
def try_void(_payment)
raise ::NotImplementedError,
"You need to implement `try_void` for #{self.class.name}. In that " \
'return a ActiveMerchant::Billing::Response object if the void succeeds '\
'or `false|nil` if the void is not possible anymore. ' \
'Solidus will refund the amount of the payment then.'
end
def store_credit?
is_a? Spree::PaymentMethod::StoreCredit
end
protected
# Represents the gateway class of this payment method
#
def gateway_class
if respond_to? :provider_class
Spree::Deprecation.warn \
"provider_class is deprecated and will be removed from Solidus 3.0 " \
"(use gateway_class instead)"
public_send :provider_class
else
raise ::NotImplementedError, "You must implement gateway_class method for #{self.class}."
end
end
end
end