diff --git a/README.md b/README.md
index 315b4a0..8d021b0 100644
--- a/README.md
+++ b/README.md
@@ -11,13 +11,10 @@ This is a newer version of https://github.com/rimian/discourse-donations.
* Be sure your site is enforcing https.
* 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.
-* 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.
-
-Visit `/patrons`
+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.
## Testing
diff --git a/app/controllers/plans_controller.rb b/app/controllers/plans_controller.rb
new file mode 100644
index 0000000..bb3bc7a
--- /dev/null
+++ b/app/controllers/plans_controller.rb
@@ -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
diff --git a/assets/javascripts/discourse/components/donation-form.js.es6 b/assets/javascripts/discourse/components/donation-form.js.es6
index 82232b8..6b182a3 100644
--- a/assets/javascripts/discourse/components/donation-form.js.es6
+++ b/assets/javascripts/discourse/components/donation-form.js.es6
@@ -10,7 +10,7 @@ export default Ember.Component.extend({
this._super(...arguments);
const settings = Discourse.SiteSettings;
- const amounts = Discourse.SiteSettings.discourse_patrons_amounts.split("|");
+ const amounts = settings.discourse_patrons_amounts.split("|");
this.setProperties({
confirmation: false,
diff --git a/assets/javascripts/discourse/controllers/patrons-subscribe.js.es6 b/assets/javascripts/discourse/controllers/patrons-subscribe.js.es6
new file mode 100644
index 0000000..8e16cae
--- /dev/null
+++ b/assets/javascripts/discourse/controllers/patrons-subscribe.js.es6
@@ -0,0 +1,11 @@
+export default Ember.Controller.extend({
+ actions: {
+ stripePaymentHandler(/* data */) {
+ // console.log('stripePaymentHandler', data);
+ },
+
+ paymentSuccessHandler(/* paymentIntentId */) {
+ // console.log('paymentSuccessHandler');
+ }
+ }
+});
diff --git a/assets/javascripts/discourse/models/admin-plan.js.es6 b/assets/javascripts/discourse/models/admin-plan.js.es6
new file mode 100644
index 0000000..53c4a83
--- /dev/null
+++ b/assets/javascripts/discourse/models/admin-plan.js.es6
@@ -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;
diff --git a/assets/javascripts/discourse/models/group.js.es6 b/assets/javascripts/discourse/models/group.js.es6
new file mode 100644
index 0000000..b230e16
--- /dev/null
+++ b/assets/javascripts/discourse/models/group.js.es6
@@ -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;
diff --git a/assets/javascripts/discourse/models/plan.js.es6 b/assets/javascripts/discourse/models/plan.js.es6
index 46c1ca4..a9fe0fa 100644
--- a/assets/javascripts/discourse/models/plan.js.es6
+++ b/assets/javascripts/discourse/models/plan.js.es6
@@ -1,12 +1,10 @@
import { ajax } from "discourse/lib/ajax";
-const Plan = Discourse.Model.extend({
- destroy() {}
-});
+const Plan = Discourse.Model.extend({});
Plan.reopenClass({
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))
);
}
diff --git a/assets/javascripts/discourse/patrons-route-map.js.es6 b/assets/javascripts/discourse/patrons-route-map.js.es6
index 2c0777c..c8fa513 100644
--- a/assets/javascripts/discourse/patrons-route-map.js.es6
+++ b/assets/javascripts/discourse/patrons-route-map.js.es6
@@ -1,5 +1,6 @@
export default function() {
this.route("patrons", function() {
+ this.route("subscribe");
this.route("show", { path: ":pid" });
});
}
diff --git a/assets/javascripts/discourse/routes/admin-plugins-discourse-patrons-plans-index.js.es6 b/assets/javascripts/discourse/routes/admin-plugins-discourse-patrons-plans-index.js.es6
index ac02092..8863e99 100644
--- a/assets/javascripts/discourse/routes/admin-plugins-discourse-patrons-plans-index.js.es6
+++ b/assets/javascripts/discourse/routes/admin-plugins-discourse-patrons-plans-index.js.es6
@@ -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({
model() {
- return Plan.find();
+ return AdminPlan.find();
},
actions: {
diff --git a/assets/javascripts/discourse/routes/patrons-subscribe.js.es6 b/assets/javascripts/discourse/routes/patrons-subscribe.js.es6
new file mode 100644
index 0000000..6d0204d
--- /dev/null
+++ b/assets/javascripts/discourse/routes/patrons-subscribe.js.es6
@@ -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 });
+ }
+});
diff --git a/assets/javascripts/discourse/templates/connectors/extra-nav-item/subscribe.hbs b/assets/javascripts/discourse/templates/connectors/extra-nav-item/subscribe.hbs
new file mode 100644
index 0000000..9cca7e6
--- /dev/null
+++ b/assets/javascripts/discourse/templates/connectors/extra-nav-item/subscribe.hbs
@@ -0,0 +1,4 @@
+
+{{#link-to 'patrons.subscribe' class='discourse-patrons-subscribe'}}
+ {{i18n 'discourse_patrons.navigation.subscribe'}}
+{{/link-to}}
diff --git a/assets/javascripts/discourse/templates/patrons/subscribe.hbs b/assets/javascripts/discourse/templates/patrons/subscribe.hbs
new file mode 100644
index 0000000..940fe65
--- /dev/null
+++ b/assets/javascripts/discourse/templates/patrons/subscribe.hbs
@@ -0,0 +1,20 @@
+
+
+ {{i18n 'discourse_patrons.subscribe.title'}}
+
+
+
+
+
{{model.group.full_name}}
+
+ {{{model.group.bio_cooked}}}
+
+
+
+ {{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}}
+
+
diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml
index 6717294..78c563c 100644
--- a/config/locales/client.en.yml
+++ b/config/locales/client.en.yml
@@ -3,6 +3,7 @@ en:
discourse_patrons_enabled: "Enable the Discourse Patrons plugin."
discourse_patrons_secret_key: "Stripe Secret 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_zip_code: "Show Zip Code"
discourse_patrons_billing_address: "Collect billing address"
@@ -15,18 +16,24 @@ en:
js:
discourse_patrons:
title: Discourse Patrons
- nav_item: Payment
- heading:
- payment: Make a Payment
- success: Thank you!
- payment:
- 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
+ navigation:
+ subscribe: Subscribe
+ subscribe:
+ title: Subscribe
+ buttons:
+ subscribe: Subscribe
+ one_time:
+ heading:
+ payment: Make a Payment
+ success: Thank you!
+ payment:
+ 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:
name: Full name
email: Email
diff --git a/config/routes.rb b/config/routes.rb
index c2b1f90..9b23cbe 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -11,8 +11,9 @@ DiscoursePatrons::Engine.routes.draw do
resources :subscriptions, only: [:index]
end
+ resources :plans, only: [:index]
+ resources :patrons, only: [:index, :create]
+
get '/' => 'patrons#index'
get '/:pid' => 'patrons#show'
-
- resources :patrons, only: [:index, :create]
end
diff --git a/config/settings.yml b/config/settings.yml
index b74281a..a00b351 100644
--- a/config/settings.yml
+++ b/config/settings.yml
@@ -7,6 +7,9 @@ plugins:
discourse_patrons_secret_key:
default: ''
client: false
+ discourse_patrons_subscription_group:
+ default: ''
+ client: true
discourse_patrons_payment_page:
client: true
default: ''
diff --git a/plugin.rb b/plugin.rb
index 9d08bb8..8e9ab8a 100644
--- a/plugin.rb
+++ b/plugin.rb
@@ -42,6 +42,7 @@ after_initialize do
"../app/controllers/admin/plans_controller",
"../app/controllers/admin/subscriptions_controller",
"../app/controllers/patrons_controller",
+ "../app/controllers/plans_controller",
"../app/models/payment",
"../app/serializers/payment_serializer",
].each { |path| require File.expand_path(path, __FILE__) }
diff --git a/spec/requests/admin/plans_controller_spec.rb b/spec/requests/admin/plans_controller_spec.rb
index 69041e3..2d59109 100644
--- a/spec/requests/admin/plans_controller_spec.rb
+++ b/spec/requests/admin/plans_controller_spec.rb
@@ -53,7 +53,7 @@ module DiscoursePatrons
before { sign_in(admin) }
describe "index" do
- it "is ok" do
+ it "lists the plans" do
::Stripe::Plan.expects(:list)
get "/patrons/admin/plans.json"
end
diff --git a/spec/requests/admin/subscriptions_controller_spec.rb b/spec/requests/admin/subscriptions_controller_spec.rb
index 640d4eb..a650ddb 100644
--- a/spec/requests/admin/subscriptions_controller_spec.rb
+++ b/spec/requests/admin/subscriptions_controller_spec.rb
@@ -3,22 +3,20 @@
require 'rails_helper'
module DiscoursePatrons
- module Admin
- RSpec.describe SubscriptionsController do
+ RSpec.describe Admin::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
- expect(DiscoursePatrons::Admin::SubscriptionsController < ::Admin::AdminController).to eq(true)
- end
+ it 'is a subclass of AdminController' do
+ expect(DiscoursePatrons::Admin::SubscriptionsController < ::Admin::AdminController).to eq(true)
+ end
- it "gets the empty subscriptions" do
- ::Stripe::Subscription.expects(:list)
- get "/patrons/admin/subscriptions.json"
- expect(response.status).to eq(204)
- end
+ it "gets the empty subscriptions" do
+ ::Stripe::Subscription.expects(:list)
+ get "/patrons/admin/subscriptions.json"
+ expect(response.status).to eq(204)
end
end
end
diff --git a/spec/requests/plans_controller_spec.rb b/spec/requests/plans_controller_spec.rb
new file mode 100644
index 0000000..070a309
--- /dev/null
+++ b/spec/requests/plans_controller_spec.rb
@@ -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
diff --git a/test/javascripts/acceptance/payments-test.js.es6 b/test/javascripts/acceptance/payments-test.js.es6
index cae338b..50aab4e 100644
--- a/test/javascripts/acceptance/payments-test.js.es6
+++ b/test/javascripts/acceptance/payments-test.js.es6
@@ -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");
assert.ok($(".donations-page-payment").length, "has payment form class");
diff --git a/test/javascripts/acceptance/plugin-outlets-test.js.es6 b/test/javascripts/acceptance/plugin-outlets-test.js.es6
new file mode 100644
index 0000000..9bf814a
--- /dev/null
+++ b/test/javascripts/acceptance/plugin-outlets-test.js.es6
@@ -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"
+ );
+});
diff --git a/test/javascripts/acceptance/subscribe-test.js.es6 b/test/javascripts/acceptance/subscribe-test.js.es6
new file mode 100644
index 0000000..026b5bc
--- /dev/null
+++ b/test/javascripts/acceptance/subscribe-test.js.es6
@@ -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");
+});
diff --git a/test/javascripts/helpers/discourse-patrons-pretender.js.es6 b/test/javascripts/helpers/discourse-patrons-pretender.js.es6
index 4339bcc..7e90b9b 100644
--- a/test/javascripts/helpers/discourse-patrons-pretender.js.es6
+++ b/test/javascripts/helpers/discourse-patrons-pretender.js.es6
@@ -2,4 +2,8 @@ export default function(helpers) {
const { response } = helpers;
this.get("/patrons", () => response({ email: "hello@example.com" }));
+
+ this.get("/groups/:plan", id => {
+ return response({ full_name: "Saboo", bio_cooked: "This is the plan" });
+ });
}