spree_gateway 3.7.5 → 3.8.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.
- checksums.yaml +4 -4
- data/.travis.yml +31 -38
- data/Appraisals +4 -10
- data/app/models/spree/check.rb +41 -0
- data/app/models/spree/gateway/stripe_ach_gateway.rb +60 -0
- data/app/models/spree/payment_decorator.rb +29 -0
- data/config/initializers/spree_permitted_attributes.rb +5 -0
- data/config/locales/en.yml +19 -0
- data/db/migrate/20200317135551_add_spree_check_payment_source.rb +22 -0
- data/gemfiles/{spree_3_5.gemfile → spree_4_1.gemfile} +1 -1
- data/gemfiles/{spree_4_0.gemfile → spree_4_2.gemfile} +1 -1
- data/lib/active_merchant/billing/stripe_gateway_decorator.rb +13 -0
- data/lib/spree_gateway/engine.rb +1 -0
- data/lib/spree_gateway/version.rb +1 -1
- data/lib/views/backend/spree/admin/payments/source_forms/_stripe_ach.html.erb +86 -0
- data/lib/views/backend/spree/admin/payments/source_views/_stripe_ach.html.erb +21 -0
- data/lib/views/frontend/spree/checkout/payment/_stripe_ach.html.erb +81 -0
- data/lib/views/frontend/spree/checkout/payment/_stripe_ach_verify.html.erb +16 -0
- data/spec/factories/check_factory.rb +10 -0
- data/spec/features/admin/stripe_elements_payment_spec.rb +13 -3
- data/spec/features/stripe_checkout_spec.rb +3 -0
- data/spec/models/gateway/stripe_ach_gateway_spec.rb +185 -0
- data/spec/spec_helper.rb +7 -65
- data/spree_gateway.gemspec +1 -20
- metadata +19 -275
- data/gemfiles/spree_3_7.gemfile +0 -9
- data/spec/support/capybara_helper.rb +0 -15
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '015491170e10b6b22d98da98f5b8e466ae2df12474e5029bff6bd8926d86732e'
|
4
|
+
data.tar.gz: 39713d557a03b1170dc36f4fcf4a6ec2e4fecb45b3a80376b00a23fd396c200b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c9d5003e95dc31701dbea5239a10dc587300d27659f29ea6deba81a309df2d24729ec3259d253d99f4c327c7f8889bc52b28dc8015d785fbde376a128d05a6f1
|
7
|
+
data.tar.gz: 11a5083fba9163eabd567cef8173d949d6f6ac5b86a97f3c65281671d3fd82f6ed93d2f4626c2ac39e7ae71d16cc559735a9d5eb4024960041a1ba2128997ba8
|
data/.travis.yml
CHANGED
@@ -1,51 +1,44 @@
|
|
1
|
-
|
2
|
-
dist:
|
1
|
+
os: linux
|
2
|
+
dist: bionic
|
3
|
+
|
4
|
+
addons:
|
5
|
+
apt:
|
6
|
+
sources:
|
7
|
+
- google-chrome
|
8
|
+
packages:
|
9
|
+
- google-chrome-stable
|
10
|
+
|
11
|
+
services:
|
12
|
+
- mysql
|
13
|
+
- postgresql
|
3
14
|
|
4
15
|
language: ruby
|
5
16
|
|
6
|
-
|
7
|
-
|
8
|
-
postgresql: 9.4
|
17
|
+
rvm:
|
18
|
+
- 2.6
|
9
19
|
|
10
20
|
env:
|
11
|
-
- DB=postgres
|
12
21
|
- DB=mysql
|
22
|
+
- DB=postgres
|
13
23
|
|
14
24
|
gemfile:
|
15
|
-
- gemfiles/
|
16
|
-
- gemfiles/
|
17
|
-
- gemfiles/spree_4_0.gemfile
|
25
|
+
# - gemfiles/spree_4_1.gemfile
|
26
|
+
- gemfiles/spree_4_2.gemfile
|
18
27
|
- gemfiles/spree_master.gemfile
|
19
28
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
rvm:
|
25
|
-
- 2.5.1
|
26
|
-
- 2.4.4
|
27
|
-
- 2.3.8
|
28
|
-
|
29
|
-
matrix:
|
30
|
-
allow_failures:
|
31
|
-
- gemfile: gemfiles/spree_master.gemfile
|
32
|
-
exclude:
|
33
|
-
- rvm: 2.3.8
|
34
|
-
gemfile: gemfiles/spree_master.gemfile
|
35
|
-
- rvm: 2.4.4
|
36
|
-
gemfile: gemfiles/spree_master.gemfile
|
37
|
-
- rvm: 2.3.8
|
38
|
-
gemfile: gemfiles/spree_4_0.gemfile
|
39
|
-
- rvm: 2.4.4
|
40
|
-
gemfile: gemfiles/spree_4_0.gemfile
|
41
|
-
- rvm: 2.5.1
|
42
|
-
gemfile: gemfiles/spree_3_5.gemfile
|
29
|
+
jobs:
|
30
|
+
allow_failures:
|
31
|
+
- gemfile: gemfiles/spree_master.gemfile
|
43
32
|
|
44
33
|
before_install:
|
45
34
|
- mysql -u root -e "GRANT ALL ON *.* TO 'travis'@'%';"
|
46
|
-
|
47
|
-
|
48
|
-
-
|
49
|
-
-
|
50
|
-
-
|
51
|
-
-
|
35
|
+
|
36
|
+
before_script:
|
37
|
+
- CHROME_MAIN_VERSION=`google-chrome-stable --version | sed -E 's/(^Google Chrome |\.[0-9]+ )//g'`
|
38
|
+
- CHROMEDRIVER_VERSION=`curl -s "https://chromedriver.storage.googleapis.com/LATEST_RELEASE_$CHROME_MAIN_VERSION"`
|
39
|
+
- curl "https://chromedriver.storage.googleapis.com/${CHROMEDRIVER_VERSION}/chromedriver_linux64.zip" -O
|
40
|
+
- unzip chromedriver_linux64.zip -d ~/bin
|
41
|
+
|
42
|
+
script:
|
43
|
+
- bundle exec rake test_app
|
44
|
+
- bundle exec rake spec
|
data/Appraisals
CHANGED
@@ -1,16 +1,10 @@
|
|
1
|
-
appraise 'spree-
|
2
|
-
gem 'spree', '~>
|
1
|
+
appraise 'spree-4-1' do
|
2
|
+
gem 'spree', '~> 4.1.0'
|
3
3
|
gem 'rails-controller-testing'
|
4
4
|
end
|
5
5
|
|
6
|
-
appraise 'spree-
|
7
|
-
gem 'spree', '~>
|
8
|
-
gem 'sass-rails'
|
9
|
-
gem 'rails-controller-testing'
|
10
|
-
end
|
11
|
-
|
12
|
-
appraise 'spree-4-0' do
|
13
|
-
gem 'spree', '~> 4.0.0.rc2'
|
6
|
+
appraise 'spree-4-2' do
|
7
|
+
gem 'spree', '~> 4.2.0.beta'
|
14
8
|
gem 'rails-controller-testing'
|
15
9
|
end
|
16
10
|
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Spree
|
2
|
+
class Check < Spree::Base
|
3
|
+
|
4
|
+
attr_accessor :imported
|
5
|
+
|
6
|
+
belongs_to :payment_method
|
7
|
+
belongs_to :user, class_name: Spree.user_class.to_s, foreign_key: 'user_id',
|
8
|
+
optional: true
|
9
|
+
has_many :payments, as: :source
|
10
|
+
|
11
|
+
scope :with_payment_profile, -> { where.not(gateway_customer_profile_id: nil) }
|
12
|
+
|
13
|
+
validates :account_holder_name, presence: true
|
14
|
+
validates :account_holder_type, presence: true, inclusion: { in: %w[Individual Company] }
|
15
|
+
validates :account_number, presence: true, numericality: { only_integer: true }
|
16
|
+
validates :routing_number, presence: true, numericality: { only_integer: true }
|
17
|
+
|
18
|
+
def has_payment_profile?
|
19
|
+
gateway_customer_profile_id.present? || gateway_payment_profile_id.present?
|
20
|
+
end
|
21
|
+
|
22
|
+
def actions
|
23
|
+
%w[capture void credit]
|
24
|
+
end
|
25
|
+
|
26
|
+
def can_capture?(payment)
|
27
|
+
payment.pending? || payment.checkout?
|
28
|
+
end
|
29
|
+
|
30
|
+
# Indicates whether its possible to void the payment.
|
31
|
+
def can_void?(payment)
|
32
|
+
!payment.failed? && !payment.void?
|
33
|
+
end
|
34
|
+
|
35
|
+
# Indicates whether its possible to credit the payment. Note that most gateways require that the
|
36
|
+
# payment be settled first which generally happens within 12-24 hours of the transaction.
|
37
|
+
def can_credit?(payment)
|
38
|
+
payment.completed? && payment.credit_allowed > 0
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module Spree
|
2
|
+
class Gateway::StripeAchGateway < Gateway::StripeGateway
|
3
|
+
|
4
|
+
def method_type
|
5
|
+
'stripe_ach'
|
6
|
+
end
|
7
|
+
|
8
|
+
def payment_source_class
|
9
|
+
Check
|
10
|
+
end
|
11
|
+
|
12
|
+
def verify(source, **gateway_options)
|
13
|
+
provider.verify(source, gateway_options)
|
14
|
+
end
|
15
|
+
|
16
|
+
def create_profile(payment)
|
17
|
+
return unless payment.source&.gateway_customer_profile_id.nil?
|
18
|
+
|
19
|
+
options = {
|
20
|
+
email: payment.order.user&.email || payment.order.email,
|
21
|
+
login: preferred_secret_key,
|
22
|
+
}.merge! address_for(payment)
|
23
|
+
|
24
|
+
source = payment.source
|
25
|
+
bank_account = if source.gateway_payment_profile_id.present?
|
26
|
+
source.gateway_payment_profile_id
|
27
|
+
else
|
28
|
+
source
|
29
|
+
end
|
30
|
+
|
31
|
+
response = provider.store(bank_account, options)
|
32
|
+
|
33
|
+
if response.success?
|
34
|
+
payment.source.update!({
|
35
|
+
gateway_customer_profile_id: response.params['id'],
|
36
|
+
gateway_payment_profile_id: response.params['default_source'] || response.params['default_card']
|
37
|
+
})
|
38
|
+
|
39
|
+
else
|
40
|
+
payment.send(:gateway_error, response.message)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def supports?(_source)
|
45
|
+
true
|
46
|
+
end
|
47
|
+
|
48
|
+
def available_for_order?(order)
|
49
|
+
# Stripe ACH payments are supported only for US customers
|
50
|
+
# Therefore we need to check order's addresses
|
51
|
+
return unless order.ship_address_id && order.bill_address_id
|
52
|
+
return unless order.ship_address && order.bill_address_id
|
53
|
+
|
54
|
+
usa_id = ::Spree::Country.find_by(iso: 'US')&.id
|
55
|
+
return false unless usa_id
|
56
|
+
|
57
|
+
order.ship_address.country_id == usa_id && order.bill_address.country_id == usa_id
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Spree
|
2
|
+
module PaymentDecorator
|
3
|
+
def verify!(**options)
|
4
|
+
process_verification(options)
|
5
|
+
end
|
6
|
+
|
7
|
+
private
|
8
|
+
|
9
|
+
def process_verification(**options)
|
10
|
+
protect_from_connection_error do
|
11
|
+
response = payment_method.verify(source, options)
|
12
|
+
|
13
|
+
record_response(response)
|
14
|
+
|
15
|
+
if response.success?
|
16
|
+
unless response.authorization.nil?
|
17
|
+
self.response_code = response.authorization
|
18
|
+
|
19
|
+
source.update(status: response.params['status'])
|
20
|
+
end
|
21
|
+
else
|
22
|
+
gateway_error(response)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
Spree::Payment.prepend Spree::PaymentDecorator
|
data/config/locales/en.yml
CHANGED
@@ -1,6 +1,14 @@
|
|
1
1
|
---
|
2
2
|
en:
|
3
|
+
activerecord:
|
4
|
+
attributes:
|
5
|
+
spree/check:
|
6
|
+
account_holder_name: Account Holder Name
|
7
|
+
account_holder_type: Account Holder Type
|
8
|
+
account_number: Account Number
|
9
|
+
routing_number: Routing Number
|
3
10
|
spree:
|
11
|
+
check: Check
|
4
12
|
payment_has_been_cancelled: The payment has been cancelled.
|
5
13
|
log_entry:
|
6
14
|
braintree:
|
@@ -21,3 +29,14 @@ en:
|
|
21
29
|
cvv_result: CVV Result
|
22
30
|
cvc_check: CVC Check
|
23
31
|
address_zip_check: Address ZIP check
|
32
|
+
stripe:
|
33
|
+
ach:
|
34
|
+
account_holder_name: Account Holder Name
|
35
|
+
account_holder_type: Account Holder Type
|
36
|
+
routing_number: Routing Number
|
37
|
+
account_number: Account Number
|
38
|
+
verify_account_number: Verify Account Number
|
39
|
+
verify_bank_account: Verify Bank Account
|
40
|
+
first_deposit: First Deposit
|
41
|
+
second_deposit: Second Deposit
|
42
|
+
bank_transfer: bank_transfer
|
@@ -0,0 +1,22 @@
|
|
1
|
+
class AddSpreeCheckPaymentSource < ActiveRecord::Migration[5.1]
|
2
|
+
def change
|
3
|
+
create_table :spree_checks do |t|
|
4
|
+
t.references :payment_method
|
5
|
+
t.references :user
|
6
|
+
t.string "account_holder_name"
|
7
|
+
t.string "account_holder_type"
|
8
|
+
t.string "routing_number"
|
9
|
+
t.string "account_number"
|
10
|
+
t.string "account_type", default: 'checking'
|
11
|
+
t.string "status"
|
12
|
+
t.string "last_digits"
|
13
|
+
t.string "gateway_customer_profile_id"
|
14
|
+
t.string "gateway_payment_profile_id"
|
15
|
+
|
16
|
+
t.datetime "created_at", null: false
|
17
|
+
t.datetime "updated_at", null: false
|
18
|
+
t.datetime "deleted_at"
|
19
|
+
end
|
20
|
+
add_index :spree_payment_methods, :id
|
21
|
+
end
|
22
|
+
end
|
@@ -1,6 +1,19 @@
|
|
1
1
|
module ActiveMerchant
|
2
2
|
module Billing
|
3
3
|
module StripeGatewayDecorator
|
4
|
+
def verify(source, **options)
|
5
|
+
customer = source.gateway_customer_profile_id
|
6
|
+
bank_account_token = source.gateway_payment_profile_id
|
7
|
+
|
8
|
+
commit(:post, "customers/#{CGI.escape(customer)}/sources/#{bank_account_token}/verify", amounts: options[:amounts])
|
9
|
+
end
|
10
|
+
|
11
|
+
def retrieve(source, **options)
|
12
|
+
customer = source.gateway_customer_profile_id
|
13
|
+
bank_account_token = source.gateway_payment_profile_id
|
14
|
+
commit(:get, "customers/#{CGI.escape(customer)}/bank_accounts/#{bank_account_token}")
|
15
|
+
end
|
16
|
+
|
4
17
|
private
|
5
18
|
|
6
19
|
def headers(options = {})
|
data/lib/spree_gateway/engine.rb
CHANGED
@@ -32,6 +32,7 @@ module SpreeGateway
|
|
32
32
|
app.config.spree.payment_methods << Spree::Gateway::StripeGateway
|
33
33
|
app.config.spree.payment_methods << Spree::Gateway::StripeElementsGateway
|
34
34
|
app.config.spree.payment_methods << Spree::Gateway::StripeApplePayGateway
|
35
|
+
app.config.spree.payment_methods << Spree::Gateway::StripeAchGateway
|
35
36
|
app.config.spree.payment_methods << Spree::Gateway::UsaEpayTransaction
|
36
37
|
app.config.spree.payment_methods << Spree::Gateway::Worldpay
|
37
38
|
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
<fieldset data-id="bank_transfer">
|
2
|
+
<div id="bank_transfer_form<%= payment_method.id %>" class="margint" data-hook>
|
3
|
+
<% param_prefix = "payment_source[#{payment_method.id}]" %>
|
4
|
+
|
5
|
+
<div data-hook="account_holder_name" class="form-group">
|
6
|
+
<%= label_tag "account_holder_name#{payment_method.id}", raw(Spree.t('stripe.ach.account_holder_name') + required_span_tag) %>
|
7
|
+
<%= text_field_tag "#{param_prefix}[account_holder_name]", '', class: 'required form-control', id: "account_holder_name#{payment_method.id}" %>
|
8
|
+
</div>
|
9
|
+
|
10
|
+
<div data-hook="account_holder_type">
|
11
|
+
<%= label_tag "account_holder_type#{payment_method.id}", raw(Spree.t('stripe.ach.account_holder_type') + required_span_tag) %>
|
12
|
+
<%= select_tag "#{param_prefix}[account_holder_type]", options_for_select(%w(Individual Company)), {id: "account_holder_type#{payment_method.id}", class: 'required form-control'} %>
|
13
|
+
</div>
|
14
|
+
|
15
|
+
<div data-hook="routing_number">
|
16
|
+
<%= label_tag "routing_number#{payment_method.id}", raw(Spree.t('stripe.ach.routing_number') + required_span_tag)%>
|
17
|
+
<%= text_field_tag "#{param_prefix}[routing_number]", '', class: 'required form-control', id: "routing_number#{payment_method.id}", maxlength: 9 %>
|
18
|
+
</div>
|
19
|
+
<div data-hook="account_number">
|
20
|
+
<%= label_tag "account_number#{payment_method.id}", raw(Spree.t('stripe.ach.account_number') + required_span_tag) %>
|
21
|
+
<%= text_field_tag "#{param_prefix}[account_number]", '', class: 'required form-control', id: "account_number#{payment_method.id}" %>
|
22
|
+
</div>
|
23
|
+
<div data-hook="account_number">
|
24
|
+
<%= label_tag "verify_account_number#{payment_method.id}", raw(Spree.t('stripe.ach.verify_account_number') + required_span_tag) %>
|
25
|
+
<%= text_field_tag "#{param_prefix}[verify_account_number]", '', class: 'required form-control', id: "verify_account_number#{payment_method.id}" %>
|
26
|
+
</div>
|
27
|
+
</div>
|
28
|
+
</fieldset>
|
29
|
+
|
30
|
+
<script type="text/javascript" src="https://js.stripe.com/v3/"></script>
|
31
|
+
<script type="text/javascript">
|
32
|
+
var stripe = Stripe("<%= payment_method.preferred_publishable_key %>");
|
33
|
+
</script>
|
34
|
+
|
35
|
+
<script>
|
36
|
+
stripeResponseHandler = function(response) {
|
37
|
+
var token, paymentMethodId;
|
38
|
+
if (response.error) {
|
39
|
+
$('#stripeError').html(response.error.message);
|
40
|
+
const param_map = {
|
41
|
+
account_holder_name: '#account_holder_name',
|
42
|
+
account_holder_type: '#account_holder_type',
|
43
|
+
account_number: '#account_number',
|
44
|
+
routing_number: '#routing_number'
|
45
|
+
}
|
46
|
+
if (response.error.param){
|
47
|
+
errorField = Spree.stripePaymentMethod.find(param_map[response.error.param])
|
48
|
+
errorField.addClass('error');
|
49
|
+
errorField.parent().addClass('has-error');
|
50
|
+
}
|
51
|
+
return $('#stripeError').show();
|
52
|
+
} else {
|
53
|
+
Spree.stripePaymentMethod.find('#account_holder_name, #account_holder_type, #account_number, #routing_number').prop("disabled", true);
|
54
|
+
token = response.token['id'];
|
55
|
+
paymentMethodId = Spree.stripePaymentMethod.prop('id').split("_")[2];
|
56
|
+
Spree.stripePaymentMethod.append("<input type='hidden' class='stripeToken' name='payment_source[" + paymentMethodId + "][gateway_payment_profile_id]' value='" + token + "'/>");
|
57
|
+
return Spree.stripePaymentMethod.parents("form").trigger('submit');
|
58
|
+
}
|
59
|
+
};
|
60
|
+
|
61
|
+
window.addEventListener('DOMContentLoaded', function() {
|
62
|
+
Spree.stripePaymentMethod = $('#payment_method_' + <%= payment_method.id %>);
|
63
|
+
|
64
|
+
Spree.ready(function() {
|
65
|
+
Spree.stripePaymentMethod.prepend("<div id='stripeError' class='errorExplanation alert alert-danger' style='display:none'></div>");
|
66
|
+
return $('#new_payment [data-hook=buttons]').click(function() {
|
67
|
+
var params;
|
68
|
+
$('#stripeError').hide();
|
69
|
+
Spree.stripePaymentMethod.find('#account_holder_name, #account_holder_type, #account_number, #routing_number').removeClass('error');
|
70
|
+
if (Spree.stripePaymentMethod.is(':visible')) {
|
71
|
+
params = $.extend({
|
72
|
+
country: 'US',
|
73
|
+
currency: 'usd',
|
74
|
+
account_holder_name: $('.account_holder_name:visible').val(),
|
75
|
+
account_holder_type: $('.account_holder_type:visible').val(),
|
76
|
+
routing_number: $('.routing_number:visible').val(),
|
77
|
+
account_number: $('.account_number:visible').val()
|
78
|
+
}, Spree.stripeAdditionalInfo);
|
79
|
+
stripe.createToken('bank_account', params).then(stripeResponseHandler);
|
80
|
+
return false;
|
81
|
+
}
|
82
|
+
});
|
83
|
+
});
|
84
|
+
});
|
85
|
+
</script>
|
86
|
+
|