solidus_braintree 0.2.1 → 1.0.0

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 (42) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +18 -2
  3. data/CHANGELOG.md +2 -0
  4. data/Gemfile +6 -1
  5. data/README.md +7 -0
  6. data/app/controllers/spree/api/braintree_client_token_controller.rb +2 -0
  7. data/app/helpers/braintree_view_helpers.rb +20 -0
  8. data/app/models/concerns/solidus_braintree/add_name_validation_concern.rb +8 -0
  9. data/app/models/concerns/solidus_braintree/inject_device_data_concern.rb +18 -0
  10. data/app/models/concerns/solidus_braintree/payment_braintree_nonce_concern.rb +8 -0
  11. data/app/models/concerns/solidus_braintree/permitted_attributes_concern.rb +11 -0
  12. data/app/models/concerns/solidus_braintree/skip_require_card_numbers_concern.rb +14 -0
  13. data/app/models/concerns/solidus_braintree/use_data_field_concern.rb +23 -0
  14. data/app/models/payment_decorator.rb +1 -0
  15. data/app/models/solidus/gateway/braintree_gateway.rb +46 -5
  16. data/app/overrides/spree/checkout/_confirm/braintree_security.html.erb.deface +9 -0
  17. data/app/views/spree/admin/payments/source_forms/_braintree.html.erb +38 -0
  18. data/app/views/spree/admin/payments/source_views/_braintree.html.erb +30 -0
  19. data/app/views/spree/checkout/payment/_braintree.html.erb +55 -0
  20. data/app/views/spree/checkout/payment/_braintree_initialization.html.erb +12 -0
  21. data/config/initializers/braintree.rb +1 -0
  22. data/config/locales/en.yml +4 -0
  23. data/db/migrate/20150910170527_add_data_to_credit_card.rb +1 -1
  24. data/db/migrate/20160426221931_add_braintree_device_data_to_order.rb +5 -0
  25. data/lib/assets/javascripts/spree/backend/braintree/solidus_braintree.js +15 -12
  26. data/lib/assets/javascripts/spree/frontend/braintree/solidus_braintree.js +144 -0
  27. data/lib/assets/javascripts/vendor/braintree.js +8 -0
  28. data/lib/assets/stylesheets/spree/frontend/solidus_braintree.scss +26 -0
  29. data/lib/generators/solidus_braintree/install/install_generator.rb +9 -0
  30. data/lib/solidus_braintree/version.rb +1 -1
  31. data/solidus_braintree.gemspec +9 -6
  32. metadata +78 -22
  33. data/Guardfile +0 -70
  34. data/app/models/concerns/add_name_validation_concern.rb +0 -6
  35. data/app/models/concerns/payment_braintree_nonce_concern.rb +0 -6
  36. data/app/models/concerns/permitted_attributes_concern.rb +0 -5
  37. data/app/models/concerns/skip_require_card_numbers_concern.rb +0 -12
  38. data/app/models/concerns/use_data_field_concern.rb +0 -21
  39. data/app/overrides/admin_payment_add_braintree_dropin.rb +0 -10
  40. data/app/overrides/admin_payment_remove_credit_card_fields.rb +0 -5
  41. data/lib/assets/javascripts/vendor/braintree-2.14.0.js +0 -7
  42. data/out.json +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b4a399277a93c8efbab357b8e7c04718847b14ce
4
- data.tar.gz: 4d338a2564ad5627d7471441c717a401079e6c9d
3
+ metadata.gz: 28f5f6d8e3a10296a22d9b5d9bf9f96f7146b5bb
4
+ data.tar.gz: f4718eedf1bcc717672fc6b97ff5cf9542f48566
5
5
  SHA512:
6
- metadata.gz: 9604aba646ce971377fe38ad54a76ee9d0186f4211903cecbf9af5b381b956f9b4348c4d54df501c7e2adb2121ede7ae2be8a36faf498628fadcbdd5936d15e9
7
- data.tar.gz: 406707b29a747f948fcbaf0f9b2897d03b11a0104f4bae9208b9ac1b35cad1c9ddceeb8a83c7973a39aff88ad3a2453c4cf95a216aa71cdd92cd83a12b9658d2
6
+ metadata.gz: 54b07c41d17e102b686ff466b4361b07bc6b0b051c53953ee229ea163e05b0dc19f8841e9f866c5ac9bc169b8098f816ae7e3e39fd0d6dff12fc773343c7e4fc
7
+ data.tar.gz: 7875589e8a6034511cbeed423e44f83cd9adb66de4bea5f94fe5b5d45bbd7ced33c7f58f1f4dce6a339e36979a11afd8224f6582f4c0d55adf8768ec0a119ffa
data/.travis.yml CHANGED
@@ -1,4 +1,20 @@
1
+ sudo: false
2
+ cache: bundler
1
3
  language: ruby
4
+ addons:
5
+ postgresql: "9.3"
6
+ env:
7
+ matrix:
8
+ - SOLIDUS_BRANCH=v1.1 DB=postgres
9
+ - SOLIDUS_BRANCH=v1.2 DB=postgres
10
+ - SOLIDUS_BRANCH=v1.3 DB=postgres
11
+ - SOLIDUS_BRANCH=master DB=postgres
12
+ - SOLIDUS_BRANCH=v1.1 DB=mysql
13
+ - SOLIDUS_BRANCH=v1.2 DB=mysql
14
+ - SOLIDUS_BRANCH=v1.3 DB=mysql
15
+ - SOLIDUS_BRANCH=master DB=mysql
16
+ script:
17
+ - bundle exec rake test_app
18
+ - bundle exec rspec
2
19
  rvm:
3
- - 2.1.6
4
- before_install: gem install bundler -v 1.10.5
20
+ - 2.1.8
data/CHANGELOG.md CHANGED
@@ -1,5 +1,7 @@
1
1
  ## HEAD
2
2
 
3
+ * Upgrade braintree to 2.23.0 for bug fixes and fraud detection with deviceData.
4
+
3
5
  ## 0.2.0
4
6
 
5
7
  * Add functionality around PayPal by adding a json or text field on credit card for storing extra data coming back from Braintree.
data/Gemfile CHANGED
@@ -1,5 +1,10 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in solidus_braintree.gemspec
4
- gem "solidus", github: "solidusio/solidus", branch: "master"
4
+ branch = ENV.fetch('SOLIDUS_BRANCH', 'master')
5
+ gem "solidus", github: "solidusio/solidus", branch: branch
6
+
7
+ gem 'pg'
8
+ gem 'mysql2'
9
+
5
10
  gemspec
data/README.md CHANGED
@@ -19,6 +19,13 @@ And then execute:
19
19
  $ bundle
20
20
  $ bundle exec rails g solidus_braintree:install
21
21
 
22
+
23
+ ## Fraud detection
24
+
25
+ This gem has support for the advanced fraud tools flow from Braintree, to activate
26
+ fully the associated Braintree account must enable advanced fraud tools in the
27
+ Control Panel.
28
+
22
29
  ## Usage
23
30
 
24
31
  This gem extends your solidus application by adding a `POST /api/payment_client_token` endpoint to you application to generate Braintree payment client token. This endpoint requires an authentication token in your request header.
@@ -1,4 +1,6 @@
1
1
  class Spree::Api::BraintreeClientTokenController < Spree::Api::BaseController
2
+ skip_before_action :authenticate_user
3
+
2
4
  def create
3
5
  if params[:payment_method_id]
4
6
  gateway = Solidus::Gateway::BraintreeGateway.find_by!(id: params[:payment_method_id])
@@ -0,0 +1,20 @@
1
+ module BraintreeViewHelpers
2
+ # Returns a link to the Braintree web UI for the given Braintree +payment_id+
3
+ # (e.g. from Spree::Payment#response_code), or just the +payment_id+ if
4
+ # Braintree's merchant ID is not configured or +payment_id+ is blank
5
+ def braintree_transaction_link(payment_id)
6
+ env = ENV['BRAINTREE_ENV'] == 'production' ? 'www' : 'sandbox'
7
+ merchant = ENV['BRAINTREE_MERCHANT_ID']
8
+
9
+ if payment_id.present? && merchant.present?
10
+ link_to(
11
+ payment_id,
12
+ "https://#{env}.braintreegateway.com/merchants/#{merchant}/transactions/#{payment_id}",
13
+ title: 'Show payment on Braintree',
14
+ target: '_blank'
15
+ )
16
+ else
17
+ payment_id
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,8 @@
1
+ module SolidusBraintree
2
+ module AddNameValidationConcern
3
+ extend ActiveSupport::Concern
4
+ included do
5
+ validates :name, presence: true, on: :create
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,18 @@
1
+ module SolidusBraintree
2
+ module InjectDeviceDataConcern
3
+ extend ActiveSupport::Concern
4
+ included do
5
+ prepend(InstanceMethods)
6
+ end
7
+
8
+ module InstanceMethods
9
+ def gateway_options
10
+ options = super
11
+
12
+ options[:device_data] = order.braintree_device_data if order.braintree_device_data
13
+
14
+ options
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,8 @@
1
+ module SolidusBraintree
2
+ module PaymentBraintreeNonceConcern
3
+ extend ActiveSupport::Concern
4
+ included do
5
+ attr_accessor :payment_method_nonce
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,11 @@
1
+ module SolidusBraintree
2
+ module PermittedAttributesConcern
3
+ def payment_attributes
4
+ super | [:payment_method_nonce]
5
+ end
6
+
7
+ def checkout_attributes
8
+ super | [:braintree_device_data]
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,14 @@
1
+ module SolidusBraintree
2
+ module SkipRequireCardNumbersConcern
3
+ extend ActiveSupport::Concern
4
+ included do
5
+ prepend(InstanceMethods)
6
+ end
7
+
8
+ module InstanceMethods
9
+ def require_card_numbers?
10
+ super && !self.payment_method.kind_of?(Solidus::Gateway::BraintreeGateway)
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,23 @@
1
+ module SolidusBraintree
2
+ module UseDataFieldConcern
3
+ extend ActiveSupport::Concern
4
+ included do
5
+ prepend(InstanceMethods)
6
+ end
7
+
8
+ module InstanceMethods
9
+
10
+ def email
11
+ data["email"]
12
+ end
13
+
14
+ def display_number
15
+ cc_type == 'paypal' ? email : super
16
+ end
17
+
18
+ def data
19
+ super.is_a?(String) ? JSON.parse(super) : super
20
+ end
21
+ end
22
+ end
23
+ end
@@ -1 +1,2 @@
1
1
  Spree::Payment.include SolidusBraintree::PaymentBraintreeNonceConcern
2
+ Spree::Payment.include SolidusBraintree::InjectDeviceDataConcern
@@ -21,6 +21,10 @@ module Solidus
21
21
  'Visa' => 'visa',
22
22
  }
23
23
 
24
+ def method_type
25
+ 'braintree'
26
+ end
27
+
24
28
  def gateway_options
25
29
  {
26
30
  environment: preferred_environment.to_sym,
@@ -49,12 +53,13 @@ module Solidus
49
53
  return if source.gateway_customer_profile_id.present? || payment.payment_method_nonce.nil?
50
54
 
51
55
  user = payment.order.user
56
+ email = user ? user.email : payment.order.email
52
57
  address = (payment.source.address || payment.order.bill_address).try(:active_merchant_hash)
53
58
 
54
59
  params = {
55
60
  first_name: source.first_name,
56
61
  last_name: source.last_name,
57
- email: user.email,
62
+ email: email,
58
63
  credit_card: {
59
64
  cardholder_name: source.name,
60
65
  billing_address: map_address(address),
@@ -63,6 +68,7 @@ module Solidus
63
68
  verify_card: true,
64
69
  },
65
70
  },
71
+ device_data: payment.order.braintree_device_data
66
72
  }
67
73
 
68
74
  result = braintree_gateway.customer.create(params)
@@ -119,9 +125,19 @@ module Solidus
119
125
  end
120
126
 
121
127
  def void(authorization_code, source = {}, options = {})
122
- result = braintree_gateway.transaction.void(authorization_code)
123
-
124
- handle_result(result)
128
+ # Allows voiding payments that are in a checkout state
129
+ if authorization_code.nil?
130
+ # Fake response since we don't need to void anything with Braintree
131
+ ActiveMerchant::Billing::Response.new(
132
+ true,
133
+ "OK",
134
+ {},
135
+ {}
136
+ )
137
+ else
138
+ result = braintree_gateway.transaction.void(authorization_code)
139
+ handle_result(result)
140
+ end
125
141
  end
126
142
 
127
143
  def credit(cents, source, authorization_code, options = {})
@@ -137,6 +153,18 @@ module Solidus
137
153
  ].include?(transaction.status)
138
154
  end
139
155
 
156
+ def card_number_placeholder
157
+ '4141 4141 4141 4141'
158
+ end
159
+
160
+ def expiration_date_placeholder
161
+ '01/2020'
162
+ end
163
+
164
+ def card_code_placeholder
165
+ '123'
166
+ end
167
+
140
168
  private
141
169
  def message_from_result(result)
142
170
  if result.success?
@@ -150,12 +178,25 @@ module Solidus
150
178
  end
151
179
  end
152
180
 
181
+ def build_results_hash(result)
182
+ if result.success?
183
+ {
184
+ authorization: result.transaction.id,
185
+ avs_result: {
186
+ code: result.transaction.avs_street_address_response_code
187
+ }
188
+ }
189
+ else
190
+ {}
191
+ end
192
+ end
193
+
153
194
  def handle_result(result)
154
195
  ActiveMerchant::Billing::Response.new(
155
196
  result.success?,
156
197
  message_from_result(result),
157
198
  {},
158
- { authorization: (result.transaction.id if result.success?) }
199
+ build_results_hash(result)
159
200
  )
160
201
  end
161
202
 
@@ -0,0 +1,9 @@
1
+ <!-- insert_before "erb[loud]:contains('place_order')" -->
2
+
3
+ <%
4
+ # Currently we assume we only have one braintree
5
+ # payment method. In practice this should be true and it simplifies the code.
6
+ braintree_payment = @order.unprocessed_payments.find {|p| p.payment_method.class == Solidus::Gateway::BraintreeGateway }
7
+ if braintree_payment %>
8
+ <%= render 'spree/checkout/payment/braintree_initialization', payment_method: braintree_payment.payment_method %>
9
+ <% end %>
@@ -0,0 +1,38 @@
1
+ <fieldset data-id='credit-card' class="no-border-bottom">
2
+ <div class="field" data-hook="previous_cards">
3
+ <% if previous_cards.any? %>
4
+ <% previous_cards.each do |card| %>
5
+ <label><%= radio_button_tag :card, card.id, card == previous_cards.first %> <%= card.display_number %><br /></label>
6
+ <% end %>
7
+ <label><%= radio_button_tag :card, 'new', false, { id: "card_new#{payment_method.id}" } %> <%= Spree.t(:use_new_cc) %></label>
8
+ <% end %>
9
+ </div>
10
+
11
+ <div id="card_form<%= payment_method.id %>" data-hook>
12
+ <% param_prefix = "payment_source[#{payment_method.id}]" %>
13
+
14
+ <div class="clear"></div>
15
+
16
+ <div class="alpha four columns">
17
+ <div data-hook="card_name">
18
+ <div class="field">
19
+ <%= label_tag "card_name#{payment_method.id}", Spree::CreditCard.human_attribute_name(:name), class: 'required' %>
20
+ <%= text_field_tag "#{param_prefix}[name]", '', id: "card_name#{payment_method.id}", class: 'required fullwidth', maxlength: 19 %>
21
+ </div>
22
+ </div>
23
+ </div>
24
+
25
+ <div id="braintree-dropin"></div>
26
+ <input type="hidden" id="payment_method_nonce" name="payment[payment_method_nonce]">
27
+
28
+ <div class="clear"></div>
29
+
30
+ <%= label_tag "card_address#{payment_method.id}", Spree.t(:billing_address) %>
31
+ <% address = @order.bill_address || @order.ship_address || Spree::Address.build_default %>
32
+ <%= fields_for "#{param_prefix}[address_attributes]", address do |f| %>
33
+ <%= render :partial => 'spree/admin/shared/address_form', :locals => { :f => f, :type => "billing" } %>
34
+ <% end %>
35
+
36
+ <div class="clear"></div>
37
+ </div>
38
+ </fieldset>
@@ -0,0 +1,30 @@
1
+ <fieldset data-hook="credit_card">
2
+ <legend align="center"><%= Spree::CreditCard.model_name.human %></legend>
3
+
4
+ <div class="row">
5
+ <div class="alpha six columns">
6
+ <dl>
7
+ <dt><%= Spree.t(:identifier) %>:</dt>
8
+ <dd><%= payment.number %></dd>
9
+
10
+ <dt><%= Spree.t(:response_code) %>:</dt>
11
+ <dd><%= braintree_transaction_link(payment.response_code) %></dd>
12
+
13
+ <dt><%= Spree.t(:name_on_card) %>:</dt>
14
+ <dd><%= payment.source.name %></dd>
15
+
16
+ <dt><%= Spree::CreditCard.human_attribute_name(:cc_type) %>:</dt>
17
+ <dd><%= payment.source.cc_type %></dd>
18
+
19
+ <dt><%= Spree::CreditCard.human_attribute_name(:number) %>:</dt>
20
+ <dd><%= payment.source.display_number %></dd>
21
+
22
+ <dt><%= Spree::CreditCard.human_attribute_name(:expiration) %>:</dt>
23
+ <dd><%= payment.source.month %>/<%= payment.source.year %></dd>
24
+ </dl>
25
+ <% if payment.source.address %>
26
+ <%= render partial: 'spree/admin/shared/address', locals: {address: payment.source.address} %>
27
+ <% end %>
28
+ </div>
29
+ </div>
30
+ </fieldset>
@@ -0,0 +1,55 @@
1
+ <div class="braintree-payment">
2
+ <%= render 'spree/checkout/payment/braintree_initialization', payment_method: payment_method %>
3
+
4
+ <div class="braintree-paypal-input">
5
+ <div class="braintree-paypal-header">
6
+ <%= t('solidus_braintree.paypal_header_html') %>
7
+ </div>
8
+ <div id="#braintree_paypal_container"></div>
9
+ </div>
10
+
11
+ <div class="braintree-cc-input">
12
+ <div class="braintree-cc-header">
13
+ <%= t('solidus_braintree.creditcard_header_html') %>
14
+ <%= image_tag 'credit_cards/credit_card.gif', :id => 'credit-card-image' %>
15
+ </div>
16
+ <% param_prefix = "payment_source[#{payment_method.id}]" %>
17
+
18
+ <p class="field">
19
+ <%= label_tag "name_on_card_#{payment_method.id}", Spree.t(:name_on_card) %><span class="required">*</span><br />
20
+ <%= text_field_tag "#{param_prefix}[name]", "#{@order.billing_firstname} #{@order.billing_lastname}", { id: "name_on_card_#{payment_method.id}", :autocomplete => "cc-name" } %>
21
+ </p>
22
+
23
+ <p class="field" data-hook="card_number">
24
+ <%= label_tag "braintree_card_number", Spree.t(:card_number) %><span class="required">*</span><br />
25
+ <label for="braintree_card_number" id="braintree_card_number" class="braintree-hosted-field"></label>
26
+ &nbsp;
27
+ <span id="card_type" style="display:none;">
28
+ ( <span id="looks_like" ><%= Spree.t(:card_type_is) %> <span id="type"></span></span>
29
+ <span id="unrecognized"><%= Spree.t(:unrecognized_card_type) %></span>
30
+ )
31
+ </span>
32
+ </p>
33
+
34
+ <p class="field" data-hook="card_expiration">
35
+ <%= label_tag "braintree_card_expiry", Spree.t(:expiration) %><span class="required">*</span><br />
36
+ <label for="braintree_card_expiry" id="braintree_card_expiry" class="braintree-hosted-field"></label>
37
+ </p>
38
+
39
+ <p class="field" data-hook="card_code">
40
+ <%= label_tag "braintree_card_code", Spree.t(:card_code) %><span class="required">*</span><br />
41
+
42
+ <label for="braintree_card_code" id="braintree_card_code" class="braintree-hosted-field card-code"></label>
43
+ <%= link_to "(#{Spree.t(:what_is_this)})", spree.cvv_path, :target => '_blank', "data-hook" => "cvv_link", :id => "cvv_link" %>
44
+ </p>
45
+ </div>
46
+
47
+ <% if @order.bill_address %>
48
+ <%= fields_for "#{param_prefix}[address_attributes]", @order.bill_address do |f| %>
49
+ <%= render :partial => 'spree/address/form_hidden', :locals => { :form => f } %>
50
+ <% end %>
51
+ <% end %>
52
+
53
+ <%= hidden_field_tag "#{param_prefix}[cc_type]", '', :id => "cc_type", :class => 'ccType' %>
54
+ <%= hidden_field_tag "order[payments_attributes][][payment_method_nonce]", '', :id => "payment_method_nonce" %>
55
+ </div>
@@ -0,0 +1,12 @@
1
+ <% content_for :head do %>
2
+ <%= javascript_tag do %>
3
+ braintree.environment = "<%= payment_method.preferred_environment %>"
4
+ braintree.placeholders = {
5
+ "number": "<%= payment_method.card_number_placeholder %>",
6
+ "expirationDate": "<%= payment_method.expiration_date_placeholder %>",
7
+ "cvv": "<%= payment_method.card_code_placeholder %>"
8
+ }
9
+ <% end %>
10
+ <% end %>
11
+
12
+ <%= hidden_field_tag "order[braintree_device_data]", '', :id => "device_data" %>