Class: Payment
Overview
== Schema Information
Table name: payments
Database name: primary
id :bigint not null, primary key
account_holder_type :string
account_type :string
address_line1_check :string
address_zip_check :string
amazon_pay_status :string
amount :decimal(, )
approval_notification_sent :boolean
authorization_code :string
authorization_reference :string
authorization_type :string
billing_address_city :string
billing_address_country :string
billing_address_line1 :string
billing_address_line2 :string
billing_address_state :string
billing_address_zip :string
brand :string
bread_token :string
capture_before :datetime
card_country :string
card_expires_on :date
card_identifier :string
card_type :string
category :string
currency :string
cvc_check :string
date :date
email :string
exp_month :integer
exp_year :integer
first_name :string
fraud_review_done :boolean
http_accept_language :string
http_user_agent :string
issuer_number :string
last4 :string
last_name :string
name :string
payment_approved :boolean
paypal_email :string
paypal_metadata :jsonb
paypal_token :string
plaid_expected_settlement_date :date
plaid_public_token :string
po_number :string
radar_network_status :string
radar_reason :string
radar_risk_level :string
radar_seller_message :string
radar_type :string
reference :string
remote_ip_address :string
routing_number :string
send_authorization_email :boolean
shipping_address_city :string
shipping_address_country :string
shipping_address_line1 :string
shipping_address_line2 :string
shipping_address_name :string
shipping_address_state :string
shipping_address_zip :string
skip_auto_receipt :boolean default(FALSE)
skip_minfraud :boolean
state :string default("pending")
stripe_capabilities :jsonb
test :boolean
uploads_count :integer
zip_code :string
created_at :datetime not null
updated_at :datetime not null
account_id :integer
amazon_pay_charge_id :string
amazon_pay_charge_permission_id :string
amazon_pay_checkout_session_id :string
creator_id :integer
credit_memo_id :integer
customer_id :integer
delivery_id :integer
invoice_id :integer
order_id :integer
paypal_payer_id :string
paypal_transaction_id :string
plaid_account_id :string
plaid_transfer_id :string
plaid_transfer_intent_id :string
rma_id :integer
stripe_payment_intent_id :string
transaction_id :string
updater_id :integer
vault_id :string
vpo_contact_id :integer
Indexes
by_did_ctry_st (delivery_id,category,state)
by_iid_st_at (invoice_id,state,authorization_type)
by_oid_ctry_st (order_id,category,state)
idx_state_category (state,category)
index_payments_on_account_id (account_id)
index_payments_on_authorization_type_and_authorization_code (authorization_type,authorization_code) WHERE ((authorization_type IS NOT NULL) AND (authorization_code IS NOT NULL))
index_payments_on_creator_id (creator_id)
index_payments_on_credit_memo_id (credit_memo_id)
index_payments_on_customer_id (customer_id)
index_payments_on_order_id_and_state (order_id,state)
index_payments_on_paypal_payer_id (paypal_payer_id)
index_payments_on_paypal_transaction_id (paypal_transaction_id)
index_payments_on_rma_id (rma_id)
index_payments_on_stripe_payment_intent_id (stripe_payment_intent_id) WHERE (stripe_payment_intent_id IS NOT NULL)
index_payments_on_transaction_id (transaction_id)
index_payments_on_updater_id (updater_id)
index_payments_on_vault_id (vault_id)
index_payments_on_vpo_contact_id (vpo_contact_id)
Foreign Keys
payments_credit_memo_id_fkey (credit_memo_id => credit_memos.id)
payments_customer_id_fkey (customer_id => parties.id) ON DELETE => cascade
payments_delivery_id_fk (delivery_id => deliveries.id) ON DELETE => nullify
payments_invoice_id_fkey (invoice_id => invoices.id)
payments_order_id_fk (order_id => orders.id) ON DELETE => cascade
Defined Under Namespace
Classes: OrderProcessor, PaypalStatusResult, StrategyResolver
Constant Summary
collapse
- ADV_REPL =
'Advance Replacement'
- CHECK =
'Check'
- CREDIT_CARD =
'Credit Card'
- CREDIT_CARD_TERMINAL =
'Credit Card Terminal'
- BREAD =
'Bread'
- PLAID =
'Plaid'
- AMAZON_PAY =
'Amazon Pay'
- PO =
'Purchase Order'
- VPO =
'Verbal Purchase Order'
- ECHECK =
'eCheck'
- PAYPAL =
'PayPal'
- PAYPAL_INVOICE =
'PayPal Invoice'
- RMA_CREDIT =
'RMA Credit'
- CASH =
'Cash'
- STORE_CREDIT =
'Store Credit'
- WIRE =
'Wire Transfer'
- ACCOUNT_HOLDER_TYPES =
%w[personal business]
- ACCOUNT_TYPES =
%w[checking savings]
- PAYPAL_MIN_SIGNATURE_REQUIRED =
750.00
- ECHECK_MIN_AMOUNT_WITHOUT_SUPERVISION =
2000.00
- CATEGORIES_REQUIRING_REVIEW =
[PAYPAL_INVOICE, RMA_CREDIT, ECHECK, CHECK, CASH, WIRE]
- CATEGORIES_NOT_ALLOWING_CAPTURE =
[PO, VPO, ECHECK, WIRE]
- PAYPAL_OVER_CAPTURE_PERCENT =
BigDecimal('1.15')
- PAYPAL_OVER_CAPTURE_MAX_INCREASE =
BigDecimal('75')
- PAYPAL_HONOR_PERIOD =
3.days
- PAYPAL_REAUTH_BUFFER =
12.hours
- PAYPAL_MAX_AUTH_DAYS =
29
- PAYPAL_REAUTH_EARLIEST_DAY =
4
Models::Auditable::ALWAYS_IGNORED
Instance Attribute Summary collapse
#creator, #updater
Class Method Summary
collapse
Instance Method Summary
collapse
#all_skipped_columns, #audit_reference_data, #should_not_save_version, #stamp_record
ransackable_associations, ransackable_attributes, ransackable_scopes, ransortable_attributes, #to_relation
#publish_event
Instance Attribute Details
#account_holder_type ⇒ Object
185
|
# File 'app/models/payment.rb', line 185
validates :account_type, :account_holder_type, presence: { if: proc { |pp| pp.vault_id.blank? && pp.authorization_type == 'check' } }
|
#account_number ⇒ Object
Returns the value of attribute account_number.
211
212
213
|
# File 'app/models/payment.rb', line 211
def account_number
@account_number
end
|
#account_type ⇒ Object
185
|
# File 'app/models/payment.rb', line 185
validates :account_type, :account_holder_type, presence: { if: proc { |pp| pp.vault_id.blank? && pp.authorization_type == 'check' } }
|
#address_city ⇒ Object
Returns the value of attribute address_city.
211
212
213
|
# File 'app/models/payment.rb', line 211
def address_city
@address_city
end
|
#address_country ⇒ Object
Returns the value of attribute address_country.
211
212
213
|
# File 'app/models/payment.rb', line 211
def address_country
@address_country
end
|
#address_id ⇒ Object
Returns the value of attribute address_id.
211
212
213
|
# File 'app/models/payment.rb', line 211
def address_id
@address_id
end
|
#address_line1 ⇒ Object
Returns the value of attribute address_line1.
211
212
213
|
# File 'app/models/payment.rb', line 211
def address_line1
@address_line1
end
|
#address_line2 ⇒ Object
Returns the value of attribute address_line2.
211
212
213
|
# File 'app/models/payment.rb', line 211
def address_line2
@address_line2
end
|
#address_state ⇒ Object
Returns the value of attribute address_state.
211
212
213
|
# File 'app/models/payment.rb', line 211
def address_state
@address_state
end
|
#address_zip ⇒ Object
Returns the value of attribute address_zip.
211
212
213
|
# File 'app/models/payment.rb', line 211
def address_zip
@address_zip
end
|
#amount ⇒ Object
181
|
# File 'app/models/payment.rb', line 181
validates :category, :amount, :currency, :state, presence: true
|
#amount_to_capture ⇒ Object
Returns the value of attribute amount_to_capture.
211
212
213
|
# File 'app/models/payment.rb', line 211
def amount_to_capture
@amount_to_capture
end
|
#bread_token ⇒ Object
Returns the value of attribute bread_token.
211
212
213
|
# File 'app/models/payment.rb', line 211
def bread_token
@bread_token
end
|
#card_token ⇒ Object
Returns the value of attribute card_token.
211
212
213
|
# File 'app/models/payment.rb', line 211
def card_token
@card_token
end
|
#category ⇒ Object
181
|
# File 'app/models/payment.rb', line 181
validates :category, :amount, :currency, :state, presence: true
|
#consent_channel ⇒ Object
Returns the value of attribute consent_channel.
211
212
213
|
# File 'app/models/payment.rb', line 211
def consent_channel
@consent_channel
end
|
#currency ⇒ Object
181
|
# File 'app/models/payment.rb', line 181
validates :category, :amount, :currency, :state, presence: true
|
#email ⇒ Object
205
|
# File 'app/models/payment.rb', line 205
validates :email, email_format: true
|
#error_codes ⇒ Object
Returns the value of attribute error_codes.
211
212
213
|
# File 'app/models/payment.rb', line 211
def error_codes
@error_codes
end
|
#issuer_number ⇒ Object
Returns the value of attribute issuer_number.
211
212
213
|
# File 'app/models/payment.rb', line 211
def issuer_number
@issuer_number
end
|
#last_response ⇒ Object
Returns the value of attribute last_response.
211
212
213
|
# File 'app/models/payment.rb', line 211
def last_response
@last_response
end
|
#paypal_email ⇒ Object
186
|
# File 'app/models/payment.rb', line 186
validates :paypal_email, presence: { if: proc { |pp| pp.authorization_type == 'paypal_invoice' } }
|
#po_number ⇒ Object
183
|
# File 'app/models/payment.rb', line 183
validates :po_number, presence: { if: proc { |pp| pp.category == PO } }
|
#rma_id ⇒ Object
184
|
# File 'app/models/payment.rb', line 184
validates :rma_id, presence: { if: proc { |pp| pp.category == ADV_REPL } }
|
#state ⇒ Object
181
|
# File 'app/models/payment.rb', line 181
validates :category, :amount, :currency, :state, presence: true
|
#store_address ⇒ Object
Returns the value of attribute store_address.
211
212
213
|
# File 'app/models/payment.rb', line 211
def store_address
@store_address
end
|
#store_card ⇒ Object
Returns the value of attribute store_card.
211
212
213
|
# File 'app/models/payment.rb', line 211
def store_card
@store_card
end
|
#store_card_name ⇒ Object
Returns the value of attribute store_card_name.
211
212
213
|
# File 'app/models/payment.rb', line 211
def store_card_name
@store_card_name
end
|
182
|
# File 'app/models/payment.rb', line 182
validates :vpo_contact_id, presence: { if: proc { |pp| pp.category == VPO } }
|
Class Method Details
.all_amazon_pay_captured ⇒ ActiveRecord::Relation<Payment>
A relation of Payments that are all amazon pay captured. Active Record Scope
233
|
# File 'app/models/payment.rb', line 233
scope :all_amazon_pay_captured, -> { all_captured.where(authorization_type: 'amazon_pay') }
|
.all_authorized ⇒ ActiveRecord::Relation<Payment>
A relation of Payments that are all authorized. Active Record Scope
236
|
# File 'app/models/payment.rb', line 236
scope :all_authorized, -> { where(state: 'authorized') }
|
.all_captured ⇒ ActiveRecord::Relation<Payment>
A relation of Payments that are all captured. Active Record Scope
228
|
# File 'app/models/payment.rb', line 228
scope :all_captured, -> { where(state: 'captured') }
|
.all_cc_captured ⇒ ActiveRecord::Relation<Payment>
A relation of Payments that are all cc captured. Active Record Scope
230
|
# File 'app/models/payment.rb', line 230
scope :all_cc_captured, -> { all_captured.where(authorization_type: %w[credit_card paypal]) }
|
.all_check_captured ⇒ ActiveRecord::Relation<Payment>
A relation of Payments that are all check captured. Active Record Scope
231
|
# File 'app/models/payment.rb', line 231
scope :all_check_captured, -> { all_captured.where(authorization_type: 'check') }
|
.all_collect_captured ⇒ ActiveRecord::Relation<Payment>
A relation of Payments that are all collect captured. Active Record Scope
235
|
# File 'app/models/payment.rb', line 235
scope :all_collect_captured, -> { all_captured.where(authorization_type: 'credit_card', reference: 'Collect Card Reader') }
|
.all_paypal_invoice_captured ⇒ ActiveRecord::Relation<Payment>
A relation of Payments that are all paypal invoice captured. Active Record Scope
234
|
# File 'app/models/payment.rb', line 234
scope :all_paypal_invoice_captured, -> { all_captured.where(authorization_type: 'paypal_invoice') }
|
.all_plaid_captured ⇒ ActiveRecord::Relation<Payment>
A relation of Payments that are all plaid captured. Active Record Scope
232
|
# File 'app/models/payment.rb', line 232
scope :all_plaid_captured, -> { all_captured.where(authorization_type: 'plaid') }
|
.amazon_payments ⇒ ActiveRecord::Relation<Payment>
A relation of Payments that are amazon payments. Active Record Scope
218
|
# File 'app/models/payment.rb', line 218
scope :amazon_payments, -> { where(category: AMAZON_PAY) }
|
.bread_payments ⇒ ActiveRecord::Relation<Payment>
A relation of Payments that are bread payments. Active Record Scope
217
|
# File 'app/models/payment.rb', line 217
scope :bread_payments, -> { where(category: BREAD) }
|
.can_be_refunded ⇒ ActiveRecord::Relation<Payment>
A relation of Payments that are can be refunded. Active Record Scope
237
|
# File 'app/models/payment.rb', line 237
scope :can_be_refunded, -> { where(state: %w[captured partially_refunded]) }
|
.cc_paypal_bread_amazon ⇒ ActiveRecord::Relation<Payment>
A relation of Payments that are cc paypal bread amazon. Active Record Scope
229
|
# File 'app/models/payment.rb', line 229
scope :cc_paypal_bread_amazon, -> { where(authorization_type: %w[credit_card paypal bread, amazon_pay]) }
|
.check_payments ⇒ ActiveRecord::Relation<Payment>
A relation of Payments that are check payments. Active Record Scope
220
|
# File 'app/models/payment.rb', line 220
scope :check_payments, -> { where(category: CHECK) }
|
.credit_cards ⇒ ActiveRecord::Relation<Payment>
A relation of Payments that are credit cards. Active Record Scope
224
|
# File 'app/models/payment.rb', line 224
scope :credit_cards, -> { where(category: CREDIT_CARD) }
|
.echeck_payment_review(amount, customer, order) ⇒ Object
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
|
# File 'app/models/payment.rb', line 699
def self.echeck_payment_review(amount, customer, order)
res = {}
fails = 0
fail_reasons = []
pass_reasons = []
if Payment.where(category: ECHECK).all_authorized.where('payments.created_at > ?', Time.current.at_midnight).count > 5
fails += 1
fail_reasons << 'Limit exceeded for number of automatic eCheck approvals in one day, company wide (5).'
end
if Payment.joins(:order).where(category: ECHECK, orders: { customer_id: order.customer_id }).all_authorized.where('payments.created_at > ?',
Time.current.at_midnight).count > 2
fails += 1
fail_reasons << 'Limit exceeded for number of automatic eCheck approvals in one day, per customer (2).'
end
if amount < 100
pass_reasons = ['Amount less than $100']
else
if amount > 10_000
fails += 1
fail_reasons << 'Amount greater than $10,000'
end
rc_res = customer.request_credit(amount)
if rc_res[:approved] == false
fails += rc_res[:fails]
fail_reasons += rc_res[:fail_reasons]
else
pass_reasons = rc_res[:pass_reasons]
end
end
if fails > 0
res[:required] = true
res[:fail_reasons] = fail_reasons
else
res[:required] = false
res[:pass_reasons] = pass_reasons
end
res
end
|
.expired ⇒ ActiveRecord::Relation<Payment>
A relation of Payments that are expired. Active Record Scope
226
|
# File 'app/models/payment.rb', line 226
scope :expired, -> { where(state: 'expired') }
|
.non_voided ⇒ ActiveRecord::Relation<Payment>
A relation of Payments that are non voided. Active Record Scope
238
|
# File 'app/models/payment.rb', line 238
scope :non_voided, -> { where.not(state: %w[voided expired]) }
|
.order ⇒ Object
937
938
939
|
# File 'app/models/payment.rb', line 937
def self.order
legacy_order || order
end
|
.payment_options(customer, order = nil, currency = nil) ⇒ Object
317
318
319
320
321
322
323
324
325
326
327
328
329
330
|
# File 'app/models/payment.rb', line 317
def self.payment_options(customer, order = nil, currency = nil)
payment_options = [CHECK, WIRE, CREDIT_CARD, PAYPAL, AMAZON_PAY, PAYPAL_INVOICE, CASH]
if customer.has_terms?
payment_options << PO
payment_options << VPO if customer.contacts.verbal_po_contacts.exists?
end
payment_options << ADV_REPL if order && order.rma.present? && order.rma.credit_available > 0
payment_options << RMA_CREDIT if order && order.precreate_rma?
payment_options << ECHECK if (order && order.currency == 'USD') || currency == 'USD'
payment_options << STORE_CREDIT if customer.available_store_credit.to_d > 0 && customer.credit_memos.available_to_apply.exists?
payment_options.sort
end
|
.paypal_invoices ⇒ ActiveRecord::Relation<Payment>
A relation of Payments that are paypal invoices. Active Record Scope
225
|
# File 'app/models/payment.rb', line 225
scope :paypal_invoices, -> { where(category: PAYPAL_INVOICE) }
|
.paypal_payments ⇒ ActiveRecord::Relation<Payment>
A relation of Payments that are paypal payments. Active Record Scope
222
|
# File 'app/models/payment.rb', line 222
scope :paypal_payments, -> { where(category: PAYPAL) }
|
.plaid_payments ⇒ ActiveRecord::Relation<Payment>
A relation of Payments that are plaid payments. Active Record Scope
219
|
# File 'app/models/payment.rb', line 219
scope :plaid_payments, -> { where(category: PLAID) }
|
.po_search ⇒ ActiveRecord::Relation<Payment>
A relation of Payments that are po search. Active Record Scope
227
|
# File 'app/models/payment.rb', line 227
scope :po_search, ->(term) { where(Payment[:po_number].matches("%#{term}%")) }
|
.purchase_orders ⇒ ActiveRecord::Relation<Payment>
A relation of Payments that are purchase orders. Active Record Scope
223
|
# File 'app/models/payment.rb', line 223
scope :purchase_orders, -> { where.not(order_id: nil).where(category: [PO, VPO]) }
|
.wire_payments ⇒ ActiveRecord::Relation<Payment>
A relation of Payments that are wire payments. Active Record Scope
221
|
# File 'app/models/payment.rb', line 221
scope :wire_payments, -> { where(category: WIRE) }
|
Instance Method Details
165
|
# File 'app/models/payment.rb', line 165
belongs_to :account, optional: true
|
#all_pi_siblings ⇒ Object
373
374
375
376
377
378
|
# File 'app/models/payment.rb', line 373
def all_pi_siblings
return Payment.none unless stripe_payment_intent_id.present?
Payment.where(stripe_payment_intent_id: stripe_payment_intent_id)
.where.not(id: id)
end
|
#amount_captured_on_paypal ⇒ Object
930
931
932
933
934
935
|
# File 'app/models/payment.rb', line 930
def amount_captured_on_paypal
return false if authorization_type != 'paypal'
auth_details = Payment::Apis::Paypal.get_authorization_details(self).parse
auth_details["amount"]["value"].to_f
end
|
#amount_captured_on_stripe ⇒ Object
909
910
911
912
913
914
915
916
917
918
919
920
|
# File 'app/models/payment.rb', line 909
def amount_captured_on_stripe
return false if authorization_type != 'credit_card'
obj = stripe_payment_object
return false unless obj
if Payment::Apis::Stripe.payment_intent?(authorization_code)
obj.amount_received.to_f
else
obj.try(:amount_captured).to_f rescue false
end
end
|
#attempt_paypal_reauthorization_if_needed(auth_expiration) ⇒ Object
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
|
# File 'app/models/payment.rb', line 536
def attempt_paypal_reauthorization_if_needed(auth_expiration)
if auth_expiration.present? && auth_expiration > PAYPAL_REAUTH_BUFFER.from_now
sync_capture_before_from_paypal_expiry(auth_expiration)
hours_left = ((auth_expiration - Time.current) / 1.hour).round(1)
return PaypalStatusResult.new(status: :still_valid, message: "PayPal authorization still valid (#{hours_left} hours remaining, expires #{auth_expiration.strftime('%b %d %H:%M %Z')})")
end
honor_deadline = capture_before || (created_at + PAYPAL_HONOR_PERIOD)
if honor_deadline > PAYPAL_REAUTH_BUFFER.from_now
hours_left = ((honor_deadline - Time.current) / 1.hour).round(1)
return PaypalStatusResult.new(status: :still_valid, message: "Honor period still valid (#{hours_left} hours remaining, capture by #{honor_deadline.strftime('%b %d %H:%M %Z')})")
end
if created_at < PAYPAL_MAX_AUTH_DAYS.days.ago
logger.error("Payment #{id}: PayPal authorization past #{PAYPAL_MAX_AUTH_DAYS}-day limit, cannot reauthorize")
Mailer.generic_mailer(
from: ADMINISTRATOR_EMAIL,
to: "#{ADMINISTRATOR_EMAIL},#{ACCOUNTS_RECEIVABLE_EMAIL}",
subject: "PAYPAL PAYMENT ##{id} AUTHORIZATION EXPIRED",
message: "PayPal payment id: #{id} has exceeded the 29-day authorization period and cannot be reauthorized. Please take action to ensure all funds are captured or a new payment is collected.",
no_verbage: true
).deliver
order.cr_hold if order.present?
payment_expired!
return PaypalStatusResult.new(status: :expired, message: "Authorization past #{PAYPAL_MAX_AUTH_DAYS}-day limit")
end
days_since_auth = ((Time.current - created_at) / 1.day).floor
if days_since_auth < PAYPAL_REAUTH_EARLIEST_DAY
return PaypalStatusResult.new(status: :still_valid, message: "Too early to reauthorize (day #{days_since_auth} of #{PAYPAL_REAUTH_EARLIEST_DAY} minimum). Authorization is still capturable.")
end
res = Payment::Gateways::Paypal.new(self).reauthorize(amount)
if res.success
PaypalStatusResult.new(status: :reauthorized, message: 'Successfully reauthorized')
else
Mailer.generic_mailer(
from: ADMINISTRATOR_EMAIL,
to: "#{ADMINISTRATOR_EMAIL},#{ACCOUNTS_RECEIVABLE_EMAIL}",
subject: "PAYPAL PAYMENT ##{id} REAUTHORIZATION ERROR",
message: "Unable to reauthorize paypal payment id: #{id}. Please take action to ensure all funds are captured or applied.",
no_verbage: true
).deliver
logger.error("#{Time.current}: PAYPAL REAUTHORIZATION ERROR: Problem reauthorizing paypal payment.")
PaypalStatusResult.new(status: :reauth_failed, message: res.message || 'Reauthorization failed')
end
end
|
#auth_code ⇒ Object
845
846
847
848
|
# File 'app/models/payment.rb', line 845
def auth_code
capture = transactions.where(action: 'capture', success: true).first
capture.nil? ? nil : capture&.params&.[]('auth_code')
end
|
#auth_url ⇒ Object
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
|
# File 'app/models/payment.rb', line 1106
def auth_url
return nil if authorization_code.nil?
if authorization_type == 'paypal'
host = test? ? "www.sandbox.paypal.com" : "www.paypal.com"
"https://#{host}/activity/payment/#{authorization_code}"
elsif test?
"https://dashboard.stripe.com/test/payments/#{authorization_code}"
else
"https://dashboard.stripe.com/payments/#{authorization_code}"
end
end
|
#authorization_review ⇒ Object
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
|
# File 'app/models/payment.rb', line 429
def authorization_review
res = {}
case category
when PAYPAL_INVOICE, RMA_CREDIT
res[:required] = true
res[:fail_reasons] = ["#{category} payment requires Accounting approval"]
when ECHECK
echeck_total = order.payments.all_authorized.where(category: ECHECK).sum(:amount)
echeck_res = Payment.echeck_payment_review(echeck_total, order.customer, order)
res[:required] = echeck_res[:required]
res[:fail_reasons] = echeck_res[:fail_reasons]
res[:pass_reasons] = echeck_res[:pass_reasons]
when CHECK, WIRE
res[:required] = true
res[:fail_reasons] = ['All Check/Wire payments requires Accounting approval']
when CASH
if order.is_warehouse_pickup?
if amount > 100
res[:required] = true
res[:fail_reasons] = ["Pickup order with #{category} payment more than $100 requires Accounting approval"]
else
res[:required] = false
res[:pass_reasons] = ["Pickup order with #{category} payment more than $100 requires Accounting approval"]
end
else
res[:required] = true
res[:fail_reasons] = ["Non-pickup order with #{category} payment requires Accounting approval"]
end
else
res[:required] = false
end
res
end
|
#authorization_review_required? ⇒ Boolean
425
426
427
|
# File 'app/models/payment.rb', line 425
def authorization_review_required?
CATEGORIES_REQUIRING_REVIEW.include?(category)
end
|
#automatically_authorized? ⇒ Boolean
466
467
468
|
# File 'app/models/payment.rb', line 466
def automatically_authorized?
authorization_review_required? and authorization_review[:required] == false
end
|
#available_to_refund ⇒ Object
941
942
943
|
# File 'app/models/payment.rb', line 941
def available_to_refund
total_captured - total_refunded
end
|
#billing_address ⇒ Object
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
|
# File 'app/models/payment.rb', line 1124
def billing_address
Address.new(
street1: billing_address_line1,
street2: billing_address_line2,
city: billing_address_city,
zip: billing_address_zip,
state_code: State.code_for_string(billing_address_state),
country_iso3: Country.iso3_for_string(billing_address_country)
)
end
|
#can_be_voided? ⇒ Boolean
361
362
363
|
# File 'app/models/payment.rb', line 361
def can_be_voided?
(authorized? and !order.editing_locked? and category != 'Credit Card Terminal')
end
|
#capture_deadline ⇒ Object
357
358
359
|
# File 'app/models/payment.rb', line 357
def capture_deadline
stripe_resolver.authorization_deadline
end
|
#captured_on_paypal? ⇒ Boolean
922
923
924
925
926
927
928
|
# File 'app/models/payment.rb', line 922
def captured_on_paypal?
return false if authorization_type != 'paypal'
auth_details = Payment::Apis::Paypal.get_authorization_details(self).parse
auth_status = auth_details['status']
auth_status == 'CAPTURED'
end
|
#captured_on_stripe? ⇒ Boolean
896
897
898
899
900
901
902
903
904
905
906
907
|
# File 'app/models/payment.rb', line 896
def captured_on_stripe?
return false if authorization_type != 'credit_card'
obj = stripe_payment_object
return false unless obj
if Payment::Apis::Stripe.payment_intent?(authorization_code)
obj.status == 'succeeded'
else
obj.try(:captured?) rescue false
end
end
|
#check_cc_payment_status ⇒ Object
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
|
# File 'app/models/payment.rb', line 591
def check_cc_payment_status
return unless authorized?
return unless authorization_type == 'credit_card'
obj = stripe_payment_object
return unless obj
pi_status = Payment::Apis::Stripe.payment_intent?(authorization_code) ? obj.status : nil
case pi_status
when 'canceled'
logger.info("Payment #{id}: PI #{stripe_payment_intent_id} canceled on Stripe, voiding")
payment_voided!
when 'succeeded'
sync_external_capture(obj)
when 'requires_capture'
if stripe_resolver.needs_reauthorization?.reauth_needed
Payment::Gateways::CreditCard.new(self).reauthorize
end
else
if obj.try(:captured?)
transaction = OrderTransaction.new(
amount: obj.try(:amount_captured).to_f,
action: 'capture',
success: true,
reference: authorization_code,
message: "THIS PAYMENT WAS MANUALLY CAPTURED ON THE PAYMENT PLATFORM",
params: obj.to_hash,
test: false
)
transactions.push(transaction)
payment_captured!
elsif stripe_resolver.needs_reauthorization?.reauth_needed
Payment::Gateways::CreditCard.new(self).reauthorize
end
end
end
|
#check_paypal_invoice_payment_status ⇒ Object
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
|
# File 'app/models/payment.rb', line 669
def check_paypal_invoice_payment_status
if authorized?
response = Payment::Apis::Paypal.get_invoice_details(authorization_code)
res = JSON.parse(response.to_s)
invoice_status = res['status']
if invoice_status.present? && invoice_status == 'PAID'
Payment::Gateways::PaypalInvoice.new(self).capture(res)
order.reload
order.release_order if order.payments.paypal_invoices.all?(&:captured?)
true
else
false
end
elsif captured?
true
end
end
|
#check_paypal_payment_status ⇒ Object
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
|
# File 'app/models/payment.rb', line 479
def check_paypal_payment_status
return PaypalStatusResult.new(status: :not_authorized, message: 'Payment is not in authorized state') unless authorized?
response = Payment::Apis::Paypal.get_authorization(authorization_code)
auth_status = response['status']
auth_expiration = response['expiration_time']&.to_time
sync_paypal_authorization_expiry(auth_expiration)
case auth_status
when 'CAPTURED'
sync_paypal_external_capture(response)
PaypalStatusResult.new(status: :captured, message: 'Payment was captured externally on PayPal')
when 'VOIDED'
logger.info("Payment #{id}: PayPal authorization voided externally")
payment_voided!
PaypalStatusResult.new(status: :voided, message: 'Authorization was voided on PayPal')
when 'EXPIRED'
logger.info("Payment #{id}: PayPal authorization expired")
payment_expired!
PaypalStatusResult.new(status: :expired, message: 'Authorization has expired')
when 'DENIED'
logger.info("Payment #{id}: PayPal authorization denied")
transaction_declined!
PaypalStatusResult.new(status: :denied, message: 'Authorization was denied')
when 'CREATED', 'PENDING'
attempt_paypal_reauthorization_if_needed(auth_expiration)
else
PaypalStatusResult.new(status: :unknown, message: "Unknown PayPal status: #{auth_status}")
end
end
|
#communication_resource ⇒ Order
315
|
# File 'app/models/payment.rb', line 315
belongs_to :order, optional: true
|
163
|
# File 'app/models/payment.rb', line 163
belongs_to :credit_card_vault, primary_key: 'vault_id', foreign_key: 'vault_id', optional: true
|
164
|
# File 'app/models/payment.rb', line 164
belongs_to :credit_memo, optional: true
|
#crm_link ⇒ Object
820
821
822
823
824
|
# File 'app/models/payment.rb', line 820
def crm_link
UrlHelper.instance.order_path(order)
rescue StandardError
''
end
|
#currency_symbol ⇒ Object
945
946
947
|
# File 'app/models/payment.rb', line 945
def currency_symbol
Money::Currency.new(currency).symbol
end
|
160
|
# File 'app/models/payment.rb', line 160
belongs_to :customer, optional: true
|
#deep_dup ⇒ Object
311
312
313
|
# File 'app/models/payment.rb', line 311
def deep_dup
deep_clone(except: :delivery_id)
end
|
#default_cc_options(ip_address = nil, email = nil) ⇒ Object
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
|
# File 'app/models/payment.rb', line 1070
def default_cc_options(ip_address = nil, email = nil)
options = {
description: (order.reference_number.present? ? "Order #{order.reference_number}" : "Order ID #{order.id}"),
statement_description: "WarmlyYours #{order.reference_number}",
currency:,
shipping_address: order.shipping_address.format_for_payment_gateway(true),
metadata: {
email:,
ip: ip_address,
order_id: order.id,
payment_id: id
}
}
options[:customer] = order.customer.stripe_customer_id if vault_id.present?
options
end
|
#default_echeck_options(_ip_address = nil) ⇒ Object
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
|
# File 'app/models/payment.rb', line 1094
def default_echeck_options(_ip_address = nil)
{
billing_address: order.billing_address.format_for_forte(true),
order_id: order.reference_number
}
end
|
#default_paypal_options ⇒ Object
1088
1089
1090
1091
1092
|
# File 'app/models/payment.rb', line 1088
def default_paypal_options
{
currency: currency
}
end
|
166
|
# File 'app/models/payment.rb', line 166
belongs_to :delivery, inverse_of: :payments, optional: true
|
#detect_fraud(force_new_report: false) ⇒ Object
826
827
828
829
830
831
832
833
834
835
836
837
|
# File 'app/models/payment.rb', line 826
def detect_fraud(force_new_report: false)
return nil if order.nil?
return nil unless category.in?(['Credit Card', 'PayPal', 'eCheck'])
return nil if order.belongs_to_smartservice_group?
begin
Order::FraudDetector.new(order, self).process(force_new_report:)
rescue StandardError => e
ErrorReporting.error(e, "FraudDetector Error - Unable to process new FraudDetector request for Payment ID:#{id}")
nil
end
end
|
#does_not_allow_capture? ⇒ Boolean
#email_collection_for_select ⇒ Object
858
859
860
861
862
|
# File 'app/models/payment.rb', line 858
def email_collection_for_select
customer_for_collection = customer
customer_for_collection ||= order&.customer
[customer_for_collection&.all_emails, email].flatten.compact.uniq
end
|
169
|
# File 'app/models/payment.rb', line 169
has_one :fraud_report
|
#full_card_number ⇒ Object
864
865
866
867
868
869
870
|
# File 'app/models/payment.rb', line 864
def full_card_number
if issuer_number and last4
"#{issuer_number}xxxxxx#{last4}"
elsif last4
"xxxxxxxxxxxx#{last4}"
end
end
|
#full_name ⇒ Object
949
950
951
|
# File 'app/models/payment.rb', line 949
def full_name
[first_name, last_name].compact.join(' ')
end
|
#full_name=(value) ⇒ Object
953
954
955
956
957
|
# File 'app/models/payment.rb', line 953
def full_name=(value)
pnp = PersonNameParser.new(value)
self.first_name = pnp.first
self.last_name = pnp.last
end
|
#funds_fully_refunded? ⇒ Boolean
850
851
852
|
# File 'app/models/payment.rb', line 850
def funds_fully_refunded?
total_refunded == total_captured
end
|
#funds_partially_refunded? ⇒ Boolean
854
855
856
|
# File 'app/models/payment.rb', line 854
def funds_partially_refunded?
total_refunded.positive? and total_refunded < total_captured
end
|
#gateway_class ⇒ Object
332
333
334
335
336
337
|
# File 'app/models/payment.rb', line 332
def gateway_class
class_name = category.parameterize(separator: '_')
class_name = { VPO: 'VerbalPurchaseOrder', PO: 'PurchaseOrder' }[class_name] || class_name
"Payment::Gateways::#{class_name.classify}".safe_constantize || Payment::Gateways::Default
end
|
162
|
# File 'app/models/payment.rb', line 162
belongs_to :invoice, optional: true
|
#is_advanced_replacement? ⇒ Boolean
748
749
750
|
# File 'app/models/payment.rb', line 748
def is_advanced_replacement?
category == ADV_REPL
end
|
#is_amazon_pay? ⇒ Boolean
760
761
762
|
# File 'app/models/payment.rb', line 760
def is_amazon_pay?
category == AMAZON_PAY
end
|
#is_crm_legacy_vault? ⇒ Boolean
1066
1067
1068
|
# File 'app/models/payment.rb', line 1066
def is_crm_legacy_vault?
order&.order_reception_type == 'CRM' && (vault_id && credit_card_vault&.address_line1.blank?)
end
|
#is_plaid? ⇒ Boolean
756
757
758
|
# File 'app/models/payment.rb', line 756
def is_plaid?
category == PLAID
end
|
#is_po? ⇒ Boolean
740
741
742
|
# File 'app/models/payment.rb', line 740
def is_po?
category.in?([PO, VPO])
end
|
#is_receipt_skippable? ⇒ Boolean
1062
1063
1064
|
# File 'app/models/payment.rb', line 1062
def is_receipt_skippable?
authorization_type.in?(%w[paypal_invoice check]) and state == 'captured' and invoice.nil?
end
|
#is_rma_credit? ⇒ Boolean
744
745
746
|
# File 'app/models/payment.rb', line 744
def is_rma_credit?
category == RMA_CREDIT
end
|
#is_store_credit? ⇒ Boolean
752
753
754
|
# File 'app/models/payment.rb', line 752
def is_store_credit?
category == STORE_CREDIT
end
|
#is_www_apple_pay? ⇒ Boolean
1039
1040
1041
|
# File 'app/models/payment.rb', line 1039
def is_www_apple_pay?
order&.order_reception_type == 'Online' && transactions.any? { |t| t.params&.dig('source')&.dig('tokenization_method') == 'apple_pay' }
end
|
#last_authorization_message ⇒ Object
812
813
814
|
# File 'app/models/payment.rb', line 812
def last_authorization_message
transactions&.first&.message
end
|
#legacy_order ⇒ Order
161
|
# File 'app/models/payment.rb', line 161
belongs_to :legacy_order, class_name: 'Order', foreign_key: 'order_id', optional: true
|
158
|
# File 'app/models/payment.rb', line 158
belongs_to :order, optional: true
|
#paypal_over_capture_headroom ⇒ Object
415
416
417
418
419
420
421
422
423
|
# File 'app/models/payment.rb', line 415
def paypal_over_capture_headroom
limit = paypal_over_capture_limit
return BigDecimal('0') unless limit
all_committed = Payment.where(authorization_code: authorization_code, authorization_type: 'paypal')
.where.not(state: %w[voided declined expired])
.sum(:amount)
[limit - all_committed, BigDecimal('0')].max
end
|
#paypal_over_capture_limit ⇒ Object
#paypal_shared_auth_total ⇒ Object
402
403
404
405
406
|
# File 'app/models/payment.rb', line 402
def paypal_shared_auth_total
BigDecimal(paypal_metadata&.dig('shared_authorization_total').to_s)
rescue ArgumentError
nil
end
|
#pending_release_authorization? ⇒ Boolean
764
765
766
|
# File 'app/models/payment.rb', line 764
def pending_release_authorization?
!payment_approved? && authorization_review[:required] == true
end
|
#po_upload ⇒ Object
816
817
818
|
# File 'app/models/payment.rb', line 816
def po_upload
uploads.in_category('purchase_order').valid.first
end
|
#process_tx_results(tx) ⇒ Object
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
|
# File 'app/models/payment.rb', line 959
def process_tx_results(tx)
if vault_id.present? and vault = CreditCardVault.find_by(vault_id:)
self.issuer_number = vault.issuer_number
self.card_type = vault.card_type
self.name = vault.name
self.exp_month = vault.exp_month
self.exp_year = vault.exp_year
self.reference = self.last4 = vault.number
self.address_line1_check = vault.address_line1_check
self.address_zip_check = vault.address_zip_check
self.cvc_check = vault.cvc_check
self.card_identifier = vault.vault_id
self.card_country = nil
self.billing_address_line1 = vault.address_line1
self.billing_address_line2 = vault.address_line2
self.billing_address_city = vault.address_city
self.billing_address_state = vault.address_state
self.billing_address_zip = vault.zip_code
self.billing_address_country = vault.address_country
end
if (shipping_atts = order&.ship_to_attributes).present?
shipping = shipping_atts[:address]
self.shipping_address_name = shipping_atts[:attention_name] unless shipping_atts[:attention_name].blank? || shipping.is_placeholder
unless shipping_atts[:name].blank? || shipping.is_placeholder || shipping_atts[:attention_name] == shipping_atts[:name]
self.shipping_address_name = shipping_atts[:name]
end
self.shipping_address_line1 = shipping.street1
self.shipping_address_line2 = shipping.street2
self.shipping_address_city = shipping.city
self.shipping_address_state = shipping.state&.name
self.shipping_address_zip = shipping.zip
self.shipping_address_country = shipping.country&.iso
end
if tx_source = tx.params['source']
self.card_type = tx_source['brand']
self.name = tx_source['name']
self.exp_month = tx_source['exp_month']
self.exp_year = tx_source['exp_year']
self.reference = "....#{tx_source['last4']}"
self.last4 = tx_source['last4']
self.address_line1_check = tx_source['address_line1_check']
self.address_zip_check = tx_source['address_zip_check']
self.cvc_check = tx_source['cvc_check']
self.card_identifier = tx_source['id']
self.card_country = tx_source['country']
self.billing_address_line1 = tx_source['address_line1']
self.billing_address_line2 = tx_source['address_line2']
self.billing_address_city = tx_source['address_city']
self.billing_address_state = tx_source['address_state']
self.billing_address_zip = tx_source['address_zip']
self.billing_address_country = tx_source['address_country']
end
if tx_outcome = tx.params['outcome']
self.radar_network_status = tx_outcome['network_status']
self.radar_reason = tx_outcome['reason']
self.radar_risk_level = tx_outcome['risk_level']
self.radar_seller_message = tx_outcome['seller_message']
self.radar_type = tx_outcome['type']
end
return unless livemode = tx.params['livemode']
self.test = livemode == false
end
|
#receipts ⇒ ActiveRecord::Relation<Receipt>
173
|
# File 'app/models/payment.rb', line 173
has_many :receipts
|
#refunded_on_stripe? ⇒ Boolean
883
884
885
886
887
888
889
890
891
892
893
894
|
# File 'app/models/payment.rb', line 883
def refunded_on_stripe?
return false if authorization_type != 'credit_card'
obj = stripe_payment_object
return false unless obj
if Payment::Apis::Stripe.payment_intent?(authorization_code)
obj.status == 'canceled'
else
obj.try(:refunded?) rescue false
end
end
|
#resend_paypal_invoice ⇒ Object
690
691
692
693
694
695
696
697
|
# File 'app/models/payment.rb', line 690
def resend_paypal_invoice
response = Payment::Apis::Paypal.remind_invoice(authorization_code)
if response['_http_success']
{ success: true }
else
{ success: false, message: 'Something went wrong with Paypal reminder.' }
end
end
|
#rma ⇒ Rma
159
|
# File 'app/models/payment.rb', line 159
belongs_to :rma, optional: true
|
#send_authorization_email_notification ⇒ Object
768
769
770
771
772
773
774
775
776
777
778
779
780
781
|
# File 'app/models/payment.rb', line 768
def send_authorization_email_notification
if category == CREDIT_CARD and email.present? and send_authorization_email == true
sender = order.customer.try(:primary_sales_rep)
CommunicationBuilder.new(
resource: self,
sender_party: sender,
sender: (sender.nil? ? INFO_EMAIL : nil),
emails: email,
bcc: (sender.nil? ? nil : sender.email)
).create
else
'Unable to send authorization email'
end
end
|
#send_wire_info_email ⇒ Object
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
|
# File 'app/models/payment.rb', line 783
def send_wire_info_email
if category == WIRE and email.present?
sender = order.customer.try(:primary_sales_rep)
comm = CommunicationBuilder.new(
resource: order,
sender_party: sender,
sender: (sender.nil? ? INFO_EMAIL : nil),
emails: email,
merge_options: {
order_reference: order.cart_identifier,
order_total: amount,
country_instructions: (order.catalog.id == 1 ? 'usa' : 'ca')
},
bcc: (sender.nil? ? nil : sender.email),
template: EmailTemplate.find_by(system_code: 'WIRE_TRANSFER_INFO')
).create
else
'Unable to send wire info email'
end
end
|
#shared_paypal_auth? ⇒ Boolean
393
394
395
396
397
|
# File 'app/models/payment.rb', line 393
def shared_paypal_auth?
authorization_type == 'paypal' && authorization_code.present? &&
Payment.where(authorization_code: authorization_code, authorization_type: 'paypal')
.where.not(id: id).exists?
end
|
#shared_paypal_auth_siblings ⇒ Object
385
386
387
388
389
390
391
|
# File 'app/models/payment.rb', line 385
def shared_paypal_auth_siblings
return Payment.none unless authorization_type == 'paypal' && authorization_code.present?
Payment.where(authorization_code: authorization_code, authorization_type: 'paypal')
.where.not(id: id)
.where(state: 'authorized')
end
|
#shared_pi? ⇒ Boolean
380
381
382
383
|
# File 'app/models/payment.rb', line 380
def shared_pi?
stripe_payment_intent_id.present? &&
Payment.where(stripe_payment_intent_id: stripe_payment_intent_id).where.not(id: id).exists?
end
|
#shared_pi_siblings ⇒ Object
365
366
367
368
369
370
371
|
# File 'app/models/payment.rb', line 365
def shared_pi_siblings
return Payment.none unless stripe_payment_intent_id.present?
Payment.where(stripe_payment_intent_id: stripe_payment_intent_id)
.where.not(id: id)
.where(state: %w[authorized])
end
|
#shipping_address ⇒ Object
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
|
# File 'app/models/payment.rb', line 1135
def shipping_address
Address.new(
street1: shipping_address_line1,
street2: shipping_address_line2,
city: shipping_address_city,
zip: shipping_address_zip,
state_code: State.code_for_string(shipping_address_state),
country_iso3: Country.iso3_for_string(shipping_address_country)
)
end
|
#stripe_api_key ⇒ Object
1035
1036
1037
|
# File 'app/models/payment.rb', line 1035
def stripe_api_key
Payment::Apis::Stripe.api_key(currency)
end
|
#stripe_payment_object ⇒ Object
Also known as:
stripe_charge
872
873
874
875
876
877
878
|
# File 'app/models/payment.rb', line 872
def stripe_payment_object
return false if authorization_code.blank?
Payment::Apis::Stripe.retrieve_payment_object(authorization_code, currency: currency)
rescue ::Stripe::StripeError
false
end
|
#stripe_resolver ⇒ Object
339
340
341
|
# File 'app/models/payment.rb', line 339
def stripe_resolver
@stripe_resolver ||= Payment::StrategyResolver.new(self)
end
|
#supports_extended_authorization? ⇒ Boolean
353
354
355
|
# File 'app/models/payment.rb', line 353
def supports_extended_authorization?
stripe_resolver.supports_extended_authorization?
end
|
#supports_incremental_authorization? ⇒ Boolean
349
350
351
|
# File 'app/models/payment.rb', line 349
def supports_incremental_authorization?
stripe_resolver.supports_incremental_authorization?
end
|
#supports_multicapture? ⇒ Boolean
343
344
345
346
347
|
# File 'app/models/payment.rb', line 343
def supports_multicapture?
return true if authorization_type == 'paypal'
stripe_resolver.supports_multicapture?
end
|
#sync_capture_before_from_paypal_expiry(auth_expiration) ⇒ Object
584
585
586
587
588
589
|
# File 'app/models/payment.rb', line 584
def sync_capture_before_from_paypal_expiry(auth_expiration)
effective_capture_before = auth_expiration - PAYPAL_HONOR_PERIOD
if capture_before.nil? || capture_before < effective_capture_before
update_column(:capture_before, effective_capture_before)
end
end
|
#sync_external_capture(pi) ⇒ Object
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
|
# File 'app/models/payment.rb', line 636
def sync_external_capture(pi)
pi_received_cents = pi.amount_received.to_i
sibling_captured_cents = all_pi_siblings
.where.not(state: %w[declined expired])
.sum { |s| (s.total_captured * 100).to_i }
unaccounted_cents = pi_received_cents - sibling_captured_cents - (total_captured * 100).to_i
if unaccounted_cents.positive?
capture_amount_cents = [unaccounted_cents, (amount * 100).to_i].min
logger.info("Payment #{id}: PI finalized on Stripe, syncing external capture of #{capture_amount_cents} cents")
transactions.create!(
amount: capture_amount_cents,
action: 'capture',
success: true,
reference: authorization_code,
message: "THIS PAYMENT WAS CAPTURED EXTERNALLY ON STRIPE",
params: pi.to_hash,
test: false
)
payment_captured!
else
logger.info("Payment #{id}: PI finalized on Stripe with no capture for this payment, voiding")
if total_captured.positive?
payment_voided!
else
payment_voided!
end
end
end
|
#sync_paypal_authorization_expiry(auth_expiration) ⇒ Object
511
512
513
514
515
516
517
|
# File 'app/models/payment.rb', line 511
def sync_paypal_authorization_expiry(auth_expiration)
return unless auth_expiration.present?
metadata = paypal_metadata || {}
metadata['authorization_expiry'] = auth_expiration.iso8601
update_column(:paypal_metadata, metadata)
end
|
#sync_paypal_external_capture(auth_response) ⇒ Object
519
520
521
522
523
524
525
526
527
528
529
530
531
532
|
# File 'app/models/payment.rb', line 519
def sync_paypal_external_capture(auth_response)
captured_amount_dollars = auth_response.dig("amount", "value").to_f
transaction = OrderTransaction.new(
amount: (captured_amount_dollars * 100).to_i,
action: 'capture',
success: true,
reference: auth_response['id'] || authorization_code,
message: "THIS PAYMENT WAS MANUALLY CAPTURED ON THE PAYMENT PLATFORM",
params: auth_response.except('_http_status', '_http_success', '_raw_body'),
test: false
)
transactions.push(transaction)
payment_captured!
end
|
#to_s ⇒ Object
804
805
806
|
# File 'app/models/payment.rb', line 804
def to_s
"Payment #{id} (#{order&.reference_number})"
end
|
#total_authorized ⇒ Object
1043
1044
1045
1046
1047
1048
1049
1050
|
# File 'app/models/payment.rb', line 1043
def total_authorized
latest_increment = transactions.select { |t| t.success && t.action == 'incremental_authorization' }.max_by(&:created_at)
if latest_increment
(latest_increment.amount.to_f / 100).round(2)
else
transactions.select { |t| t.success && t.action.in?(%w[authorization authorize]) }.sum { |t| t.amount.to_f / 100 }.to_f.round(2)
end
end
|
#total_captured ⇒ Object
1052
1053
1054
1055
1056
|
# File 'app/models/payment.rb', line 1052
def total_captured
transactions.select do |t|
t.success and (t.action == 'capture' or t.action == 'purchase' or t.action == 'settle')
end.sum { |t| t.amount.to_f / 100 }.to_f.round(2)
end
|
#total_refunded ⇒ Object
1058
1059
1060
|
# File 'app/models/payment.rb', line 1058
def total_refunded
transactions.select { |t| t.success and t.action == 'refund' }.sum { |t| t.amount.to_f / 100 }.to_f.round(2)
end
|
#transaction_reference(action) ⇒ Object
839
840
841
842
843
|
# File 'app/models/payment.rb', line 839
def transaction_reference(action)
return unless payment = transactions.where(action:).where(success: true).order(:id).first
payment.reference
end
|
#transactions ⇒ ActiveRecord::Relation<OrderTransaction>
171
|
# File 'app/models/payment.rb', line 171
has_many :transactions, class_name: 'OrderTransaction', dependent: :destroy
|
#update_authorization_code(action) ⇒ Object
1119
1120
1121
1122
|
# File 'app/models/payment.rb', line 1119
def update_authorization_code(action)
update(authorization_code: transaction_reference(action))
payment.update(amount:)
end
|
#uploads ⇒ ActiveRecord::Relation<Upload>
172
|
# File 'app/models/payment.rb', line 172
has_many :uploads, as: :resource, dependent: :destroy
|
167
|
# File 'app/models/payment.rb', line 167
belongs_to :vpo_contact, class_name: 'Contact', optional: true
|