trado_stripe_module 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/Rakefile +34 -0
  4. data/app/assets/images/american-express.png +0 -0
  5. data/app/assets/images/diners-club.png +0 -0
  6. data/app/assets/images/discover.png +0 -0
  7. data/app/assets/images/grouped-cards.png +0 -0
  8. data/app/assets/images/jcb.png +0 -0
  9. data/app/assets/images/mastercard.png +0 -0
  10. data/app/assets/images/stripe.png +0 -0
  11. data/app/assets/images/visa.png +0 -0
  12. data/app/assets/javascripts/trado-stripe.js +90 -0
  13. data/app/controllers/trado_stripe_module/stripe_controller.rb +40 -0
  14. data/app/helpers/stripe_helper.rb +26 -0
  15. data/app/views/admin/_order_credit_card_data.html.erb +7 -0
  16. data/app/views/admin/_store_setting_fields.html.erb +16 -0
  17. data/app/views/carts/_stripe_credit_card_form.html.erb +16 -0
  18. data/app/views/layout/_meta_tags.html.erb +2 -0
  19. data/app/views/orders/_credit_card_data.html.erb +5 -0
  20. data/lib/generators/templates/controller.rb +6 -0
  21. data/lib/generators/templates/helper.rb +6 -0
  22. data/lib/generators/templates/migration.rb +19 -0
  23. data/lib/generators/trado_stripe_module/install_generator.rb +44 -0
  24. data/lib/generators/trado_stripe_module/views_generator.rb +13 -0
  25. data/lib/tasks/trado_stripe_module_tasks.rake +4 -0
  26. data/lib/trado_stripe_module.rb +12 -0
  27. data/lib/trado_stripe_module/active_record.rb +77 -0
  28. data/lib/trado_stripe_module/engine.rb +6 -0
  29. data/lib/trado_stripe_module/striper.rb +79 -0
  30. data/lib/trado_stripe_module/version.rb +3 -0
  31. data/test/dummy/README.rdoc +28 -0
  32. data/test/dummy/Rakefile +6 -0
  33. data/test/dummy/app/assets/javascripts/application.js +13 -0
  34. data/test/dummy/app/assets/stylesheets/application.css +15 -0
  35. data/test/dummy/app/controllers/application_controller.rb +5 -0
  36. data/test/dummy/app/helpers/application_helper.rb +2 -0
  37. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  38. data/test/dummy/bin/bundle +3 -0
  39. data/test/dummy/bin/rails +4 -0
  40. data/test/dummy/bin/rake +4 -0
  41. data/test/dummy/bin/setup +29 -0
  42. data/test/dummy/config.ru +4 -0
  43. data/test/dummy/config/application.rb +26 -0
  44. data/test/dummy/config/boot.rb +5 -0
  45. data/test/dummy/config/database.yml +25 -0
  46. data/test/dummy/config/environment.rb +5 -0
  47. data/test/dummy/config/environments/development.rb +41 -0
  48. data/test/dummy/config/environments/production.rb +79 -0
  49. data/test/dummy/config/environments/test.rb +42 -0
  50. data/test/dummy/config/initializers/assets.rb +11 -0
  51. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  52. data/test/dummy/config/initializers/cookies_serializer.rb +3 -0
  53. data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  54. data/test/dummy/config/initializers/inflections.rb +16 -0
  55. data/test/dummy/config/initializers/mime_types.rb +4 -0
  56. data/test/dummy/config/initializers/session_store.rb +3 -0
  57. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  58. data/test/dummy/config/locales/en.yml +23 -0
  59. data/test/dummy/config/routes.rb +56 -0
  60. data/test/dummy/config/secrets.yml +22 -0
  61. data/test/dummy/public/404.html +67 -0
  62. data/test/dummy/public/422.html +67 -0
  63. data/test/dummy/public/500.html +66 -0
  64. data/test/dummy/public/favicon.ico +0 -0
  65. data/test/test_helper.rb +20 -0
  66. data/test/trado_stripe_module_test.rb +7 -0
  67. metadata +187 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 42993db433214fe1a517ed66f498d76696762611
4
+ data.tar.gz: e9c9954bb23a3c0a8a827181f7da533d14c2db9f
5
+ SHA512:
6
+ metadata.gz: 3f35644e677c962061518689518c4ab45d0df86da8372d4e507f8d48125f40a20ae0f09b281a3d10da44c19f2d2031f7de75891745b7af6a161f02faa8db6d8a
7
+ data.tar.gz: c7c1adbdcbbaee42b2a1fcedf7180704bf8c1243757980c6921b821d4276a96af8c516937865dbd9dc2605c06ac298ed191171d9f1037eae84a1ac7b2968c821
@@ -0,0 +1,20 @@
1
+ Copyright 2016 Tom Dallimore
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,34 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'rdoc/task'
8
+
9
+ RDoc::Task.new(:rdoc) do |rdoc|
10
+ rdoc.rdoc_dir = 'rdoc'
11
+ rdoc.title = 'TradoStripeModule'
12
+ rdoc.options << '--line-numbers'
13
+ rdoc.rdoc_files.include('README.rdoc')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ end
16
+
17
+
18
+
19
+
20
+
21
+
22
+ Bundler::GemHelper.install_tasks
23
+
24
+ require 'rake/testtask'
25
+
26
+ Rake::TestTask.new(:test) do |t|
27
+ t.libs << 'lib'
28
+ t.libs << 'test'
29
+ t.pattern = 'test/**/*_test.rb'
30
+ t.verbose = false
31
+ end
32
+
33
+
34
+ task default: :test
Binary file
@@ -0,0 +1,90 @@
1
+ var order;
2
+
3
+ jQuery(function() {
4
+ Stripe.setPublishableKey($('meta[name="stripe-key"]').attr('content'));
5
+ order.setupForm();
6
+ });
7
+
8
+ order = {
9
+ setupForm: function() {
10
+ $('.process_order').submit(function() {
11
+ var $form = $(this);
12
+ $('input[type=submit]').attr('disabled', true);
13
+ $('.stripe-error, .alert-stripe-checkout, .stripe-error-terms').remove();
14
+ $('#stripe_card_error').html('');
15
+ $('.field_with_errors').each(function()
16
+ {
17
+ $(this).find('input').unwrap();
18
+ $(this).find('select').unwrap();
19
+ });
20
+ if ($('#stripe_card_number').length) {
21
+ order.validateForm($form);
22
+ return false;
23
+ } else {
24
+ return true;
25
+ }
26
+ });
27
+ },
28
+ processCard: function() {
29
+ var card;
30
+ card = {
31
+ number: $('#stripe_card_number').val(),
32
+ cvc: $('#stripe_card_code').val(),
33
+ expMonth: $('#stripe_card_month').val(),
34
+ expYear: $('#stripe_card_year').val()
35
+ };
36
+ Stripe.createToken(card, order.handleStripeResponse);
37
+ },
38
+ handleStripeResponse: function(status, response) {
39
+ if (status === 200) {
40
+ $('#order_stripe_card_token').val(response.id);
41
+ $('.process_order')[0].submit();
42
+ } else {
43
+ $('#stripe_card_error').text(response.error.message);
44
+ $('#stripe-form-fields input, #stripe-form-fields select').each(function()
45
+ {
46
+ $(this).wrap('<div class="field_with_errors"></div>');
47
+ });
48
+ stripeCheckoutErrors();
49
+ $('input[type=submit]').attr('disabled', false);
50
+ $('#checkoutLoadingModal').modal('hide');
51
+ }
52
+ },
53
+ validateForm: function(form)
54
+ {
55
+ $.ajax(
56
+ {
57
+ url: '/carts/stripe/confirm',
58
+ type: 'POST',
59
+ data: form.serialize(),
60
+ dataType: 'json',
61
+ success: function (data)
62
+ {
63
+ order.processCard();
64
+ },
65
+ error: function(xhr, evt, status)
66
+ {
67
+ order.jsonErrors(xhr, evt, status, form);
68
+ }
69
+ });
70
+ },
71
+ jsonErrors: function(xhr, evt, status, form)
72
+ {
73
+ var content, value, _i, _len, _ref, $this;
74
+ $this = form;
75
+ errorKeys = $.parseJSON(xhr.responseText).errors;
76
+ $('input[type=submit]').attr('disabled', false);
77
+ $('#checkoutLoadingModal').modal('hide');
78
+
79
+ $.each(errorKeys, function(index, value)
80
+ {
81
+ var $input = $('#' + value);
82
+ if (value === 'terms')
83
+ {
84
+ stripeTermsValidationMessage();
85
+ }
86
+ $input.wrap('<div class="field_with_errors"></div>')
87
+ });
88
+ stripeCheckoutErrors();
89
+ }
90
+ };
@@ -0,0 +1,40 @@
1
+ class TradoStripeModule::StripeController < ApplicationController
2
+ skip_before_action :authenticate_user!
3
+ include CartBuilder
4
+
5
+ def confirm
6
+ set_order
7
+ set_cart_totals
8
+ set_grouped_countries
9
+ set_browser_data
10
+ @order.attributes = params[:order]
11
+ respond_to do |format|
12
+ format.html do
13
+ begin
14
+ if @order.save
15
+ set_order_id_session
16
+ @order.remove_redundant_stripe_cards
17
+ @order.create_stripe_card
18
+ @order.calculate(current_cart, Store.tax_rate)
19
+ redirect_to confirm_order_url(@order)
20
+ else
21
+ flash_message :error, 'An error ocurred with your order. Please try again.'
22
+ render theme_presenter.page_template_path('carts/checkout'), layout: theme_presenter.layout_template_path
23
+ end
24
+ rescue Stripe::InvalidRequestError, Stripe::APIConnectionError => e
25
+ flash_message :error, 'An error ocurred with your order. Please try again.'
26
+ Rails.logger.error "Stripe Error: #{e} ? #{@order.email} | #{@order.id}"
27
+ redirect_to checkout_carts_url
28
+ end
29
+ end
30
+
31
+ format.json do
32
+ if @order.valid?
33
+ render json: { }, status: 200
34
+ else
35
+ render json: { errors: @order.errors.keys.map{|e| e.to_s.split('.').join('_') } }, status: 422
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,26 @@
1
+ module StripeHelper
2
+
3
+ def stripe_checkout_fields f
4
+ render "themes/#{Store.settings.theme.name}/carts/stripe_credit_card_form", format: [:html], f: f
5
+ rescue ActionView::MissingTemplate
6
+ render 'carts/stripe_credit_card_form', format: [:html], f: f
7
+ end
8
+
9
+ def stripe_credit_card_data order
10
+ render "themes/#{Store.settings.theme.name}/orders/credit_card_data", format: [:html], order: order
11
+ rescue ActionView::MissingTemplate
12
+ render 'orders/credit_card_data', format: [:html], order: order
13
+ end
14
+
15
+ def stripe_application_layout
16
+ render 'layout/meta_tags', format: [:html]
17
+ end
18
+
19
+ def stripe_admin_credit_card_data order
20
+ render 'admin/order_credit_card_data', format: [:html], order: order
21
+ end
22
+
23
+ def stripe_store_setting_fields f
24
+ render 'admin/store_setting_fields', format: [:html], f: f
25
+ end
26
+ end
@@ -0,0 +1,7 @@
1
+ <div id="stripe-card-data">
2
+ <%= image_tag "#{order.stripe_card_brand.parameterize}.png" %>
3
+ <ul>
4
+ <li><b>Card Number</b>: **** **** **** <%= order.stripe_card_last4 %></li>
5
+ <li><b>Expiry Date</b>: <%= order.stripe_card_expiry_date %></li>
6
+ </ul>
7
+ </div>
@@ -0,0 +1,16 @@
1
+ <div class="threecol">
2
+ <h3>Stripe</h3>
3
+ <p>Set the text you would like to display on your customer credit card statements.</p>
4
+ </div>
5
+ <div class="ninecol last">
6
+ <fieldset class="widget widget-content">
7
+ <div class="row">
8
+ <div class="twelvecol">
9
+ <div class="form-group form-last">
10
+ <%= f.label 'Statement Descriptor' %>
11
+ <%= f.text_field :stripe_statement_descriptor, :class => 'form-control' %>
12
+ </div>
13
+ </div>
14
+ </div>
15
+ </fieldset>
16
+ </div>
@@ -0,0 +1,16 @@
1
+ <div id='stripe-form-wrapper'>
2
+ <%= f.radio_button(:payment_type, 'stripe', checked: true) %>
3
+ <%= image_tag('grouped-cards.png', height: 50) %>
4
+ </div>
5
+ <div id="stripe-form-fields">
6
+ <%= f.hidden_field :stripe_card_token %>
7
+ <%= label_tag :stripe_card_number, "Credit Card Number" %>
8
+ <%= text_field_tag :stripe_card_number, nil, name: nil %>
9
+ <%= label_tag :stripe_card_month, "Expiry Month" %>
10
+ <%= select_month nil, {add_month_numbers: false}, {name: nil, id: "stripe_card_month" } %>
11
+ <%= label_tag :stripe_card_year, "Expiry Year" %>
12
+ <%= select_year nil, {start_year: Date.today.year, end_year: Date.today.year+15}, {name: nil, id: "stripe_card_year" } %>
13
+ <%= label_tag :stripe_card_code, "CV Code" %>
14
+ <%= text_field_tag :stripe_card_code, nil, name: nil %>
15
+ </div>
16
+ <div id="stripe_card_error"></div>
@@ -0,0 +1,2 @@
1
+ <%= stripe_javascript_tag %>
2
+ <%= tag :meta, name: "stripe-key", content: Rails.application.secrets.stripe_public_key %>
@@ -0,0 +1,5 @@
1
+ <div id="stripe-card-data">
2
+ <%= image_tag "#{order.stripe_card_brand.parameterize}.png" %>
3
+ <p><b>Card Number</b>: **** **** **** <%= order.stripe_card_last4 %></p>
4
+ <p><b>Expiry Date</b>: <%= order.stripe_card_expiry_date %></p>
5
+ </div>
@@ -0,0 +1,6 @@
1
+ class Carts::StripeController < TradoStripeModule::StripeController
2
+
3
+ def confirm
4
+ super
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ module StripeHelper
2
+
3
+ def stripe_form_tag f
4
+ raw("<div class='paypal-form-wrapper'>#{f.radio_button(:payment_type, 'paypal', checked: true)}#{image_tag('paypal-icon.png')}</div>")
5
+ end
6
+ end
@@ -0,0 +1,19 @@
1
+ class AddStripeAttributes < ActiveRecord::Migration
2
+ def self.up
3
+ add_column :orders, :stripe_customer_id, :string
4
+ add_column :orders, :stripe_card_last4, :string
5
+ add_column :orders, :stripe_card_brand, :string
6
+ add_column :orders, :stripe_card_expiry_date, :string
7
+ add_column :transactions, :stripe_charge_id, :string
8
+ add_column :store_settings, :stripe_statement_descriptor, :string
9
+ end
10
+
11
+ def self.down
12
+ remove_column :orders, :stripe_customer_id, :string
13
+ remove_column :orders, :stripe_card_last4, :string
14
+ remove_column :orders, :stripe_card_brand, :string
15
+ remove_column :orders, :stripe_card_expiry_date, :string
16
+ remove_column :transactions, :stripe_charge_id, :string
17
+ remove_column :store_settings, :stripe_statement_descriptor, :string
18
+ end
19
+ end
@@ -0,0 +1,44 @@
1
+ module TradoStripeModule
2
+ module Generators
3
+ class InstallGenerator < Rails::Generators::Base
4
+ source_root File.expand_path("../../templates", __FILE__)
5
+
6
+ def copy_migration
7
+ unless stripe_migration_already_exists?
8
+ timestamp_number = Time.now.utc.strftime("%Y%m%d%H%M%S").to_i
9
+ copy_file "migration.rb", "db/migrate/#{timestamp_number}_add_stripe_attributes.rb"
10
+ end
11
+ end
12
+
13
+ def copy_controller
14
+ template "controller.rb", "app/controllers/carts/stripe_controller.rb"
15
+ end
16
+
17
+ def assign_model_concerns
18
+ order_content = <<-CONTENT
19
+
20
+ has_order_stripe
21
+ CONTENT
22
+ transaction_content = <<-CONTENT
23
+
24
+ has_transaction_stripe
25
+ CONTENT
26
+
27
+ store_setting_content = <<-CONTENT
28
+
29
+ has_store_setting_stripe
30
+ CONTENT
31
+
32
+ inject_into_file "app/models/order.rb", order_content, after: "class Order < ActiveRecord::Base"
33
+ inject_into_file "app/models/transaction.rb", transaction_content, after: "class Transaction < ActiveRecord::Base"
34
+ inject_into_file "app/models/store_setting.rb", store_setting_content, after: "class StoreSetting < ActiveRecord::Base"
35
+ end
36
+
37
+ private
38
+
39
+ def stripe_migration_already_exists?
40
+ Dir.glob("#{File.join(destination_root, File.join("db", "migrate"))}/[0-9]*_*.rb").grep(/\d+_add_stripe_attributes.rb$/).first
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,13 @@
1
+ module TradoStripeModule
2
+ module Generators
3
+ class ViewsGenerator < Rails::Generators::Base
4
+ source_root File.expand_path("../../../../app/views", __FILE__)
5
+
6
+ def copy_views
7
+ copy_file 'carts/_stripe_credit_card_form.html.erb', "app/views/themes/#{Store.settings.theme.name}/carts/_stripe_credit_card_form.html.erb"
8
+ copy_file 'orders/_credit_card_data.html.erb', "app/views/themes/#{Store.settings.theme.name}/orders/_credit_card_data.html.erb"
9
+ puts "Copied to your #{Store.settings.theme.name} theme!"
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :trado_stripe_module do
3
+ # # Task goes here
4
+ # end
@@ -0,0 +1,12 @@
1
+ module TradoStripeModule
2
+ end
3
+
4
+ require 'stripe'
5
+ require 'stripe-rails'
6
+
7
+ require 'trado_stripe_module/engine'
8
+ require 'trado_stripe_module/version'
9
+ require 'trado_stripe_module/active_record'
10
+ require 'trado_stripe_module/striper'
11
+
12
+ ActiveRecord::Base.send(:include, TradoStripeModule::ActiveRecord)
@@ -0,0 +1,77 @@
1
+ module TradoStripeModule
2
+ module ActiveRecord
3
+ extend ActiveSupport::Concern
4
+
5
+ module ClassMethods
6
+ def has_order_stripe
7
+ attr_accessible :stripe_customer_id, :stripe_card_token, :stripe_card_last4, :stripe_card_brand, :stripe_card_expiry_date
8
+ attr_accessor :stripe_card_token
9
+
10
+ after_commit :create_stripe_customer, on: :create, if: :no_stripe_customer_id?
11
+ after_commit :update_stripe_customer, on: :update, if: :email_or_billing_name_changed?
12
+
13
+ define_method("stripe_customer") do
14
+ begin
15
+ @customer ||= Stripe::Customer.retrieve(stripe_customer_id)
16
+ rescue Stripe::InvalidRequestError
17
+ create_stripe_customer
18
+ end
19
+ end
20
+
21
+ define_method("stripe_cards") do
22
+ stripe_customer.sources.all(:object => "card")['data']
23
+ end
24
+
25
+ define_method("default_card") do
26
+ stripe_customer.sources.retrieve(stripe_customer.default_source)
27
+ end
28
+
29
+ define_method("create_stripe_card") do
30
+ card = stripe_customer.sources.create(source: stripe_card_token)
31
+ self.update(
32
+ stripe_card_last4: card.last4,
33
+ stripe_card_brand: card.brand,
34
+ stripe_card_expiry_date: "#{card.exp_month}/#{card.exp_year}"
35
+ )
36
+ end
37
+
38
+ define_method("remove_redundant_stripe_cards") do
39
+ stripe_cards.each do |card|
40
+ stripe_customer.sources.retrieve(card.id).delete()
41
+ end
42
+ end
43
+
44
+ define_method("create_stripe_customer") do
45
+ customer = Stripe::Customer.create(
46
+ email: email,
47
+ description: billing_address.full_name
48
+ )
49
+ self.update_column(:stripe_customer_id, customer.id)
50
+ end
51
+
52
+ define_method("update_stripe_customer") do
53
+ customer = stripe_customer
54
+ customer.email = email
55
+ customer.description = billing_address.full_name
56
+ customer.save
57
+ end
58
+
59
+ define_method("no_stripe_customer_id?") do
60
+ stripe_customer_id.present? ? false : true
61
+ end
62
+
63
+ define_method("email_or_billing_name_changed?") do
64
+ email_changed? || billing_address.first_name_changed? || billing_address.last_name_changed?
65
+ end
66
+ end
67
+
68
+ def has_transaction_stripe
69
+ attr_accessible :stripe_charge_id
70
+ end
71
+
72
+ def has_store_setting_stripe
73
+ attr_accessible :stripe_statement_descriptor
74
+ end
75
+ end
76
+ end
77
+ end