spree_braintree_vzero 3.5.1

Sign up to get free protection for your applications and to get access to all the features.
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