basic subscribe page
This commit is contained in:
parent
91045583ad
commit
bb31deae89
|
@ -11,13 +11,10 @@ This is a newer version of https://github.com/rimian/discourse-donations.
|
||||||
* Be sure your site is enforcing https.
|
* Be sure your site is enforcing https.
|
||||||
* Follow the install instructions here: https://meta.discourse.org/t/install-a-plugin/19157
|
* Follow the install instructions here: https://meta.discourse.org/t/install-a-plugin/19157
|
||||||
* Add your Stripe public and private keys in settings and set the currency to your local value.
|
* Add your Stripe public and private keys in settings and set the currency to your local value.
|
||||||
* Enable the plugin and wait for people to donate money.
|
|
||||||
|
|
||||||
## Usage
|
## Creating Subscription Plans
|
||||||
|
|
||||||
Enable the plugin and enter your Stripe API keys in the settings. You can also configure amounts and the default currency.
|
When users subscribe to your Discourse application, they are added to a user group. You can create new user groups or use existing ones. Of course, you should be careful what permissions you apply to the user group.
|
||||||
|
|
||||||
Visit `/patrons`
|
|
||||||
|
|
||||||
## Testing
|
## Testing
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module DiscoursePatrons
|
||||||
|
class PlansController < ::ApplicationController
|
||||||
|
include DiscoursePatrons::Stripe
|
||||||
|
|
||||||
|
before_action :set_api_key
|
||||||
|
|
||||||
|
def index
|
||||||
|
plans = ::Stripe::Plan.list
|
||||||
|
render json: plans.data
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -10,7 +10,7 @@ export default Ember.Component.extend({
|
||||||
this._super(...arguments);
|
this._super(...arguments);
|
||||||
|
|
||||||
const settings = Discourse.SiteSettings;
|
const settings = Discourse.SiteSettings;
|
||||||
const amounts = Discourse.SiteSettings.discourse_patrons_amounts.split("|");
|
const amounts = settings.discourse_patrons_amounts.split("|");
|
||||||
|
|
||||||
this.setProperties({
|
this.setProperties({
|
||||||
confirmation: false,
|
confirmation: false,
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
export default Ember.Controller.extend({
|
||||||
|
actions: {
|
||||||
|
stripePaymentHandler(/* data */) {
|
||||||
|
// console.log('stripePaymentHandler', data);
|
||||||
|
},
|
||||||
|
|
||||||
|
paymentSuccessHandler(/* paymentIntentId */) {
|
||||||
|
// console.log('paymentSuccessHandler');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
|
@ -0,0 +1,15 @@
|
||||||
|
import { ajax } from "discourse/lib/ajax";
|
||||||
|
|
||||||
|
const AdminPlan = Discourse.Model.extend({
|
||||||
|
destroy() {}
|
||||||
|
});
|
||||||
|
|
||||||
|
AdminPlan.reopenClass({
|
||||||
|
find() {
|
||||||
|
return ajax("/patrons/admin/plans", { method: "get" }).then(result =>
|
||||||
|
result.plans.map(plan => AdminPlan.create(plan))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default AdminPlan;
|
|
@ -0,0 +1,18 @@
|
||||||
|
import { ajax } from "discourse/lib/ajax";
|
||||||
|
|
||||||
|
const Group = Discourse.Model.extend({});
|
||||||
|
|
||||||
|
Group.reopenClass({
|
||||||
|
subscriptionGroup:
|
||||||
|
Discourse.SiteSettings.discourse_patrons_subscription_group,
|
||||||
|
|
||||||
|
find() {
|
||||||
|
return ajax(`/groups/${this.subscriptionGroup}`, { method: "get" }).then(
|
||||||
|
result => {
|
||||||
|
return Group.create(result.group);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default Group;
|
|
@ -1,12 +1,10 @@
|
||||||
import { ajax } from "discourse/lib/ajax";
|
import { ajax } from "discourse/lib/ajax";
|
||||||
|
|
||||||
const Plan = Discourse.Model.extend({
|
const Plan = Discourse.Model.extend({});
|
||||||
destroy() {}
|
|
||||||
});
|
|
||||||
|
|
||||||
Plan.reopenClass({
|
Plan.reopenClass({
|
||||||
find() {
|
find() {
|
||||||
return ajax("/patrons/admin/plans", { method: "get" }).then(result =>
|
return ajax("/patrons/plans", { method: "get" }).then(result =>
|
||||||
result.plans.map(plan => Plan.create(plan))
|
result.plans.map(plan => Plan.create(plan))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
export default function() {
|
export default function() {
|
||||||
this.route("patrons", function() {
|
this.route("patrons", function() {
|
||||||
|
this.route("subscribe");
|
||||||
this.route("show", { path: ":pid" });
|
this.route("show", { path: ":pid" });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
import Plan from "discourse/plugins/discourse-patrons/discourse/models/plan";
|
import AdminPlan from "discourse/plugins/discourse-patrons/discourse/models/admin-plan";
|
||||||
|
|
||||||
export default Discourse.Route.extend({
|
export default Discourse.Route.extend({
|
||||||
model() {
|
model() {
|
||||||
return Plan.find();
|
return AdminPlan.find();
|
||||||
},
|
},
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
import Group from "discourse/plugins/discourse-patrons/discourse/models/group";
|
||||||
|
import Plan from "discourse/plugins/discourse-patrons/discourse/models/plan";
|
||||||
|
|
||||||
|
export default Discourse.Route.extend({
|
||||||
|
model() {
|
||||||
|
const group = Group.find();
|
||||||
|
const plans = Plan.find().then(results => results.map(p => p.id));
|
||||||
|
|
||||||
|
return Ember.RSVP.hash({ group, plans });
|
||||||
|
}
|
||||||
|
});
|
|
@ -0,0 +1,4 @@
|
||||||
|
|
||||||
|
{{#link-to 'patrons.subscribe' class='discourse-patrons-subscribe'}}
|
||||||
|
{{i18n 'discourse_patrons.navigation.subscribe'}}
|
||||||
|
{{/link-to}}
|
|
@ -0,0 +1,20 @@
|
||||||
|
|
||||||
|
<h3>
|
||||||
|
{{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>
|
||||||
|
</div>
|
||||||
|
<div class="section-column">
|
||||||
|
{{combo-box valueAttribute="id" content=model.plans value=model.plan}}
|
||||||
|
|
||||||
|
{{#d-button class="btn btn-primary btn-payment btn-discourse-patrons"}}
|
||||||
|
{{i18n 'discourse_patrons.subscribe.buttons.subscribe'}}
|
||||||
|
{{/d-button}}
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -3,6 +3,7 @@ en:
|
||||||
discourse_patrons_enabled: "Enable the Discourse Patrons plugin."
|
discourse_patrons_enabled: "Enable the Discourse Patrons plugin."
|
||||||
discourse_patrons_secret_key: "Stripe Secret Key"
|
discourse_patrons_secret_key: "Stripe Secret Key"
|
||||||
discourse_patrons_public_key: "Stripe Public Key"
|
discourse_patrons_public_key: "Stripe Public Key"
|
||||||
|
discourse_patrons_subscription_group: The name of the group the user is added to when successfully subscribed
|
||||||
discourse_patrons_currency: "Currency Code"
|
discourse_patrons_currency: "Currency Code"
|
||||||
discourse_patrons_zip_code: "Show Zip Code"
|
discourse_patrons_zip_code: "Show Zip Code"
|
||||||
discourse_patrons_billing_address: "Collect billing address"
|
discourse_patrons_billing_address: "Collect billing address"
|
||||||
|
@ -15,18 +16,24 @@ en:
|
||||||
js:
|
js:
|
||||||
discourse_patrons:
|
discourse_patrons:
|
||||||
title: Discourse Patrons
|
title: Discourse Patrons
|
||||||
nav_item: Payment
|
navigation:
|
||||||
heading:
|
subscribe: Subscribe
|
||||||
payment: Make a Payment
|
subscribe:
|
||||||
success: Thank you!
|
title: Subscribe
|
||||||
payment:
|
buttons:
|
||||||
optional: Optional
|
subscribe: Subscribe
|
||||||
receipt_info: A receipt is sent to this email address
|
one_time:
|
||||||
your_information: Your information
|
heading:
|
||||||
payment_information: Payment information
|
payment: Make a Payment
|
||||||
payment_confirmation: Confirm information
|
success: Thank you!
|
||||||
amount: Amount
|
payment:
|
||||||
payment_intent_id: Payment ID
|
optional: Optional
|
||||||
|
receipt_info: A receipt is sent to this email address
|
||||||
|
your_information: Your information
|
||||||
|
payment_information: Payment information
|
||||||
|
payment_confirmation: Confirm information
|
||||||
|
amount: Amount
|
||||||
|
payment_intent_id: Payment ID
|
||||||
billing:
|
billing:
|
||||||
name: Full name
|
name: Full name
|
||||||
email: Email
|
email: Email
|
||||||
|
|
|
@ -11,8 +11,9 @@ DiscoursePatrons::Engine.routes.draw do
|
||||||
resources :subscriptions, only: [:index]
|
resources :subscriptions, only: [:index]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
resources :plans, only: [:index]
|
||||||
|
resources :patrons, only: [:index, :create]
|
||||||
|
|
||||||
get '/' => 'patrons#index'
|
get '/' => 'patrons#index'
|
||||||
get '/:pid' => 'patrons#show'
|
get '/:pid' => 'patrons#show'
|
||||||
|
|
||||||
resources :patrons, only: [:index, :create]
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -7,6 +7,9 @@ plugins:
|
||||||
discourse_patrons_secret_key:
|
discourse_patrons_secret_key:
|
||||||
default: ''
|
default: ''
|
||||||
client: false
|
client: false
|
||||||
|
discourse_patrons_subscription_group:
|
||||||
|
default: ''
|
||||||
|
client: true
|
||||||
discourse_patrons_payment_page:
|
discourse_patrons_payment_page:
|
||||||
client: true
|
client: true
|
||||||
default: ''
|
default: ''
|
||||||
|
|
|
@ -42,6 +42,7 @@ after_initialize do
|
||||||
"../app/controllers/admin/plans_controller",
|
"../app/controllers/admin/plans_controller",
|
||||||
"../app/controllers/admin/subscriptions_controller",
|
"../app/controllers/admin/subscriptions_controller",
|
||||||
"../app/controllers/patrons_controller",
|
"../app/controllers/patrons_controller",
|
||||||
|
"../app/controllers/plans_controller",
|
||||||
"../app/models/payment",
|
"../app/models/payment",
|
||||||
"../app/serializers/payment_serializer",
|
"../app/serializers/payment_serializer",
|
||||||
].each { |path| require File.expand_path(path, __FILE__) }
|
].each { |path| require File.expand_path(path, __FILE__) }
|
||||||
|
|
|
@ -53,7 +53,7 @@ module DiscoursePatrons
|
||||||
before { sign_in(admin) }
|
before { sign_in(admin) }
|
||||||
|
|
||||||
describe "index" do
|
describe "index" do
|
||||||
it "is ok" do
|
it "lists the plans" do
|
||||||
::Stripe::Plan.expects(:list)
|
::Stripe::Plan.expects(:list)
|
||||||
get "/patrons/admin/plans.json"
|
get "/patrons/admin/plans.json"
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,22 +3,20 @@
|
||||||
require 'rails_helper'
|
require 'rails_helper'
|
||||||
|
|
||||||
module DiscoursePatrons
|
module DiscoursePatrons
|
||||||
module Admin
|
RSpec.describe Admin::SubscriptionsController do
|
||||||
RSpec.describe SubscriptionsController do
|
|
||||||
|
|
||||||
let(:admin) { Fabricate(:admin) }
|
let(:admin) { Fabricate(:admin) }
|
||||||
|
|
||||||
before { sign_in(admin) }
|
before { sign_in(admin) }
|
||||||
|
|
||||||
it 'is a subclass of AdminController' do
|
it 'is a subclass of AdminController' do
|
||||||
expect(DiscoursePatrons::Admin::SubscriptionsController < ::Admin::AdminController).to eq(true)
|
expect(DiscoursePatrons::Admin::SubscriptionsController < ::Admin::AdminController).to eq(true)
|
||||||
end
|
end
|
||||||
|
|
||||||
it "gets the empty subscriptions" do
|
it "gets the empty subscriptions" do
|
||||||
::Stripe::Subscription.expects(:list)
|
::Stripe::Subscription.expects(:list)
|
||||||
get "/patrons/admin/subscriptions.json"
|
get "/patrons/admin/subscriptions.json"
|
||||||
expect(response.status).to eq(204)
|
expect(response.status).to eq(204)
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
module DiscoursePatrons
|
||||||
|
RSpec.describe PlansController do
|
||||||
|
describe "index" do
|
||||||
|
it "lists the plans" do
|
||||||
|
::Stripe::Plan.expects(:list)
|
||||||
|
get "/patrons/plans.json"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -11,7 +11,7 @@ acceptance("Discourse Patrons", {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
QUnit.test("viewing", async assert => {
|
QUnit.test("viewing the one-off payment page", async assert => {
|
||||||
await visit("/patrons");
|
await visit("/patrons");
|
||||||
|
|
||||||
assert.ok($(".donations-page-payment").length, "has payment form class");
|
assert.ok($(".donations-page-payment").length, "has payment form class");
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
import { acceptance } from "helpers/qunit-helpers";
|
||||||
|
|
||||||
|
acceptance("Discourse Patrons");
|
||||||
|
|
||||||
|
QUnit.test("plugin outlets", async assert => {
|
||||||
|
await visit("/");
|
||||||
|
|
||||||
|
assert.ok(
|
||||||
|
$("#navigation-bar .discourse-patrons-subscribe").length,
|
||||||
|
"has a subscribe button"
|
||||||
|
);
|
||||||
|
});
|
|
@ -0,0 +1,13 @@
|
||||||
|
import { acceptance } from "helpers/qunit-helpers";
|
||||||
|
|
||||||
|
acceptance("Discourse Patrons", {
|
||||||
|
settings: {
|
||||||
|
discourse_patrons_subscription_group: "plan-id"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
QUnit.test("subscribing", async assert => {
|
||||||
|
await visit("/patrons/subscribe");
|
||||||
|
|
||||||
|
assert.ok($("h3").length, "has a heading");
|
||||||
|
});
|
|
@ -2,4 +2,8 @@ export default function(helpers) {
|
||||||
const { response } = helpers;
|
const { response } = helpers;
|
||||||
|
|
||||||
this.get("/patrons", () => response({ email: "hello@example.com" }));
|
this.get("/patrons", () => response({ email: "hello@example.com" }));
|
||||||
|
|
||||||
|
this.get("/groups/:plan", id => {
|
||||||
|
return response({ full_name: "Saboo", bio_cooked: "This is the plan" });
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue