the subscribe product page
This commit is contained in:
parent
2b3c52bdd4
commit
e14b4dcd96
|
@ -8,7 +8,11 @@ module DiscoursePatrons
|
|||
|
||||
def index
|
||||
begin
|
||||
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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
);
|
||||
});
|
|
@ -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))
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -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))
|
||||
);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -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)
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
export default function() {
|
||||
this.route("patrons", function() {
|
||||
this.route("subscribe");
|
||||
this.route("subscribe", function() {
|
||||
this.route("show", { path: "/:subscription-id" });
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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 });
|
||||
},
|
||||
});
|
|
@ -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();
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1,34 +1,12 @@
|
|||
|
||||
<h3>
|
||||
<div class="container">
|
||||
<div class="title-wrapper">
|
||||
<h1>
|
||||
{{i18n 'discourse_patrons.subscribe.title'}}
|
||||
</h3>
|
||||
|
||||
<div class="discourse-patrons-section-columns">
|
||||
<div class="section-column discourse-patrons-confirmation-billing">
|
||||
<h4>{{model.group.full_name}}</h4>
|
||||
<p>
|
||||
{{{model.group.bio_cooked}}}
|
||||
</p>
|
||||
</h1>
|
||||
</div>
|
||||
<div class="section-column">
|
||||
{{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}}
|
||||
|
||||
<hr>
|
||||
|
||||
<h4>{{i18n 'discourse_patrons.subscribe.card.title'}}</h4>
|
||||
{{subscribe-card cardElement=cardElement}}
|
||||
|
||||
<div id="discourse-patrons-subscribe-customer">
|
||||
<h4>{{i18n 'discourse_patrons.subscribe.customer.title'}}</h4>
|
||||
<div class="discourse-patrons-subscribe-customer-empty">
|
||||
{{i18n 'discourse_patrons.subscribe.customer.empty'}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{outlet}}
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
|
||||
|
||||
{{#each model as |product|}}
|
||||
<div>
|
||||
<h2>{{product.name}}</h2>
|
||||
|
||||
<p>
|
||||
{{product.description}}
|
||||
</p>
|
||||
|
||||
<div class="pull-right">
|
||||
{{#link-to "patrons.subscribe.show" product.id class="btn btn-primary"}}
|
||||
{{i18n 'discourse_patrons.subscribe.title'}}
|
||||
{{/link-to}}
|
||||
</div>
|
||||
</div>
|
||||
{{/each}}
|
|
@ -0,0 +1,32 @@
|
|||
|
||||
<div class="discourse-patrons-section-columns">
|
||||
<div class="section-column discourse-patrons-confirmation-billing">
|
||||
<h2>
|
||||
{{model.product.name}}
|
||||
</h2>
|
||||
<p>
|
||||
{{model.product.description}}
|
||||
</p>
|
||||
</div>
|
||||
<div class="section-column">
|
||||
{{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}}
|
||||
|
||||
<hr>
|
||||
|
||||
<h4>{{i18n 'discourse_patrons.subscribe.card.title'}}</h4>
|
||||
{{subscribe-card cardElement=cardElement}}
|
||||
|
||||
{{!-- <div id="discourse-patrons-subscribe-customer">
|
||||
<h4>{{i18n 'discourse_patrons.subscribe.customer.title'}}</h4>
|
||||
<div class="discourse-patrons-subscribe-customer-empty">
|
||||
{{i18n 'discourse_patrons.subscribe.customer.empty'}}
|
||||
</div>
|
||||
</div> --}}
|
||||
</div>
|
||||
</div>
|
|
@ -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
|
||||
|
|
|
@ -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: [
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue