spree_braintree_vzero 3.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (169) hide show
  1. checksums.yaml +7 -0
  2. data/.codeclimate.yml +15 -0
  3. data/.gitignore +19 -0
  4. data/.rspec +1 -0
  5. data/.travis.yml +63 -0
  6. data/Appraisals +45 -0
  7. data/CONTRIBUTING.md +28 -0
  8. data/Gemfile +14 -0
  9. data/LICENSE +26 -0
  10. data/README.md +83 -0
  11. data/Rakefile +21 -0
  12. data/app/assets/images/credit_cards/icons/jcb.png +0 -0
  13. data/app/assets/javascripts/spree/backend/payments.js +19 -0
  14. data/app/assets/javascripts/spree/backend/spree_braintree_vzero.js +81 -0
  15. data/app/assets/javascripts/spree/frontend/spree_braintree_vzero.js +68 -0
  16. data/app/assets/stylesheets/spree/backend/spree_braintree_vzero.css +5 -0
  17. data/app/controllers/spree/admin/payments_controller_decorator.rb +43 -0
  18. data/app/controllers/spree/api/v1/braintree_client_token_controller.rb +31 -0
  19. data/app/controllers/spree/checkout_controller_decorator.rb +38 -0
  20. data/app/controllers/spree/orders_controller_decorator.rb +62 -0
  21. data/app/controllers/spree/user_sessions_controller_decorator.rb +15 -0
  22. data/app/helpers/spree/admin/decorated_navigation_helper.rb +10 -0
  23. data/app/helpers/spree/admin/payment_methods_helper.rb +101 -0
  24. data/app/helpers/spree/braintree_helper.rb +27 -0
  25. data/app/models/braintree/successful_result_decorator.rb +21 -0
  26. data/app/models/spree/address_decorator.rb +18 -0
  27. data/app/models/spree/braintree_checkout.rb +105 -0
  28. data/app/models/spree/gateway/braintree_vzero_base.rb +168 -0
  29. data/app/models/spree/gateway/braintree_vzero_base/address.rb +33 -0
  30. data/app/models/spree/gateway/braintree_vzero_base/braintree_user.rb +25 -0
  31. data/app/models/spree/gateway/braintree_vzero_base/purchase_data.rb +48 -0
  32. data/app/models/spree/gateway/braintree_vzero_base/transaction.rb +39 -0
  33. data/app/models/spree/gateway/braintree_vzero_base/utils.rb +108 -0
  34. data/app/models/spree/gateway/braintree_vzero_drop_in_ui.rb +24 -0
  35. data/app/models/spree/gateway/braintree_vzero_hosted_fields.rb +28 -0
  36. data/app/models/spree/gateway/braintree_vzero_paypal_express.rb +36 -0
  37. data/app/models/spree/order_decorator.rb +153 -0
  38. data/app/models/spree/payment_decorator.rb +10 -0
  39. data/app/models/spree/payment_processing_decorator.rb +61 -0
  40. data/app/models/spree/preferences/preferable_decorator.rb +54 -0
  41. data/app/overrides/spree/admin/orders/customer_details/_form.rb +8 -0
  42. data/app/overrides/spree/admin/payment_methods/_form.rb +10 -0
  43. data/app/overrides/spree/admin/shared/_order_summary.rb +23 -0
  44. data/app/overrides/spree/admin/shared/_refunds.rb +13 -0
  45. data/app/overrides/spree/checkout/_new_user.rb +6 -0
  46. data/app/overrides/spree/checkout/_payment.rb +20 -0
  47. data/app/overrides/spree/checkout/registration.rb +6 -0
  48. data/app/overrides/spree/orders/edit.rb +6 -0
  49. data/app/overrides/spree/shared/_order_details.rb +13 -0
  50. data/app/overrides/spree/shared/_payment.rb +28 -0
  51. data/app/views/spree/admin/payment_methods/_braintree_vzero_form.html.erb +83 -0
  52. data/app/views/spree/admin/payments/source_forms/_braintree_vzero_dropin_ui.html.erb +1 -0
  53. data/app/views/spree/admin/payments/source_forms/_braintree_vzero_hosted_fields.html.erb +1 -0
  54. data/app/views/spree/admin/payments/source_forms/_braintree_vzero_paypal_express.html.erb +1 -0
  55. data/app/views/spree/admin/payments/source_forms/braintree_vzero/_payments.html.erb +87 -0
  56. data/app/views/spree/admin/payments/source_views/_braintree_vzero_dropin_ui.html.erb +1 -0
  57. data/app/views/spree/admin/payments/source_views/_braintree_vzero_hosted_fields.html.erb +1 -0
  58. data/app/views/spree/admin/payments/source_views/_braintree_vzero_paypal_express.html.erb +1 -0
  59. data/app/views/spree/admin/payments/source_views/braintree_vzero/_payment.html.erb +17 -0
  60. data/app/views/spree/braintree_vzero/_paypal_checkout.html.erb +87 -0
  61. data/app/views/spree/checkout/payment/_braintree_vzero_dropin_ui.html.erb +4 -0
  62. data/app/views/spree/checkout/payment/_braintree_vzero_hosted_fields.html.erb +4 -0
  63. data/app/views/spree/checkout/payment/_braintree_vzero_paypal_express.html.erb +4 -0
  64. data/app/views/spree/checkout/payment/braintree_vzero/_buttons.html.erb +11 -0
  65. data/app/views/spree/checkout/payment/braintree_vzero/_dropin_on_error_callback.js.erb +3 -0
  66. data/app/views/spree/checkout/payment/braintree_vzero/_dropin_on_ready_callback.js.erb +1 -0
  67. data/app/views/spree/checkout/payment/braintree_vzero/_hosted_fields_on_field_event_callback.js.erb +12 -0
  68. data/app/views/spree/checkout/payment/braintree_vzero/_hosted_fields_styles.js.erb +29 -0
  69. data/app/views/spree/checkout/payment/braintree_vzero/_payment.html.erb +84 -0
  70. data/app/views/spree/checkout/payment/braintree_vzero/_three_d_secure.html.erb +218 -0
  71. data/app/views/spree/shared/braintree_vzero/_dropin.js.erb +70 -0
  72. data/app/views/spree/shared/braintree_vzero/_hosted.js.erb +66 -0
  73. data/app/views/spree/shared/braintree_vzero/_paypal.js.erb +39 -0
  74. data/bin/rails +7 -0
  75. data/config/initializers/extend_spree_permitted_checkout_attributes.rb +5 -0
  76. data/config/initializers/prepend_helpers.rb +1 -0
  77. data/config/locales/en.yml +79 -0
  78. data/config/routes.rb +7 -0
  79. data/db/migrate/20150904143013_create_spree_braintree_checkouts.rb +9 -0
  80. data/db/migrate/20151002094655_add_braintree_id_to_spree_addresses.rb +5 -0
  81. data/db/migrate/20151018123907_add_braintree_token_and_nonce_to_spree_payments.rb +6 -0
  82. data/db/migrate/20151027135109_add_paypal_email_to_spree_braintree_checkout.rb +5 -0
  83. data/db/migrate/20151028095515_add_advanced_fraud_data_to_spree_braintree_checkout.rb +5 -0
  84. data/db/migrate/20151202095956_add_risk_id_and_risk_decision_to_spree_braintree_checkouts.rb +6 -0
  85. data/db/migrate/20151211100203_add_braintree_last_digits_and_braintree_card_type_to_spree_braintree_checkouts.rb +6 -0
  86. data/db/migrate/20160112153422_add_admin_payment_to_spree_braintree_checkout.rb +5 -0
  87. data/gemfiles/spree_3_5.gemfile +12 -0
  88. data/gemfiles/spree_3_5_spree_auth_devise.gemfile +13 -0
  89. data/gemfiles/spree_3_7.gemfile +12 -0
  90. data/gemfiles/spree_3_7_spree_auth_devise.gemfile +13 -0
  91. data/gemfiles/spree_4_0.gemfile +11 -0
  92. data/gemfiles/spree_4_0_spree_auth_devise.gemfile +12 -0
  93. data/gemfiles/spree_master.gemfile +11 -0
  94. data/gemfiles/spree_master_spree_auth_devise.gemfile +12 -0
  95. data/lib/generators/spree_braintree_vzero/install/install_generator.rb +34 -0
  96. data/lib/spree_braintree_vzero.rb +6 -0
  97. data/lib/spree_braintree_vzero/engine.rb +28 -0
  98. data/lib/spree_braintree_vzero/factories.rb +10 -0
  99. data/lib/spree_braintree_vzero/railtie.rb +8 -0
  100. data/lib/spree_braintree_vzero/version.rb +17 -0
  101. data/lib/tasks/spree_braintree_vzero.rake +6 -0
  102. data/spec/controllers/spree/api/v1/braintree_client_token_controller_spec.rb +49 -0
  103. data/spec/controllers/spree/checkout_controller_spec.rb +85 -0
  104. data/spec/controllers/spree/orders_controller_spec.rb +97 -0
  105. data/spec/factories/braintree_checkout_factory.rb +7 -0
  106. data/spec/factories/braintree_gateway_factory.rb +35 -0
  107. data/spec/factories/payment_factory_decorator.rb +28 -0
  108. data/spec/features/spree/admin/log_entries_spec.rb +63 -0
  109. data/spec/models/gateway/braintree_vzero_base/address_spec.rb +28 -0
  110. data/spec/models/gateway/braintree_vzero_base/purchase_data_spec.rb +57 -0
  111. data/spec/models/gateway/braintree_vzero_base_spec.rb +346 -0
  112. data/spec/models/gateway/braintree_vzero_dropin_ui_spec.rb +35 -0
  113. data/spec/models/gateway/braintree_vzero_hosted_fields_spec.rb +35 -0
  114. data/spec/models/spree/order_spec.rb +186 -0
  115. data/spec/spec_helper.rb +100 -0
  116. data/spec/support/vcr.rb +10 -0
  117. data/spec/vcr/Log_entries/with_a_failed_log_entry/shows_a_failed_attempt.yml +91 -0
  118. data/spec/vcr/Log_entries/with_a_successful_log_entry/shows_a_successful_attempt.yml +113 -0
  119. data/spec/vcr/Spree_Api_V1_BraintreeClientTokenController/POST_create/guest_checkout/returns_proper_json_data_when_gateway_not_specified.yml +90 -0
  120. data/spec/vcr/Spree_Api_V1_BraintreeClientTokenController/POST_create/guest_checkout/returns_proper_json_data_when_gateway_specified.yml +90 -0
  121. data/spec/vcr/Spree_Api_V1_BraintreeClientTokenController/POST_create/user_checkout/returns_proper_json_data_when_gateway_not_specified.yml +191 -0
  122. data/spec/vcr/Spree_Api_V1_BraintreeClientTokenController/POST_create/user_checkout/returns_proper_json_data_when_gateway_specified.yml +191 -0
  123. data/spec/vcr/Spree_CheckoutController/_update/braintree_payment/advanced_fraud_data_in_source_should_be_updated.yml +71 -0
  124. data/spec/vcr/Spree_CheckoutController/_update/braintree_payment/when_token_credit_card_data_in_source_should_be_updated_from_Braintree_Vault.yml +139 -0
  125. data/spec/vcr/Spree_CheckoutController/_update/braintree_paypal_express_payment/amount_in_payment_should_be_updated.yml +280 -0
  126. data/spec/vcr/Spree_Gateway_BraintreeVzeroBase/valid_credentials/_capture/captures_authorized_amount/updates_Payment_state.yml +206 -0
  127. data/spec/vcr/Spree_Gateway_BraintreeVzeroBase/valid_credentials/_credit/with_refundable_state/should_be_a_success.yml +211 -0
  128. data/spec/vcr/Spree_Gateway_BraintreeVzeroBase/valid_credentials/_credit/with_unrefundable_state/should_not_be_a_success.yml +177 -0
  129. data/spec/vcr/Spree_Gateway_BraintreeVzeroBase/valid_credentials/_purchase/data_from_admin_panel/should_include_only_essential_data.yml +78 -0
  130. data/spec/vcr/Spree_Gateway_BraintreeVzeroBase/valid_credentials/_purchase/does_not_store_Transaction_in_Vault_by_default.yml +111 -0
  131. data/spec/vcr/Spree_Gateway_BraintreeVzeroBase/valid_credentials/_purchase/returns_false_with_invalid_nonce.yml +88 -0
  132. data/spec/vcr/Spree_Gateway_BraintreeVzeroBase/valid_credentials/_purchase/returns_false_with_invalid_token.yml +88 -0
  133. data/spec/vcr/Spree_Gateway_BraintreeVzeroBase/valid_credentials/_purchase/returns_success_with_valid_token.yml +113 -0
  134. data/spec/vcr/Spree_Gateway_BraintreeVzeroBase/valid_credentials/_purchase/returns_suceess_with_valid_nonce.yml +111 -0
  135. data/spec/vcr/Spree_Gateway_BraintreeVzeroBase/valid_credentials/_purchase/using_Vault/saves_Braintree_Address_id_to_Spree_Address_when_address_is_being_saved.yml +130 -0
  136. data/spec/vcr/Spree_Gateway_BraintreeVzeroBase/valid_credentials/_purchase/using_Vault/saves_unique_Braintree_Addresses_ids.yml +143 -0
  137. data/spec/vcr/Spree_Gateway_BraintreeVzeroBase/valid_credentials/_purchase/using_Vault/sends_empty_address_id_when_address_is_already_in_vault.yml +553 -0
  138. data/spec/vcr/Spree_Gateway_BraintreeVzeroBase/valid_credentials/_purchase/using_Vault/stores_Transaction.yml +181 -0
  139. data/spec/vcr/Spree_Gateway_BraintreeVzeroBase/valid_credentials/_purchase/with_3DSecure_option_turned_on/performs_3DSecure_check.yml +112 -0
  140. data/spec/vcr/Spree_Gateway_BraintreeVzeroBase/valid_credentials/_purchase/with_3DSecure_option_turned_on/returns_error.yml +112 -0
  141. data/spec/vcr/Spree_Gateway_BraintreeVzeroBase/valid_credentials/_purchase/with_advanced_fraud_tool_enabled/returns_fraud_data.yml +111 -0
  142. data/spec/vcr/Spree_Gateway_BraintreeVzeroBase/valid_credentials/_purchase/with_advanced_fraud_tool_enabled/returns_success.yml +111 -0
  143. data/spec/vcr/Spree_Gateway_BraintreeVzeroBase/valid_credentials/_settle/settles_authorized_amount/does_not_update_Order_payment_state.yml +204 -0
  144. data/spec/vcr/Spree_Gateway_BraintreeVzeroBase/valid_credentials/_settle/settles_authorized_amount/prepares_Checkout_for_status_updating.yml +204 -0
  145. data/spec/vcr/Spree_Gateway_BraintreeVzeroBase/valid_credentials/_settle/settles_authorized_amount/submits_Transaction_for_settlement.yml +377 -0
  146. data/spec/vcr/Spree_Gateway_BraintreeVzeroBase/valid_credentials/_settle/settles_authorized_amount/updates_Payment_state.yml +203 -0
  147. data/spec/vcr/Spree_Gateway_BraintreeVzeroBase/valid_credentials/_update_states/does_not_update_completed_Checkout_on_subsequent_runs.yml +202 -0
  148. data/spec/vcr/Spree_Gateway_BraintreeVzeroBase/valid_credentials/_update_states/updates_Order_payment_state_when_Checkout_is_updated.yml +202 -0
  149. data/spec/vcr/Spree_Gateway_BraintreeVzeroBase/valid_credentials/_update_states/updates_Payment_state_when_Checkout_is_updated.yml +202 -0
  150. data/spec/vcr/Spree_Gateway_BraintreeVzeroBase/valid_credentials/_update_states/updates_payment_State.yml +202 -0
  151. data/spec/vcr/Spree_Gateway_BraintreeVzeroBase/valid_credentials/_void/with_unvoidable_state/should_not_change_payment_source_state.yml +178 -0
  152. data/spec/vcr/Spree_Gateway_BraintreeVzeroBase/valid_credentials/_void/with_voidable_state/should_change_payment_source_state_to_voided.yml +199 -0
  153. data/spec/vcr/Spree_Gateway_BraintreeVzeroBase/valid_credentials/generates_token_for_User_registered_in_Braintree.yml +356 -0
  154. data/spec/vcr/Spree_Gateway_BraintreeVzeroBase/valid_credentials/generates_token_for_new_User.yml +189 -0
  155. data/spec/vcr/Spree_Gateway_BraintreeVzeroBase/valid_credentials/generates_token_without_User.yml +89 -0
  156. data/spec/vcr/Spree_Gateway_BraintreeVzeroBase/with_invalid_credentials/raises_Braintree_error.yml +62 -0
  157. data/spec/vcr/Spree_Gateway_BraintreeVzeroBase_Address/_create/creates_Braintree_Address.yml +84 -0
  158. data/spec/vcr/Spree_Gateway_BraintreeVzeroBase_Address/_create/deletes_Braintree_Address.yml +141 -0
  159. data/spec/vcr/Spree_Gateway_BraintreeVzeroBase_Address/_create/finds_Braintree_Address.yml +148 -0
  160. data/spec/vcr/Spree_Gateway_BraintreeVzeroBase_Address/_create/updates_Braintree_Address.yml +154 -0
  161. data/spec/vcr/Spree_Order/complete_with_braintree_vzero_standard_payment/with_auto_capture/should_complete_payment.yml +116 -0
  162. data/spec/vcr/Spree_Order/complete_with_braintree_vzero_standard_payment/with_auto_capture/should_update_payment_s_response_code.yml +111 -0
  163. data/spec/vcr/Spree_Order/complete_with_braintree_vzero_standard_payment/with_auto_capture/should_update_payment_s_source_state_and_transaction_id.yml +111 -0
  164. data/spec/vcr/Spree_Order/complete_with_braintree_vzero_standard_payment/without_auto_capture/should_pend_payment.yml +111 -0
  165. data/spec/vcr/Spree_Order/complete_with_braintree_vzero_standard_payment/without_auto_capture/should_update_payment_s_response_code.yml +111 -0
  166. data/spec/vcr/Spree_Order/complete_with_braintree_vzero_standard_payment/without_auto_capture/should_update_payment_s_source_state_and_transaction_id.yml +110 -0
  167. data/spree_braintree_vzero.gemspec +52 -0
  168. data/vendor/assets/javascripts/maskedinput/jquery.maskedinput.min.js +7 -0
  169. metadata +643 -0
@@ -0,0 +1,5 @@
1
+ /*
2
+ Placeholder manifest file.
3
+ the installer will append this file to the app vendored assets here: 'vendor/assets/stylesheets/spree/backend/all.css'
4
+ */
5
+
@@ -0,0 +1,43 @@
1
+ module Spree
2
+ module Admin
3
+ module PaymentsControllerDecorator
4
+ def create
5
+ invoke_callbacks(:create, :before)
6
+ @payment ||= @order.payments.build(object_params)
7
+ # not only credit card may require source
8
+ if @payment.payment_method.source_required?
9
+ if params[:card].present? && params[:card] != 'new'
10
+ @payment.source = @payment.payment_method.payment_source_class.find_by_id(params[:card])
11
+ elsif @payment.payment_source.is_a?(Spree::Gateway::BraintreeVzeroBase)
12
+ @payment.braintree_token = params[:payment_method_token]
13
+ @payment.braintree_nonce = params[:payment_method_nonce]
14
+ @payment.source = Spree::BraintreeCheckout.create!(admin_payment: true)
15
+ end
16
+ end
17
+
18
+ begin
19
+ if @payment.save
20
+ invoke_callbacks(:create, :after)
21
+ # Transition order as far as it will go.
22
+ while @order.next; end
23
+ # If "@order.next" didn't trigger payment processing already (e.g. if the order was
24
+ # already complete) then trigger it manually now
25
+ @payment.process! if @order.completed? && @payment.checkout?
26
+ flash[:success] = flash_message_for(@payment, :successfully_created)
27
+ redirect_to admin_order_payments_path(@order)
28
+ else
29
+ invoke_callbacks(:create, :fails)
30
+ flash[:error] = Spree.t(:payment_could_not_be_created)
31
+ render :new
32
+ end
33
+ rescue Spree::Core::GatewayError => e
34
+ invoke_callbacks(:create, :fails)
35
+ flash[:error] = e.message.to_s
36
+ redirect_to new_admin_order_payment_path(@order)
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+
43
+ ::Spree::Admin::PaymentsController.prepend(Spree::Admin::PaymentsControllerDecorator)
@@ -0,0 +1,31 @@
1
+ module Spree
2
+ module Api
3
+ module V1
4
+ class BraintreeClientTokenController < Spree::Api::BaseController
5
+ skip_before_action :authenticate_user
6
+
7
+ before_action :find_order, only: :create
8
+
9
+ def create
10
+ gateway = if params[:payment_method_id]
11
+ Spree::Gateway::BraintreeVzeroBase.find(params[:payment_method_id])
12
+ else
13
+ Spree::Gateway::BraintreeVzeroBase.active.first
14
+ end
15
+
16
+ render json: {
17
+ client_token: gateway.client_token(@order, @current_api_user),
18
+ payment_method_id: gateway.id
19
+ }
20
+ end
21
+
22
+ private
23
+
24
+ # We're skipping permission check for order, because it is needed only to get a currency
25
+ def find_order
26
+ @order = Spree::Order.find_by(number: params[:order_number])
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,38 @@
1
+ module Spree
2
+ module CheckoutControllerDecorator
3
+ def self.prepended(base)
4
+ base.include Spree::BraintreeHelper
5
+ base.helper_method [:asset_available?, :options_from_braintree_payments]
6
+ base.after_action :allow_braintree_iframe
7
+ base.after_action :update_source_data, only: :update, if: proc { params[:state].eql?('payment') }
8
+ end
9
+
10
+ private
11
+
12
+ def allow_braintree_iframe
13
+ response.headers['X-Frame-Options'] = 'ALLOW-FROM https://assets.braintreegateway.com'
14
+ end
15
+
16
+ def check_registration
17
+ return unless Spree::Auth::Config[:registration_step]
18
+ return if spree_current_user || current_order.email
19
+ store_location
20
+ redirect_to spree.checkout_registration_path
21
+ end
22
+
23
+ def update_source_data
24
+ return true unless current_order
25
+ payment = current_order.payments.last
26
+ return true unless payment
27
+ source = payment.source
28
+ return true unless source.is_a?(Spree::BraintreeCheckout)
29
+ update_advanced_fraud_data(source)
30
+ end
31
+
32
+ def update_advanced_fraud_data(source)
33
+ source.update(advanced_fraud_data: params[:device_data])
34
+ end
35
+ end
36
+ end
37
+
38
+ ::Spree::CheckoutController.prepend(Spree::CheckoutControllerDecorator)
@@ -0,0 +1,62 @@
1
+ module Spree
2
+ module OrdersControllerDecorator
3
+ def self.prepended(base)
4
+ base.include Spree::BraintreeHelper
5
+ base.helper_method [:asset_available?, :options_from_braintree_payments]
6
+ base.before_action :process_paypal_express, only: :update
7
+ end
8
+
9
+ def process_paypal_express
10
+ if params[:paypal].blank? || params[:paypal][:payment_method_nonce].blank?
11
+ # when user goes back from checkout, paypal express payments should be invalidated to ensure standard checkout flow
12
+ current_order.invalidate_paypal_express_payments
13
+ return true
14
+ end
15
+ payment_method = Spree::PaymentMethod.find_by_id(params[:paypal][:payment_method_id])
16
+ return true unless payment_method
17
+
18
+ email = params[:order][:email]
19
+ # when user goes back from checkout, order's state should be resetted to ensure paypal checkout flow
20
+ current_order.state = 'cart'
21
+ payment_method.push_order_to_state(current_order, 'address', email)
22
+ current_order.save_paypal_payment(payment_params)
23
+
24
+ manage_paypal_addresses
25
+ current_order.remove_phone_number_placeholder
26
+
27
+ redirect_to checkout_state_path(current_order.state, paypal_email: email)
28
+ end
29
+
30
+ private
31
+
32
+ def address_params(key)
33
+ params[:order].require(key).permit(:firstname, :lastname, :zipcode, :city, :address1, :address2, :phone, :full_name, :country, :state)
34
+ end
35
+
36
+ def payment_params
37
+ {
38
+ braintree_nonce: params[:paypal][:payment_method_nonce],
39
+ payment_method_id: params[:paypal][:payment_method_id],
40
+ paypal_email: params[:order][:email],
41
+ advanced_fraud_data: params[:device_data]
42
+ }
43
+ end
44
+
45
+ def manage_paypal_addresses
46
+ # addresses need to be cleared for restarted checkout orders
47
+ current_order.ship_address_id = nil
48
+ current_order.bill_address_id = nil
49
+
50
+ %w(ship_address bill_address).each do |address_type|
51
+ next if params[:order][address_type].blank?
52
+ current_order.save_paypal_address(address_type, address_params(address_type))
53
+ end
54
+ current_order.set_billing_address
55
+ return false if current_order.no_phone_number?
56
+
57
+ current_order.ship_address && current_order.bill_address
58
+ end
59
+ end
60
+ end
61
+
62
+ ::Spree::OrdersController.prepend(Spree::OrdersControllerDecorator)
@@ -0,0 +1,15 @@
1
+ module Spree
2
+ module UserSessionsControllerDecorator
3
+ def self.prepended(base)
4
+ base.before_action :associate_user, only: :create
5
+ end
6
+
7
+ def current_order_params
8
+ { currency: current_currency, guest_token: cookies.signed[:guest_token], store_id: current_store.id }
9
+ end
10
+ end
11
+ end
12
+
13
+ if defined?(::Spree::UserSessionsController)
14
+ ::Spree::UserSessionsController.prepend(Spree::UserSessionsControllerDecorator)
15
+ end
@@ -0,0 +1,10 @@
1
+ module Spree
2
+ module Admin
3
+ module DecoratedNavigationHelper
4
+ def link_to_with_icon(icon_name, text, url, options = {})
5
+ icon_name = 'capture' if icon_name.eql?('settle')
6
+ super
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,101 @@
1
+ module Spree
2
+ module Admin
3
+ module PaymentMethodsHelper
4
+ def preference_field_for(form, field, options)
5
+ case options[:type]
6
+ when :integer
7
+ form.text_field(field, preference_field_options(options))
8
+ when :boolean
9
+ form.check_box(field, preference_field_options(options))
10
+ when :string
11
+ if field.eql?('preferred_descriptor_name')
12
+ content_tag(:div,
13
+ Spree.t('descriptor_name_information_box_text').html_safe,
14
+ class: 'alert alert-warning'
15
+ )
16
+ end.to_s.html_safe +
17
+ form.text_field(field, preference_field_options(options))
18
+ when :password
19
+ form.password_field(field, preference_field_options(options))
20
+ when :text
21
+ form.text_area(field, preference_field_options(options))
22
+ when :boolean_select
23
+ label_tag(field, Spree.t(field))
24
+ form.select(field, {
25
+ Spree.t(:enabled) => true,
26
+ Spree.t(:disabled) => false
27
+ },
28
+ {},
29
+ class: 'select2')
30
+ when :select
31
+ label_tag(field, Spree.t(field))
32
+ form.select(field, options_for_select(options[:values].map { |key| [I18n.t(key, scope: 'braintree.preferences'), key] }, options[:selected]), {}, class: 'select2')
33
+ else
34
+ form.text_field(field, preference_field_options(options))
35
+ end
36
+ end
37
+
38
+ def preference_field_tag(name, value, options)
39
+ case options[:type]
40
+ when :integer
41
+ text_field_tag(name, value, preference_field_options(options))
42
+ when :boolean
43
+ hidden_field_tag(name, 0, id: "#{name}_hidden") +
44
+ check_box_tag(name, 1, value, preference_field_options(options))
45
+ when :string
46
+ text_field_tag(name, value, preference_field_options(options))
47
+ when :password
48
+ password_field_tag(name, value, preference_field_options(options))
49
+ when :text
50
+ text_area_tag(name, value, preference_field_options(options))
51
+ when :boolean_select
52
+ select_tag(name, value, preference_field_options(options))
53
+ when :select
54
+ select_tag(name, value, preference_field_options(options))
55
+ else
56
+ text_field_tag(name, value, preference_field_options(options))
57
+ end
58
+ end
59
+
60
+ def preference_fields(object, form)
61
+ return unless object.respond_to?(:preferences)
62
+
63
+ get_preference_fields(object, object.preferences.keys, form)
64
+ end
65
+
66
+ def braintree_basic_preference_fields(object, form)
67
+ return unless object.respond_to?(:preferences)
68
+
69
+ keys = object.preferences.slice(*basic_braintree_preference_keys).keys
70
+ get_preference_fields(object, keys, form)
71
+ end
72
+
73
+ def braintree_advanced_preference_fields(object, form)
74
+ return unless object.respond_to?(:preferences)
75
+
76
+ keys = object.preferences.keys.reverse - basic_braintree_preference_keys
77
+ keys_left, keys_right = keys.each_slice((keys.size / 2.0).ceil).to_a
78
+
79
+ content_tag(:div, get_preference_fields(object, keys_left, form), class: 'col-md-6') +
80
+ content_tag(:div, get_preference_fields(object, keys_right, form), class: 'col-md-6')
81
+ end
82
+
83
+ def get_preference_fields(object, keys, form)
84
+ keys.reject { |k| k == :currency_merchant_accounts }.map do |key|
85
+ next unless object.has_preference?(key)
86
+
87
+ content_tag(:div, class: 'form-group', 'data-hook' => "preferred_#{key}") do
88
+ form.label("preferred_#{key}", Spree.t(key) + ': ') +
89
+ preference_field_for(form, "preferred_#{key}", type: object.preference_type(key),
90
+ values: object.send("preferred_#{key}_default").is_a?(Hash) ? object.send("preferred_#{key}_default")[:values] : nil,
91
+ selected: object.preferences[key])
92
+ end
93
+ end.join(' ').html_safe
94
+ end
95
+
96
+ def basic_braintree_preference_keys
97
+ %i[merchant_id public_key private_key server test_mode]
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,27 @@
1
+ module Spree
2
+ module BraintreeHelper
3
+ def options_from_braintree_payments(payment_methods, include_empty = false)
4
+ additional_options = if include_empty
5
+ ["<option value=''>#{t('braintree.checkout.blank_saved_payment_method')}</option>"]
6
+ else
7
+ []
8
+ end
9
+ additional_options + payment_methods.map do |method|
10
+ text = if method.is_a?(Braintree::CreditCard)
11
+ Spree.t('admin.vaulted_payments.credit_card', card_type: method.card_type, last_4: method.last_4)
12
+ elsif method.is_a?(Braintree::PayPalAccount)
13
+ Spree.t('admin.vaulted_payments.paypal', email: method.email)
14
+ end
15
+ "<option value='#{method.token}'>#{text}</option>"
16
+ end.join.html_safe
17
+ end
18
+
19
+ def asset_available?(logical_path)
20
+ if Rails.configuration.assets.compile
21
+ Rails.application.precompiled_assets.include? logical_path
22
+ else
23
+ Rails.application.assets_manifest.assets[logical_path].present?
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,21 @@
1
+ module Braintree
2
+ module SuccessfulResultDecorator
3
+ def self.prepended(base)
4
+ base.mattr_reader :authorization, :message
5
+ end
6
+
7
+ def authorization
8
+ transaction.id
9
+ end
10
+
11
+ def avs_result
12
+ { code: transaction.avs_street_address_response_code }.with_indifferent_access
13
+ end
14
+
15
+ def cvv_result
16
+ { code: transaction.cvv_response_code, message: nil }.with_indifferent_access
17
+ end
18
+ end
19
+ end
20
+
21
+ ::Braintree::SuccessfulResult.prepend(Braintree::SuccessfulResultDecorator)
@@ -0,0 +1,18 @@
1
+ module Spree
2
+ module AddressDecorator
3
+ def self.prepended(base)
4
+ base.scope :vaulted_duplicates, ->(address) do
5
+ where.not(id: address.id).
6
+ where.not(braintree_id: nil).
7
+ where(address.attributes.except('id', 'updated_at', 'created_at', 'braintree_id'))
8
+ end
9
+ end
10
+
11
+ def same_as?(other)
12
+ return false if other.nil?
13
+ attributes.except('id', 'updated_at', 'created_at', 'braintree_id') == other.attributes.except('id', 'updated_at', 'created_at', 'braintree_id')
14
+ end
15
+ end
16
+ end
17
+
18
+ ::Spree::Address.prepend(Spree::AddressDecorator)
@@ -0,0 +1,105 @@
1
+ module Spree
2
+ class BraintreeCheckout < ActiveRecord::Base
3
+ scope :in_state, ->(state) { where(state: state) }
4
+ scope :not_in_state, ->(state) { where.not(state: state) }
5
+
6
+ after_commit :update_payment_and_order
7
+
8
+ FINAL_STATES = %w(authorization_expired processor_declined gateway_rejected failed voided settled settlement_declined refunded released).freeze
9
+
10
+ has_one :payment, foreign_key: :source_id, as: :source, class_name: 'Spree::Payment'
11
+ has_one :order, through: :payment
12
+
13
+ def self.create_from_params(params)
14
+ type = braintree_card_type_to_spree(params[:braintree_card_type])
15
+ create!(paypal_email: params[:paypal_email],
16
+ braintree_last_digits: params[:braintree_last_two],
17
+ braintree_card_type: type)
18
+ end
19
+
20
+ def self.create_from_token(token, payment_method_id)
21
+ gateway = Spree::PaymentMethod.find(payment_method_id)
22
+ vaulted_payment_method = gateway.vaulted_payment_method(token)
23
+ type = braintree_card_type_to_spree(vaulted_payment_method.try(:card_type))
24
+ create!(paypal_email: vaulted_payment_method.try(:email),
25
+ braintree_last_digits: vaulted_payment_method.try(:last_4),
26
+ braintree_card_type: type)
27
+ end
28
+
29
+ def self.update_states
30
+ braintree = Gateway::BraintreeVzeroBase.first.provider
31
+ result = { changed: 0, unchanged: 0 }
32
+ not_in_state(FINAL_STATES).find_each do |checkout|
33
+ checkout.state = braintree::Transaction.find(checkout.transaction_id).status
34
+ if checkout.state_changed?
35
+ result[:changed] += 1
36
+ checkout.save
37
+ else
38
+ result[:unchanged] += 1
39
+ end
40
+ end
41
+ result
42
+ end
43
+
44
+ def update_state
45
+ status = Transaction.new(Gateway::BraintreeVzeroBase.first.provider, transaction_id).status
46
+ payment.send(payment_action(status))
47
+ status
48
+ end
49
+
50
+ def actions
51
+ %w(void settle credit)
52
+ end
53
+
54
+ def can_void?(_payment)
55
+ %w(authorized submitted_for_settlement).include? state
56
+ end
57
+
58
+ def can_settle?(_)
59
+ %w(authorized).include? state
60
+ end
61
+
62
+ def can_credit?(_payment)
63
+ %w(settled settling).include? state
64
+ end
65
+
66
+ private
67
+
68
+ def update_payment_and_order
69
+ return unless (changes = previous_changes[:state])
70
+ return unless changes[0] != changes[1]
71
+ return unless payment
72
+
73
+ utils = Gateway::BraintreeVzeroBase::Utils.new(Gateway::BraintreeVzeroBase.first, order)
74
+ payment_state = utils.map_payment_status(state)
75
+ payment.send(payment_action(payment_state))
76
+ end
77
+
78
+ def self.braintree_card_type_to_spree(type)
79
+ return '' unless type
80
+ case type
81
+ when 'AmericanExpress'
82
+ 'american_express'
83
+ when 'Diners Club'
84
+ 'diners_club'
85
+ when 'MasterCard'
86
+ 'master'
87
+ else
88
+ type.downcase
89
+ end
90
+ end
91
+
92
+ def payment_action(state)
93
+ case state
94
+ when 'pending'
95
+ 'pend'
96
+ when 'void'
97
+ 'void'
98
+ when 'completed'
99
+ 'complete'
100
+ else
101
+ 'failure'
102
+ end
103
+ end
104
+ end
105
+ end