diff --git a/app/controllers/plans_controller.rb b/app/controllers/plans_controller.rb index 2680bab..f7a8150 100644 --- a/app/controllers/plans_controller.rb +++ b/app/controllers/plans_controller.rb @@ -8,7 +8,11 @@ module DiscoursePatrons def index begin - plans = ::Stripe::Plan.list(active: true) + if params[:product_id].present? + plans = ::Stripe::Plan.list(active: true, product: params[:product_id]) + else + plans = ::Stripe::Plan.list(active: true) + end serialized = plans[:data].map do |plan| plan.to_h.slice(:id, :amount, :currency, :interval) diff --git a/app/controllers/products_controller.rb b/app/controllers/products_controller.rb index 74ca99d..a943996 100644 --- a/app/controllers/products_controller.rb +++ b/app/controllers/products_controller.rb @@ -8,14 +8,38 @@ module DiscoursePatrons def index begin - products = ::Stripe::Product.list(active: true) + response = ::Stripe::Product.list(active: true) - # TODO: Serialize. Remove some attributes like metadata - render_json_dump products.data + products = response[:data].map do |p| + serialize(p) + end + + render_json_dump products rescue ::Stripe::InvalidRequestError => e return render_json_error e.message end end + + def show + begin + product = ::Stripe::Product.retrieve(params[:id]) + + render_json_dump serialize(product) + + rescue ::Stripe::InvalidRequestError => e + return render_json_error e.message + end + end + + private + + def serialize(product) + { + id: product[:id], + name: product[:name], + description: product[:metadata][:description] + } + end end end diff --git a/app/controllers/subscriptions_controller.rb b/app/controllers/subscriptions_controller.rb index cbe50e2..b544763 100644 --- a/app/controllers/subscriptions_controller.rb +++ b/app/controllers/subscriptions_controller.rb @@ -8,14 +8,14 @@ module DiscoursePatrons def index begin - customers = ::Stripe::Customer.list( - email: current_user.email, - expand: ['data.subscriptions'] - ) + products = ::Stripe::Product.list(active: true) - subscriptions = customers[:data].map do |customer| - customer[:subscriptions][:data] - end.flatten(1) + subscriptions = products[:data].map do |p| + { + id: p[:id], + description: p.dig(:metadata, :description) + } + end render_json_dump subscriptions @@ -50,17 +50,6 @@ module DiscoursePatrons end end - def destroy - begin - subscription = ::Stripe::Subscription.delete(params[:id]) - - render_json_dump subscription - - rescue ::Stripe::InvalidRequestError => e - return render_json_error e.message - end - end - private def plan_group(plan) diff --git a/app/controllers/user/subscriptions_controller.rb b/app/controllers/user/subscriptions_controller.rb index c9a7595..7128f37 100644 --- a/app/controllers/user/subscriptions_controller.rb +++ b/app/controllers/user/subscriptions_controller.rb @@ -25,6 +25,17 @@ module DiscoursePatrons return render_json_error e.message end end + + def destroy + begin + subscription = ::Stripe::Subscription.delete(params[:id]) + + render_json_dump subscription + + rescue ::Stripe::InvalidRequestError => e + return render_json_error e.message + end + end end end end diff --git a/assets/javascripts/discourse/controllers/patrons-subscribe.js.es6 b/assets/javascripts/discourse/controllers/patrons-subscribe-show.js.es6 similarity index 91% rename from assets/javascripts/discourse/controllers/patrons-subscribe.js.es6 rename to assets/javascripts/discourse/controllers/patrons-subscribe-show.js.es6 index 74ec8e2..a0ce61b 100644 --- a/assets/javascripts/discourse/controllers/patrons-subscribe.js.es6 +++ b/assets/javascripts/discourse/controllers/patrons-subscribe-show.js.es6 @@ -34,9 +34,9 @@ export default Ember.Controller.extend({ } subscription.save().then(() => { - bootbox.alert("ok payment good... some kind of message"); + bootbox.alert(I18n.t("discourse_patrons.transactions.payment.success")); this.transitionToRoute( - "user.billing", + "user.subscriptions", Discourse.User.current().username.toLowerCase() ); }); diff --git a/assets/javascripts/discourse/models/plan.js.es6 b/assets/javascripts/discourse/models/plan.js.es6 index f0d2956..dac6f16 100644 --- a/assets/javascripts/discourse/models/plan.js.es6 +++ b/assets/javascripts/discourse/models/plan.js.es6 @@ -20,8 +20,8 @@ const Plan = Discourse.Model.extend({ }); Plan.reopenClass({ - findAll() { - return ajax("/patrons/plans", { method: "get" }).then(result => + findAll(data) { + return ajax("/patrons/plans", { method: "get", data }).then(result => result.map(plan => Plan.create(plan)) ); } diff --git a/assets/javascripts/discourse/models/product.js.es6 b/assets/javascripts/discourse/models/product.js.es6 index 71dd5a2..1f9e490 100644 --- a/assets/javascripts/discourse/models/product.js.es6 +++ b/assets/javascripts/discourse/models/product.js.es6 @@ -7,6 +7,12 @@ Product.reopenClass({ return ajax("/patrons/products", { method: "get" }).then(result => result.map(product => Product.create(product)) ); + }, + + find(id) { + return ajax(`/patrons/products/${id}`, { method: "get" }).then( + product => Product.create(product) + ); } }); diff --git a/assets/javascripts/discourse/models/subscription.js.es6 b/assets/javascripts/discourse/models/subscription.js.es6 index 2231b25..fc4c355 100644 --- a/assets/javascripts/discourse/models/subscription.js.es6 +++ b/assets/javascripts/discourse/models/subscription.js.es6 @@ -15,22 +15,13 @@ const Subscription = Discourse.Model.extend({ }; return ajax("/patrons/subscriptions", { method: "post", data }); - }, - - destroy() { - return ajax(`/patrons/subscriptions/${this.id}`, { method: "delete" }).then( - result => Subscription.create(result) - ); } }); Subscription.reopenClass({ findAll() { return ajax("/patrons/subscriptions", { method: "get" }).then(result => - result.map(subscription => { - subscription.plan = Plan.create(subscription.plan); - return Subscription.create(subscription); - }) + result.map(subscription => Subscription.create(subscription)) ); } }); diff --git a/assets/javascripts/discourse/models/user-subscription.js.es6 b/assets/javascripts/discourse/models/user-subscription.js.es6 index 31e66d7..8782a6a 100644 --- a/assets/javascripts/discourse/models/user-subscription.js.es6 +++ b/assets/javascripts/discourse/models/user-subscription.js.es6 @@ -6,6 +6,12 @@ const UserSubscription = Discourse.Model.extend({ @computed("status") canceled(status) { return status === "canceled"; + }, + + destroy() { + return ajax(`/patrons/user/subscriptions/${this.id}`, { method: "delete" }).then( + result => UserSubscription.create(result) + ); } }); diff --git a/assets/javascripts/discourse/patrons-route-map.js.es6 b/assets/javascripts/discourse/patrons-route-map.js.es6 index 2969cda..566c954 100644 --- a/assets/javascripts/discourse/patrons-route-map.js.es6 +++ b/assets/javascripts/discourse/patrons-route-map.js.es6 @@ -1,5 +1,7 @@ export default function() { this.route("patrons", function() { - this.route("subscribe"); + this.route("subscribe", function() { + this.route("show", { path: "/:subscription-id" }); + }); }); } diff --git a/assets/javascripts/discourse/routes/patrons-subscribe-show.js.es6 b/assets/javascripts/discourse/routes/patrons-subscribe-show.js.es6 new file mode 100644 index 0000000..41f9c14 --- /dev/null +++ b/assets/javascripts/discourse/routes/patrons-subscribe-show.js.es6 @@ -0,0 +1,16 @@ +import Product from "discourse/plugins/discourse-patrons/discourse/models/product"; +import Plan from "discourse/plugins/discourse-patrons/discourse/models/plan"; +import Subscription from "discourse/plugins/discourse-patrons/discourse/models/subscription"; + +export default Discourse.Route.extend({ + model(params) { + const product_id = params["subscription-id"]; + const product = Product.find(product_id); + const subscription = Subscription.create(); + const plans = Plan.findAll({ product_id: product_id }).then(results => + results.map(p => ({ id: p.id, name: p.subscriptionRate })) + ); + + return Ember.RSVP.hash({ plans, product, subscription }); + }, +}); diff --git a/assets/javascripts/discourse/routes/patrons-subscribe.js.es6 b/assets/javascripts/discourse/routes/patrons-subscribe.js.es6 index 336bf20..dbc5431 100644 --- a/assets/javascripts/discourse/routes/patrons-subscribe.js.es6 +++ b/assets/javascripts/discourse/routes/patrons-subscribe.js.es6 @@ -1,14 +1,7 @@ -import Plan from "discourse/plugins/discourse-patrons/discourse/models/plan"; -import Subscription from "discourse/plugins/discourse-patrons/discourse/models/subscription"; +import Product from "discourse/plugins/discourse-patrons/discourse/models/product"; export default Discourse.Route.extend({ model() { - const plans = Plan.findAll().then(results => - results.map(p => ({ id: p.id, name: p.subscriptionRate })) - ); - - const subscription = Subscription.create(); - - return Ember.RSVP.hash({ plans, subscription }); - } + return Product.findAll(); + }, }); diff --git a/assets/javascripts/discourse/templates/patrons/subscribe.hbs b/assets/javascripts/discourse/templates/patrons/subscribe.hbs index b37fe6b..42e43d5 100644 --- a/assets/javascripts/discourse/templates/patrons/subscribe.hbs +++ b/assets/javascripts/discourse/templates/patrons/subscribe.hbs @@ -1,34 +1,12 @@ -

- {{i18n 'discourse_patrons.subscribe.title'}} -

- -
-
-

{{model.group.full_name}}

-

- {{{model.group.bio_cooked}}} -

+
+
+

+ {{i18n 'discourse_patrons.subscribe.title'}} +

-
- {{combo-box valueAttribute="id" content=model.plans value=model.subscription.plan}} - {{#d-button - action="stripePaymentHandler" - class="btn btn-primary btn-payment btn-discourse-patrons"}} - {{i18n 'discourse_patrons.subscribe.buttons.subscribe'}} - {{/d-button}} +
-
- -

{{i18n 'discourse_patrons.subscribe.card.title'}}

- {{subscribe-card cardElement=cardElement}} - -
-

{{i18n 'discourse_patrons.subscribe.customer.title'}}

-
- {{i18n 'discourse_patrons.subscribe.customer.empty'}} -
-
-
+ {{outlet}}
diff --git a/assets/javascripts/discourse/templates/patrons/subscribe/index.hbs b/assets/javascripts/discourse/templates/patrons/subscribe/index.hbs new file mode 100644 index 0000000..c836cc6 --- /dev/null +++ b/assets/javascripts/discourse/templates/patrons/subscribe/index.hbs @@ -0,0 +1,17 @@ + + +{{#each model as |product|}} +
+

{{product.name}}

+ +

+ {{product.description}} +

+ +
+ {{#link-to "patrons.subscribe.show" product.id class="btn btn-primary"}} + {{i18n 'discourse_patrons.subscribe.title'}} + {{/link-to}} +
+
+{{/each}} diff --git a/assets/javascripts/discourse/templates/patrons/subscribe/show.hbs b/assets/javascripts/discourse/templates/patrons/subscribe/show.hbs new file mode 100644 index 0000000..fbd4e47 --- /dev/null +++ b/assets/javascripts/discourse/templates/patrons/subscribe/show.hbs @@ -0,0 +1,32 @@ + +
+
+

+ {{model.product.name}} +

+

+ {{model.product.description}} +

+
+
+ {{combo-box valueAttribute="id" content=model.plans value=model.product.plan}} + + {{#d-button + action="stripePaymentHandler" + class="btn btn-primary btn-payment btn-discourse-patrons"}} + {{i18n 'discourse_patrons.subscribe.buttons.subscribe'}} + {{/d-button}} + +
+ +

{{i18n 'discourse_patrons.subscribe.card.title'}}

+ {{subscribe-card cardElement=cardElement}} + + {{!--
+

{{i18n 'discourse_patrons.subscribe.customer.title'}}

+
+ {{i18n 'discourse_patrons.subscribe.customer.empty'}} +
+
--}} +
+
diff --git a/config/routes.rb b/config/routes.rb index 17d3ff6..ca478bb 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -13,16 +13,17 @@ DiscoursePatrons::Engine.routes.draw do end namespace :user do - resources :subscriptions, only: [:index] + resources :subscriptions, only: [:index, :destroy] end resources :customers, only: [:create] resources :invoices, only: [:index] resources :patrons, only: [:index, :create] resources :plans, only: [:index] - resources :products, only: [:index] - resources :subscriptions, only: [:index, :create, :destroy] + resources :products, only: [:index, :show] + resources :subscriptions, only: [:create] get '/' => 'patrons#index' get '/subscribe' => 'patrons#index' + get '/subscribe/:id' => 'patrons#index' end diff --git a/spec/requests/admin/products_controller_spec.rb b/spec/requests/admin/products_controller_spec.rb index 358c5df..78c2a01 100644 --- a/spec/requests/admin/products_controller_spec.rb +++ b/spec/requests/admin/products_controller_spec.rb @@ -81,7 +81,7 @@ module DiscoursePatrons it 'has a description' do ::Stripe::Product.expects(:create).with(has_entry(metadata: { description: 'Oi, I think he just said bless be all the bignoses!' })) - post "/patrons/admin/products.json", params: { metadata: { description: 'Oi, I think he just said bless be all the bignoses!' }} + post "/patrons/admin/products.json", params: { metadata: { description: 'Oi, I think he just said bless be all the bignoses!' } } end end diff --git a/spec/requests/plans_controller_spec.rb b/spec/requests/plans_controller_spec.rb index 61a2e09..b0b5b2c 100644 --- a/spec/requests/plans_controller_spec.rb +++ b/spec/requests/plans_controller_spec.rb @@ -10,6 +10,11 @@ module DiscoursePatrons get "/patrons/plans.json" end + it "lists the active plans for a product" do + ::Stripe::Plan.expects(:list).with(active: true, product: 'prod_3765') + get "/patrons/plans.json", params: { product_id: 'prod_3765' } + end + it "orders and serialises the plans" do ::Stripe::Plan.expects(:list).returns( data: [ diff --git a/spec/requests/products_controller_spec.rb b/spec/requests/products_controller_spec.rb index b9fc776..3e77075 100644 --- a/spec/requests/products_controller_spec.rb +++ b/spec/requests/products_controller_spec.rb @@ -4,10 +4,41 @@ require 'rails_helper' module DiscoursePatrons RSpec.describe ProductsController do + let(:product) do + { + id: "prodct_23456", + name: "Very Special Product", + metadata: { + description: "Many people listened to my phone call with the Ukrainian President while it was being made" + }, + otherstuff: true, + } + end + describe "index" do - it "lists the active products" do - ::Stripe::Product.expects(:list).with(active: true) + it "gets products" do + ::Stripe::Product.expects(:list).with(active: true).returns(data: [product]) + get "/patrons/products.json" + + expect(JSON.parse(response.body)).to eq([{ + "id" => "prodct_23456", + "name" => "Very Special Product", + "description" => "Many people listened to my phone call with the Ukrainian President while it was being made" + }]) + end + end + + describe 'show' do + it 'retrieves the product' do + ::Stripe::Product.expects(:retrieve).with('prod_walterwhite').returns(product) + get "/patrons/products/prod_walterwhite.json" + + expect(JSON.parse(response.body)).to eq( + "id" => "prodct_23456", + "name" => "Very Special Product", + "description" => "Many people listened to my phone call with the Ukrainian President while it was being made" + ) end end end diff --git a/spec/requests/subscriptions_controller_spec.rb b/spec/requests/subscriptions_controller_spec.rb index b66c8d8..8e45aa0 100644 --- a/spec/requests/subscriptions_controller_spec.rb +++ b/spec/requests/subscriptions_controller_spec.rb @@ -10,11 +10,6 @@ module DiscoursePatrons ::Stripe::Subscription.expects(:create).never post "/patrons/subscriptions.json", params: { plan: 'plan_1234', customer: 'cus_1234' } end - - it "does not destroy a subscription" do - ::Stripe::Subscription.expects(:delete).never - patch "/patrons/subscriptions/sub_12345.json" - end end context "authenticated" do @@ -24,30 +19,6 @@ module DiscoursePatrons sign_in(user) end - describe "index" do - let(:customers) do - { - data: [{ - id: "cus_23456", - subscriptions: { - data: [{ id: "sub_1234" }, { id: "sub_4567" }] - }, - }] - } - end - - it "gets subscriptions" do - ::Stripe::Customer.expects(:list).with( - email: user.email, - expand: ['data.subscriptions'] - ).returns(customers) - - get "/patrons/subscriptions.json" - - expect(JSON.parse(response.body)).to eq([{ "id" => "sub_1234" }, { "id" => "sub_4567" }]) - end - end - describe "create" do it "creates a subscription" do ::Stripe::Plan.expects(:retrieve).returns(metadata: { group_name: 'awesome' }) @@ -58,7 +29,7 @@ module DiscoursePatrons post "/patrons/subscriptions.json", params: { plan: 'plan_1234', customer: 'cus_1234' } end - it "creates a customer" do + it "creates a customer model" do ::Stripe::Plan.expects(:retrieve).returns(metadata: {}) ::Stripe::Subscription.expects(:create).returns(status: 'active') @@ -66,15 +37,6 @@ module DiscoursePatrons post "/patrons/subscriptions.json", params: { plan: 'plan_1234', customer: 'cus_1234' } }.to change { DiscoursePatrons::Customer.count } end - - it "does not create a customer id one existeth" do - ::Stripe::Plan.expects(:retrieve).returns(metadata: {}) - ::Stripe::Subscription.expects(:create).returns(status: 'active') - DiscoursePatrons::Customer.create(user_id: user.id, customer_id: 'cus_1234') - - DiscoursePatrons::Customer.expects(:create).never - post "/patrons/subscriptions.json", params: { plan: 'plan_1234', customer: 'cus_1234' } - end end describe "user groups" do @@ -135,13 +97,6 @@ module DiscoursePatrons end end end - - describe "delete" do - it "deletes a subscription" do - ::Stripe::Subscription.expects(:delete).with('sub_12345') - delete "/patrons/subscriptions/sub_12345.json" - end - end end end end diff --git a/spec/requests/user/subscriptions_controller_spec.rb b/spec/requests/user/subscriptions_controller_spec.rb index b99aa60..be9d321 100644 --- a/spec/requests/user/subscriptions_controller_spec.rb +++ b/spec/requests/user/subscriptions_controller_spec.rb @@ -13,6 +13,11 @@ module DiscoursePatrons ::Stripe::Customer.expects(:list).never get "/patrons/user/subscriptions.json" end + + it "does not destroy a subscription" do + ::Stripe::Subscription.expects(:delete).never + patch "/patrons/user/subscriptions/sub_12345.json" + end end context "authenticated" do @@ -45,6 +50,13 @@ module DiscoursePatrons expect(JSON.parse(response.body)).to eq([{ "id" => "sub_1234" }, { "id" => "sub_4567" }]) end end + + describe "delete" do + it "deletes a subscription" do + ::Stripe::Subscription.expects(:delete).with('sub_12345') + delete "/patrons/user/subscriptions/sub_12345.json" + end + end end end end