effective_orders 5.0.1 → 5.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +67 -1
  3. data/app/assets/config/effective_orders_manifest.js +3 -0
  4. data/app/assets/images/effective_orders/logo.png +0 -0
  5. data/app/assets/javascripts/effective_orders.js +1 -1
  6. data/app/assets/javascripts/effective_orders/providers/moneris_checkout.js.coffee +71 -0
  7. data/app/assets/stylesheets/effective_orders/_order.scss +4 -0
  8. data/app/controllers/effective/concerns/purchase.rb +14 -21
  9. data/app/controllers/effective/orders_controller.rb +2 -2
  10. data/app/controllers/effective/providers/free.rb +0 -1
  11. data/app/controllers/effective/providers/mark_as_paid.rb +1 -2
  12. data/app/controllers/effective/providers/moneris_checkout.rb +59 -0
  13. data/app/controllers/effective/providers/pretend.rb +1 -2
  14. data/app/controllers/effective/providers/refund.rb +1 -2
  15. data/app/controllers/effective/providers/stripe.rb +1 -2
  16. data/app/datatables/admin/effective_orders_datatable.rb +5 -8
  17. data/app/datatables/effective_orders_datatable.rb +3 -7
  18. data/app/helpers/effective_moneris_checkout_helper.rb +65 -0
  19. data/app/helpers/effective_orders_helper.rb +3 -26
  20. data/app/mailers/effective/orders_mailer.rb +6 -4
  21. data/app/models/concerns/acts_as_purchasable.rb +2 -0
  22. data/app/models/concerns/acts_as_subscribable.rb +1 -0
  23. data/app/models/concerns/acts_as_subscribable_buyer.rb +2 -1
  24. data/app/models/effective/order.rb +56 -35
  25. data/app/models/effective/order_item.rb +10 -0
  26. data/app/models/effective/product.rb +2 -0
  27. data/app/views/effective/orders/_checkout_step2.html.haml +3 -0
  28. data/app/views/effective/orders/_order.html.haml +1 -1
  29. data/app/views/effective/orders/_order_actions.html.haml +1 -1
  30. data/app/views/effective/orders/_order_footer.html.haml +3 -1
  31. data/app/views/effective/orders/_order_header.html.haml +4 -20
  32. data/app/views/effective/orders/_order_payment.html.haml +7 -18
  33. data/app/views/effective/orders/declined.html.haml +2 -4
  34. data/app/views/effective/orders/deferred.html.haml +2 -4
  35. data/app/views/effective/orders/moneris_checkout/_element.html.haml +4 -0
  36. data/app/views/effective/orders/moneris_checkout/_form.html.haml +23 -0
  37. data/app/views/effective/orders/purchased.html.haml +2 -4
  38. data/app/views/effective/orders/show.html.haml +5 -2
  39. data/config/effective_orders.rb +19 -0
  40. data/config/routes.rb +1 -0
  41. data/lib/effective_orders.rb +23 -2
  42. data/lib/effective_orders/engine.rb +2 -2
  43. data/lib/effective_orders/version.rb +1 -1
  44. data/lib/generators/templates/effective_orders_mailer_preview.rb +6 -0
  45. metadata +69 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 72eca7d0655eb6a817f703d2787d7a211d5b9da7889aaf1920ec79edb49384b7
4
- data.tar.gz: ff602f25ce5ab9294c292e98060d505da015b6fb38c57f11463705d404871183
3
+ metadata.gz: 31540d5d07a5c2afc891284cd037d88d25ea6aba9db57073a53b5dd60c952a11
4
+ data.tar.gz: 502ad51ce45452ff307292de9d2f16c934a7e1a0aca7c62376272b1f3ede07b0
5
5
  SHA512:
6
- metadata.gz: 251acbaf892b9f51e0fd6ebd739d1a64538b6a597f071afcd4d2c0fd3b4b839c9f4423594620ae31ed7ce4dc8c92a79bbe30e7d6f2d07a3ab29607eab04accbe
7
- data.tar.gz: bd8a5fd153eba267e138a32f598e01888c427fde5171327cc6271b8a1d7dcea7931f2d4b3187981f7bcea1e3e397cef4aafedd00aa64504c66b221f0deadd04b
6
+ metadata.gz: 7541296d5ee6f6d52dc2edf94938f28734bf8da046612c46b0de23ee51fcd70c2eb80a32dd53534d16ffd3a9de72a923de9fffa709c4b96651ccf49fe7779b9b
7
+ data.tar.gz: 7be888605850e6dda6b952bf6ba9cceb575577b3d18ded8c118c8028890715bf41c8d8ded387de1bd5378c38207329af61709d071f2a80eec528ce6c7e701164
data/README.md CHANGED
@@ -594,8 +594,74 @@ You will need an external IP address to work with these sandboxes.
594
594
 
595
595
  We suggest the free application `https://ngrok.com/` for this ability.
596
596
 
597
+ ## Paying with Moneris Checkout
597
598
 
598
- ## Paying via Moneris
599
+ Use the following instructions to set up a Moneris Checkout store.
600
+
601
+ This is the javascript / pay in place form implementation.
602
+
603
+ We do not use or implement tokenization of credentials with Moneris Checkout.
604
+
605
+ We are also going to use ngrok to give us a public facing URL
606
+
607
+ ### Create Test / Development Store
608
+
609
+ Visit https://esqa.moneris.com/mpg/ and login with: demouser / store1 / password
610
+
611
+ - Select Admin -> Moneris Checkout Config from the menu
612
+ - Click Create Profile
613
+
614
+ Checkout Type: I have my custom order form and want to use Moneris simply for payment processing
615
+
616
+ Multi-Currency: None
617
+
618
+ Payment:
619
+
620
+ - Google Pay: No
621
+ - Card Logos: Yes
622
+ - Payment Security: CVV
623
+ - Transaction Type: Purchase
624
+ - Transaction Limits: None
625
+
626
+ Branding & Design
627
+
628
+ - Logo Url: None
629
+ - Colors: Default
630
+
631
+ Customizations
632
+
633
+ - Enable Fullscreen: No false (important)
634
+ - Card Borders/Shadows: Yes
635
+
636
+ Order Confirmation
637
+
638
+ - Order Confirmation Processing: Use Moneris
639
+ - Confirmation Page Content: Check all
640
+
641
+ Email Communications
642
+
643
+ - None
644
+ - Customer Emails: None
645
+
646
+
647
+ Now copy the Checkout id, something like `chktJF76Btore1` into the config/initializers/effective_orders.rb file.
648
+
649
+ For the store_id and api_token values, you can use
650
+
651
+ ```
652
+ config.moneris_checkout = {
653
+ environment: 'qa',
654
+ store_id: 'store1',
655
+ api_token: 'yesguy1',
656
+ checkout_id: '', # You need to generate this one
657
+ }
658
+ ```
659
+
660
+ [Testing a Solution](https://developer.moneris.com/en/More/Testing/Testing%20a%20Solution)
661
+
662
+
663
+
664
+ ## Paying via Moneris (hosted pay page - old)
599
665
 
600
666
  Use the following instructions to set up a Moneris TEST store.
601
667
 
@@ -0,0 +1,3 @@
1
+ //= link_directory ../javascripts .js
2
+ //= link_directory ../stylesheets .css
3
+ //= link_directory ../images/effective_orders
@@ -2,5 +2,5 @@
2
2
 
3
3
  //= require ./effective_orders/customers
4
4
  //= require ./effective_orders/subscriptions
5
- //= require_tree ./effective_orders/providers
6
5
 
6
+ //= require_tree ./effective_orders/providers
@@ -0,0 +1,71 @@
1
+ this.MonerisCheckoutForm ||= class MonerisCheckoutForm
2
+ constructor: ->
3
+ @form = null
4
+ @data = null
5
+ @moneris = null
6
+
7
+ initialize: ->
8
+ @form = $('form[data-moneris-checkout-form]:not(.initialized)').first()
9
+ return false unless @form.length > 0
10
+
11
+ @data = @form.data('moneris-checkout-form')
12
+ @moneris = new monerisCheckout()
13
+
14
+ @mount()
15
+ @form.addClass('initialized')
16
+
17
+ mount: ->
18
+ @moneris.setCheckoutDiv('monerisCheckout')
19
+
20
+ @moneris.setCallback('page_loaded', @pageLoaded)
21
+ @moneris.setCallback('cancel_transaction', @cancelTransaction)
22
+ @moneris.setCallback('error_event', @errorEvent)
23
+ @moneris.setCallback('payment_receipt', @paymentReceipt)
24
+ @moneris.setCallback('payment_complete', @paymentComplete)
25
+
26
+ @moneris.setMode(@data.environment)
27
+ @moneris.startCheckout(@data.ticket)
28
+
29
+ success: (payload) ->
30
+ @moneris.closeCheckout()
31
+
32
+ payment = JSON.parse(payload)
33
+ @form.find('#moneris-checkout-success').html("Transaction complete! Please wait a moment...")
34
+ @form.find("input[name$='[ticket]']").first().val(payment['ticket'])
35
+ @form.submit()
36
+
37
+ error: (text) ->
38
+ @moneris.closeCheckout()
39
+
40
+ text = "<p>" + text + "<p>Please <a href='#', onclick='window.location.reload();' class='alert-link'>reload the page</a> and try again.</p>"
41
+ @form.find('#moneris-checkout-error').addClass('alert').html(text)
42
+ EffectiveForm.invalidate(@form)
43
+
44
+ # Moneris iframe has loaded
45
+ pageLoaded: (payload) =>
46
+ preload = JSON.parse(payload)
47
+
48
+ switch preload['response_code']
49
+ when '001'
50
+ true # Success. Nothing to do.
51
+ when '902'
52
+ @error('3-D secure failed on response')
53
+ when '2001'
54
+ @error('Invalid ticket/ticket re-use')
55
+ else
56
+ @error('Unknown payment gateway preload status')
57
+
58
+ # Cancel Button
59
+ cancelTransaction: (payload) => false
60
+
61
+ # Any kind of error
62
+ errorEvent: (payload) =>
63
+ error = JSON.parse(payload)
64
+ @error("A payment gateway error #{error['response_code']} has occurred. Your card has not been charged.")
65
+
66
+ # Payment is all done
67
+ paymentReceipt: (payload) => @success(payload)
68
+ paymentComplete: (payload) => @success(payload)
69
+
70
+ $ -> (new MonerisCheckoutForm()).initialize()
71
+ $(document).on 'turbolinks:load', -> (new MonerisCheckoutForm()).initialize()
@@ -45,6 +45,10 @@ form.new_effective_order {
45
45
  a.remove_fields.dynamic { float: right; }
46
46
  }
47
47
 
48
+ .effective-moneris-checkout {
49
+ height: 640px;
50
+ }
51
+
48
52
  @media print {
49
53
  .effective-orders-page-content { display: none; }
50
54
 
@@ -5,28 +5,21 @@ module Effective
5
5
 
6
6
  protected
7
7
 
8
- def order_purchased(payment:, provider:, card: 'none', email: true, skip_buyer_validations: false, purchased_url: nil, declined_url: nil)
9
- begin
10
- @order.purchase!(payment: payment, provider: provider, card: card, email: email, skip_buyer_validations: skip_buyer_validations)
11
-
12
- Effective::Cart.where(user: @order.user).destroy_all
13
-
14
- if flash[:success].blank?
15
- if EffectiveOrders.mailer[:send_order_receipt_to_buyer] && email
16
- flash[:success] = "Payment successful! A receipt has been sent to #{@order.emails_send_to}"
17
- else
18
- flash[:success] = "Payment successful! An email receipt has not been sent."
19
- end
20
- end
8
+ def order_purchased(payment:, provider:, card: 'none', email: true, skip_buyer_validations: false, purchased_url: nil)
9
+ @order.purchase!(payment: payment, provider: provider, card: card, email: email, skip_buyer_validations: skip_buyer_validations)
21
10
 
22
- purchased_url ||= effective_orders.purchased_order_path(':id')
23
- redirect_to purchased_url.gsub(':id', @order.to_param.to_s)
24
- rescue => e
25
- flash[:danger] = "An error occurred while processing your payment: #{e.message}. Please try again."
11
+ Effective::Cart.where(user: @order.user).destroy_all
26
12
 
27
- declined_url ||= effective_orders.declined_order_path(':id')
28
- redirect_to declined_url.gsub(':id', @order.to_param.to_s)
13
+ if flash[:success].blank?
14
+ if email && EffectiveOrders.mailer[:send_order_receipt_to_buyer]
15
+ flash[:success] = "Payment successful! A receipt has been sent to #{@order.emails_send_to}"
16
+ else
17
+ flash[:success] = "Payment successful! An email receipt has not been sent."
18
+ end
29
19
  end
20
+
21
+ purchased_url = effective_orders.purchased_order_path(':id') if purchased_url.blank?
22
+ redirect_to purchased_url.gsub(':id', @order.to_param.to_s)
30
23
  end
31
24
 
32
25
  def order_deferred(provider:, email: true, deferred_url: nil)
@@ -42,7 +35,7 @@ module Effective
42
35
  end
43
36
  end
44
37
 
45
- deferred_url ||= effective_orders.deferred_order_path(':id')
38
+ deferred_url = effective_orders.deferred_order_path(':id') if deferred_url.blank?
46
39
  redirect_to deferred_url.gsub(':id', @order.to_param.to_s)
47
40
  end
48
41
 
@@ -53,7 +46,7 @@ module Effective
53
46
  flash[:danger] = 'Payment was unsuccessful. Your credit card was declined by the payment processor. Please try again.'
54
47
  end
55
48
 
56
- declined_url ||= effective_orders.declined_order_path(':id')
49
+ declined_url = effective_orders.declined_order_path(':id') if declined_url.blank?
57
50
  redirect_to declined_url.gsub(':id', @order.to_param.to_s)
58
51
  end
59
52
 
@@ -1,19 +1,19 @@
1
1
  module Effective
2
2
  class OrdersController < ApplicationController
3
+ include Effective::CrudController
3
4
  include Concerns::Purchase
4
5
 
5
6
  include Providers::Cheque
6
7
  include Providers::Free
7
8
  include Providers::MarkAsPaid
8
9
  include Providers::Moneris
10
+ include Providers::MonerisCheckout
9
11
  include Providers::Paypal
10
12
  include Providers::Phone
11
13
  include Providers::Pretend
12
14
  include Providers::Refund
13
15
  include Providers::Stripe
14
16
 
15
- include Effective::CrudController
16
-
17
17
  if (config = EffectiveOrders.layout)
18
18
  layout(config.kind_of?(Hash) ? (config[:orders] || config[:application]) : config)
19
19
  end
@@ -21,7 +21,6 @@ module Effective
21
21
  provider: 'free',
22
22
  card: 'none',
23
23
  purchased_url: free_params[:purchased_url],
24
- declined_url: free_params[:declined_url],
25
24
  email: false
26
25
  )
27
26
  end
@@ -19,8 +19,7 @@ module Effective
19
19
  card: mark_as_paid_params[:payment_card],
20
20
  email: @order.send_mark_as_paid_email_to_buyer?,
21
21
  skip_buyer_validations: true,
22
- purchased_url: effective_orders.admin_order_path(@order),
23
- declined_url: effective_orders.admin_order_path(@order)
22
+ purchased_url: effective_orders.admin_order_path(@order)
24
23
  )
25
24
  end
26
25
 
@@ -0,0 +1,59 @@
1
+ module Effective
2
+ module Providers
3
+ module MonerisCheckout
4
+ extend ActiveSupport::Concern
5
+
6
+ def moneris_checkout
7
+ raise('moneris_checkout provider is not available') unless EffectiveOrders.moneris_checkout?
8
+
9
+ @order = Order.find(params[:id])
10
+ (EffectiveResources.authorize!(self, :update, @order) rescue false)
11
+
12
+ payment = moneris_checkout_receipt_request(moneris_checkout_params[:ticket])
13
+ purchased = (1..49).include?(payment['response_code'].to_i) # Must be > 0 and < 50 to be valid. Sometimes we get the string 'null'
14
+
15
+ if purchased == false
16
+ return order_declined(
17
+ payment: payment,
18
+ provider: 'moneris_checkout',
19
+ declined_url: moneris_checkout_params[:declined_url]
20
+ )
21
+ end
22
+
23
+ order_purchased(
24
+ payment: payment,
25
+ provider: 'moneris_checkout',
26
+ card: payment['card_type'],
27
+ purchased_url: moneris_checkout_params[:purchased_url]
28
+ )
29
+ end
30
+
31
+ private
32
+
33
+ def moneris_checkout_params
34
+ params.require(:moneris_checkout).permit(:ticket, :purchased_url, :declined_url)
35
+ end
36
+
37
+ def moneris_checkout_receipt_request(ticket)
38
+ params = {
39
+ environment: EffectiveOrders.moneris_checkout.fetch(:environment),
40
+
41
+ api_token: EffectiveOrders.moneris_checkout.fetch(:api_token),
42
+ store_id: EffectiveOrders.moneris_checkout.fetch(:store_id),
43
+ checkout_id: EffectiveOrders.moneris_checkout.fetch(:checkout_id),
44
+
45
+ action: :receipt,
46
+ ticket: ticket
47
+ }
48
+
49
+ response = Effective::Http.post(EffectiveOrders.moneris_request_url, params: params)
50
+ response = response['response'] if response
51
+
52
+ raise("moneris receipt error #{response}") unless response && response['success'].to_s == 'true'
53
+
54
+ response.dig('receipt', 'cc') || response.dig('receipt', 'gift') || response
55
+ end
56
+
57
+ end
58
+ end
59
+ end
@@ -14,8 +14,7 @@ module Effective
14
14
  payment: 'for pretend',
15
15
  provider: 'pretend',
16
16
  card: 'none',
17
- purchased_url: pretend_params[:purchased_url],
18
- declined_url: pretend_params[:declined_url]
17
+ purchased_url: pretend_params[:purchased_url]
19
18
  )
20
19
  end
21
20
 
@@ -21,8 +21,7 @@ module Effective
21
21
  order_purchased(
22
22
  payment: 'refund. no payment required.',
23
23
  provider: 'refund',
24
- purchased_url: refund_params[:purchased_url],
25
- declined_url: refund_params[:declined_url]
24
+ purchased_url: refund_params[:purchased_url]
26
25
  )
27
26
  end
28
27
 
@@ -26,8 +26,7 @@ module Effective
26
26
  payment: payment,
27
27
  provider: 'stripe',
28
28
  card: payment[:card],
29
- purchased_url: stripe_params[:purchased_url],
30
- declined_url: stripe_params[:declined_url]
29
+ purchased_url: stripe_params[:purchased_url]
31
30
  )
32
31
  end
33
32
 
@@ -14,12 +14,12 @@ class Admin::EffectiveOrdersDatatable < Effective::Datatable
14
14
  end
15
15
 
16
16
  filters do
17
- if attributes[:user_id].blank? && attributes[:parent_id].blank?
18
- scope :purchased, default: true
17
+ unless attributes[:skip_filters]
18
+ scope :all
19
+ scope :purchased
19
20
  scope :deferred
20
21
  scope :refunds
21
22
  scope :not_purchased
22
- scope :all
23
23
  end
24
24
  end
25
25
 
@@ -84,13 +84,14 @@ class Admin::EffectiveOrdersDatatable < Effective::Datatable
84
84
  end
85
85
 
86
86
  collection do
87
- scope = Effective::Order.all.includes(:addresses, :order_items, :user)
87
+ scope = Effective::Order.all.deep
88
88
 
89
89
  if EffectiveOrders.orders_collection_scope.respond_to?(:call)
90
90
  scope = EffectiveOrders.orders_collection_scope.call(scope)
91
91
  end
92
92
 
93
93
  if attributes[:user_id].present?
94
+ user = current_user.class.find(attributes[:user_id])
94
95
  scope = scope.where(user: user)
95
96
  end
96
97
 
@@ -101,8 +102,4 @@ class Admin::EffectiveOrdersDatatable < Effective::Datatable
101
102
  scope
102
103
  end
103
104
 
104
- def user
105
- @user ||= current_user.class.find(attributes[:user_id])
106
- end
107
-
108
105
  end
@@ -6,8 +6,6 @@ class EffectiveOrdersDatatable < Effective::Datatable
6
6
  scope :purchased, default: true
7
7
  scope :deferred
8
8
  scope :refunds
9
- scope :not_purchased
10
- scope :all
11
9
  end
12
10
  end
13
11
 
@@ -61,7 +59,9 @@ class EffectiveOrdersDatatable < Effective::Datatable
61
59
  end
62
60
 
63
61
  collection do
64
- scope = Effective::Order.all.where(user: user).includes(:addresses, :order_items, :user)
62
+ user = current_user.class.find(attributes[:user_id])
63
+
64
+ scope = Effective::Order.all.deep.where(user: user)
65
65
 
66
66
  if EffectiveOrders.orders_collection_scope.respond_to?(:call)
67
67
  scope = EffectiveOrders.orders_collection_scope.call(scope)
@@ -78,8 +78,4 @@ class EffectiveOrdersDatatable < Effective::Datatable
78
78
  scope
79
79
  end
80
80
 
81
- def user
82
- @user ||= current_user.class.find(attributes[:user_id])
83
- end
84
-
85
81
  end