effective_orders 6.12.2 → 6.12.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e90f1a83b2e9268d377b28a05fbc423bef5618f2270231e018f04bb0f297ea02
4
- data.tar.gz: a4ed543e612da4fddbc2086b30de01ed5252cb53f033db9d538468a4cdd361df
3
+ metadata.gz: 6d365ebd0599f72ed9c5bbf7e9e9f26284647135153befe60799452b87fc1e0c
4
+ data.tar.gz: 913c9fa4e2d528382652c297d6806fb827c79974baebb9890ddd1854449f98d1
5
5
  SHA512:
6
- metadata.gz: 923d207995eb5f2861cd8425c57902357c9d5141d5169eec8911703051e9be4ecf9e90da6ee71aad88d777ec503d18bcc6478323620ade521a2b995c839a8d64
7
- data.tar.gz: 700ab00a86078101fd05a561b7b8c02098a8941f5a9d645e859bfdbafcc40dd363be9dadc3abfefda4b1a9c33673829451f71eefac07261715cec817641f6b2e
6
+ metadata.gz: d5fde239931a219f5e15a29a085dd4f056f12174fb4e6331b5b05369d8798518781e7eddae549edb3d3e8d34a9a8a18896a6a271fa0883a15161f75766cad95b
7
+ data.tar.gz: 2b0daaa3d6a0b0111efe48cac3db3d2b0073a3a8ed3b51ec5e9beaeaadaee604ae515cdc5c309b33c8a4ad28598bfcbdd3c6f345aa95052543588f31dcc1a6bc
@@ -19,7 +19,7 @@ module Admin
19
19
  scope :all
20
20
  scope :purchased
21
21
 
22
- scope :deferred if EffectiveOrders.deferred_providers.present?
22
+ scope :deferred if EffectiveOrders.deferred? || EffectiveOrders.delayed?
23
23
  scope :voided
24
24
 
25
25
  scope :pending_refunds if EffectiveOrders.refund && !EffectiveOrders.buyer_purchases_refund?
@@ -6,7 +6,7 @@ class EffectiveOrdersDatatable < Effective::Datatable
6
6
  scope :all
7
7
  scope :purchased
8
8
 
9
- scope :deferred if EffectiveOrders.deferred_providers.present?
9
+ scope :deferred if EffectiveOrders.deferred? || EffectiveOrders.delayed?
10
10
  scope :refunds if EffectiveOrders.refund
11
11
  scope :not_purchased
12
12
  end
@@ -45,7 +45,7 @@ module EffectiveOrdersHelper
45
45
  when :deluxe
46
46
  'Pay Now'
47
47
  when :deluxe_delayed
48
- 'Save now and charge me later'
48
+ 'Save card and charge me later'
49
49
  when :etransfer
50
50
  'Pay by E-transfer'
51
51
  when :free
@@ -39,8 +39,19 @@ module ActsAsPurchasableParent
39
39
  end
40
40
 
41
41
  included do
42
- has_many :orders, -> { order(:id) }, as: :parent, class_name: 'Effective::Order', dependent: :nullify
42
+ has_many :orders, -> { order(:id) }, as: :parent, class_name: 'Effective::Order'
43
+
43
44
  accepts_nested_attributes_for :orders
45
+
46
+ before_destroy do
47
+ orders.each do |order|
48
+ raise('unable to destroy a purchasable_parent with purchased orders') if order.purchased?
49
+ order.voided? ? order.save! : order.void!
50
+ end
51
+
52
+ true
53
+ end
54
+
44
55
  end
45
56
 
46
57
  end
@@ -23,15 +23,51 @@ module Effective
23
23
  self.currency = currency || EffectiveOrders.deluxe.fetch(:currency)
24
24
  end
25
25
 
26
- def payment
27
- raise('expected purchase response to be present') unless purchase_response.kind_of?(Hash)
28
- purchase_response
26
+ def health_check
27
+ get('/')
28
+ end
29
+
30
+ def healthy?
31
+ response = health_check()
32
+
33
+ return false unless response.kind_of?(Hash)
34
+ return false unless response['appName'].present?
35
+ return false unless response['environment'].present?
36
+
37
+ true
38
+ end
39
+
40
+ # Decode the base64 encoded JSON object into a Hash
41
+ def decode_payment_intent_payload(payload)
42
+ raise('expected a string') unless payload.kind_of?(String)
43
+
44
+ payment_intent = (JSON.parse(Base64.decode64(payload)) rescue nil)
45
+
46
+ raise('expected payment_intent to be a Hash') unless payment_intent.kind_of?(Hash)
47
+ raise('expected a token payment') unless payment_intent['type'] == 'Token'
48
+
49
+ payment_intent
50
+ end
51
+
52
+ # Takes a payment_intent and returns the card info we can store
53
+ def card_info(payment_intent)
54
+ token = extract_token(payment_intent)
55
+
56
+ # Return the authorization params merged with the card info
57
+ last4 = token['maskedPan'].to_s.last(4)
58
+ card = token['cardType'].to_s.downcase
59
+ date = token['expDate']
60
+ cvv = token['cvv']
61
+
62
+ active_card = "**** **** **** #{last4} #{card} #{date}" if last4.present?
63
+
64
+ { 'active_card' => active_card, 'card' => card, 'expDate' => date, 'cvv' => cvv }.compact
29
65
  end
30
66
 
67
+ # After we store a payment intent we can call purchase! immediately or wait till later.
31
68
  # This calls Authorize Payment and Complete Payment
32
- # Returns true if all good.
33
- # Returns false if there was an error.
34
- # Always sets the @purchase_response which is api.payment
69
+ # Returns true when purchased. Returns false when declined.
70
+ # The response is stored in api.payment() after this is run
35
71
  def purchase!(order, payment_intent)
36
72
  payment_intent = decode_payment_intent_payload(payment_intent) if payment_intent.kind_of?(String)
37
73
  raise('expected payment_intent to be a Hash') unless payment_intent.kind_of?(Hash)
@@ -58,21 +94,56 @@ module Effective
58
94
  true
59
95
  end
60
96
 
61
- # Health Check
62
- def health_check
63
- get('/')
97
+ def payment
98
+ raise('expected purchase response to be present') unless purchase_response.kind_of?(Hash)
99
+ purchase_response
64
100
  end
65
101
 
66
- def healthy?
67
- response = health_check()
102
+ def purchase_delayed_orders!(orders)
103
+ now = Time.zone.now
68
104
 
69
- return false unless response.kind_of?(Hash)
70
- return false unless response['timestamp'].to_s.start_with?(Time.zone.now.strftime('%Y-%m-%d'))
71
- return false unless response['environment'].present?
105
+ Array(orders).each do |order|
106
+ puts "Trying order #{order.id}"
107
+
108
+ begin
109
+ raise('expected a delayed order') unless order.delayed?
110
+ raise('expected a deferred order') unless order.deferred?
111
+ raise('expected delayed payment intent') unless order.delayed_payment_intent.present?
112
+
113
+ order.update_columns(delayed_payment_purchase_ran_at: now, delayed_payment_purchase_result: nil)
114
+
115
+ purchased = purchase!(order, order.delayed_payment_intent)
116
+ provider = order.payment_provider
117
+ payment = self.payment()
118
+ card = payment["card"]
119
+
120
+ if purchased
121
+ order.assign_attributes(delayed_payment_purchase_result: "success")
122
+ order.purchase!(payment: payment, provider: provider, card: card, email: true, skip_buyer_validations: true)
123
+
124
+ puts "Successfully purchased order #{order.id}"
125
+ else
126
+ order.assign_attributes(delayed_payment_purchase_result: "failed with message: #{Array(payment['responseMessage']).to_sentence.presence || 'none'}.")
127
+ order.decline!(payment: payment, provider: provider, card: card)
128
+
129
+ puts "Failed to purchase order #{order.id} #{order.delayed_payment_purchase_result}"
130
+ end
131
+
132
+ rescue => e
133
+ order.update_columns(delayed_payment_purchase_ran_at: now, delayed_payment_purchase_result: "error: #{e.message}")
134
+
135
+ EffectiveLogger.error(e.message, associated: order) if defined?(EffectiveLogger)
136
+ ExceptionNotifier.notify_exception(e, data: { order_id: order.id }) if defined?(ExceptionNotifier)
137
+
138
+ puts "Error purchasing #{order.id}: #{e.message}"
139
+ end
140
+ end
72
141
 
73
142
  true
74
143
  end
75
144
 
145
+ protected
146
+
76
147
  # Authorize Payment
77
148
  def authorize_payment(order, payment_intent)
78
149
  response = post('/payments/authorize', params: authorize_payment_params(order, payment_intent))
@@ -117,19 +188,6 @@ module Effective
117
188
  response.reverse_merge(authorization)
118
189
  end
119
190
 
120
- def complete_payment_params(order, payment_intent)
121
- raise('expected an Effective::Order') unless order.kind_of?(Effective::Order)
122
-
123
- payment_id = extract_payment_id(payment_intent)
124
- amount = { amount: order.total_to_f, currency: currency }
125
-
126
- # Params passed into Complete Payment
127
- {
128
- paymentId: payment_id,
129
- amount: amount
130
- }
131
- end
132
-
133
191
  def authorize_payment_params(order, payment_intent)
134
192
  raise('expected an Effective::Order') unless order.kind_of?(Effective::Order)
135
193
 
@@ -183,6 +241,19 @@ module Effective
183
241
  }.compact
184
242
  end
185
243
 
244
+ def complete_payment_params(order, payment_intent)
245
+ raise('expected an Effective::Order') unless order.kind_of?(Effective::Order)
246
+
247
+ payment_id = extract_payment_id(payment_intent)
248
+ amount = { amount: order.total_to_f, currency: currency }
249
+
250
+ # Params passed into Complete Payment
251
+ {
252
+ paymentId: payment_id,
253
+ amount: amount
254
+ }
255
+ end
256
+
186
257
  def get(endpoint, params: nil)
187
258
  query = ('?' + params.compact.map { |k, v| "$#{k}=#{v}" }.join('&')) if params.present?
188
259
 
@@ -223,33 +294,6 @@ module Effective
223
294
  JSON.parse(result.body)
224
295
  end
225
296
 
226
- # Takes a payment_intent and returns the card info we can store
227
- def card_info(payment_intent)
228
- token = extract_token(payment_intent)
229
-
230
- # Return the authorization params merged with the card info
231
- last4 = token['maskedPan'].to_s.last(4)
232
- card = token['cardType'].to_s.downcase
233
- date = token['expDate']
234
- cvv = token['cvv']
235
-
236
- active_card = "**** **** **** #{last4} #{card} #{date}" if last4.present?
237
-
238
- { 'active_card' => active_card, 'card' => card, 'expDate' => date, 'cvv' => cvv }.compact
239
- end
240
-
241
- # Decode the base64 encoded JSON object into a Hash
242
- def decode_payment_intent_payload(payload)
243
- raise('expected a string') unless payload.kind_of?(String)
244
-
245
- payment_intent = (JSON.parse(Base64.decode64(payload)) rescue nil)
246
-
247
- raise('expected payment_intent to be a Hash') unless payment_intent.kind_of?(Hash)
248
- raise('expected a token payment') unless payment_intent['type'] == 'Token'
249
-
250
- payment_intent
251
- end
252
-
253
297
  private
254
298
 
255
299
  def headers
@@ -241,7 +241,9 @@ module Effective
241
241
  validates :payment_provider, presence: true
242
242
 
243
243
  validate do
244
- errors.add(:payment_provider, "unknown deferred payment provider") unless EffectiveOrders.deferred_providers.include?(payment_provider)
244
+ unless EffectiveOrders.deferred_providers.include?(payment_provider) || EffectiveOrders.delayed_providers.include?(payment_provider)
245
+ errors.add(:payment_provider, "unknown deferred payment provider")
246
+ end
245
247
  end
246
248
  end
247
249
 
@@ -546,13 +548,20 @@ module Effective
546
548
  return false unless delayed?
547
549
  return false unless deferred?
548
550
  return false unless delayed_payment_intent.present?
549
- return false if delayed_payment_date_future?
551
+ return false if delayed_payment_date_upcoming?
550
552
  return false if delayed_payment_purchase_ran_at.present? # We ran before and probably failed
551
553
 
552
554
  true
553
555
  end
554
556
 
555
- def delayed_payment_date_future?
557
+ def delayed_payment_info
558
+ return unless delayed? && deferred?
559
+ return unless delayed_payment_date_upcoming?
560
+
561
+ "Your #{delayed_payment_method} will be charged on #{delayed_payment_date.strftime('%F')} for the full amount of $#{'%0.2f' % total_to_f}"
562
+ end
563
+
564
+ def delayed_payment_date_upcoming?
556
565
  return false unless delayed?
557
566
  delayed_payment_date > Time.zone.now.to_date
558
567
  end
@@ -673,7 +682,9 @@ module Effective
673
682
 
674
683
  payment: payment_to_h(payment.presence || 'none'),
675
684
  payment_provider: (provider.presence || 'none'),
676
- payment_card: (card.presence || 'none')
685
+ payment_card: (card.presence || 'none'),
686
+
687
+ delayed_payment_intent: nil # Do not store the delayed payment intent any longer
677
688
  )
678
689
 
679
690
  if current_user&.email.present?
@@ -828,6 +839,15 @@ module Effective
828
839
  unvoided!(skip_buyer_validations: true)
829
840
  end
830
841
 
842
+ def deluxe_delayed_purchase!
843
+ raise('expected a delayed order') unless delayed?
844
+ raise('expected a deferred order') unless deferred?
845
+ raise('expected delayed payment intent') unless delayed_payment_intent.present?
846
+ raise('expected a deluxe_delayed payment provider') unless payment_provider == 'deluxe_delayed'
847
+
848
+ Effective::DeluxeApi.new().purchase_delayed_orders!(self)
849
+ end
850
+
831
851
  # These are all the emails we send all notifications to
832
852
  def emails
833
853
  ([purchased_by.try(:email)] + [email] + [user.try(:email)] + Array(organization.try(:billing_emails))).map(&:presence).compact.uniq
@@ -8,6 +8,6 @@
8
8
  %tr
9
9
  %td
10
10
  - if order.delayed?
11
- Your #{order.delayed_payment_method} will be charged on #{order.delayed_payment_date.strftime('%F')} for the full amount
11
+ Your #{order.delayed_payment_method} will be charged on #{order.delayed_payment_date.strftime('%F')}
12
12
  - else
13
13
  Waiting for payment by #{order.payment_provider}
@@ -8,18 +8,24 @@
8
8
 
9
9
  The payment date for this order
10
10
 
11
- - if order.delayed_payment_date_future?
11
+ - if order.delayed_payment_date_upcoming?
12
12
  is in #{distance} from now on #{order.delayed_payment_date.strftime('%F')}
13
13
  - elsif order.delayed_payment_date_today?
14
14
  was today
15
15
  - else
16
16
  was #{distance} ago on #{order.delayed_payment_date.strftime('%F')}
17
+
18
+ - provider_locals = { order: order, deferred_url: deferred_url, declined_url: declined_url }
17
19
 
18
- %p
19
- Instead of charging your card right away, the following action will securely save a token
20
- representing your card information. The full amount will be charged on the payment date.
20
+ - if order.deferred? && order.delayed?
21
+ %p Your existing #{order.delayed_payment_method} will be charged on the payment date.
21
22
 
22
- - provider_locals = { order: order, deferred_url: deferred_url, declined_url: declined_url }
23
+ = collapse('Change my card info') do
24
+ - EffectiveOrders.delayed_providers.each do |provider|
25
+ = render partial: "/effective/orders/#{provider}/form", locals: provider_locals
26
+ - else
27
+ %p
28
+ Please enter your card information so it can be charged on the payment date.
23
29
 
24
- - EffectiveOrders.delayed_providers.each do |provider|
25
- = render partial: "/effective/orders/#{provider}/form", locals: provider_locals
30
+ - EffectiveOrders.delayed_providers.each do |provider|
31
+ = render partial: "/effective/orders/#{provider}/form", locals: provider_locals
@@ -13,7 +13,7 @@
13
13
 
14
14
  The payment date for this order
15
15
 
16
- - if order.delayed_payment_date_future?
16
+ - if order.delayed_payment_date_upcoming?
17
17
  is in #{distance} from now on #{order.delayed_payment_date.strftime('%F')}
18
18
  - elsif order.delayed_payment_date_today?
19
19
  was today
@@ -1,3 +1,3 @@
1
1
  module EffectiveOrders
2
- VERSION = '6.12.2'.freeze
2
+ VERSION = '6.12.4'.freeze
3
3
  end
@@ -185,8 +185,9 @@ module EffectiveOrders
185
185
  ].compact
186
186
  end
187
187
 
188
+ # Should not include delayed providers
188
189
  def self.deferred_providers
189
- [('cheque' if cheque?), ('deluxe_delayed' if deluxe_delayed?), ('etransfer' if etransfer?), ('phone' if phone?)].compact
190
+ [('cheque' if cheque?), ('etransfer' if etransfer?), ('phone' if phone?)].compact
190
191
  end
191
192
 
192
193
  def self.delayed_providers
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: effective_orders
3
3
  version: !ruby/object:Gem::Version
4
- version: 6.12.2
4
+ version: 6.12.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Code and Effect
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-05-30 00:00:00.000000000 Z
11
+ date: 2024-06-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails