spree_gateway 3.7.4 → 3.9.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +36 -37
- data/Appraisals +3 -8
- data/app/models/spree/check.rb +41 -0
- data/app/models/spree/credit_card_decorator.rb +10 -0
- data/app/models/spree/gateway/stripe_ach_gateway.rb +60 -0
- data/app/models/spree/gateway/stripe_elements_gateway.rb +50 -0
- data/app/models/spree/gateway/stripe_gateway.rb +1 -0
- data/app/models/spree/order_decorator.rb +28 -0
- data/app/models/spree/payment_decorator.rb +36 -0
- data/app/views/spree/checkout/_payment_confirm.html.erb +39 -0
- data/config/initializers/spree_permitted_attributes.rb +5 -0
- data/config/locales/en.yml +23 -0
- data/config/routes.rb +12 -0
- data/db/migrate/20200317135551_add_spree_check_payment_source.rb +22 -0
- data/db/migrate/20200422114908_add_intent_key_to_payment.rb +5 -0
- data/gemfiles/{spree_3_5.gemfile → spree_4_1.gemfile} +1 -1
- data/lib/active_merchant/billing/stripe_gateway_decorator.rb +13 -0
- data/lib/controllers/spree/api/v2/storefront/intents_controller.rb +27 -0
- data/lib/spree_frontend/controllers/spree/checkout_controller_decorator.rb +19 -0
- data/lib/spree_gateway/engine.rb +8 -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_forms/_stripe_apple_pay.html.erb +0 -0
- data/lib/views/backend/spree/admin/payments/source_views/_stripe_ach.html.erb +21 -0
- data/lib/views/backend/spree/admin/payments/source_views/_stripe_apple_pay.html.erb +1 -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/lib/views/frontend/spree/checkout/payment/_stripe_apple_pay.html.erb +10 -1
- data/lib/views/frontend/spree/checkout/payment/_stripe_elements.html.erb +2 -1
- data/spec/factories/check_factory.rb +10 -0
- data/spec/features/admin/stripe_elements_payment_spec.rb +32 -32
- data/spec/features/stripe_checkout_spec.rb +30 -19
- data/spec/features/stripe_elements_3ds_checkout_spec.rb +224 -0
- data/spec/models/gateway/stripe_ach_gateway_spec.rb +186 -0
- data/spec/models/gateway/stripe_gateway_spec.rb +1 -0
- data/spec/spec_helper.rb +7 -65
- data/spec/support/order_walktrough.rb +73 -0
- data/spec/support/within_stripe_3ds_popup.rb +10 -0
- data/spree_gateway.gemspec +10 -22
- metadata +44 -282
- data/gemfiles/spree_4_0.gemfile +0 -8
- 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: 82e672befb08ae277659eec1f7686002af0b6f66b1f8ecb467649ab29667aee2
|
4
|
+
data.tar.gz: 47d5a54c20d7987569af842d50f471e6f3eea69647f6807865191c55e97f61cc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5efb8b8ce887c92a222bf0b4c4b53b0ea16ee45f6efff3ad2908acdc5e3383139b778362be57ed858d15a0497df4d6aa37fa2c8f56ddee81f334945bcb67fe9f
|
7
|
+
data.tar.gz: fb61b3afbfc998e819bd11cf5d1f53c782e0e486c5b0fea6c43710fba74664398384badff68298a593b715185da3eaa68727e18b3d575dc5f14511d232d7f437
|
data/.travis.yml
CHANGED
@@ -1,51 +1,50 @@
|
|
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
|
-
|
17
|
+
rvm:
|
18
|
+
- 2.7
|
19
|
+
- 3.0
|
9
20
|
|
10
21
|
env:
|
11
|
-
- DB=postgres
|
12
22
|
- DB=mysql
|
23
|
+
- DB=postgres
|
13
24
|
|
14
25
|
gemfile:
|
15
|
-
- gemfiles/spree_3_5.gemfile
|
16
26
|
- gemfiles/spree_3_7.gemfile
|
17
|
-
- gemfiles/
|
27
|
+
- gemfiles/spree_4_1.gemfile
|
18
28
|
- gemfiles/spree_master.gemfile
|
19
29
|
|
20
|
-
|
21
|
-
|
22
|
-
-
|
23
|
-
|
24
|
-
rvm:
|
25
|
-
|
26
|
-
-
|
27
|
-
|
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
|
30
|
+
jobs:
|
31
|
+
allow_failures:
|
32
|
+
- gemfile: gemfiles/spree_master.gemfile
|
33
|
+
exclude:
|
34
|
+
- rvm: 3.0
|
35
|
+
gemfile: gemfiles/spree_4_1.gemfile
|
36
|
+
- rvm: 3.0
|
37
|
+
gemfile: gemfiles/spree_3_7.gemfile
|
43
38
|
|
44
39
|
before_install:
|
45
40
|
- mysql -u root -e "GRANT ALL ON *.* TO 'travis'@'%';"
|
46
|
-
|
47
|
-
|
48
|
-
-
|
49
|
-
-
|
50
|
-
-
|
51
|
-
-
|
41
|
+
|
42
|
+
before_script:
|
43
|
+
- CHROME_MAIN_VERSION=`google-chrome-stable --version | sed -E 's/(^Google Chrome |\.[0-9]+ )//g'`
|
44
|
+
- CHROMEDRIVER_VERSION=`curl -s "https://chromedriver.storage.googleapis.com/LATEST_RELEASE_$CHROME_MAIN_VERSION"`
|
45
|
+
- curl "https://chromedriver.storage.googleapis.com/${CHROMEDRIVER_VERSION}/chromedriver_linux64.zip" -O
|
46
|
+
- unzip chromedriver_linux64.zip -d ~/bin
|
47
|
+
|
48
|
+
script:
|
49
|
+
- bundle exec rake test_app
|
50
|
+
- bundle exec rake spec
|
data/Appraisals
CHANGED
@@ -1,16 +1,11 @@
|
|
1
|
-
appraise 'spree-3-5' do
|
2
|
-
gem 'spree', '~> 3.5.0'
|
3
|
-
gem 'rails-controller-testing'
|
4
|
-
end
|
5
|
-
|
6
1
|
appraise 'spree-3-7' do
|
7
2
|
gem 'spree', '~> 3.7.0'
|
8
|
-
gem 'sass-rails'
|
9
3
|
gem 'rails-controller-testing'
|
4
|
+
gem 'sass-rails'
|
10
5
|
end
|
11
6
|
|
12
|
-
appraise 'spree-4-
|
13
|
-
gem 'spree', '~> 4.
|
7
|
+
appraise 'spree-4-1' do
|
8
|
+
gem 'spree', '~> 4.1.0'
|
14
9
|
gem 'rails-controller-testing'
|
15
10
|
end
|
16
11
|
|
@@ -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
|
@@ -1,7 +1,57 @@
|
|
1
1
|
module Spree
|
2
2
|
class Gateway::StripeElementsGateway < Gateway::StripeGateway
|
3
|
+
preference :intents, :boolean, default: true
|
4
|
+
|
3
5
|
def method_type
|
4
6
|
'stripe_elements'
|
5
7
|
end
|
8
|
+
|
9
|
+
def provider_class
|
10
|
+
if get_preference(:intents)
|
11
|
+
ActiveMerchant::Billing::StripePaymentIntentsGateway
|
12
|
+
else
|
13
|
+
ActiveMerchant::Billing::StripeGateway
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def create_profile(payment)
|
18
|
+
return unless payment.source.gateway_customer_profile_id.nil?
|
19
|
+
|
20
|
+
options = {
|
21
|
+
email: payment.order.email,
|
22
|
+
login: preferred_secret_key
|
23
|
+
}.merge! address_for(payment)
|
24
|
+
|
25
|
+
source = update_source!(payment.source)
|
26
|
+
creditcard = source.gateway_payment_profile_id.present? ? source.gateway_payment_profile_id : source
|
27
|
+
response = provider.store(creditcard, options)
|
28
|
+
if response.success?
|
29
|
+
if get_preference(:intents)
|
30
|
+
payment.source.update!(
|
31
|
+
cc_type: payment.source.cc_type,
|
32
|
+
gateway_customer_profile_id: response.params['id'],
|
33
|
+
gateway_payment_profile_id: response.params['sources']['data'].first['id']
|
34
|
+
)
|
35
|
+
else
|
36
|
+
response_cc_type = response.params['sources']['data'].first['brand']
|
37
|
+
cc_type = CARD_TYPE_MAPPING.include?(response_cc_type) ? CARD_TYPE_MAPPING[response_cc_type] : payment.source.cc_type
|
38
|
+
payment.source.update!({
|
39
|
+
cc_type: cc_type, # side-effect of update_source!
|
40
|
+
gateway_customer_profile_id: response.params['id'],
|
41
|
+
gateway_payment_profile_id: response.params['default_source'] || response.params['default_card']
|
42
|
+
})
|
43
|
+
end
|
44
|
+
else
|
45
|
+
payment.send(:gateway_error, response.message)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def options_for_purchase_or_auth(money, creditcard, gateway_options)
|
52
|
+
money, creditcard, options = super
|
53
|
+
options[:execute_threed] = get_preference(:intents)
|
54
|
+
return money, creditcard, options
|
55
|
+
end
|
6
56
|
end
|
7
57
|
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Spree
|
2
|
+
module OrderDecorator
|
3
|
+
def self.prepended(base)
|
4
|
+
return if base.checkout_steps.key?(:payment_confirm)
|
5
|
+
|
6
|
+
base.insert_checkout_step(
|
7
|
+
:payment_confirm,
|
8
|
+
before: :complete,
|
9
|
+
if: ->(order) { order.intents? }
|
10
|
+
)
|
11
|
+
end
|
12
|
+
|
13
|
+
def process_payments!
|
14
|
+
# Payments are processed in confirm_payment step where after successful
|
15
|
+
# 3D Secure authentication `intent_client_key` is saved for payment.
|
16
|
+
# In case authentication is unsuccessful, `intent_client_key` is removed.
|
17
|
+
return if intents? && payments.valid.last.intent_client_key.present?
|
18
|
+
|
19
|
+
super
|
20
|
+
end
|
21
|
+
|
22
|
+
def intents?
|
23
|
+
payments.valid.map { |p| p.payment_method&.has_preference?(:intents) && p.payment_method&.get_preference(:intents) }.any?
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
Spree::Order.prepend(Spree::OrderDecorator)
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Spree
|
2
|
+
module PaymentDecorator
|
3
|
+
def handle_response(response, success_state, failure_state)
|
4
|
+
if response.success? && response.respond_to?(:params)
|
5
|
+
self.intent_client_key = response.params['client_secret'] if response.params['client_secret']
|
6
|
+
end
|
7
|
+
super
|
8
|
+
end
|
9
|
+
|
10
|
+
def verify!(**options)
|
11
|
+
process_verification(options)
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def process_verification(**options)
|
17
|
+
protect_from_connection_error do
|
18
|
+
response = payment_method.verify(source, options)
|
19
|
+
|
20
|
+
record_response(response)
|
21
|
+
|
22
|
+
if response.success?
|
23
|
+
unless response.authorization.nil?
|
24
|
+
self.response_code = response.authorization
|
25
|
+
|
26
|
+
source.update(status: response.params['status'])
|
27
|
+
end
|
28
|
+
else
|
29
|
+
gateway_error(response)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
Spree::Payment.prepend(Spree::PaymentDecorator)
|
@@ -0,0 +1,39 @@
|
|
1
|
+
<div id='errorBox' class='errorExplanation alert alert-danger' style='display:none'></div>
|
2
|
+
<div id='successBox' class='alert alert-success' style='display:none'></div>
|
3
|
+
<div id='infoBox' class='alert alert-info'><%= t('spree.please_wait_for_confirmation_popup') %></div>
|
4
|
+
|
5
|
+
<% if @client_secret.present? && @pk_key.present? %>
|
6
|
+
<script type="text/javascript" src="https://js.stripe.com/v3/"></script>
|
7
|
+
<script>
|
8
|
+
var form = document.getElementById('checkout_form_payment_confirm');
|
9
|
+
|
10
|
+
function confirmCardPaymentResponseHandler(response) {
|
11
|
+
$.post("/api/v2/storefront/intents/handle_response", { response: response, order_token: "<%= @order.token %>" }).done(function (result) {
|
12
|
+
// conditional needs for spree 3.7
|
13
|
+
if(form.elements["commit"]) {
|
14
|
+
form.elements["commit"].disabled = false;
|
15
|
+
}
|
16
|
+
$('#successBox').html(result.message);
|
17
|
+
$('#successBox').show();
|
18
|
+
form.submit();
|
19
|
+
}).fail(function(result) {
|
20
|
+
if(form.elements["commit"]) {
|
21
|
+
form.elements["commit"].disabled = false;
|
22
|
+
}
|
23
|
+
$('#errorBox').html(result.responseJSON.error);
|
24
|
+
$('#errorBox').show();
|
25
|
+
});
|
26
|
+
}
|
27
|
+
|
28
|
+
var stripeElements = Stripe("<%= @pk_key %>");
|
29
|
+
stripeElements.confirmCardPayment("<%= @client_secret %>").then(function(result) {
|
30
|
+
$('#infoBox').hide();
|
31
|
+
confirmCardPaymentResponseHandler(result);
|
32
|
+
});
|
33
|
+
|
34
|
+
document.addEventListener('DOMContentLoaded', function(){
|
35
|
+
form.elements["commit"].value = "continue"
|
36
|
+
form.elements["commit"].disabled = true
|
37
|
+
});
|
38
|
+
</script>
|
39
|
+
<% end %>
|
data/config/locales/en.yml
CHANGED
@@ -1,7 +1,19 @@
|
|
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.
|
13
|
+
please_wait_for_confirmation_popup: Please wait for payment confirmation popup to appear.
|
14
|
+
payment_successfully_authorized: The payment was successfully authorized.
|
15
|
+
order_state:
|
16
|
+
payment_confirm: Verify payment
|
5
17
|
log_entry:
|
6
18
|
braintree:
|
7
19
|
message: Message
|
@@ -21,3 +33,14 @@ en:
|
|
21
33
|
cvv_result: CVV Result
|
22
34
|
cvc_check: CVC Check
|
23
35
|
address_zip_check: Address ZIP check
|
36
|
+
stripe:
|
37
|
+
ach:
|
38
|
+
account_holder_name: Account Holder Name
|
39
|
+
account_holder_type: Account Holder Type
|
40
|
+
routing_number: Routing Number
|
41
|
+
account_number: Account Number
|
42
|
+
verify_account_number: Verify Account Number
|
43
|
+
verify_bank_account: Verify Bank Account
|
44
|
+
first_deposit: First Deposit
|
45
|
+
second_deposit: Second Deposit
|
46
|
+
bank_transfer: bank_transfer
|