pay_me 0.5.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 (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