DEV: Remove deprecations & improve error handling (#12)

- Replace deprecated methods on client
- Fix broken dropdowns due to select kit 2 upgrade
- Graceful error handling when Stripe keys are not configured but plugin enabled
This commit is contained in:
Justin DiRose 2020-05-28 10:32:57 -05:00 committed by GitHub
parent fb4fac197b
commit 9c46794e80
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
40 changed files with 261 additions and 179 deletions

View File

@ -12,9 +12,11 @@ module DiscourseSubscriptions
product_ids = Product.all.pluck(:external_id) product_ids = Product.all.pluck(:external_id)
products = [] products = []
if product_ids.present? if product_ids.present? && is_stripe_configured?
products = ::Stripe::Product.list({ ids: product_ids }) products = ::Stripe::Product.list({ ids: product_ids })
products = products[:data] products = products[:data]
elsif !is_stripe_configured?
products = nil
end end
render_json_dump products render_json_dump products

View File

@ -12,9 +12,11 @@ module DiscourseSubscriptions
subscription_ids = Subscription.all.pluck(:external_id) subscription_ids = Subscription.all.pluck(:external_id)
subscriptions = [] subscriptions = []
if subscription_ids.present? if subscription_ids.present? && is_stripe_configured?
subscriptions = ::Stripe::Subscription.list(expand: ['data.plan.product']) subscriptions = ::Stripe::Subscription.list(expand: ['data.plan.product'])
subscriptions = subscriptions.select { |sub| subscription_ids.include?(sub[:id]) } subscriptions = subscriptions.select { |sub| subscription_ids.include?(sub[:id]) }
elsif !is_stripe_configured?
subscriptions = nil
end end
render_json_dump subscriptions render_json_dump subscriptions

View File

@ -7,5 +7,9 @@ module DiscourseSubscriptions
def set_api_key def set_api_key
::Stripe.api_key = SiteSetting.discourse_subscriptions_secret_key ::Stripe.api_key = SiteSetting.discourse_subscriptions_secret_key
end end
def is_stripe_configured?
SiteSetting.discourse_subscriptions_public_key.present? && SiteSetting.discourse_subscriptions_secret_key.present?
end
end end
end end

View File

@ -11,7 +11,7 @@ module DiscourseSubscriptions
product_ids = Product.all.pluck(:external_id) product_ids = Product.all.pluck(:external_id)
products = [] products = []
if product_ids.present? if product_ids.present? && is_stripe_configured?
response = ::Stripe::Product.list({ response = ::Stripe::Product.list({
ids: product_ids, ids: product_ids,
active: true active: true

View File

@ -1,6 +1,7 @@
import { equal } from "@ember/object/computed"; import { equal } from "@ember/object/computed";
import Component from "@ember/component";
export default Ember.Component.extend({ export default Component.extend({
planButtonSelected: equal("planTypeIsSelected", true), planButtonSelected: equal("planTypeIsSelected", true),
paymentButtonSelected: equal("planTypeIsSelected", false), paymentButtonSelected: equal("planTypeIsSelected", false),

View File

@ -1,9 +1,16 @@
import computed from "discourse-common/utils/decorators"; import discourseComputed from "discourse-common/utils/decorators";
import User from "discourse/models/user"; import User from "discourse/models/user";
import { isEmpty } from "@ember/utils";
import Component from "@ember/component";
export default Ember.Component.extend({ export default Component.extend({
@computed() @discourseComputed("products")
currentUser() { emptyProducts(products) {
return isEmpty(products);
},
@discourseComputed()
isLoggedIn() {
return User.current(); return User.current();
} }
}); });

View File

@ -1,4 +1,6 @@
export default Ember.Component.extend({ import Component from "@ember/component";
export default Component.extend({
didInsertElement() { didInsertElement() {
this._super(...arguments); this._super(...arguments);
this.cardElement.mount("#card-element"); this.cardElement.mount("#card-element");

View File

@ -1,4 +1,6 @@
export default Ember.Controller.extend({ import Controller from "@ember/controller";
export default Controller.extend({
queryParams: ["order", "descending"], queryParams: ["order", "descending"],
order: null, order: null,
descending: true, descending: true,

View File

@ -1,6 +1,7 @@
import Controller from "@ember/controller";
import DiscourseURL from "discourse/lib/url"; import DiscourseURL from "discourse/lib/url";
export default Ember.Controller.extend({ export default Controller.extend({
actions: { actions: {
editPlan(id) { editPlan(id) {
return DiscourseURL.redirectTo( return DiscourseURL.redirectTo(

View File

@ -1,6 +1,7 @@
import Controller from "@ember/controller";
import { popupAjaxError } from "discourse/lib/ajax-error"; import { popupAjaxError } from "discourse/lib/ajax-error";
export default Ember.Controller.extend({ export default Controller.extend({
actions: { actions: {
createPlan() { createPlan() {
if (this.get("model.plan.product_id") === undefined) { if (this.get("model.plan.product_id") === undefined) {

View File

@ -1 +1,3 @@
export default Ember.Controller.extend({}); import Controller from "@ember/controller";
export default Controller.extend({});

View File

@ -1 +1,3 @@
export default Ember.Controller.extend({}); import Controller from "@ember/controller";
export default Controller.extend({});

View File

@ -1,16 +1,40 @@
import computed from "ember-addons/ember-computed-decorators"; import discourseComputed from "discourse-common/utils/decorators";
import DiscourseURL from "discourse/lib/url"; import DiscourseURL from "discourse/lib/url";
import Controller from "@ember/controller";
export default Ember.Controller.extend({ export default Controller.extend({
// Also defined in settings. // Also defined in settings.
currencies: ["AUD", "CAD", "EUR", "GBP", "USD", "INR"], selectedCurrency: Ember.computed.alias("model.plan.currency"),
selectedInterval: Ember.computed.alias("model.plan.interval"),
@computed("model.plan.isNew") @discourseComputed
currencies() {
return [
{ id: "AUD", name: "AUD" },
{ id: "CAD", name: "CAD" },
{ id: "EUR", name: "EUR" },
{ id: "GBP", name: "GBP" },
{ id: "USD", name: "USD" },
{ id: "INR", name: "INR" }
];
},
@discourseComputed
availableIntervals() {
return [
{ id: "day", name: "day" },
{ id: "week", name: "week" },
{ id: "month", name: "month" },
{ id: "year", name: "year" }
];
},
@discourseComputed("model.plan.isNew")
planFieldDisabled(isNew) { planFieldDisabled(isNew) {
return !isNew; return !isNew;
}, },
@computed("model.product.id") @discourseComputed("model.product.id")
productId(id) { productId(id) {
return id; return id;
}, },

View File

@ -1,6 +1,7 @@
import { popupAjaxError } from "discourse/lib/ajax-error"; import { popupAjaxError } from "discourse/lib/ajax-error";
import Controller from "@ember/controller";
export default Ember.Controller.extend({ export default Controller.extend({
actions: { actions: {
cancelProduct() { cancelProduct() {
this.transitionToRoute("adminPlugins.discourse-subscriptions.products"); this.transitionToRoute("adminPlugins.discourse-subscriptions.products");

View File

@ -1 +1,3 @@
export default Ember.Controller.extend({}); import Controller from "@ember/controller";
export default Controller.extend({});

View File

@ -1 +1,3 @@
export default Ember.Controller.extend({}); import Controller from "@ember/controller";
export default Controller.extend({});

View File

@ -1,18 +1,19 @@
import Controller from "@ember/controller";
import Customer from "discourse/plugins/discourse-subscriptions/discourse/models/customer"; import Customer from "discourse/plugins/discourse-subscriptions/discourse/models/customer";
import Payment from "discourse/plugins/discourse-subscriptions/discourse/models/payment"; import Payment from "discourse/plugins/discourse-subscriptions/discourse/models/payment";
import Subscription from "discourse/plugins/discourse-subscriptions/discourse/models/subscription"; import Subscription from "discourse/plugins/discourse-subscriptions/discourse/models/subscription";
import computed from "discourse-common/utils/decorators"; import discourseComputed from "discourse-common/utils/decorators";
import I18n from "I18n"; import I18n from "I18n";
export default Ember.Controller.extend({ export default Controller.extend({
planTypeIsSelected: true, planTypeIsSelected: true,
@computed("planTypeIsSelected") @discourseComputed("planTypeIsSelected")
type(planTypeIsSelected) { type(planTypeIsSelected) {
return planTypeIsSelected ? "plans" : "payment"; return planTypeIsSelected ? "plans" : "payment";
}, },
@computed("type") @discourseComputed("type")
buttonText(type) { buttonText(type) {
return I18n.t(`discourse_subscriptions.${type}.payment_button`); return I18n.t(`discourse_subscriptions.${type}.payment_button`);
}, },

View File

@ -1 +1,3 @@
export default Ember.Controller.extend({}); import Controller from "@ember/controller";
export default Controller.extend({});

View File

@ -1,20 +0,0 @@
// TODO: typo in this helper name: currency not curency.
export default Ember.Helper.helper(function(params) {
let currencySign;
switch (Discourse.SiteSettings.discourse_subscriptions_currency) {
case "EUR":
currencySign = "€";
break;
case "GBP":
currencySign = "£";
break;
case "INR":
currencySign = "₹";
break;
default:
currencySign = "$";
}
return [currencySign, params[0]].join("");
});

View File

@ -1,4 +1,6 @@
export default Ember.Helper.helper(function(params) { import Helper from "@ember/component/helper";
export default Helper.helper(function(params) {
let currencySign; let currencySign;
switch (params[0]) { switch (params[0]) {

View File

@ -1,4 +1,6 @@
export default Ember.Helper.helper(function(params) { import Helper from "@ember/component/helper";
export default Helper.helper(function(params) {
const payment = params[0]; const payment = params[0];
return `<a href=\"${payment.url}\">${payment.payment_intent_id}</a>`; return `<a href=\"${payment.url}\">${payment.payment_intent_id}</a>`;

View File

@ -1,10 +1,10 @@
import { registerUnbound } from "discourse-common/lib/helpers"; import { registerUnbound } from "discourse-common/lib/helpers";
import User from "discourse/models/user";
export default registerUnbound("user-viewing-self", function(model) { export default registerUnbound("user-viewing-self", function(model) {
if (Discourse.User.current()) { if (User.current()) {
return ( return (
Discourse.User.current().username.toLowerCase() === User.current().username.toLowerCase() === model.username.toLowerCase()
model.username.toLowerCase()
); );
} }

View File

@ -1,5 +1,5 @@
import Plan from "discourse/plugins/discourse-subscriptions/discourse/models/plan"; import Plan from "discourse/plugins/discourse-subscriptions/discourse/models/plan";
import computed from "ember-addons/ember-computed-decorators"; import discourseComputed from "discourse-common/utils/decorators";
import { ajax } from "discourse/lib/ajax"; import { ajax } from "discourse/lib/ajax";
const AdminPlan = Plan.extend({ const AdminPlan = Plan.extend({
@ -10,7 +10,7 @@ const AdminPlan = Plan.extend({
intervals: ["day", "week", "month", "year"], intervals: ["day", "week", "month", "year"],
metadata: {}, metadata: {},
@computed("trial_period_days") @discourseComputed("trial_period_days")
parseTrialPeriodDays(trial_period_days) { parseTrialPeriodDays(trial_period_days) {
if (trial_period_days) { if (trial_period_days) {
return parseInt(0 + trial_period_days, 10); return parseInt(0 + trial_period_days, 10);

View File

@ -40,9 +40,12 @@ const AdminProduct = EmberObject.extend({
AdminProduct.reopenClass({ AdminProduct.reopenClass({
findAll() { findAll() {
return ajax("/s/admin/products", { method: "get" }).then(result => return ajax("/s/admin/products", { method: "get" }).then(result => {
result.map(product => AdminProduct.create(product)) if (result === null) {
); return { unconfigured: true };
}
result.map(product => AdminProduct.create(product));
});
}, },
find(id) { find(id) {

View File

@ -1,19 +1,19 @@
import computed from "ember-addons/ember-computed-decorators"; import discourseComputed from "discourse-common/utils/decorators";
import { ajax } from "discourse/lib/ajax"; import { ajax } from "discourse/lib/ajax";
import EmberObject from "@ember/object"; import EmberObject from "@ember/object";
const AdminSubscription = EmberObject.extend({ const AdminSubscription = EmberObject.extend({
@computed("status") @discourseComputed("status")
canceled(status) { canceled(status) {
return status === "canceled"; return status === "canceled";
}, },
@computed("metadata") @discourseComputed("metadata")
metadataUserExists(metadata) { metadataUserExists(metadata) {
return metadata.user_id && metadata.username; return metadata.user_id && metadata.username;
}, },
@computed("metadata") @discourseComputed("metadata")
subscriptionUserPath(metadata) { subscriptionUserPath(metadata) {
return Discourse.getURL( return Discourse.getURL(
`/admin/users/${metadata.user_id}/${metadata.username}` `/admin/users/${metadata.user_id}/${metadata.username}`
@ -31,9 +31,12 @@ AdminSubscription.reopenClass({
find() { find() {
return ajax("/s/admin/subscriptions", { return ajax("/s/admin/subscriptions", {
method: "get" method: "get"
}).then(result => }).then(result => {
result.map(subscription => AdminSubscription.create(subscription)) if (result === null) {
); return { unconfigured: true };
}
result.map(subscription => AdminSubscription.create(subscription));
});
} }
}); });

View File

@ -1,5 +1,5 @@
import EmberObject from "@ember/object"; import EmberObject from "@ember/object";
import computed from "discourse-common/utils/decorators"; import discourseComputed from "discourse-common/utils/decorators";
import { ajax } from "discourse/lib/ajax"; import { ajax } from "discourse/lib/ajax";
const Plan = EmberObject.extend({ const Plan = EmberObject.extend({
@ -14,7 +14,7 @@ const Plan = EmberObject.extend({
} }
}), }),
@computed("amountDollars", "currency", "interval") @discourseComputed("amountDollars", "currency", "interval")
subscriptionRate(amountDollars, currency, interval) { subscriptionRate(amountDollars, currency, interval) {
return `${amountDollars} ${currency.toUpperCase()} / ${interval}`; return `${amountDollars} ${currency.toUpperCase()} / ${interval}`;
} }

View File

@ -1,9 +1,9 @@
import computed from "ember-addons/ember-computed-decorators"; import discourseComputed from "discourse-common/utils/decorators";
import { ajax } from "discourse/lib/ajax"; import { ajax } from "discourse/lib/ajax";
import EmberObject from "@ember/object"; import EmberObject from "@ember/object";
const Subscription = EmberObject.extend({ const Subscription = EmberObject.extend({
@computed("status") @discourseComputed("status")
canceled(status) { canceled(status) {
return status === "canceled"; return status === "canceled";
}, },

View File

@ -1,9 +1,9 @@
import EmberObject from "@ember/object"; import EmberObject from "@ember/object";
import computed from "discourse-common/utils/decorators"; import discourseComputed from "discourse-common/utils/decorators";
import { ajax } from "discourse/lib/ajax"; import { ajax } from "discourse/lib/ajax";
const UserPayment = EmberObject.extend({ const UserPayment = EmberObject.extend({
@computed("amount") @discourseComputed("amount")
amountDollars(amount) { amountDollars(amount) {
return parseFloat(amount / 100).toFixed(2); return parseFloat(amount / 100).toFixed(2);
} }

View File

@ -1,10 +1,10 @@
import EmberObject from "@ember/object"; import EmberObject from "@ember/object";
import computed from "ember-addons/ember-computed-decorators"; import discourseComputed from "discourse-common/utils/decorators";
import { ajax } from "discourse/lib/ajax"; import { ajax } from "discourse/lib/ajax";
import Plan from "discourse/plugins/discourse-subscriptions/discourse/models/plan"; import Plan from "discourse/plugins/discourse-subscriptions/discourse/models/plan";
const UserSubscription = EmberObject.extend({ const UserSubscription = EmberObject.extend({
@computed("status") @discourseComputed("status")
canceled(status) { canceled(status) {
return status === "canceled"; return status === "canceled";
}, },

View File

@ -1,6 +1,7 @@
import Route from "@ember/routing/route"; import Route from "@ember/routing/route";
import AdminPlan from "discourse/plugins/discourse-subscriptions/discourse/models/admin-plan"; import AdminPlan from "discourse/plugins/discourse-subscriptions/discourse/models/admin-plan";
import Group from "discourse/models/group"; import Group from "discourse/models/group";
import { hash } from "rsvp";
export default Route.extend({ export default Route.extend({
model(params) { model(params) {
@ -14,6 +15,7 @@ export default Route.extend({
plan = AdminPlan.create({ plan = AdminPlan.create({
active: true, active: true,
isNew: true, isNew: true,
interval: "month",
currency: Discourse.SiteSettings.discourse_subscriptions_currency, currency: Discourse.SiteSettings.discourse_subscriptions_currency,
product: product.get("id") product: product.get("id")
}); });
@ -23,7 +25,7 @@ export default Route.extend({
const groups = Group.findAll({ ignore_automatic: true }); const groups = Group.findAll({ ignore_automatic: true });
return Ember.RSVP.hash({ plan, product, groups }); return hash({ plan, product, groups });
}, },
renderTemplate() { renderTemplate() {

View File

@ -2,6 +2,7 @@ import Route from "@ember/routing/route";
import AdminProduct from "discourse/plugins/discourse-subscriptions/discourse/models/admin-product"; import AdminProduct from "discourse/plugins/discourse-subscriptions/discourse/models/admin-product";
import AdminPlan from "discourse/plugins/discourse-subscriptions/discourse/models/admin-plan"; import AdminPlan from "discourse/plugins/discourse-subscriptions/discourse/models/admin-plan";
import I18n from "I18n"; import I18n from "I18n";
import { hash } from "rsvp";
export default Route.extend({ export default Route.extend({
model(params) { model(params) {
@ -16,7 +17,7 @@ export default Route.extend({
plans = AdminPlan.findAll({ product_id }); plans = AdminPlan.findAll({ product_id });
} }
return Ember.RSVP.hash({ plans, product }); return hash({ plans, product });
}, },
actions: { actions: {

View File

@ -1,6 +1,7 @@
import Route from "@ember/routing/route"; import Route from "@ember/routing/route";
import Product from "discourse/plugins/discourse-subscriptions/discourse/models/product"; import Product from "discourse/plugins/discourse-subscriptions/discourse/models/product";
import Plan from "discourse/plugins/discourse-subscriptions/discourse/models/plan"; import Plan from "discourse/plugins/discourse-subscriptions/discourse/models/plan";
import { hash } from "rsvp";
export default Route.extend({ export default Route.extend({
model(params) { model(params) {
@ -9,6 +10,6 @@ export default Route.extend({
const product = Product.find(product_id); const product = Product.find(product_id);
const plans = Plan.findAll({ product_id }); const plans = Plan.findAll({ product_id });
return Ember.RSVP.hash({ plans, product }); return hash({ plans, product });
} }
}); });

View File

@ -1,12 +1,15 @@
{{#if model.unconfigured }}
<p class="btn-right"> <p>{{i18n 'discourse_subscriptions.admin.unconfigured'}}</p>
<p><a href="https://meta.discourse.org/t/discourse-subscriptions/140818/">Discourse Subscriptions on Meta</a></p>
{{else}}
<p class="btn-right">
{{#link-to 'adminPlugins.discourse-subscriptions.products.show' 'new' class="btn btn-primary"}} {{#link-to 'adminPlugins.discourse-subscriptions.products.show' 'new' class="btn btn-primary"}}
{{d-icon "plus"}} {{d-icon "plus"}}
<span>{{i18n 'discourse_subscriptions.admin.products.operations.new'}}</span> <span>{{i18n 'discourse_subscriptions.admin.products.operations.new'}}</span>
{{/link-to}} {{/link-to}}
</p> </p>
{{#if model}} {{#if model}}
<table class="table discourse-patrons-table"> <table class="table discourse-patrons-table">
<thead> <thead>
<th>{{i18n 'discourse_subscriptions.admin.products.product.name'}}</th> <th>{{i18n 'discourse_subscriptions.admin.products.product.name'}}</th>
@ -34,8 +37,9 @@
</tr> </tr>
{{/each}} {{/each}}
</table> </table>
{{else}} {{else}}
<p> <p>
{{i18n 'discourse_subscriptions.admin.products.product_help'}} {{i18n 'discourse_subscriptions.admin.products.product_help'}}
</p> </p>
{{/if}}
{{/if}} {{/if}}

View File

@ -15,7 +15,12 @@
</p> </p>
<p> <p>
<label for="interval">{{i18n 'discourse_subscriptions.admin.plans.plan.group'}}</label> <label for="interval">{{i18n 'discourse_subscriptions.admin.plans.plan.group'}}</label>
{{combo-box valueAttribute="name" content=model.groups value=model.plan.metadata.group_name}} {{combo-box
valueProperty="name"
content=model.groups
value=model.plan.metadata.group_name
onChange=(action (mut model.plan.metadata.group_name))
}}
<div class="control-instructions"> <div class="control-instructions">
{{i18n 'discourse_subscriptions.admin.plans.plan.group_help'}} {{i18n 'discourse_subscriptions.admin.plans.plan.group_help'}}
</div> </div>
@ -25,7 +30,12 @@
{{#if planFieldDisabled}} {{#if planFieldDisabled}}
{{input class="plan-amount plan-currency" disabled=true value=model.plan.currency}} {{input class="plan-amount plan-currency" disabled=true value=model.plan.currency}}
{{else}} {{else}}
{{combo-box disabled=planFieldDisabled content=currencies value=model.plan.currency}} {{combo-box
disabled=planFieldDisabled
content=currencies
value=model.plan.currency
onChange=(action (mut model.plan.currency))
}}
{{/if}} {{/if}}
{{input class="plan-amount" type="text" name="name" value=model.plan.amountDollars disabled=planFieldDisabled}} {{input class="plan-amount" type="text" name="name" value=model.plan.amountDollars disabled=planFieldDisabled}}
</p> </p>
@ -44,9 +54,14 @@
{{i18n 'discourse_subscriptions.admin.plans.plan.interval'}} {{i18n 'discourse_subscriptions.admin.plans.plan.interval'}}
</label> </label>
{{#if planFieldDisabled}} {{#if planFieldDisabled}}
{{input disabled=true value=model.plan.interval}} {{input disabled=true value=selectedInterval}}
{{else}} {{else}}
{{combo-box valueAttribute="value" content=model.plan.intervals value=model.plan.interval}} {{combo-box
valueProperty="name"
content=availableIntervals
value=selectedInterval
onChange=(action (mut selectedInterval))
}}
{{/if}} {{/if}}
</p> </p>
<p> <p>

View File

@ -1,5 +1,8 @@
{{#if model.unconfigured}}
<table class="table discourse-patrons-table"> <p>{{i18n 'discourse_subscriptions.admin.unconfigured'}}</p>
<p><a href="https://meta.discourse.org/t/discourse-subscriptions/140818/">Discourse Subscriptions on Meta</a></p>
{{else}}
<table class="table discourse-patrons-table">
<thead> <thead>
<tr> <tr>
<th>{{i18n 'discourse_subscriptions.admin.subscriptions.subscription.user'}}</th> <th>{{i18n 'discourse_subscriptions.admin.subscriptions.subscription.user'}}</th>
@ -36,4 +39,5 @@
</td> </td>
</tr> </tr>
{{/each}} {{/each}}
</table> </table>
{{/if}}

View File

@ -1,5 +1,7 @@
{{#if emptyProducts}}
{{#each products as |product|}} <p>{{i18n 'discourse_subscriptions.subscribe.no_products'}}</p>
{{else}}
{{#each products as |product|}}
<div class="product"> <div class="product">
<h2>{{product.name}}</h2> <h2>{{product.name}}</h2>
@ -7,7 +9,7 @@
{{product.description}} {{product.description}}
</p> </p>
{{#if currentUser}} {{#if isLoggedIn}}
<div class="pull-right"> <div class="pull-right">
{{#link-to "s.show" product.id disabled=product.subscribed class="btn btn-primary"}} {{#link-to "s.show" product.id disabled=product.subscribed class="btn btn-primary"}}
{{i18n 'discourse_subscriptions.subscribe.title'}} {{i18n 'discourse_subscriptions.subscribe.title'}}
@ -15,10 +17,11 @@
</div> </div>
{{/if}} {{/if}}
</div> </div>
{{/each}} {{/each}}
{{#unless currentUser}} {{#unless isLoggedIn}}
<p> <p>
{{i18n 'discourse_subscriptions.subscribe.unauthenticated'}} {{i18n 'discourse_subscriptions.subscribe.unauthenticated'}}
</p> </p>
{{/unless}} {{/unless}}
{{/if}}

View File

@ -1,3 +1,3 @@
{{#if (user-viewing-self model)}} {{#if (user-viewing-self model)}}
{{#link-to 'user.billing'}}{{d-icon "credit-card"}}{{I18n 'discourse_subscriptions.navigation.billing'}}{{/link-to}} {{#link-to 'user.billing'}}{{d-icon "far-credit-card"}}{{I18n 'discourse_subscriptions.navigation.billing'}}{{/link-to}}
{{/if}} {{/if}}

View File

@ -67,6 +67,7 @@ en:
confirm: Are you sure you want to cancel this subscription? confirm: Are you sure you want to cancel this subscription?
subscribe: subscribe:
title: Subscribe title: Subscribe
no_products: There are currently no products available.
unauthenticated: You need to create an account to subscribe. unauthenticated: You need to create an account to subscribe.
card: card:
title: Payment title: Payment
@ -86,6 +87,7 @@ en:
confirm_payment: Confirm payment confirm_payment: Confirm payment
success: Go back success: Go back
admin: admin:
unconfigured: 'Stripe is not configured correctly. Please see Discourse Meta for information.'
dashboard: dashboard:
title: Dashboard title: Dashboard
table: table:

View File

@ -14,7 +14,7 @@ register_asset "stylesheets/common/main.scss"
register_asset "stylesheets/common/layout.scss" register_asset "stylesheets/common/layout.scss"
register_asset "stylesheets/common/subscribe.scss" register_asset "stylesheets/common/subscribe.scss"
register_asset "stylesheets/mobile/main.scss" register_asset "stylesheets/mobile/main.scss"
register_svg_icon "credit-card" if respond_to?(:register_svg_icon) register_svg_icon "far-credit-card" if respond_to?(:register_svg_icon)
register_html_builder('server:before-head-close') do register_html_builder('server:before-head-close') do
"<script src='https://js.stripe.com/v3/'></script>" "<script src='https://js.stripe.com/v3/'></script>"

View File

@ -1,8 +1,10 @@
import { Promise } from "rsvp";
export function stubStripe() { export function stubStripe() {
window.Stripe = () => { window.Stripe = () => {
return { return {
createPaymentMethod() { createPaymentMethod() {
return new Ember.RSVP.Promise(resolve => { return new Promise(resolve => {
resolve({}); resolve({});
}); });
}, },