solidus_stripe 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.circleci/config.yml +35 -0
- data/.gem_release.yml +8 -0
- data/.gitignore +11 -0
- data/.rspec +1 -0
- data/.rubocop.yml +319 -0
- data/.travis.yml +28 -0
- data/Gemfile +25 -0
- data/LICENSE.md +26 -0
- data/README.md +115 -0
- data/Rakefile +23 -0
- data/app/models/spree/payment_method/stripe_credit_card.rb +136 -0
- data/bin/rails +7 -0
- data/db/migrate/20181010123508_update_stripe_payment_method_type_to_credit_card.rb +21 -0
- data/db/seeds.rb +26 -0
- data/lib/assets/stylesheets/spree/frontend/solidus_stripe.scss +6 -0
- data/lib/generators/solidus_stripe/install/install_generator.rb +46 -0
- data/lib/solidus_stripe.rb +7 -0
- data/lib/solidus_stripe/engine.rb +23 -0
- data/lib/solidus_stripe/version.rb +5 -0
- data/lib/tasks/solidus_stripe/db/seed.rake +14 -0
- data/lib/views/api/spree/api/payments/source_views/_stripe.json.jbuilder +3 -0
- data/lib/views/backend/spree/admin/log_entries/_stripe.html.erb +28 -0
- data/lib/views/backend/spree/admin/payments/source_forms/_stripe.html.erb +1 -0
- data/lib/views/backend/spree/admin/payments/source_views/_stripe.html.erb +1 -0
- data/lib/views/frontend/spree/checkout/existing_payment/_stripe.html.erb +1 -0
- data/lib/views/frontend/spree/checkout/payment/_stripe.html.erb +6 -0
- data/lib/views/frontend/spree/checkout/payment/v2/_javascript.html.erb +77 -0
- data/lib/views/frontend/spree/checkout/payment/v3/_stripe.html.erb +133 -0
- data/solidus_stripe.gemspec +45 -0
- data/spec/features/stripe_checkout_spec.rb +279 -0
- data/spec/models/spree/payment_method/stripe_credit_card_spec.rb +205 -0
- data/spec/spec_helper.rb +20 -0
- metadata +264 -0
data/README.md
ADDED
@@ -0,0 +1,115 @@
|
|
1
|
+
Solidus Stripe
|
2
|
+
===============
|
3
|
+
|
4
|
+
[![CircleCI](https://circleci.com/gh/solidusio/solidus_stripe.svg?style=svg)](https://circleci.com/gh/solidusio/solidus_stripe)
|
5
|
+
|
6
|
+
Stripe Payment Method for Solidus. It works as a wrapper for the ActiveMerchant Stripe gateway.
|
7
|
+
|
8
|
+
---
|
9
|
+
|
10
|
+
Installation
|
11
|
+
------------
|
12
|
+
|
13
|
+
In your Gemfile:
|
14
|
+
|
15
|
+
```ruby
|
16
|
+
gem "solidus_stripe", github: "solidusio/solidus_stripe"
|
17
|
+
```
|
18
|
+
|
19
|
+
Then run from the command line:
|
20
|
+
|
21
|
+
```shell
|
22
|
+
bundle install
|
23
|
+
bundle exec rails g solidus_stripe:install
|
24
|
+
bundle exec rails db:migrate
|
25
|
+
```
|
26
|
+
|
27
|
+
Usage
|
28
|
+
-----
|
29
|
+
|
30
|
+
Navigate to *Settings > Payments > Payment Methods* in the admin panel.
|
31
|
+
You can now create a new payment method that uses Stripe by selecting
|
32
|
+
`Stripe credit card` under Type in the New Payment Method form and saving.
|
33
|
+
The Stripe payment method's extra fields will be now shown in the form.
|
34
|
+
|
35
|
+
**Configure via database configuration**
|
36
|
+
|
37
|
+
If you want to store your Stripe credentials in the database just
|
38
|
+
fill the new fields in the form, selecting `custom` (default) in the
|
39
|
+
Preference Source field.
|
40
|
+
|
41
|
+
**Configure via static configuration**
|
42
|
+
|
43
|
+
If you want to store your credentials into your codebase or use ENV
|
44
|
+
variables you can create the following static configuration:
|
45
|
+
|
46
|
+
```ruby
|
47
|
+
# config/initializers/spree.rb
|
48
|
+
|
49
|
+
Spree.config do |config|
|
50
|
+
# ...
|
51
|
+
|
52
|
+
config.static_model_preferences.add(
|
53
|
+
Spree::PaymentMethod::StripeCreditCard,
|
54
|
+
'stripe_env_credentials',
|
55
|
+
secret_key: ENV['STRIPE_SECRET_KEY'],
|
56
|
+
publishable_key: ENV['STRIPE_PUBLISHABLE_KEY'],
|
57
|
+
v3_elements: false,
|
58
|
+
server: Rails.env.production? ? 'production' : 'test',
|
59
|
+
test_mode: !Rails.env.production?
|
60
|
+
)
|
61
|
+
end
|
62
|
+
```
|
63
|
+
|
64
|
+
Once your server has been restarted, you can select in the Preference
|
65
|
+
Source field a new entry called `stripe_env_credentials`. After saving,
|
66
|
+
your application will start using the static configuration to process
|
67
|
+
Stripe payments.
|
68
|
+
|
69
|
+
|
70
|
+
Migrating from solidus_gateway
|
71
|
+
------------------------------
|
72
|
+
|
73
|
+
If you were previously using `solidus_gateway` gem you might want to
|
74
|
+
check out our [Wiki page](https://github.com/solidusio/solidus_stripe/wiki/Migrating-from-solidus_gateway)
|
75
|
+
that describes how to handle this migration.
|
76
|
+
|
77
|
+
Testing
|
78
|
+
-------
|
79
|
+
|
80
|
+
Then just run the following to automatically build a dummy app if necessary and
|
81
|
+
run the tests:
|
82
|
+
|
83
|
+
```shell
|
84
|
+
bundle exec rake
|
85
|
+
```
|
86
|
+
|
87
|
+
Releasing
|
88
|
+
---------
|
89
|
+
|
90
|
+
We use [gem-release](https://github.com/svenfuchs/gem-release) to release this
|
91
|
+
extension with ease.
|
92
|
+
|
93
|
+
Supposing you are on the master branch and you are working on a fork of this
|
94
|
+
extension, `upstream` is the main remote and you have write access to it, you
|
95
|
+
can simply run:
|
96
|
+
|
97
|
+
```
|
98
|
+
gem bump --version minor --tag --release --remote upstream
|
99
|
+
```
|
100
|
+
|
101
|
+
This command will:
|
102
|
+
|
103
|
+
- bump the gem version to the next minor (changing the `version.rb` file)
|
104
|
+
- commit the change and push it to upstream master
|
105
|
+
- create a git tag
|
106
|
+
- push the tag to the upstream remote
|
107
|
+
- release the new version on RubyGems
|
108
|
+
|
109
|
+
Or you can run these commands individually:
|
110
|
+
|
111
|
+
```
|
112
|
+
gem bump --version minor --remote upstream
|
113
|
+
gem tag --remote upstream
|
114
|
+
gem release
|
115
|
+
```
|
data/Rakefile
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bundler'
|
4
|
+
Bundler::GemHelper.install_tasks
|
5
|
+
|
6
|
+
require 'rspec/core/rake_task'
|
7
|
+
require 'spree/testing_support/common_rake'
|
8
|
+
|
9
|
+
RSpec::Core::RakeTask.new
|
10
|
+
|
11
|
+
task :default do
|
12
|
+
if Dir["spec/dummy"].empty?
|
13
|
+
Rake::Task[:test_app].invoke
|
14
|
+
Dir.chdir("../../")
|
15
|
+
end
|
16
|
+
Rake::Task[:spec].invoke
|
17
|
+
end
|
18
|
+
|
19
|
+
desc "Generates a dummy app for testing"
|
20
|
+
task :test_app do
|
21
|
+
ENV['LIB_NAME'] = 'solidus_stripe'
|
22
|
+
Rake::Task['common:test_app'].invoke
|
23
|
+
end
|
@@ -0,0 +1,136 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Spree
|
4
|
+
class PaymentMethod
|
5
|
+
class StripeCreditCard < Spree::PaymentMethod::CreditCard
|
6
|
+
preference :secret_key, :string
|
7
|
+
preference :publishable_key, :string
|
8
|
+
preference :v3_elements, :boolean
|
9
|
+
|
10
|
+
CARD_TYPE_MAPPING = {
|
11
|
+
'American Express' => 'american_express',
|
12
|
+
'Diners Club' => 'diners_club',
|
13
|
+
'Visa' => 'visa'
|
14
|
+
}
|
15
|
+
|
16
|
+
def partial_name
|
17
|
+
'stripe'
|
18
|
+
end
|
19
|
+
|
20
|
+
def v3_elements?
|
21
|
+
!!preferred_v3_elements
|
22
|
+
end
|
23
|
+
|
24
|
+
def gateway_class
|
25
|
+
ActiveMerchant::Billing::StripeGateway
|
26
|
+
end
|
27
|
+
|
28
|
+
def payment_profiles_supported?
|
29
|
+
true
|
30
|
+
end
|
31
|
+
|
32
|
+
def purchase(money, creditcard, transaction_options)
|
33
|
+
gateway.purchase(*options_for_purchase_or_auth(money, creditcard, transaction_options))
|
34
|
+
end
|
35
|
+
|
36
|
+
def authorize(money, creditcard, transaction_options)
|
37
|
+
gateway.authorize(*options_for_purchase_or_auth(money, creditcard, transaction_options))
|
38
|
+
end
|
39
|
+
|
40
|
+
def capture(money, response_code, transaction_options)
|
41
|
+
gateway.capture(money, response_code, transaction_options)
|
42
|
+
end
|
43
|
+
|
44
|
+
def credit(money, _creditcard, response_code, _transaction_options)
|
45
|
+
gateway.refund(money, response_code, {})
|
46
|
+
end
|
47
|
+
|
48
|
+
def void(response_code, _creditcard, _transaction_options)
|
49
|
+
gateway.void(response_code, {})
|
50
|
+
end
|
51
|
+
|
52
|
+
def cancel(response_code)
|
53
|
+
gateway.void(response_code, {})
|
54
|
+
end
|
55
|
+
|
56
|
+
def create_profile(payment)
|
57
|
+
return unless payment.source.gateway_customer_profile_id.nil?
|
58
|
+
|
59
|
+
options = {
|
60
|
+
email: payment.order.email,
|
61
|
+
login: preferred_secret_key,
|
62
|
+
}.merge! address_for(payment)
|
63
|
+
|
64
|
+
source = update_source!(payment.source)
|
65
|
+
if source.number.blank? && source.gateway_payment_profile_id.present?
|
66
|
+
creditcard = source.gateway_payment_profile_id
|
67
|
+
else
|
68
|
+
creditcard = source
|
69
|
+
end
|
70
|
+
|
71
|
+
response = gateway.store(creditcard, options)
|
72
|
+
if response.success?
|
73
|
+
payment.source.update_attributes!({
|
74
|
+
cc_type: payment.source.cc_type, # side-effect of update_source!
|
75
|
+
gateway_customer_profile_id: response.params['id'],
|
76
|
+
gateway_payment_profile_id: response.params['default_source'] || response.params['default_card']
|
77
|
+
})
|
78
|
+
|
79
|
+
else
|
80
|
+
payment.send(:gateway_error, response.message)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
|
86
|
+
# In this gateway, what we call 'secret_key' is the 'login'
|
87
|
+
def options
|
88
|
+
options = super
|
89
|
+
options.merge(login: preferred_secret_key)
|
90
|
+
end
|
91
|
+
|
92
|
+
def options_for_purchase_or_auth(money, creditcard, transaction_options)
|
93
|
+
options = {}
|
94
|
+
options[:description] = "Spree Order ID: #{transaction_options[:order_id]}"
|
95
|
+
options[:currency] = transaction_options[:currency]
|
96
|
+
|
97
|
+
if customer = creditcard.gateway_customer_profile_id
|
98
|
+
options[:customer] = customer
|
99
|
+
end
|
100
|
+
if token_or_card_id = creditcard.gateway_payment_profile_id
|
101
|
+
# The Stripe ActiveMerchant gateway supports passing the token directly as the creditcard parameter
|
102
|
+
# The Stripe ActiveMerchant gateway supports passing the customer_id and credit_card id
|
103
|
+
# https://github.com/Shopify/active_merchant/issues/770
|
104
|
+
creditcard = token_or_card_id
|
105
|
+
end
|
106
|
+
[money, creditcard, options]
|
107
|
+
end
|
108
|
+
|
109
|
+
def address_for(payment)
|
110
|
+
{}.tap do |options|
|
111
|
+
if address = payment.order.bill_address
|
112
|
+
options[:address] = {
|
113
|
+
address1: address.address1,
|
114
|
+
address2: address.address2,
|
115
|
+
city: address.city,
|
116
|
+
zip: address.zipcode
|
117
|
+
}
|
118
|
+
|
119
|
+
if country = address.country
|
120
|
+
options[:address][:country] = country.name
|
121
|
+
end
|
122
|
+
|
123
|
+
if state = address.state
|
124
|
+
options[:address].merge!(state: state.name)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def update_source!(source)
|
131
|
+
source.cc_type = CARD_TYPE_MAPPING[source.cc_type] if CARD_TYPE_MAPPING.include?(source.cc_type)
|
132
|
+
source
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
data/bin/rails
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class UpdateStripePaymentMethodTypeToCreditCard < SolidusSupport::Migration[5.1]
|
4
|
+
def up
|
5
|
+
Spree::PaymentMethod.where(type: 'Spree::Gateway::StripeGateway').update_all(type: 'Spree::PaymentMethod::StripeCreditCard')
|
6
|
+
|
7
|
+
Spree::Preference.where("#{ActiveRecord::Base.connection.quote_column_name('key')} LIKE 'spree/gateway/stripe_gateway'").each do |pref|
|
8
|
+
pref.key = pref.key.gsub('spree/gateway/stripe_gateway', 'spree/payment_method/stripe_credit_card')
|
9
|
+
pref.save
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def down
|
14
|
+
Spree::PaymentMethod.where(type: 'Spree::PaymentMethod::StripeCreditCard').update_all(type: 'Spree::Gateway::StripeGateway')
|
15
|
+
|
16
|
+
Spree::Preference.where("#{ActiveRecord::Base.connection.quote_column_name('key')} LIKE 'spree/payment_method/stripe_credit_card'").each do |pref|
|
17
|
+
pref.key = pref.key.gsub('spree/payment_method/stripe_credit_card', 'spree/gateway/stripe_gateway')
|
18
|
+
pref.save
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/db/seeds.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
secret_key = ENV['STRIPE_SEEDS_SECRET_KEY']
|
4
|
+
publishable_key = ENV['STRIPE_SEEDS_PUBLISHABLE_KEY']
|
5
|
+
|
6
|
+
puts "Loading seed: solidus_stripe/stripe_payment_method"
|
7
|
+
|
8
|
+
if secret_key.blank? || publishable_key.blank?
|
9
|
+
puts "Failure: You have to set both STRIPE_SEEDS_SECRET_KEY and STRIPE_SEEDS_PUBLISHABLE_KEY environment variables."
|
10
|
+
else
|
11
|
+
stripe_payment_method = Spree::PaymentMethod::StripeCreditCard.new do |payment_method|
|
12
|
+
payment_method.name = 'Credit Card'
|
13
|
+
payment_method.preferred_test_mode = true
|
14
|
+
payment_method.preferred_secret_key = secret_key
|
15
|
+
payment_method.preferred_publishable_key = publishable_key
|
16
|
+
end
|
17
|
+
|
18
|
+
if stripe_payment_method.save
|
19
|
+
puts "Stripe Payment Method correctly created."
|
20
|
+
else
|
21
|
+
puts "There was some problems with creating Stripe Payment Method:"
|
22
|
+
stripe_payment_method.errors.full_messages.each do |error|
|
23
|
+
puts error
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SolidusStripe
|
4
|
+
module Generators
|
5
|
+
class InstallGenerator < Rails::Generators::Base
|
6
|
+
class_option :migrate, type: :boolean, default: true
|
7
|
+
class_option :auto_run_migrations, type: :boolean, default: false
|
8
|
+
class_option :auto_run_seeds, type: :boolean, default: false
|
9
|
+
|
10
|
+
def add_stylesheets
|
11
|
+
filename = 'vendor/assets/stylesheets/spree/frontend/all.css'
|
12
|
+
if File.file? filename
|
13
|
+
inject_into_file filename, " *= require spree/frontend/solidus_stripe\n", before: '*/', verbose: true
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def add_migrations
|
18
|
+
run 'bundle exec rake railties:install:migrations FROM=solidus_stripe'
|
19
|
+
end
|
20
|
+
|
21
|
+
def run_migrations
|
22
|
+
if options.migrate? && running_migrations?
|
23
|
+
run 'bundle exec rake db:migrate'
|
24
|
+
else
|
25
|
+
puts "Skiping rake db:migrate, don't forget to run it!"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def populate_seed_data
|
30
|
+
return unless options.auto_run_seeds?
|
31
|
+
|
32
|
+
say_status :loading, 'stripe seed data'
|
33
|
+
rake('db:seed:solidus_stripe')
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def running_migrations?
|
39
|
+
options.auto_run_migrations? || begin
|
40
|
+
response = ask 'Would you like to run the migrations now? [Y/n]'
|
41
|
+
['', 'y'].include? response.downcase
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SolidusStripe
|
4
|
+
class Engine < Rails::Engine
|
5
|
+
engine_name 'solidus_stripe'
|
6
|
+
|
7
|
+
initializer "spree.payment_method.add_stripe_credit_card", after: "spree.register.payment_methods" do |app|
|
8
|
+
app.config.spree.payment_methods << Spree::PaymentMethod::StripeCreditCard
|
9
|
+
end
|
10
|
+
|
11
|
+
if SolidusSupport.backend_available?
|
12
|
+
paths["app/views"] << "lib/views/backend"
|
13
|
+
end
|
14
|
+
|
15
|
+
if SolidusSupport.frontend_available?
|
16
|
+
paths["app/views"] << "lib/views/frontend"
|
17
|
+
end
|
18
|
+
|
19
|
+
if SolidusSupport.api_available?
|
20
|
+
paths["app/views"] << "lib/views/api"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
namespace :db do
|
4
|
+
namespace :seed do
|
5
|
+
desc 'Loads stripe sample data'
|
6
|
+
task solidus_stripe: :environment do
|
7
|
+
seed_file = Dir[SolidusStripe::Engine.root.join('db', 'seeds.rb')][0]
|
8
|
+
return unless File.exist?(seed_file)
|
9
|
+
|
10
|
+
puts "Seeding #{seed_file}..."
|
11
|
+
load(seed_file)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|