Class: Payment

Inherits:
ActiveRecord::Base
  • Object
show all
Includes:
DateExpander, Redis::Objects, Stateflow
Defined in:
app/models/payment.rb

Constant Summary collapse

SOURCE_INNER =
0
SOURCE_MANUAL =
1
SOURCE_IMPORT =
2
TYPE_CASH =
0
TYPE_INNER_CARD =
1
TYPE_FOREIGN_CARD =
2
TYPE_IBANK =
3
TYPE_MBANK =
4
TYPE_PURSE =
5
TYPE_ACCOUNT =
6

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.acquirer(payment) ⇒ Object

METHODS



171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
# File 'app/models/payment.rb', line 171

def self.acquirer(payment)
  unless payment.externally_paid
    unless @acquiring_settings
      @acquiring_settings = YAML::load File.read(Rails.root.join 'config/acquiring.yml')
      @acquiring_settings.each do |x|
        x['type'] = x['type'].split(',').map do |t|
          Payment.const_get "TYPE_#{t.strip.upcase}"
        end
        x['class'] = x['class'].constantize
      end
    end

    acquirer = @acquiring_settings.find{|x| x['type'].include?(payment.payment_type)}
    raise "unsupported payment type: #{payment.payment_type}" unless acquirer

    acquirer['class'].new(acquirer)
  else
    EmptyAcquirer.new
  end
end

.build!(terminal, provider, attributes) ⇒ Object



263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
# File 'app/models/payment.rb', line 263

def self.build!(terminal, provider, attributes)
  payment = new(attributes)

  if provider.blank?
    payment.plog :warn, :model, "Provider not found"
    return false
  end
  
  provider_gateway = provider.provider_gateways.enabled.order(:priority).first

  if provider_gateway.blank?
    payment.plog :info, :model, "Provider has no gateways attached"
    return false
  end

  payment.terminal = terminal
  payment.provider_gateway = provider_gateway
  payment.raw_fields = payment.fields
  payment.fields = provider_gateway.map(payment., payment.fields)

  if payment.save
    payment.plog :info, :model, "Payment created"
    payment
  else
    payment.plog :warn, :model, "Payment was invalidated: #{payment.errors.full_messages.join(', ')}"
    false
  end
end

.plog(severity, progname, message, data = {}) ⇒ Object



218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
# File 'app/models/payment.rb', line 218

def self.plog(severity, progname, message, data={})
  data[:progname] = progname

  unless block_given?
    plogger.send(severity, data){ message }
  else
    begin
      yield
    rescue Exception => e
      plogger.error(data){ e }
      raise e
    else
      plogger.send(severity, data){ message }
    end
  end
end

.ploggerObject



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
# File 'app/models/payment.rb', line 192

def self.plogger
  return @plogger if @plogger

  @plogger ||= Logger.new(Rails.root.join('log/payments.log'), 10, 1024000)

  separator = ' | '

  @plogger.formatter = proc { |severity, datetime, data, message|
    data ||= {}

    header = "~~ " + [
      severity,
      datetime.iso8601(12),
      data[:progname],
      "##{data[:payment_id]}",
      data[:payment_state],
      data[:session_id],
      data[:terminal_id],
      data[:gateway_id],
      message
    ].join(separator) + "\n"
  }

  @plogger
end

Instance Method Details

#approved?Boolean

Returns:

  • (Boolean)


365
366
367
# File 'app/models/payment.rb', line 365

def approved?
  !['new', 'declined'].include?(state)
end

#assign_payment_attributes(attributes) ⇒ Object



72
73
74
75
76
77
78
# File 'app/models/payment.rb', line 72

def assign_payment_attributes(attributes)
  [ :paid_amount, :receipt_number, :card_track1, :card_track2, :meta ].each do |key|
    if attributes.include? key
      write_attribute key, attributes[key]
    end
  end
end

#cash?Boolean

Returns:

  • (Boolean)


255
256
257
# File 'app/models/payment.rb', line 255

def cash?
  payment_type == TYPE_CASH
end

#cashless?Boolean

Returns:

  • (Boolean)


259
260
261
# File 'app/models/payment.rb', line 259

def cashless?
  !cash?
end

#check?Boolean

Returns:

  • (Boolean)


312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
# File 'app/models/payment.rb', line 312

def check?
  result = self.gateway.librarize.check(self)

  if result[:success]
    plog :info, :model, "Checked"
    self.gateway_error      = nil
    self.gateway_payment_id = result[:gateway_payment_id] unless result[:gateway_payment_id].blank?
    self.save!
    return :checked
  else
    plog :info, :model, "Declined: #{result[:error]}"
    self.update_attribute(:gateway_error, result[:error])
    return :declined
  end
end

#complete?Boolean

Returns:

  • (Boolean)


251
252
253
# File 'app/models/payment.rb', line 251

def complete?
  %w(paid manual).include? state
end

#enqueue!(attributes = {}) ⇒ Object



80
81
82
83
84
85
86
87
# File 'app/models/payment.rb', line 80

def enqueue!(attributes={})
  plog :info, :model, "Sent to queue" do
    assign_payment_attributes attributes
    enqueue
    save!
    PayWorker.perform_async(id)
  end
end

#human_fieldsObject



307
308
309
310
# File 'app/models/payment.rb', line 307

def human_fields
  return '' if self.fields.blank?
  self.fields.collect{|k,v| "#{k}=#{v}"}.join("\n")
end

#human_fields=(value) ⇒ Object



302
303
304
305
# File 'app/models/payment.rb', line 302

def human_fields=(value)
  self.fields = value.gsub("\r", '').split("\n").map{|x| x.split('=')}
  self.fields = Hash[*self.fields.select{|x| x.length > 1}.flatten]
end

#manual?Boolean

Returns:

  • (Boolean)


247
248
249
# File 'app/models/payment.rb', line 247

def manual?
  source == SOURCE_MANUAL
end

#pay!(attributes = {}) ⇒ Object



89
90
91
92
93
94
95
# File 'app/models/payment.rb', line 89

def pay!(attributes={})
  plog :info, :model, "Paid" do
    assign_payment_attributes attributes
    pay
    save!
  end
end

#pay?Boolean

Returns:

  • (Boolean)


328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
# File 'app/models/payment.rb', line 328

def pay?
  acquirer = Payment.acquirer(self)
  acquirer.transaction(self) do |transaction|
    self.update_attribute(:acquirer_transaction, transaction.id)

    if transaction.authorize
      result = self.gateway.librarize.pay(self)

      if result[:success]
        self.gateway_error      = nil
        self.gateway_payment_id = result[:gateway_payment_id] unless result[:gateway_payment_id].blank?
        self.paid_at            = DateTime.now
        self.meta[:gateway]     = self.gateway.serialize_options

        self.save!
        if !transaction.confirm
          self.plog :error, :model, "unable to confirm transaction: #{transaction.error}"

          # TODO: reverse on gateway if possible
        end

        return :paid
      else
        self.update_attribute(:gateway_error, result[:error])
        if !transaction.reverse
          self.plog :error, :model, "unable to reverse transaction: #{transaction.error}"
        end

        return :error
      end
    else
      self.update_attribute(:acquirer_error, transaction.error)
      return :error
    end
  end
end

#pay_manually!(user) ⇒ Object



47
48
49
50
51
52
53
54
55
56
57
# File 'app/models/payment.rb', line 47

def pay_manually!(user)
  pay_manually
  save!

  Version.create!(
    :item_type => self.class.to_s,
    :item_id   => self.id,
    :event     => "payment.paid_manually",
    :whodunnit => user.id
  )
end

#plog(severity, progname, message, &block) ⇒ Object



235
236
237
238
239
240
241
242
243
244
245
# File 'app/models/payment.rb', line 235

def plog(severity, progname, message, &block)
  data = {
    :payment_id    => self.id,
    :payment_state => self.state,
    :session_id    => self.session_id,
    :terminal_id   => self.terminal_id,
    :gateway_id    => self.gateway_id
  }

  self.class.plog(severity, progname, message, data, &block)
end

#provider_gateway=(pg) ⇒ Object



292
293
294
295
296
# File 'app/models/payment.rb', line 292

def provider_gateway=(pg)
  self.gateway = pg.gateway
  self.provider = pg.provider
  self.gateway_provider_id = pg.gateway_provider_id
end

#requeue!(user) ⇒ Object



59
60
61
62
63
64
65
66
67
68
69
70
# File 'app/models/payment.rb', line 59

def requeue!(user)
  requeue
  save!

  PayWorker.perform_async(self.id)
  Version.create!(
    :item_type => self.class.to_s,
    :item_id   => self.id,
    :event     => "payment.requeued",
    :whodunnit => user.id
  )
end

#titleObject



298
299
300
# File 'app/models/payment.rb', line 298

def title
  "##{id}: #{provider.try(:title)} (#{ || '--'})"
end