trado_stripe_module 0.1.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 (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