effective_orders 6.9.10 → 6.11.0

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: ac026da4d01fe889cb9e9afd765b25bce3e95c8fda855888a5f76a29626fefdc
4
- data.tar.gz: 15eee62b2710cff5f788f3f21a1a12e4f0391edae37c9f6d5cc452af5dd9ac8d
3
+ metadata.gz: 6a3716b7b619276755bac8d33027424eed7b97519fdc1454d498349074e2a022
4
+ data.tar.gz: 5071eb8c4733b659c1d6e72f0ad7974301af0c32d0df6701547eedddaefc723b
5
5
  SHA512:
6
- metadata.gz: 8ba372d127fa4fa088f2a3c0e241dc97c37c0e41c09d4268ca991e660ac6e28fc3cc067c054df082476596c3f6d1f6724525cbeb174dbf7cf42b6b8a3f2101bd
7
- data.tar.gz: 107208024d4318fb320ace396c092781f69bc0da508b15a17506666a6b720c5a948e93046e75bdce4da3215e161f80c719ef1b3448dd3ea43f84498d55e3db4c
6
+ metadata.gz: 30ee25cf450e876da9a219c303c51d1469994fc055d039fe610e7abcddd3d988bc7fa5558ac17dcf05f06ff8afc0d23d6094a1aa106271b091b0fe2c69390310
7
+ data.tar.gz: abc8e9d7fac67cac46be97ad522f7ffce5e64a47a4a12e8bc9d6f1c17368788dceab075b9ef302f7d937872ebf7f16cbcaabd5d5f9b38ba4f316a46c5dcedc9e
@@ -0,0 +1,32 @@
1
+ // https://developer.deluxe.com/s/article-hosted-payment-form
2
+
3
+ function initializeDeluxe() {
4
+ let $deluxe = $('form[data-deluxe-checkout]:not(.initialized)');
5
+ if($deluxe.length == 0) return;
6
+
7
+ let options = $deluxe.data('deluxe-checkout');
8
+
9
+ HostedForm.init(options, {
10
+ onFailure: (data) => { $('#deluxe-checkout-errors').text(JSON.stringify(data)); },
11
+ onInvalid: (data) => { $('#deluxe-checkout-errors').text(JSON.stringify(data)); },
12
+
13
+ onSuccess: (data) => {
14
+ let value = btoa(JSON.stringify(data)); // A base64 encoded JSON object
15
+
16
+ $form = $('form[data-deluxe-checkout]').first();
17
+ $form.find('input[name="deluxe[payment_intent]"]').val(value);
18
+ $form.submit();
19
+
20
+ $('#deluxeCheckout').fadeOut('slow');
21
+ $('#deluxe-checkout-loading').text('Thank you! Processing payment information. Please wait...');
22
+ },
23
+ }).then((instance) => {
24
+ $('#deluxe-checkout-loading').text('');
25
+ instance.renderHpf();
26
+ });
27
+
28
+ $deluxe.addClass('initialized');
29
+ };
30
+
31
+ $(document).ready(function() { initializeDeluxe() });
32
+ $(document).on('turbolinks:load', function() { initializeDeluxe() });
@@ -56,6 +56,10 @@ form.new_effective_order {
56
56
  height: 720px;
57
57
  }
58
58
 
59
+ .effective-deluxe-checkout {
60
+ #deluxeCheckout { height: 280px; }
61
+ }
62
+
59
63
  @media print {
60
64
  .effective-orders-page-content { display: none; }
61
65
 
@@ -4,6 +4,7 @@ module Effective
4
4
  include Concerns::Purchase
5
5
 
6
6
  include Providers::Cheque
7
+ include Providers::Deluxe
7
8
  include Providers::Etransfer
8
9
  include Providers::Free
9
10
  include Providers::MarkAsPaid
@@ -0,0 +1,75 @@
1
+ module Effective
2
+ module Providers
3
+ module Deluxe
4
+ extend ActiveSupport::Concern
5
+
6
+ def deluxe
7
+ raise('deluxe provider is not available') unless EffectiveOrders.deluxe?
8
+
9
+ @order = Effective::Order.deep.find(params[:id])
10
+
11
+ EffectiveResources.authorize!(self, :update, @order)
12
+
13
+ ## Process Payment Intent
14
+
15
+ # The payment_intent is set by the Deluxe HostedPaymentForm
16
+ payment_intent = deluxe_params[:payment_intent]
17
+
18
+ if payment_intent.blank?
19
+ flash[:danger] = 'Unable to process deluxe order without payment. please try again.'
20
+ return order_not_processed(declined_url: payment_intent[:declined_url])
21
+ end
22
+
23
+ # Decode the base64 encoded JSON object into a Hash
24
+ payment_intent = (JSON.parse(Base64.decode64(payment_intent)) rescue nil)
25
+ raise('expected payment_intent to be a Hash') unless payment_intent.kind_of?(Hash)
26
+ raise('expected a token payment') unless payment_intent['type'] == 'Token'
27
+
28
+ valid = payment_intent['status'] == 'success'
29
+
30
+ if valid == false
31
+ card_info = deluxe_api.card_info(payment_intent)
32
+ return order_declined(payment: card_info, provider: 'deluxe', card: card_info['card'], declined_url: declined_url)
33
+ end
34
+
35
+ ## Process Authorization
36
+ authorization = deluxe_api.authorize_payment(@order, payment_intent)
37
+ valid = [0].include?(authorization['responseCode'])
38
+
39
+ if valid == false
40
+ flash[:danger] = "Payment was unsuccessful. The credit card authorization failed with message: #{Array(authorization['responseMessage']).to_sentence.presence || 'none'}. Please try again."
41
+ return order_declined(payment: authorization, provider: 'deluxe', card: authorization['card'], declined_url: deluxe_params[:declined_url])
42
+ end
43
+
44
+ ## Complete Payment
45
+ payment = deluxe_api.complete_payment(@order, authorization)
46
+ valid = [0].include?(payment['responseCode'])
47
+
48
+ if valid == false
49
+ flash[:danger] = "Payment was unsuccessful. The credit card payment failed with message: #{Array(payment['responseMessage']).to_sentence.presence || 'none'}. Please try again."
50
+ return order_declined(payment: payment, provider: 'deluxe', card: payment['card'], declined_url: deluxe_params[:declined_url])
51
+ end
52
+
53
+ # Valid Authorized and Completed Payment
54
+ order_purchased(
55
+ payment: payment,
56
+ provider: 'deluxe',
57
+ card: payment['card'],
58
+ purchased_url: deluxe_params[:purchased_url],
59
+ current_user: (current_user unless admin_checkout?(deluxe_params))
60
+ )
61
+ end
62
+
63
+ private
64
+
65
+ def deluxe_params
66
+ params.require(:deluxe).permit(:payment_intent, :purchased_url, :declined_url)
67
+ end
68
+
69
+ def deluxe_api
70
+ @deluxe_api ||= Effective::DeluxeApi.new
71
+ end
72
+
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,15 @@
1
+ module EffectiveDeluxeHelper
2
+
3
+ # https://developer.deluxe.com/s/article-hosted-payment-form
4
+ def deluxe_hosted_payment_form_options(order)
5
+ {
6
+ xtoken: EffectiveOrders.deluxe.fetch(:access_token),
7
+ containerId: "deluxeCheckout",
8
+ xcssid: "deluxeCheckoutCss",
9
+ xrtype: "Generate Token",
10
+ xpm: "1", # 0 = CC & ACH, 1 = CC, 2 = ACH
11
+ xautoprompt: false,
12
+ xbtntext: order_checkout_label(:deluxe)
13
+ }
14
+ end
15
+ end
@@ -42,6 +42,8 @@ module EffectiveOrdersHelper
42
42
  case processor
43
43
  when :cheque
44
44
  'Pay by Cheque'
45
+ when :deluxe
46
+ 'Pay Now'
45
47
  when :etransfer
46
48
  'Pay by E-transfer'
47
49
  when :free
@@ -62,6 +62,14 @@ module ActsAsPurchasable
62
62
  module ClassMethods
63
63
  def acts_as_purchasable?; true; end
64
64
 
65
+ def before_defer(&block)
66
+ send :define_method, :before_defer do |order, order_item| self.instance_exec(order, order_item, &block) end
67
+ end
68
+
69
+ def after_defer(&block)
70
+ send :define_method, :after_defer do |order, order_item| self.instance_exec(order, order_item, &block) end
71
+ end
72
+
65
73
  def before_purchase(&block)
66
74
  send :define_method, :before_purchase do |order, order_item| self.instance_exec(order, order_item, &block) end
67
75
  end
@@ -70,6 +78,10 @@ module ActsAsPurchasable
70
78
  send :define_method, :after_purchase do |order, order_item| self.instance_exec(order, order_item, &block) end
71
79
  end
72
80
 
81
+ def before_decline(&block)
82
+ send :define_method, :before_decline do |order, order_item| self.instance_exec(order, order_item, &block) end
83
+ end
84
+
73
85
  def after_decline(&block)
74
86
  send :define_method, :after_decline do |order, order_item| self.instance_exec(order, order_item, &block) end
75
87
  end
@@ -13,6 +13,14 @@ module ActsAsPurchasableParent
13
13
  module ClassMethods
14
14
  def acts_as_purchasable_parent?; true; end
15
15
 
16
+ def before_defer(&block)
17
+ send :define_method, :before_defer do |order| self.instance_exec(order, &block) end
18
+ end
19
+
20
+ def after_defer(&block)
21
+ send :define_method, :after_defer do |order| self.instance_exec(order, &block) end
22
+ end
23
+
16
24
  def before_purchase(&block)
17
25
  send :define_method, :before_purchase do |order| self.instance_exec(order, &block) end
18
26
  end
@@ -21,6 +29,10 @@ module ActsAsPurchasableParent
21
29
  send :define_method, :after_purchase do |order| self.instance_exec(order, &block) end
22
30
  end
23
31
 
32
+ def before_decline(&block)
33
+ send :define_method, :before_decline do |order| self.instance_exec(order, &block) end
34
+ end
35
+
24
36
  def after_decline(&block)
25
37
  send :define_method, :after_decline do |order| self.instance_exec(order, &block) end
26
38
  end
@@ -0,0 +1,277 @@
1
+ # https://developer.deluxe.com/s/article-api-reference
2
+ # We use Oauth2 client to get an authorization token. Then pass that token into a REST api.
3
+ # We get a payment_intent from the front end HostedPaymentForm, then call authorize and complete on it.
4
+ # Effective::DeluxeApi.new.health_check
5
+ module Effective
6
+ class DeluxeApi
7
+ SCRUB = /[^\w\d#,\s]/
8
+
9
+ # All required
10
+ attr_accessor :environment
11
+ attr_accessor :client_id
12
+ attr_accessor :client_secret
13
+ attr_accessor :access_token
14
+ attr_accessor :currency
15
+
16
+ def initialize(environment: nil, client_id: nil, client_secret: nil, access_token: nil, currency: nil)
17
+ self.environment = environment || EffectiveOrders.deluxe.fetch(:environment)
18
+ self.client_id = client_id || EffectiveOrders.deluxe.fetch(:client_id)
19
+ self.client_secret = client_secret || EffectiveOrders.deluxe.fetch(:client_secret)
20
+ self.access_token = access_token || EffectiveOrders.deluxe.fetch(:access_token)
21
+ self.currency = currency || EffectiveOrders.deluxe.fetch(:currency)
22
+ end
23
+
24
+ # Health Check
25
+ def health_check
26
+ get('/')
27
+ end
28
+
29
+ # Authorize Payment
30
+ def authorize_payment(order, payment_intent)
31
+ response = post('/payments/authorize', params: authorize_payment_params(order, payment_intent))
32
+
33
+ # Sanity check response
34
+ raise('expected responseCode') unless response.kind_of?(Hash) && response['responseCode'].present?
35
+
36
+ # Sanity check response approved vs authorized
37
+ valid = [0].include?(response['responseCode'])
38
+
39
+ # We might be approved for an amount less than the order total. Not sure what to do here
40
+ if valid && (amountApproved = response['amountApproved']) != (amountAuthorized = order.total_to_f)
41
+ raise("expected authorize payment amountApproved #{amountApproved} to be the same as the amountAuthorized #{amountAuthorized} but it was not")
42
+ end
43
+
44
+ # Generate the card info we can store
45
+ card = card_info(payment_intent)
46
+
47
+ # Return the authorization params merged with the card info
48
+ response.reverse_merge(card)
49
+ end
50
+
51
+ # Complete Payment
52
+ def complete_payment(order, authorization)
53
+ response = post('/payments/complete', params: complete_payment_params(order, authorization))
54
+
55
+ # Sanity check response
56
+ raise('expected responseCode') unless response.kind_of?(Hash) && response['responseCode'].present?
57
+
58
+ # Sanity check response approved vs authorized
59
+ valid = [0].include?(response['responseCode'])
60
+
61
+ # We might be approved for an amount less than the order total. Not sure what to do here
62
+ if valid && (amountApproved = response['amountApproved']) != (amountAuthorized = order.total_to_f)
63
+ raise("expected complete payment amountApproved #{amountApproved} to be the same as the amountAuthorized #{amountAuthorized} but it was not")
64
+ end
65
+
66
+ # The authorization information
67
+ authorization = { 'paymentId' => authorization } if authorization.kind_of?(String)
68
+
69
+ # Return the complete params merged with the authorization params
70
+ response.reverse_merge(authorization)
71
+ end
72
+
73
+ def complete_payment_params(order, payment_intent)
74
+ raise('expected an Effective::Order') unless order.kind_of?(Effective::Order)
75
+
76
+ payment_id = extract_payment_id(payment_intent)
77
+ amount = { amount: order.total_to_f, currency: currency }
78
+
79
+ # Params passed into Complete Payment
80
+ {
81
+ paymentId: payment_id,
82
+ amount: amount
83
+ }
84
+ end
85
+
86
+ def authorize_payment_params(order, payment_intent)
87
+ raise('expected an Effective::Order') unless order.kind_of?(Effective::Order)
88
+
89
+ token = extract_token(payment_intent)
90
+
91
+ amount = {
92
+ amount: order.total_to_f,
93
+ currency: currency
94
+ }
95
+
96
+ billingAddress = if (address = order.billing_address).present?
97
+ {
98
+ email: order.email,
99
+ address: scrub(address.address1, limit: 250),
100
+ address2: scrub(address.address2),
101
+ city: scrub(address.city, limit: 50),
102
+ state: address.state_code,
103
+ country: address.country_code,
104
+ postalCode: address.postal_code
105
+ }.compact
106
+ end
107
+
108
+ shippingAddress = if (address = order.shipping_address).present?
109
+ {
110
+ address: scrub(address.address1, limit: 250),
111
+ address2: scrub(address.address2),
112
+ city: scrub(address.city, limit: 50),
113
+ state: address.state_code,
114
+ country: address.country_code,
115
+ postalCode: address.postal_code
116
+ }.compact
117
+ end
118
+
119
+ paymentMethod = {
120
+ token: { token: token['token'], expiry: (token['expDate'] || token['expiry']), cvv: token['cvv'] }.compact,
121
+ billingAddress: billingAddress
122
+ }.compact
123
+
124
+ customData = [
125
+ ({ name: 'order_id', value: order.to_param }),
126
+ ({ name: 'user_id', value: order.user_id.to_s } if order.user_id.present?),
127
+ ({ name: 'organization_id', value: order.organization_id.to_s } if order.organization_id.present?)
128
+ ].compact
129
+
130
+ # Params passed into Authorize Payment
131
+ {
132
+ amount: amount,
133
+ paymentMethod: paymentMethod,
134
+ shippingAddress: shippingAddress,
135
+ customData: customData,
136
+ }.compact
137
+ end
138
+
139
+ def get(endpoint, params: nil)
140
+ query = ('?' + params.compact.map { |k, v| "$#{k}=#{v}" }.join('&')) if params.present?
141
+
142
+ uri = URI.parse(api_url + endpoint + query.to_s)
143
+
144
+ http = Net::HTTP.new(uri.host, uri.port)
145
+ http.read_timeout = 10
146
+ http.use_ssl = true
147
+
148
+ result = with_retries do
149
+ puts "[GET] #{uri}" if Rails.env.development?
150
+
151
+ response = http.get(uri, headers)
152
+ raise Exception.new("#{response.code} #{response.body}") unless response.code == '200'
153
+
154
+ response
155
+ end
156
+
157
+ JSON.parse(result.body)
158
+ end
159
+
160
+ def post(endpoint, params:)
161
+ uri = URI.parse(api_url + endpoint)
162
+
163
+ http = Net::HTTP.new(uri.host, uri.port)
164
+ http.read_timeout = 10
165
+ http.use_ssl = true
166
+
167
+ result = with_retries do
168
+ puts "[POST] #{uri} #{params}" if Rails.env.development?
169
+
170
+ response = http.post(uri.path, params.to_json, headers)
171
+ raise Exception.new("#{response.code} #{response.body}") unless response.code == '200'
172
+
173
+ response
174
+ end
175
+
176
+ JSON.parse(result.body)
177
+ end
178
+
179
+ # Takes a payment_intent and returns the card info we can store
180
+ def card_info(payment_intent)
181
+ token = extract_token(payment_intent)
182
+
183
+ # Return the authorization params merged with the card info
184
+ last4 = token['maskedPan'].to_s.last(4)
185
+ card = token['cardType'].to_s.downcase
186
+ date = token['expDate']
187
+ cvv = token['cvv']
188
+
189
+ active_card = "**** **** **** #{last4} #{card} #{date}" if last4.present?
190
+
191
+ { 'active_card' => active_card, 'card' => card, 'expDate' => date, 'cvv' => cvv }.compact
192
+ end
193
+
194
+ private
195
+
196
+ def headers
197
+ { "Content-Type": "application/json", "Authorization": "Bearer #{authorization_token}", "PartnerToken": access_token }
198
+ end
199
+
200
+ def client
201
+ OAuth2::Client.new(
202
+ client_id,
203
+ client_secret,
204
+ site: client_url,
205
+ token_url: '/secservices/oauth2/v2/token' # https://sandbox.api.deluxe.com/secservices/oauth2/v2/token
206
+ )
207
+ end
208
+
209
+ def authorization_token
210
+ @authorization_token ||= Rails.cache.fetch(authorization_cache_key, expires_in: 60.minutes) do
211
+ puts "[AUTH] Oauth2 Get Token" if Rails.env.development?
212
+ client.client_credentials.get_token.token
213
+ end
214
+ end
215
+
216
+ # https://sandbox.api.deluxe.com
217
+ def client_url
218
+ case environment
219
+ when 'production' then 'https://api.deluxe.com'
220
+ when 'sandbox' then 'https://sandbox.api.deluxe.com' # No trailing /
221
+ else raise('unexpected deluxe environment')
222
+ end
223
+ end
224
+
225
+ # https://sandbox.api.deluxe.com/dpp/v1/gateway/
226
+ def api_url
227
+ client_url + '/dpp/v1/gateway'
228
+ end
229
+
230
+ def extract_token(payment_intent)
231
+ raise('expected a payment intent') unless payment_intent.kind_of?(Hash)
232
+
233
+ token = payment_intent['data'] || payment_intent
234
+ raise('expected a payment intent Hash') unless token['token'].present? && token['expDate'].present?
235
+
236
+ token
237
+ end
238
+
239
+ def extract_payment_id(authorization)
240
+ return authorization if authorization.kind_of?(String)
241
+ raise('expected an authorization Hash') unless authorization.kind_of?(Hash)
242
+
243
+ payment_id = authorization['paymentId']
244
+ raise('expected a paymentId') unless payment_id.present?
245
+
246
+ payment_id
247
+ end
248
+
249
+ def scrub(value, limit: 100)
250
+ return value unless value.kind_of?(String)
251
+ value.gsub(SCRUB, '').first(limit)
252
+ end
253
+
254
+ def authorization_cache_key
255
+ "deluxe_api_#{client_id}"
256
+ end
257
+
258
+ def with_retries(retries: (Rails.env.development? ? 0 : 3), wait: 2, &block)
259
+ raise('expected a block') unless block_given?
260
+
261
+ begin
262
+ return yield
263
+ rescue Exception => e
264
+ # Reset cache and query for a new authorization token on any error
265
+ Rails.cache.delete(authorization_cache_key)
266
+ @authorization_token = nil
267
+
268
+ if (retries -= 1) > 0
269
+ sleep(wait); retry
270
+ else
271
+ raise
272
+ end
273
+ end
274
+ end
275
+
276
+ end
277
+ end
@@ -515,6 +515,10 @@ module Effective
515
515
  self[:total] || get_total()
516
516
  end
517
517
 
518
+ def total_to_f
519
+ ((total || 0) / 100.0).to_f
520
+ end
521
+
518
522
  def total_with_surcharge
519
523
  get_total_with_surcharge()
520
524
  end
@@ -692,11 +696,41 @@ module Effective
692
696
  sync_quickbooks!(skip: true)
693
697
  end
694
698
 
695
- def defer!(provider: 'none', email: true)
696
- return false if purchased?
699
+ def defer!(provider: 'none', email: true, validate: true)
700
+ raise('order already purchased') if purchased?
701
+
702
+ # Assign attributes
703
+ assign_attributes(
704
+ payment_provider: provider,
705
+
706
+ status: :deferred,
707
+ purchased_at: nil,
708
+ purchased_by: nil,
709
+
710
+ deferred_at: (deferred_at.presence || Time.zone.now),
711
+ deferred_by: (deferred_by.presence || current_user)
712
+ )
713
+
714
+ if current_user&.email.present?
715
+ assign_attributes(email: current_user.email)
716
+ end
717
+
718
+ error = nil
719
+
720
+ begin
721
+ Effective::Order.transaction do
722
+ run_purchasable_callbacks(:before_defer)
723
+ save!(validate: validate)
724
+ run_purchasable_callbacks(:after_defer)
725
+ end
726
+ rescue ActiveRecord::RecordInvalid => e
727
+ self.status = status_was
728
+
729
+ error = e.message
730
+ raise ::ActiveRecord::Rollback
731
+ end
697
732
 
698
- assign_attributes(payment_provider: provider)
699
- deferred!
733
+ raise "Failed to defer order: #{error || errors.full_messages.to_sentence}" unless error.nil?
700
734
 
701
735
  send_payment_request_to_buyer! if email
702
736
 
@@ -705,11 +739,8 @@ module Effective
705
739
 
706
740
  def decline!(payment: 'none', provider: 'none', card: 'none', validate: true)
707
741
  return false if declined?
708
-
709
742
  raise('order already purchased') if purchased?
710
743
 
711
- error = nil
712
-
713
744
  assign_attributes(
714
745
  skip_buyer_validations: true,
715
746
 
@@ -722,6 +753,12 @@ module Effective
722
753
  payment_card: (card.presence || 'none')
723
754
  )
724
755
 
756
+ if current_user&.email.present?
757
+ assign_attributes(email: current_user.email)
758
+ end
759
+
760
+ error = nil
761
+
725
762
  Effective::Order.transaction do
726
763
  begin
727
764
  run_purchasable_callbacks(:before_decline)
@@ -18,6 +18,9 @@
18
18
  - if EffectiveOrders.pretend?
19
19
  = render partial: '/effective/orders/pretend/form', locals: provider_locals
20
20
 
21
+ - if EffectiveOrders.deluxe?
22
+ = render partial: '/effective/orders/deluxe/form', locals: provider_locals
23
+
21
24
  - if EffectiveOrders.moneris?
22
25
  = render partial: '/effective/orders/moneris/form', locals: provider_locals
23
26
 
@@ -0,0 +1,12 @@
1
+ :css
2
+ #dppPaymentContainer {
3
+ .form-control { }
4
+ .form-label { }
5
+
6
+ button {
7
+ color: #fff;
8
+ background-color: #0d6efd;
9
+ border_color: #0d6efd;
10
+ padding: 0.375rem 0.75rem;
11
+ }
12
+ }
@@ -0,0 +1,9 @@
1
+ .effective-deluxe-checkout
2
+ #deluxe-checkout-loading.text-center Loading...
3
+ #deluxe-checkout-errors.text-danger
4
+
5
+ %style#deluxeCheckoutCss
6
+ -# Pass in custom CSS to the Deluxe hosted payment form iframe
7
+ = render('effective/orders/deluxe/css')
8
+
9
+ #deluxeCheckout
@@ -0,0 +1,19 @@
1
+ - deluxe = deluxe_hosted_payment_form_options(order)
2
+
3
+ .card
4
+ .card-body
5
+ %h2 Checkout
6
+ %p
7
+ %em This checkout is powered by #{link_to('Deluxe', 'https://www.deluxe.com/', target: '_blank', class: 'btn-link')}
8
+
9
+ .my-4.text-center
10
+ = image_tag('effective_orders/deluxe.png', alt: 'Deluxe.com Logo', width: 200)
11
+
12
+ = effective_form_with(scope: :deluxe, url: effective_orders.deluxe_order_path(order), data: { 'deluxe-checkout': deluxe.to_json }) do |f|
13
+ = f.hidden_field :purchased_url, value: purchased_url
14
+ = f.hidden_field :declined_url, value: declined_url
15
+
16
+ -# This is set by the deluxe.js javascript on Submit
17
+ = f.hidden_field :payment_intent, required: true
18
+
19
+ = render('effective/orders/deluxe/element')
@@ -122,6 +122,17 @@ EffectiveOrders.setup do |config|
122
122
  # success: 'Thank you! You have indicated that this order will be purchased by cheque. Please send us a cheque and a copy of this invoice at your earliest convenience.'
123
123
  # }
124
124
 
125
+ # Deluxe
126
+ config.deluxe = false
127
+
128
+ # config.deluxe = {
129
+ # environment: (Rails.env.production? ? 'production' : 'sandbox'),
130
+ # client_id: '',
131
+ # client_secret: '',
132
+ # access_token: '',
133
+ # currency: 'CAD'
134
+ # }
135
+
125
136
  # E-transfer
126
137
  # This is an deferred payment
127
138
  config.etransfer = false
data/config/routes.rb CHANGED
@@ -8,6 +8,7 @@ EffectiveOrders::Engine.routes.draw do
8
8
  post :send_buyer_receipt
9
9
 
10
10
  post :cheque
11
+ post :deluxe
11
12
  post :etransfer
12
13
  post :free
13
14
  post :mark_as_paid
@@ -1,3 +1,3 @@
1
1
  module EffectiveOrders
2
- VERSION = '6.9.10'.freeze
2
+ VERSION = '6.11.0'.freeze
3
3
  end
@@ -42,7 +42,7 @@ module EffectiveOrders
42
42
  :free_enabled, :mark_as_paid_enabled, :pretend_enabled, :pretend_message, :buyer_purchases_refund,
43
43
 
44
44
  # Payment processors. false or Hash
45
- :cheque, :etransfer, :moneris, :moneris_checkout, :paypal, :phone, :refund, :stripe, :subscriptions, :trial
45
+ :cheque, :deluxe, :etransfer, :moneris, :moneris_checkout, :paypal, :phone, :refund, :stripe, :subscriptions, :trial
46
46
  ]
47
47
  end
48
48
 
@@ -81,6 +81,10 @@ module EffectiveOrders
81
81
  free_enabled == true
82
82
  end
83
83
 
84
+ def self.deluxe?
85
+ deluxe.kind_of?(Hash)
86
+ end
87
+
84
88
  def self.deferred?
85
89
  deferred_providers.present?
86
90
  end
@@ -130,7 +134,7 @@ module EffectiveOrders
130
134
  end
131
135
 
132
136
  def self.single_payment_processor?
133
- [moneris?, moneris_checkout?, paypal?, stripe?].select { |enabled| enabled }.length == 1
137
+ [deluxe?, moneris?, moneris_checkout?, paypal?, stripe?].select { |enabled| enabled }.length == 1
134
138
  end
135
139
 
136
140
  # The Effective::Order.payment_provider value must be in this collection
@@ -138,6 +142,7 @@ module EffectiveOrders
138
142
  [
139
143
  ('cheque' if cheque?),
140
144
  ('credit card' if mark_as_paid?),
145
+ ('deluxe' if deluxe?),
141
146
  ('etransfer' if etransfer?),
142
147
  ('free' if free?),
143
148
  ('moneris' if moneris?),
@@ -157,6 +162,7 @@ module EffectiveOrders
157
162
  [
158
163
  ('cheque' if mark_as_paid?),
159
164
  ('credit card' if mark_as_paid?),
165
+ ('deluxe' if deluxe?),
160
166
  ('etransfer' if etransfer?),
161
167
  #('free' if free?),
162
168
  ('moneris' if moneris?),
@@ -176,7 +182,7 @@ module EffectiveOrders
176
182
  end
177
183
 
178
184
  def self.credit_card_payment_providers
179
- ['credit card', 'moneris', 'moneris_checkout', 'paypal', 'stripe']
185
+ ['credit card', 'deluxe', 'moneris', 'moneris_checkout', 'paypal', 'stripe']
180
186
  end
181
187
 
182
188
  def self.qb_sync?
@@ -276,6 +282,10 @@ module EffectiveOrders
276
282
  stripe_plans.map { |plan| [plan[:name], plan[:id]] }
277
283
  end
278
284
 
285
+ def self.deluxe_script_url
286
+ "https://hostedform2.deluxe.com/V2/deluxe.js"
287
+ end
288
+
279
289
  def self.moneris_checkout_script_url
280
290
  case EffectiveOrders.moneris_checkout.fetch(:environment)
281
291
  when 'prod' then 'https://gateway.moneris.com/chktv2/js/chkt_v2.00.js'
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.9.10
4
+ version: 6.11.0
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-04-26 00:00:00.000000000 Z
11
+ date: 2024-05-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -189,10 +189,12 @@ files:
189
189
  - MIT-LICENSE
190
190
  - README.md
191
191
  - app/assets/config/effective_orders_manifest.js
192
+ - app/assets/images/effective_orders/deluxe.png
192
193
  - app/assets/images/effective_orders/logo.png
193
194
  - app/assets/images/effective_orders/stripe.png
194
195
  - app/assets/javascripts/effective_orders.js
195
196
  - app/assets/javascripts/effective_orders/customers.js.coffee
197
+ - app/assets/javascripts/effective_orders/providers/deluxe.js
196
198
  - app/assets/javascripts/effective_orders/providers/moneris_checkout.js.coffee
197
199
  - app/assets/javascripts/effective_orders/providers/stripe.js.coffee
198
200
  - app/assets/javascripts/effective_orders/subscriptions.js.coffee
@@ -208,6 +210,7 @@ files:
208
210
  - app/controllers/effective/customers_controller.rb
209
211
  - app/controllers/effective/orders_controller.rb
210
212
  - app/controllers/effective/providers/cheque.rb
213
+ - app/controllers/effective/providers/deluxe.rb
211
214
  - app/controllers/effective/providers/etransfer.rb
212
215
  - app/controllers/effective/providers/free.rb
213
216
  - app/controllers/effective/providers/mark_as_paid.rb
@@ -228,6 +231,7 @@ files:
228
231
  - app/datatables/admin/report_transactions_grouped_by_qb_name_datatable.rb
229
232
  - app/datatables/effective_orders_datatable.rb
230
233
  - app/helpers/effective_carts_helper.rb
234
+ - app/helpers/effective_deluxe_helper.rb
231
235
  - app/helpers/effective_moneris_checkout_helper.rb
232
236
  - app/helpers/effective_orders_helper.rb
233
237
  - app/helpers/effective_paypal_helper.rb
@@ -241,6 +245,7 @@ files:
241
245
  - app/models/effective/cart.rb
242
246
  - app/models/effective/cart_item.rb
243
247
  - app/models/effective/customer.rb
248
+ - app/models/effective/deluxe_api.rb
244
249
  - app/models/effective/order.rb
245
250
  - app/models/effective/order_item.rb
246
251
  - app/models/effective/product.rb
@@ -291,6 +296,9 @@ files:
291
296
  - app/views/effective/orders/declined.html.haml
292
297
  - app/views/effective/orders/deferred.html.haml
293
298
  - app/views/effective/orders/deferred/_form.html.haml
299
+ - app/views/effective/orders/deluxe/_css.html.haml
300
+ - app/views/effective/orders/deluxe/_element.html.haml
301
+ - app/views/effective/orders/deluxe/_form.html.haml
294
302
  - app/views/effective/orders/edit.html.haml
295
303
  - app/views/effective/orders/etransfer/_form.html.haml
296
304
  - app/views/effective/orders/free/_form.html.haml