pay_me 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (113) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +111 -0
  3. data/Rakefile +33 -0
  4. data/app/controllers/pay_me/api/v1/invoices_controller.rb +30 -0
  5. data/app/controllers/pay_me/api/v1/stripe_plans_controller.rb +35 -0
  6. data/app/controllers/pay_me/application_controller.rb +10 -0
  7. data/app/helpers/pay_me/application_helper.rb +4 -0
  8. data/app/subscribers/pay_me/customer_subscriber.rb +42 -0
  9. data/app/subscribers/pay_me/plan_subscriber.rb +38 -0
  10. data/app/subscribers/pay_me/subscription_subscriber.rb +50 -0
  11. data/app/subscribers/pay_me/webhook_event_subscriber.rb +11 -0
  12. data/config/routes.rb +11 -0
  13. data/db/migrate/20160725204454_create_pay_me_subscriptions.rb +16 -0
  14. data/db/migrate/20161011170659_create_pay_me_plans.rb +8 -0
  15. data/db/migrate/20171030174731_add_quantity_to_pay_me_subscription.rb +6 -0
  16. data/db/migrate/20171130192525_create_pay_me_customers.rb +11 -0
  17. data/db/migrate/20180116193943_remove_customerable_from_subscriptions.rb +11 -0
  18. data/db/migrate/20180215235017_add_past_due_to_subscriptions.rb +5 -0
  19. data/lib/generators/pay_me/controllers/controllers_generator.rb +11 -0
  20. data/lib/generators/pay_me/controllers/templates/subscriptions_controller.rb +12 -0
  21. data/lib/generators/pay_me/install_generator.rb +39 -0
  22. data/lib/generators/pay_me/migrate_to_customer_model/USAGE +8 -0
  23. data/lib/generators/pay_me/migrate_to_customer_model/migrate_to_customer_model_generator.rb +9 -0
  24. data/lib/generators/pay_me/migrate_to_customer_model/templates/migration.rb.erb +64 -0
  25. data/lib/generators/pay_me/models/models_generator.rb +13 -0
  26. data/lib/generators/pay_me/models/templates/customer.rb +14 -0
  27. data/lib/generators/pay_me/models/templates/plan.rb +14 -0
  28. data/lib/generators/pay_me/models/templates/subscription.rb +14 -0
  29. data/lib/generators/pay_me/policies/policies_generator.rb +11 -0
  30. data/lib/generators/pay_me/policies/templates/subscription_policy.rb +34 -0
  31. data/lib/generators/pay_me/templates/pay_me_initializer.rb +17 -0
  32. data/lib/pay_me.rb +28 -0
  33. data/lib/pay_me/active_record.rb +6 -0
  34. data/lib/pay_me/concerns/controllers/customerable.rb +116 -0
  35. data/lib/pay_me/concerns/models/customerable.rb +54 -0
  36. data/lib/pay_me/concerns/models/stripe_customerable.rb +24 -0
  37. data/lib/pay_me/concerns/models/stripe_plannable.rb +31 -0
  38. data/lib/pay_me/concerns/models/stripe_subscribable.rb +43 -0
  39. data/lib/pay_me/configuration.rb +27 -0
  40. data/lib/pay_me/engine.rb +8 -0
  41. data/lib/pay_me/models.rb +4 -0
  42. data/lib/pay_me/railtie.rb +2 -0
  43. data/lib/pay_me/route_helpers.rb +21 -0
  44. data/lib/pay_me/services/customer.rb +141 -0
  45. data/lib/pay_me/version.rb +3 -0
  46. data/lib/pay_me/view_models/charge.rb +48 -0
  47. data/lib/tasks/pay_me_tasks.rake +4 -0
  48. data/lib/tasks/stripe.rake +22 -0
  49. data/test/controllers/pay_me/api/v1/invoices_controller_test.rb +82 -0
  50. data/test/controllers/pay_me/api/v1/plans_controller_test.rb +62 -0
  51. data/test/dummy/README.rdoc +28 -0
  52. data/test/dummy/Rakefile +6 -0
  53. data/test/dummy/app/assets/javascripts/application.js +13 -0
  54. data/test/dummy/app/assets/stylesheets/application.css +15 -0
  55. data/test/dummy/app/controllers/api/v1/users_controller.rb +10 -0
  56. data/test/dummy/app/controllers/application_controller.rb +5 -0
  57. data/test/dummy/app/controllers/pay_me/api/v1/subscriptions_controller.rb +12 -0
  58. data/test/dummy/app/helpers/application_helper.rb +2 -0
  59. data/test/dummy/app/models/pay_me/customer.rb +5 -0
  60. data/test/dummy/app/models/pay_me/plan.rb +5 -0
  61. data/test/dummy/app/models/pay_me/subscription.rb +5 -0
  62. data/test/dummy/app/models/user.rb +8 -0
  63. data/test/dummy/app/policies/pay_me/subscription_policy.rb +34 -0
  64. data/test/dummy/app/serializers/pay_me/subscription_serializer.rb +3 -0
  65. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  66. data/test/dummy/bin/bundle +3 -0
  67. data/test/dummy/bin/rails +4 -0
  68. data/test/dummy/bin/rake +4 -0
  69. data/test/dummy/bin/setup +29 -0
  70. data/test/dummy/config.ru +4 -0
  71. data/test/dummy/config/application.rb +25 -0
  72. data/test/dummy/config/boot.rb +5 -0
  73. data/test/dummy/config/database.yml +25 -0
  74. data/test/dummy/config/environment.rb +5 -0
  75. data/test/dummy/config/environments/development.rb +41 -0
  76. data/test/dummy/config/environments/production.rb +79 -0
  77. data/test/dummy/config/environments/test.rb +42 -0
  78. data/test/dummy/config/initializers/assets.rb +11 -0
  79. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  80. data/test/dummy/config/initializers/cookies_serializer.rb +3 -0
  81. data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  82. data/test/dummy/config/initializers/inflections.rb +16 -0
  83. data/test/dummy/config/initializers/mime_types.rb +4 -0
  84. data/test/dummy/config/initializers/pay_me.rb +17 -0
  85. data/test/dummy/config/initializers/session_store.rb +3 -0
  86. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  87. data/test/dummy/config/locales/en.yml +23 -0
  88. data/test/dummy/config/routes.rb +10 -0
  89. data/test/dummy/config/secrets.yml +22 -0
  90. data/test/dummy/db/migrate/20160630181300_create_pay_me_users.rb +12 -0
  91. data/test/dummy/db/migrate/20180118001940_api_me_migrate_to_customer_model.rb +9 -0
  92. data/test/dummy/db/schema.rb +53 -0
  93. data/test/dummy/public/404.html +67 -0
  94. data/test/dummy/public/422.html +67 -0
  95. data/test/dummy/public/500.html +66 -0
  96. data/test/dummy/public/favicon.ico +0 -0
  97. data/test/integration/customer_test.rb +395 -0
  98. data/test/lib/generators/pay_me/pay_me/migrate_to_customer_model_generator_test.rb +16 -0
  99. data/test/models/pay_me/customer_test.rb +9 -0
  100. data/test/models/pay_me/subscription_test.rb +9 -0
  101. data/test/models/pay_me/user_test.rb +6 -0
  102. data/test/pay_me_test.rb +7 -0
  103. data/test/support/concerns/api_test_helper.rb +15 -0
  104. data/test/support/concerns/stripe_helpers.rb +25 -0
  105. data/test/test_helper.rb +52 -0
  106. data/test/unit/concerns/customerable.rb +21 -0
  107. data/test/unit/models/customerable_test.rb +18 -0
  108. data/test/unit/services/customer_test.rb +75 -0
  109. data/test/unit/stubs/customerable.rb +28 -0
  110. data/test/unit/stubs/customerable_test.rb +16 -0
  111. data/test/webhooks/customer_webhook_test.rb +74 -0
  112. data/test/webhooks/plan_webhook_test.rb +74 -0
  113. metadata +288 -0
@@ -0,0 +1,11 @@
1
+ class RemoveCustomerableFromSubscriptions < ActiveRecord::Migration
2
+ def up
3
+ add_reference(:pay_me_subscriptions, :customer, index: true)
4
+ remove_reference(:pay_me_subscriptions, :customerable, polymorphic: true)
5
+ end
6
+
7
+ def down
8
+ remove_reference(:pay_me_subscriptions, :customer, index: true)
9
+ add_reference(:pay_me_subscriptions, :customerable, polymorphic: true)
10
+ end
11
+ end
@@ -0,0 +1,5 @@
1
+ class AddPastDueToSubscriptions < ActiveRecord::Migration
2
+ def change
3
+ add_column :pay_me_subscriptions, :past_due, :boolean, default: false
4
+ end
5
+ end
@@ -0,0 +1,11 @@
1
+ module PayMe
2
+ module Generators
3
+ class ControllersGenerator < ::Rails::Generators::Base
4
+ source_root File.expand_path('../templates', __FILE__)
5
+
6
+ def create_subscription_controller
7
+ copy_file 'subscriptions_controller.rb', 'app/controllers/pay_me/api/v1/subscriptions_controller.rb'
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,12 @@
1
+ module PayMe
2
+ module Api
3
+ module V1
4
+ class SubscriptionsController < ::Api::BaseController # TODO ApiMe Integration
5
+ include ApiMe
6
+
7
+ model PayMe::Subscription
8
+ serializer PayMe::SubscriptionSerializer
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,39 @@
1
+ module PayMe
2
+ module Generators
3
+ class InstallGenerator < Rails::Generators::Base
4
+ source_root File.expand_path("../templates", __FILE__)
5
+
6
+ def update_secrets
7
+ if yes? "Add pay_me to secrets.yml?"
8
+ secrets = [
9
+ " stripe_publishable_key: <%= ENV['STRIPE_PUBLISHABLE_KEY'] %>",
10
+ " stripe_secret_key: <%= ENV['STRIPE_SECRET_KEY'] %>",
11
+ " stripe_webhook_secret: <%= ENV['STRIPE_WEBHOOK_SECRET'] %>",
12
+ " stripe_signing_secret: <%= ENV['STRIPE_SIGNING_SECRET'] %>"
13
+ ].map{|s| s + "\n"}.join
14
+ insert_into_file "config/secrets.yml", secrets, after: "development:\n", force: true
15
+ insert_into_file "config/secrets.yml", secrets, after: "test:\n", force: true
16
+ insert_into_file "config/secrets.yml", secrets, after: "production:\n", force: true
17
+ end
18
+ end
19
+
20
+ def copy_initializer
21
+ template "pay_me_initializer.rb", "config/initializers/pay_me.rb"
22
+ end
23
+
24
+ def run_generators
25
+ rake 'pay_me:install:migrations'
26
+ generate 'pay_me:controllers'
27
+ generate 'pay_me:models'
28
+ generate 'pay_me:policies'
29
+ generate 'serializer pay_me/subscription subscription_id is_active past_due stripe_plan_id quantity coupon_id created_at updated_at'
30
+ end
31
+
32
+ def mount_engine_routes
33
+ if yes? "Add pay_me to routes.rb?"
34
+ route 'mount PayMe::Engine => PayMe.configuration.mount_path'
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,8 @@
1
+ Description:
2
+ Explain the generator
3
+
4
+ Example:
5
+ rails generate migrate_to_customer_model <customerable_model>
6
+
7
+ This will create:
8
+ A migration to migrate from the former customerable model polymorphism to the new customer base model for polymorphism. See upgrade instructions for details.
@@ -0,0 +1,9 @@
1
+ require 'rails/generators/active_record'
2
+
3
+ class PayMe::MigrateToCustomerModelGenerator < ActiveRecord::Generators::Base
4
+ source_root File.expand_path('../templates', __FILE__)
5
+
6
+ def create_migration_file
7
+ migration_template "migration.rb.erb", "#{Rails.root.join('db', 'migrate', 'api_me_migrate_to_customer_model.rb')}"
8
+ end
9
+ end
@@ -0,0 +1,64 @@
1
+ class <%= migration_class_name %> < ActiveRecord::Migration
2
+ # This migration moves from storing a polymorphic relationship on subscriptions
3
+ # to a belongs_to relationship on a model that should be associated with a
4
+ # customer and a concrete customer model. I.E a user belongs to a customer.
5
+ # If your app had previous multiple different customerable models,
6
+ # update this migration to add this reference to each of those and have
7
+ # it migrate the data correctly.
8
+ #
9
+ #
10
+ #
11
+ # !!!
12
+ # TODO: DELETE THE remove_customerable_from_subscriptions MIGRATION
13
+ # created from `rails api_me::install:migrations` command, this migration is meant
14
+ # for applications that are installing this engine for the first time.
15
+ # !!!
16
+
17
+ class Subscription < ActiveRecord::Base
18
+ self.table_name = "pay_me_subscriptions"
19
+
20
+ belongs_to :customer, class_name: "<%= migration_class_name %>::Customer"
21
+ belongs_to :customerable, class_name: "<%= migration_class_name %>::<%= class_name %>", polymorphic: true
22
+ end
23
+
24
+ class Customer < ActiveRecord::Base
25
+ self.table_name = "pay_me_customers"
26
+
27
+ has_one <%= plural_name.singularize %>, class_name: "<%= migration_class_name %>::Customer"
28
+ has_many :subscriptions, class_name: "<%= migration_class_name %>::Subscription"
29
+ end
30
+
31
+ class <%= class_name %> < ActiveRecord::Base
32
+ self.table_name = <%= plural_name %>
33
+
34
+ belongs_to :customer, class_name: "<%= migration_class_name %>::Customer"
35
+ end
36
+
37
+ def up
38
+ add_reference(:<%= plural_name %>, :pay_me_customers, index: true)
39
+ add_reference(:pay_me_subscriptions, :customer, index: true)
40
+
41
+ <%= class_name %>.find_each do |customerable|
42
+ customer = Customer.create!(customerable.customer_id)
43
+ customerable.customer = customer
44
+ customerable.save!
45
+ end
46
+
47
+ remove_reference(:pay_me_subscriptions, :customerable, polymorphic: true)
48
+ end
49
+
50
+ def down
51
+ add_reference(:pay_me_subscriptions, :customerable, polymorphic: true, index: true)
52
+
53
+ <%= class_name %>.find_each do |customer|
54
+ customerable = customer.<%= plural_name.singularize %>
55
+ customer.subscriptions.each do |subscription|
56
+ subscription.customerable = customerable
57
+ subscription.save!
58
+ end
59
+ end
60
+
61
+ remove_reference(:pay_me_subscriptions, :customer)
62
+ remove_reference(:<%= plural_name %>, :pay_me_customers)
63
+ end
64
+ end
@@ -0,0 +1,13 @@
1
+ module PayMe
2
+ module Generators
3
+ class ModelsGenerator < ::Rails::Generators::Base
4
+ source_root File.expand_path('../templates', __FILE__)
5
+
6
+ def create_stripe_models
7
+ copy_file 'customer.rb', 'app/models/pay_me/customer.rb'
8
+ copy_file 'plan.rb', 'app/models/pay_me/plan.rb'
9
+ copy_file 'subscription.rb', 'app/models/pay_me/subscription.rb'
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,14 @@
1
+ module PayMe
2
+ class Customer < ActiveRecord::Base
3
+ include PayMe::Concerns::Models::StripeCustomerable
4
+
5
+ def self.created_webhook!(stripe_customer)
6
+ end
7
+
8
+ def self.updated_webhook!(stripe_customer)
9
+ end
10
+
11
+ def self.deleted_webhook!(stripe_customer)
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ module PayMe
2
+ class Plan < ActiveRecord::Base
3
+ include PayMe::Concerns::Models::StripePlannable
4
+
5
+ def self.created_webhook!(stripe_plan)
6
+ end
7
+
8
+ def self.updated_webhook!(stripe_plan)
9
+ end
10
+
11
+ def self.deleted_webhook!(stripe_plan)
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ module PayMe
2
+ class Subscription < ActiveRecord::Base
3
+ include PayMe::Concerns::Models::StripeSubscribable
4
+
5
+ def self.created_webhook!(stripe_subscription)
6
+ end
7
+
8
+ def self.updated_webhook!(stripe_subscription)
9
+ end
10
+
11
+ def self.deleted_webhook!(stripe_subscription)
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,11 @@
1
+ module PayMe
2
+ module Generators
3
+ class PoliciesGenerator < ::Rails::Generators::Base
4
+ source_root File.expand_path('../templates', __FILE__)
5
+
6
+ def create_subscription_poliocy
7
+ copy_file 'subscription_policy.rb', 'app/policies/pay_me/subscription_policy.rb'
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,34 @@
1
+ module PayMe
2
+ class SubscriptionPolicy < ApplicationPolicy
3
+ ###
4
+ # Add the policy
5
+ ###
6
+ # def create?
7
+ # record.owner_id == current_user.id || super
8
+ # end
9
+
10
+ # def show?
11
+ # true
12
+ # end
13
+
14
+ # def update?
15
+ # create?
16
+ # end
17
+
18
+ # def destroy?
19
+ # record.owner_id == current_user.id || super
20
+ # end
21
+
22
+ # def accept?
23
+ # current_user
24
+ # end
25
+
26
+ # def decline?
27
+ # current_user
28
+ # end
29
+
30
+ # def permitted_attributes
31
+ # [:email, :accepted, :meta]
32
+ # end
33
+ end
34
+ end
@@ -0,0 +1,17 @@
1
+ require "stripe"
2
+ Rails.configuration.stripe = {
3
+ publishable_key: Rails.application.secrets.stripe_publishable_key,
4
+ secret_key: Rails.application.secrets.stripe_secret_key
5
+ }
6
+
7
+ Stripe.api_key = Rails.application.secrets.stripe_secret_key
8
+
9
+ StripeEvent.configure do |events|
10
+ events.all PayMe::WebhookEventSubscriber.new(Rails.logger)
11
+ events.subscribe 'customer.subscription.', PayMe::SubscriptionSubscriber.new(Rails.logger)
12
+ events.subscribe 'customer.', PayMe::CustomerSubscriber.new(Rails.logger)
13
+ events.subscribe 'plan.', PayMe::PlanSubscriber.new(Rails.logger)
14
+ end
15
+
16
+ StripeEvent.authentication_secret = Rails.application.secrets.stripe_webhook_secret
17
+ StripeEvent.signing_secret = Rails.application.secrets.stripe_signing_secret
@@ -0,0 +1,28 @@
1
+ require 'pay_me/engine'
2
+ require 'pay_me/version'
3
+ require 'pay_me/configuration'
4
+ require 'stripe_event'
5
+
6
+ module PayMe
7
+ extend self
8
+
9
+ module Concerns
10
+ module Controllers
11
+ autoload :Customerable, 'pay_me/concerns/controllers/customerable'
12
+ end
13
+
14
+ module Models
15
+ autoload :StripeCustomerable, 'pay_me/concerns/models/stripe_customerable'
16
+ autoload :StripePlannable, 'pay_me/concerns/models/stripe_plannable'
17
+ autoload :StripeSubscribable, 'pay_me/concerns/models/stripe_subscribable'
18
+ end
19
+ end
20
+
21
+ def configure
22
+ yield configuration
23
+ end
24
+
25
+ def configuration
26
+ Configuration.instance
27
+ end
28
+ end
@@ -0,0 +1,6 @@
1
+ require 'active_support/concern'
2
+
3
+ module PayMe
4
+ module ActiveRecord
5
+ end
6
+ end
@@ -0,0 +1,116 @@
1
+ require 'stripe'
2
+ require 'pay_me/services/customer'
3
+ require 'pay_me/view_models/charge'
4
+
5
+ module PayMe
6
+ module Concerns
7
+ module Controllers
8
+ module Customerable
9
+ extend ActiveSupport::Concern
10
+
11
+ included do
12
+ rescue_from Stripe::StripeError, Stripe::CardError do |e|
13
+ Rails.logger.debug "ERROR: #{e}"
14
+ render status: :unprocessable_entity, json: {
15
+ stripe_code: e.code, reason: e.message, message: e.message, errors: [e.message]
16
+ }
17
+ end
18
+
19
+ def charge
20
+ charge = create_stripe_charge
21
+ render status: 201, json: { charge_id: charge.id }
22
+ end
23
+
24
+ def subscribe
25
+ resource = find_resource
26
+ @subscription = resource.subscribe(**subscription_params)
27
+ customer = resource.stripe_customer
28
+ render status: 201, json: {
29
+ subscription: @subscription.as_json.tap { |i| i.store('stripe_id', i.delete('id')) },
30
+ default_source: customer.default_source.blank? ? nil : customer.sources.retrieve(customer.default_source)
31
+ }
32
+ end
33
+
34
+ def update_subscription
35
+ resource = find_resource
36
+ @subscription = resource.update_subscription(coupon_id: params[:coupon_id], quantity: params[:quantity])
37
+ if @subscription.blank?
38
+ render status: 404, json: @subscription.as_json
39
+ else
40
+ render status: 200, json: @subscription.as_json
41
+ end
42
+ end
43
+
44
+ def cancel_subscription
45
+ resource = find_resource
46
+ @subscription = resource.cancel_subscription(cancel_immediately: params[:cancel_now])
47
+ if @subscription.blank?
48
+ render status: 404, json: @subscription.as_json
49
+ else
50
+ render status: 200, json: @subscription.as_json
51
+ end
52
+ end
53
+
54
+ def invoices
55
+ resource = find_resource
56
+ invoices = PayMe.configuration.stripe_invoice_class.list(
57
+ customer: resource.stripe_customer_id
58
+ )
59
+
60
+ render status: 200, json: {
61
+ invoices: invoices.map do |invoice|
62
+ invoice.as_json.tap do |i|
63
+ i.store('stripe_id', i.delete('id'))
64
+ end
65
+ end
66
+ }
67
+ end
68
+
69
+ def update_default_source
70
+ resource = find_resource
71
+ resource.add_default_payment_source(params["source_id"])
72
+ customer = PayMe.configuration.stripe_customer_class.retrieve(resource.stripe_customer_id)
73
+ render status: 200, json: { customer: customer, default_source: customer.default_source.blank? ? nil : customer.sources.retrieve(customer.default_source) }
74
+ end
75
+
76
+ def default_source
77
+ resource = find_resource
78
+ unless resource.customer_id
79
+ resource.customer_service.customer
80
+ end
81
+ customer = PayMe.configuration.stripe_customer_class.retrieve(resource.stripe_customer_id)
82
+ render status: 200, json: {
83
+ customer: customer,
84
+ default_source: customer.default_source.blank? ? nil : customer.sources.retrieve(customer.default_source)
85
+ }
86
+ end
87
+
88
+ def charge_params
89
+ {
90
+ amount: params[:amount],
91
+ currency: params[:currency],
92
+ description: params[:description],
93
+ source: params[:source]
94
+ }
95
+ end
96
+
97
+ def subscription_params
98
+ {
99
+ plan_id: params[:plan_id],
100
+ coupon_id: params[:coupon_id],
101
+ quantity: params[:quantity],
102
+ source: params[:source]
103
+ }
104
+ end
105
+
106
+ def create_stripe_charge
107
+ resource = find_resource
108
+ PayMe::ViewModels::Charge.new(
109
+ charge_params.merge(customerable: resource)
110
+ ).charge
111
+ end
112
+ end
113
+ end
114
+ end
115
+ end
116
+ end