mirror of
https://github.com/discourse/discourse-subscriptions.git
synced 2025-07-08 15:22:46 +00:00
various
This commit is contained in:
parent
fd0b53c67c
commit
1b1eb215e4
@ -1,14 +1,20 @@
|
|||||||
module DiscourseDonations
|
module DiscourseDonations
|
||||||
class ChargesController < ::ApplicationController
|
class ChargesController < ::ApplicationController
|
||||||
skip_before_action :verify_authenticity_token, only: [:create]
|
skip_before_action :verify_authenticity_token, only: [:create]
|
||||||
|
|
||||||
|
before_action :ensure_logged_in, only: [:cancel_subscription]
|
||||||
before_action :set_user, only: [:index, :create]
|
before_action :set_user, only: [:index, :create]
|
||||||
before_action :set_email, only: [:create]
|
before_action :set_email, only: [:index, :create, :cancel_subscription]
|
||||||
|
|
||||||
def index
|
def index
|
||||||
if @user && @user.stripe_customer_id
|
|
||||||
result = DiscourseDonations::Stripe.new(secret_key, stripe_options).list(@user)
|
|
||||||
else
|
|
||||||
result = {}
|
result = {}
|
||||||
|
|
||||||
|
if current_user
|
||||||
|
stripe = DiscourseDonations::Stripe.new(secret_key, stripe_options)
|
||||||
|
|
||||||
|
list_result = stripe.list(current_user, email: current_user.email)
|
||||||
|
|
||||||
|
result = list_result if list_result.present?
|
||||||
end
|
end
|
||||||
|
|
||||||
render json: success_json.merge(result)
|
render json: success_json.merge(result)
|
||||||
@ -36,7 +42,7 @@ module DiscourseDonations
|
|||||||
end
|
end
|
||||||
|
|
||||||
Rails.logger.debug "Creating a Stripe payment"
|
Rails.logger.debug "Creating a Stripe payment"
|
||||||
payment = DiscourseDonations::Stripe.new(secret_key, stripe_options)
|
stripe = DiscourseDonations::Stripe.new(secret_key, stripe_options)
|
||||||
result = {}
|
result = {}
|
||||||
|
|
||||||
begin
|
begin
|
||||||
@ -48,17 +54,27 @@ module DiscourseDonations
|
|||||||
}
|
}
|
||||||
|
|
||||||
if user_params[:type] === 'once'
|
if user_params[:type] === 'once'
|
||||||
result[:payment] = payment.charge(@user, opts)
|
result[:charge] = stripe.charge(@user, opts)
|
||||||
else
|
else
|
||||||
opts[:type] = user_params[:type]
|
opts[:type] = user_params[:type]
|
||||||
result[:subscription] = payment.subscribe(@user, opts)
|
|
||||||
|
subscription = stripe.subscribe(@user, opts)
|
||||||
|
|
||||||
|
if subscription && subscription['id']
|
||||||
|
invoices = stripe.invoices_for_subscription(@user,
|
||||||
|
email: opts[:email],
|
||||||
|
subscription_id: subscription['id']
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
result[:subscription] = {}
|
||||||
|
result[:subscription][:subscription] = subscription if subscription
|
||||||
|
result[:subscription][:invoices] = invoices if invoices
|
||||||
end
|
end
|
||||||
|
|
||||||
rescue ::Stripe::CardError => e
|
rescue ::Stripe::CardError => e
|
||||||
err = e.json_body[:error]
|
err = e.json_body[:error]
|
||||||
|
|
||||||
puts "HERE IS THE ERROR: #{e.inspect}"
|
|
||||||
|
|
||||||
output['messages'] << "There was an error (#{err[:type]})."
|
output['messages'] << "There was an error (#{err[:type]})."
|
||||||
output['messages'] << "Error code: #{err[:code]}" if err[:code]
|
output['messages'] << "Error code: #{err[:code]}" if err[:code]
|
||||||
output['messages'] << "Decline code: #{err[:decline_code]}" if err[:decline_code]
|
output['messages'] << "Decline code: #{err[:decline_code]}" if err[:decline_code]
|
||||||
@ -68,18 +84,16 @@ module DiscourseDonations
|
|||||||
end
|
end
|
||||||
|
|
||||||
if (result[:charge] && result[:charge]['paid'] == true) ||
|
if (result[:charge] && result[:charge]['paid'] == true) ||
|
||||||
(result[:subscription] && result[:subscription]['status'] === 'active')
|
(result[:subscription] && result[:subscription][:subscription] &&
|
||||||
|
result[:subscription][:subscription]['status'] === 'active')
|
||||||
|
|
||||||
output['messages'] << I18n.t('donations.payment.success')
|
output['messages'] << I18n.t('donations.payment.success')
|
||||||
|
|
||||||
if result[:charge]
|
if (result[:charge] && result[:charge]['receipt_number']) ||
|
||||||
|
(result[:subscription] && result[:subscription][:invoices].first['receipt_number'])
|
||||||
output['messages'] << " #{I18n.t('donations.payment.receipt_sent', email: @email)}"
|
output['messages'] << " #{I18n.t('donations.payment.receipt_sent', email: @email)}"
|
||||||
end
|
end
|
||||||
|
|
||||||
if result[:subscription]
|
|
||||||
output['messages'] << " #{I18n.t('donations.payment.invoice_sent', email: @email)}"
|
|
||||||
end
|
|
||||||
|
|
||||||
output['charge'] = result[:charge] if result[:charge]
|
output['charge'] = result[:charge] if result[:charge]
|
||||||
output['subscription'] = result[:subscription] if result[:subscription]
|
output['subscription'] = result[:subscription] if result[:subscription]
|
||||||
|
|
||||||
@ -95,6 +109,20 @@ module DiscourseDonations
|
|||||||
render json: output
|
render json: output
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def cancel_subscription
|
||||||
|
params.require(:subscription_id)
|
||||||
|
|
||||||
|
stripe = DiscourseDonations::Stripe.new(secret_key, stripe_options)
|
||||||
|
|
||||||
|
result = stripe.cancel_subscription(params[:subscription_id])
|
||||||
|
|
||||||
|
if result[:success]
|
||||||
|
render json: success_json.merge(subscription: result[:subscription])
|
||||||
|
else
|
||||||
|
render json: failed_json.merge(message: result[:message])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def create_account
|
def create_account
|
||||||
@ -132,16 +160,20 @@ module DiscourseDonations
|
|||||||
user = current_user
|
user = current_user
|
||||||
|
|
||||||
if user_params[:user_id].present?
|
if user_params[:user_id].present?
|
||||||
user = User.find(user_params[:user_id])
|
if record = User.find_by(user_params[:user_id])
|
||||||
|
user = record
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@user = user
|
@user = user
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_email
|
def set_email
|
||||||
|
email = nil
|
||||||
|
|
||||||
if user_params[:email].present?
|
if user_params[:email].present?
|
||||||
email = user_params[:email]
|
email = user_params[:email]
|
||||||
else
|
elsif @user
|
||||||
email = @user.try(:email)
|
email = @user.try(:email)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -9,7 +9,13 @@ module DiscourseDonations
|
|||||||
end
|
end
|
||||||
|
|
||||||
def checkoutCharge(user = nil, email, token, amount)
|
def checkoutCharge(user = nil, email, token, amount)
|
||||||
customer = customer(user, email, token)
|
customer = customer(user,
|
||||||
|
email: email,
|
||||||
|
source: token,
|
||||||
|
create: true
|
||||||
|
)
|
||||||
|
|
||||||
|
return if !customer
|
||||||
|
|
||||||
charge = ::Stripe::Charge.create(
|
charge = ::Stripe::Charge.create(
|
||||||
customer: customer.id,
|
customer: customer.id,
|
||||||
@ -22,7 +28,13 @@ module DiscourseDonations
|
|||||||
end
|
end
|
||||||
|
|
||||||
def charge(user = nil, opts)
|
def charge(user = nil, opts)
|
||||||
customer = customer(user, opts[:email], opts[:token])
|
customer = customer(user,
|
||||||
|
email: opts[:email],
|
||||||
|
source: opts[:token],
|
||||||
|
create: true
|
||||||
|
)
|
||||||
|
|
||||||
|
return if !customer
|
||||||
|
|
||||||
@charge = ::Stripe::Charge.create(
|
@charge = ::Stripe::Charge.create(
|
||||||
customer: customer.id,
|
customer: customer.id,
|
||||||
@ -36,28 +48,40 @@ module DiscourseDonations
|
|||||||
end
|
end
|
||||||
|
|
||||||
def subscribe(user = nil, opts)
|
def subscribe(user = nil, opts)
|
||||||
customer = customer(user, opts[:email], opts[:token])
|
customer = customer(user,
|
||||||
|
email: opts[:email],
|
||||||
|
source: opts[:token],
|
||||||
|
create: true
|
||||||
|
)
|
||||||
|
|
||||||
|
return if !customer
|
||||||
|
|
||||||
|
type = opts[:type]
|
||||||
|
amount = opts[:amount]
|
||||||
|
|
||||||
plans = ::Stripe::Plan.list
|
plans = ::Stripe::Plan.list
|
||||||
type = opts[:type]
|
plan_id = create_plan_id(type, amount)
|
||||||
plan_id = create_plan_id(type)
|
|
||||||
|
|
||||||
unless plans.data && plans.data.any? { |p| p['id'] === plan_id }
|
unless plans.data && plans.data.any? { |p| p['id'] === plan_id }
|
||||||
result = create_plan(type, opts[:amount])
|
result = create_plan(type, amount)
|
||||||
|
|
||||||
plan_id = result['id']
|
plan_id = result['id']
|
||||||
end
|
end
|
||||||
|
|
||||||
@subscription = ::Stripe::Subscription.create(
|
::Stripe::Subscription.create(
|
||||||
customer: customer.id,
|
customer: customer.id,
|
||||||
items: [{ plan: plan_id }]
|
items: [{
|
||||||
|
plan: plan_id
|
||||||
|
}]
|
||||||
)
|
)
|
||||||
|
|
||||||
@subscription
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def list(user)
|
def list(user, opts = {})
|
||||||
customer = customer(user)
|
customer = customer(user, opts)
|
||||||
result = {}
|
|
||||||
|
return if !customer
|
||||||
|
|
||||||
|
result = { customer: customer }
|
||||||
|
|
||||||
raw_invoices = ::Stripe::Invoice.list(customer: customer.id)
|
raw_invoices = ::Stripe::Invoice.list(customer: customer.id)
|
||||||
raw_invoices = raw_invoices.is_a?(Object) ? raw_invoices['data'] : []
|
raw_invoices = raw_invoices.is_a?(Object) ? raw_invoices['data'] : []
|
||||||
@ -66,7 +90,8 @@ module DiscourseDonations
|
|||||||
raw_charges = raw_charges.is_a?(Object) ? raw_charges['data'] : []
|
raw_charges = raw_charges.is_a?(Object) ? raw_charges['data'] : []
|
||||||
|
|
||||||
if raw_invoices.any?
|
if raw_invoices.any?
|
||||||
raw_subscriptions = ::Stripe::Subscription.list(customer: customer.id)['data']
|
raw_subscriptions = ::Stripe::Subscription.list(customer: customer.id, status: 'all')
|
||||||
|
raw_subscriptions = raw_subscriptions.is_a?(Object) ? raw_subscriptions['data'] : []
|
||||||
|
|
||||||
if raw_subscriptions.any?
|
if raw_subscriptions.any?
|
||||||
subscriptions = []
|
subscriptions = []
|
||||||
@ -97,30 +122,90 @@ module DiscourseDonations
|
|||||||
result
|
result
|
||||||
end
|
end
|
||||||
|
|
||||||
def customer(user, email = nil, source = nil)
|
def invoices_for_subscription(user, opts)
|
||||||
if user && user.stripe_customer_id
|
customer = customer(user,
|
||||||
::Stripe::Customer.retrieve(user.stripe_customer_id)
|
email: opts[:email]
|
||||||
|
)
|
||||||
|
|
||||||
|
invoices = []
|
||||||
|
|
||||||
|
if customer
|
||||||
|
result = ::Stripe::Invoice.list(
|
||||||
|
customer: customer.id,
|
||||||
|
subscription: opts[:subscription_id]
|
||||||
|
)
|
||||||
|
|
||||||
|
invoices = result['data'] if result['data']
|
||||||
|
end
|
||||||
|
|
||||||
|
invoices
|
||||||
|
end
|
||||||
|
|
||||||
|
def cancel_subscription(subscription_id)
|
||||||
|
if subscription = ::Stripe::Subscription.retrieve(subscription_id)
|
||||||
|
result = subscription.delete
|
||||||
|
|
||||||
|
if result['status'] === 'canceled'
|
||||||
|
{ success: true, subscription: subscription }
|
||||||
else
|
else
|
||||||
|
{ success: false, message: I18n.t('donations.subscription.error.not_cancelled') }
|
||||||
|
end
|
||||||
|
else
|
||||||
|
{ success: false, message: I18n.t('donations.subscription.error.not_found') }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def customer(user, opts = {})
|
||||||
|
customer = nil
|
||||||
|
|
||||||
|
if user && user.stripe_customer_id
|
||||||
|
begin
|
||||||
|
customer = ::Stripe::Customer.retrieve(user.stripe_customer_id)
|
||||||
|
rescue ::Stripe::StripeError => e
|
||||||
|
user.custom_fields['stripe_customer_id'] = nil
|
||||||
|
user.save_custom_fields(true)
|
||||||
|
customer = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if !customer && opts[:email]
|
||||||
|
begin
|
||||||
|
customers = ::Stripe::Customer.list(email: opts[:email])
|
||||||
|
|
||||||
|
if customers && customers['data']
|
||||||
|
customer = customers['data'].first if customers['data'].any?
|
||||||
|
end
|
||||||
|
|
||||||
|
if customer && user
|
||||||
|
user.custom_fields['stripe_customer_id'] = customer.id
|
||||||
|
user.save_custom_fields(true)
|
||||||
|
end
|
||||||
|
rescue ::Stripe::StripeError => e
|
||||||
|
customer = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if !customer && opts[:create]
|
||||||
customer = ::Stripe::Customer.create(
|
customer = ::Stripe::Customer.create(
|
||||||
email: email,
|
email: opts[:email],
|
||||||
source: source
|
source: opts[:source]
|
||||||
)
|
)
|
||||||
|
|
||||||
if user
|
if user
|
||||||
user.custom_fields['stripe_customer_id'] = customer.id
|
user.custom_fields['stripe_customer_id'] = customer.id
|
||||||
user.save_custom_fields(true)
|
user.save_custom_fields(true)
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
customer
|
customer
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
def successful?
|
def successful?
|
||||||
@charge[:paid]
|
@charge[:paid]
|
||||||
end
|
end
|
||||||
|
|
||||||
def create_plan(type, amount)
|
def create_plan(type, amount)
|
||||||
id = create_plan_id(type)
|
id = create_plan_id(type, amount)
|
||||||
nickname = id.gsub(/_/, ' ').titleize
|
nickname = id.gsub(/_/, ' ').titleize
|
||||||
|
|
||||||
products = ::Stripe::Product.list(type: 'service')
|
products = ::Stripe::Product.list(type: 'service')
|
||||||
@ -151,15 +236,15 @@ module DiscourseDonations
|
|||||||
end
|
end
|
||||||
|
|
||||||
def product_id
|
def product_id
|
||||||
@product_id ||= "#{SiteSetting.title}_recurring_donation"
|
@product_id ||= "#{SiteSetting.title}_recurring_donation".freeze
|
||||||
end
|
end
|
||||||
|
|
||||||
def product_name
|
def product_name
|
||||||
@product_name ||= I18n.t('discourse_donations.recurring', site_title: SiteSetting.title)
|
@product_name ||= I18n.t('donations.recurring', site_title: SiteSetting.title)
|
||||||
end
|
end
|
||||||
|
|
||||||
def create_plan_id(type)
|
def create_plan_id(type, amount)
|
||||||
"discourse_donation_recurring_#{type}"
|
"discourse_donation_recurring_#{type}_#{amount}".freeze
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
import { ajax } from 'discourse/lib/ajax';
|
||||||
|
import { popupAjaxError } from 'discourse/lib/ajax-error';
|
||||||
|
|
||||||
export default Ember.Component.extend({
|
export default Ember.Component.extend({
|
||||||
classNames: 'donation-list',
|
classNames: 'donation-list',
|
||||||
hasSubscriptions: Ember.computed.notEmpty('subscriptions'),
|
hasSubscriptions: Ember.computed.notEmpty('subscriptions'),
|
||||||
|
96
assets/javascripts/discourse/components/donation-row.js.es6
Normal file
96
assets/javascripts/discourse/components/donation-row.js.es6
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
import { ajax } from 'discourse/lib/ajax';
|
||||||
|
import { popupAjaxError } from 'discourse/lib/ajax-error';
|
||||||
|
import { formatAnchor, formatAmount } from '../lib/donation-utilities';
|
||||||
|
import { default as computed, observes, on } from 'ember-addons/ember-computed-decorators';
|
||||||
|
import showModal from "discourse/lib/show-modal";
|
||||||
|
|
||||||
|
export default Ember.Component.extend({
|
||||||
|
classNameBindings: [':donation-row', 'canceled', 'updating'],
|
||||||
|
includePrefix: Ember.computed.or('invoice', 'charge'),
|
||||||
|
canceled: Ember.computed.equal('subscription.status', 'canceled'),
|
||||||
|
|
||||||
|
@computed('subscription', 'invoice', 'charge', 'customer')
|
||||||
|
data(subscription, invoice, charge, customer) {
|
||||||
|
if (subscription) {
|
||||||
|
return $.extend({}, subscription.plan, {
|
||||||
|
anchor: subscription.billing_cycle_anchor
|
||||||
|
});
|
||||||
|
} else if (invoice) {
|
||||||
|
let receiptSent = false;
|
||||||
|
|
||||||
|
if (invoice.receipt_number && customer.email) {
|
||||||
|
receiptSent = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $.extend({}, invoice.lines.data[0], {
|
||||||
|
anchor: invoice.date,
|
||||||
|
invoiceLink: invoice.invoice_pdf,
|
||||||
|
receiptSent
|
||||||
|
});
|
||||||
|
} else if (charge) {
|
||||||
|
let receiptSent = false;
|
||||||
|
|
||||||
|
if (charge.receipt_number && charge.receipt_email) {
|
||||||
|
receiptSent = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $.extend({}, charge, {
|
||||||
|
anchor: charge.created,
|
||||||
|
receiptSent
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
@computed('data.currency')
|
||||||
|
currency(currency) {
|
||||||
|
return currency ? currency.toUpperCase() : null;
|
||||||
|
},
|
||||||
|
|
||||||
|
@computed('data.amount', 'currency')
|
||||||
|
amount(amount, currency) {
|
||||||
|
return formatAmount(amount, currency);
|
||||||
|
},
|
||||||
|
|
||||||
|
@computed('data.interval')
|
||||||
|
interval(interval) {
|
||||||
|
return interval || 'once';
|
||||||
|
},
|
||||||
|
|
||||||
|
@computed('data.anchor', 'interval')
|
||||||
|
period(anchor, interval) {
|
||||||
|
return I18n.t(`discourse_donations.period.${interval}`, {
|
||||||
|
anchor: formatAnchor(interval, moment.unix(anchor))
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
cancelSubscription() {
|
||||||
|
const subscriptionId = this.get('subscription.id');
|
||||||
|
this.set('updating', true);
|
||||||
|
|
||||||
|
ajax('/donate/charges/cancel-subscription', {
|
||||||
|
data: {
|
||||||
|
subscription_id: subscriptionId
|
||||||
|
},
|
||||||
|
method: 'put'
|
||||||
|
}).then(result => {
|
||||||
|
if (result.success) {
|
||||||
|
this.set('subscription', result.subscription);
|
||||||
|
}
|
||||||
|
}).catch(popupAjaxError).finally(() => {
|
||||||
|
this.set('updating', false);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
cancelSubscription() {
|
||||||
|
showModal('cancel-subscription', {
|
||||||
|
model: {
|
||||||
|
currency: this.get('currency'),
|
||||||
|
amount: this.get('amount'),
|
||||||
|
period: this.get('period'),
|
||||||
|
confirm: () => this.cancelSubscription()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
@ -175,7 +175,7 @@ export default Ember.Component.extend({
|
|||||||
const transactionFeeEnabled = settings.discourse_donations_enable_transaction_fee;
|
const transactionFeeEnabled = settings.discourse_donations_enable_transaction_fee;
|
||||||
let amount = transactionFeeEnabled ? this.get('totalAmount') : this.get('amount');
|
let amount = transactionFeeEnabled ? this.get('totalAmount') : this.get('amount');
|
||||||
|
|
||||||
if (zeroDecimalCurrencies.indexOf(setting.discourse_donations_currency) === -1) {
|
if (zeroDecimalCurrencies.indexOf(settings.discourse_donations_currency) === -1) {
|
||||||
amount = amount * 100;
|
amount = amount * 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -189,22 +189,26 @@ export default Ember.Component.extend({
|
|||||||
};
|
};
|
||||||
|
|
||||||
if(!self.get('paymentSuccess')) {
|
if(!self.get('paymentSuccess')) {
|
||||||
ajax('/donate/charges', { data: params, method: 'post' }).then(d => {
|
ajax('/donate/charges', {
|
||||||
let donation = d.donation;
|
data: params,
|
||||||
|
method: 'post'
|
||||||
if (donation) {
|
}).then(result => {
|
||||||
if (donation.object === 'subscription') {
|
if (result.subscription) {
|
||||||
let subscriptions = this.get('subscriptions') || [];
|
let subscription = $.extend({}, result.subscription, {
|
||||||
subscriptions.push(donation);
|
new: true
|
||||||
this.set('subscriptions', subscriptions);
|
});
|
||||||
} else if (donation.object === 'charge') {
|
this.get('subscriptions').unshiftObject(subscription);
|
||||||
let charges = this.get('charges') || [];
|
|
||||||
charges.push(donation);
|
|
||||||
this.set('charges', charges);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.concatMessages(d.messages);
|
if (result.charge) {
|
||||||
|
let charge = $.extend({}, result.charge, {
|
||||||
|
new: true
|
||||||
|
});
|
||||||
|
this.get('charges').unshiftObject(charge);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.concatMessages(result.messages);
|
||||||
|
|
||||||
self.endTranscation();
|
self.endTranscation();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,12 @@
|
|||||||
|
export default Ember.Controller.extend({
|
||||||
|
actions: {
|
||||||
|
confirm() {
|
||||||
|
this.get('model.confirm')();
|
||||||
|
this.send('closeModal');
|
||||||
|
},
|
||||||
|
|
||||||
|
cancel() {
|
||||||
|
this.send('closeModal');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
@ -1,11 +1,53 @@
|
|||||||
import { default as computed } from 'ember-addons/ember-computed-decorators';
|
import { default as computed } from 'ember-addons/ember-computed-decorators';
|
||||||
|
import { popupAjaxError } from 'discourse/lib/ajax-error';
|
||||||
|
import { ajax } from 'discourse/lib/ajax';
|
||||||
|
import { getOwner } from 'discourse-common/lib/get-owner';
|
||||||
|
|
||||||
export default Ember.Controller.extend({
|
export default Ember.Controller.extend({
|
||||||
loadingDonations: false,
|
loadingDonations: false,
|
||||||
|
loadDonationsDisabled: Ember.computed.not('emailVaild'),
|
||||||
|
|
||||||
@computed('charges', 'subscriptions')
|
@computed('charges.[]', 'subscriptions.[]')
|
||||||
hasDonations(charges, subscriptions) {
|
hasDonations(charges, subscriptions) {
|
||||||
return (charges && charges.length > 0) ||
|
return (charges && charges.length > 0) ||
|
||||||
(subscriptions && subscriptions.length > 0);
|
(subscriptions && subscriptions.length > 0);
|
||||||
|
},
|
||||||
|
|
||||||
|
@computed('email')
|
||||||
|
emailVaild(email) {
|
||||||
|
return emailValid(email);
|
||||||
|
},
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
loadDonations() {
|
||||||
|
let email = this.get('email');
|
||||||
|
|
||||||
|
this.set('loadingDonations', true);
|
||||||
|
|
||||||
|
ajax('/donate/charges', {
|
||||||
|
data: { email },
|
||||||
|
type: 'GET'
|
||||||
|
}).then((result) => {
|
||||||
|
this.setProperties({
|
||||||
|
charges: Ember.A(result.charges),
|
||||||
|
subscriptions: Ember.A(result.subscriptions),
|
||||||
|
customer: result.customer
|
||||||
|
});
|
||||||
|
}).catch(popupAjaxError).finally(() => {
|
||||||
|
this.setProperties({
|
||||||
|
loadingDonations: false,
|
||||||
|
hasEmailResult: true
|
||||||
|
});
|
||||||
|
|
||||||
|
Ember.run.later(() => {
|
||||||
|
this.set('hasEmailResult', false);
|
||||||
|
}, 6000)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
showLogin() {
|
||||||
|
const controller = getOwner(this).lookup('route:application');
|
||||||
|
controller.send('showLogin');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -1,56 +0,0 @@
|
|||||||
import { registerHelper } from "discourse-common/lib/helpers";
|
|
||||||
import { formatAnchor, formatAmount } from '../lib/donation-utilities';
|
|
||||||
|
|
||||||
registerHelper("donation-subscription", function([subscription]) {
|
|
||||||
let currency = subscription.plan.currency.toUpperCase();
|
|
||||||
let html = currency;
|
|
||||||
|
|
||||||
html += ` ${formatAmount(subscription.plan.amount, currency)} `;
|
|
||||||
|
|
||||||
html += I18n.t(`discourse_donations.period.${subscription.plan.interval}`, {
|
|
||||||
anchor: formatAnchor(subscription.plan.interval, moment.unix(subscription.billing_cycle_anchor))
|
|
||||||
});
|
|
||||||
|
|
||||||
return new Handlebars.SafeString(html);
|
|
||||||
});
|
|
||||||
|
|
||||||
registerHelper("donation-invoice", function([invoice]) {
|
|
||||||
let details = invoice.lines.data[0];
|
|
||||||
let html = I18n.t('discourse_donations.invoice_prefix');
|
|
||||||
let currency = details.currency.toUpperCase();
|
|
||||||
|
|
||||||
html += ` ${currency}`;
|
|
||||||
|
|
||||||
html += ` ${formatAmount(details.amount, currency)} `;
|
|
||||||
|
|
||||||
html += I18n.t(`discourse_donations.period.once`, {
|
|
||||||
anchor: formatAnchor('once', moment.unix(invoice.date))
|
|
||||||
});
|
|
||||||
|
|
||||||
if (invoice.invoice_pdf) {
|
|
||||||
html += ` (<a href='${invoice.invoice_pdf}' target='_blank'>${I18n.t('discourse_donations.invoice')}</a>)`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Handlebars.SafeString(html);
|
|
||||||
});
|
|
||||||
|
|
||||||
registerHelper("donation-charge", function([charge]) {
|
|
||||||
let html = I18n.t('discourse_donations.invoice_prefix');
|
|
||||||
let currency = charge.currency.toUpperCase();
|
|
||||||
|
|
||||||
html += ` ${currency}`;
|
|
||||||
|
|
||||||
html += ` ${formatAmount(charge.amount, currency)} `;
|
|
||||||
|
|
||||||
html += I18n.t(`discourse_donations.period.once`, {
|
|
||||||
anchor: formatAnchor('once', moment.unix(charge.created))
|
|
||||||
});
|
|
||||||
|
|
||||||
if (charge.receipt_email) {
|
|
||||||
html += `. ${I18n.t('discourse_donations.receipt', {
|
|
||||||
email: charge.receipt_email
|
|
||||||
})}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Handlebars.SafeString(html);
|
|
||||||
});
|
|
@ -4,15 +4,24 @@ import { ajax } from 'discourse/lib/ajax';
|
|||||||
|
|
||||||
export default DiscourseRoute.extend({
|
export default DiscourseRoute.extend({
|
||||||
setupController(controller) {
|
setupController(controller) {
|
||||||
|
let charges = [];
|
||||||
|
let subscriptions = [];
|
||||||
|
let customer = {};
|
||||||
|
|
||||||
controller.set('loadingDonations', true);
|
controller.set('loadingDonations', true);
|
||||||
|
|
||||||
ajax('/donate/charges').then((result) => {
|
ajax('/donate/charges').then((result) => {
|
||||||
if (result && (result.charges || result.subscriptions)) {
|
if (result) {
|
||||||
controller.setProperties({
|
charges = result.charges;
|
||||||
charges: result.charges,
|
subscriptions = result.subscriptions;
|
||||||
subscriptions: result.subscriptions
|
customer = result.customer;
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
controller.setProperties({
|
||||||
|
charges: Ember.A(charges),
|
||||||
|
subscriptions: Ember.A(subscriptions),
|
||||||
|
customer
|
||||||
|
});
|
||||||
}).catch(popupAjaxError).finally(() => {
|
}).catch(popupAjaxError).finally(() => {
|
||||||
controller.set('loadingDonations', false);
|
controller.set('loadingDonations', false);
|
||||||
})
|
})
|
||||||
|
@ -3,12 +3,11 @@
|
|||||||
<div class="underline">{{i18n 'discourse_donations.donations.subscriptions'}}</div>
|
<div class="underline">{{i18n 'discourse_donations.donations.subscriptions'}}</div>
|
||||||
<ul>
|
<ul>
|
||||||
{{#each subscriptions as |s|}}
|
{{#each subscriptions as |s|}}
|
||||||
<li>{{donation-subscription s.subscription}}</li>
|
<li>{{donation-row subscription=s.subscription customer=customer new=s.new}}</li>
|
||||||
|
|
||||||
{{#if s.invoices}}
|
{{#if s.invoices}}
|
||||||
<ul>
|
<ul>
|
||||||
{{#each s.invoices as |invoice|}}
|
{{#each s.invoices as |invoice|}}
|
||||||
<li>{{donation-invoice invoice}}</li>
|
<li>{{donation-row invoice=invoice customer=customer new=s.new}}</li>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</ul>
|
</ul>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
@ -22,7 +21,7 @@
|
|||||||
<div class='underline'>{{i18n 'discourse_donations.donations.charges'}}</div>
|
<div class='underline'>{{i18n 'discourse_donations.donations.charges'}}</div>
|
||||||
<ul>
|
<ul>
|
||||||
{{#each charges as |charge|}}
|
{{#each charges as |charge|}}
|
||||||
<li>{{donation-charge charge}}</li>
|
<li>{{donation-row charge=charge customer=customer new=charge.new}}</li>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
@ -0,0 +1,39 @@
|
|||||||
|
{{#if includePrefix}}
|
||||||
|
<span>{{i18n 'discourse_donations.invoice_prefix'}}</span>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
<span>{{currency}}</span>
|
||||||
|
|
||||||
|
<span>{{amount}}</span>
|
||||||
|
|
||||||
|
<span>{{period}}</span>
|
||||||
|
|
||||||
|
{{#if invoice}}
|
||||||
|
<a href='{{data.invoiceLink}}' target='_blank'>({{i18n 'discourse_donations.invoice'}})</a>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if currentUser}}
|
||||||
|
{{#if subscription}}
|
||||||
|
{{#if updating}}
|
||||||
|
{{loading-spinner size='small'}}
|
||||||
|
{{else}}
|
||||||
|
{{#unless canceled}}
|
||||||
|
<a {{action 'cancelSubscription'}}>
|
||||||
|
{{i18n 'cancel'}}
|
||||||
|
</a>
|
||||||
|
{{/unless}}
|
||||||
|
{{/if}}
|
||||||
|
{{/if}}
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if receiptSent}}
|
||||||
|
<span>–</span>
|
||||||
|
<span>{{i18n 'discourse_donations.receipt' email=customer.email}}</span>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if new}}
|
||||||
|
<span class="new-flag">
|
||||||
|
{{d-icon 'circle'}}
|
||||||
|
<span>{{i18n 'new_item'}}</span>
|
||||||
|
</span>
|
||||||
|
{{/if}}
|
@ -33,6 +33,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="control-group">
|
<div class="control-group">
|
||||||
<label class='control-label'>
|
<label class='control-label'>
|
||||||
{{i18n 'discourse_donations.transaction_fee.total'}}
|
{{i18n 'discourse_donations.transaction_fee.total'}}
|
||||||
|
@ -8,17 +8,29 @@
|
|||||||
{{stripe-card charges=charges subscriptions=subscriptions}}
|
{{stripe-card charges=charges subscriptions=subscriptions}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{#if currentUser}}
|
|
||||||
<div class="donations-page-donations">
|
<div class="donations-page-donations">
|
||||||
<h3>{{i18n 'discourse_donations.donations.title'}}</h3>
|
<h3>{{i18n 'discourse_donations.donations.title'}}</h3>
|
||||||
{{#if loadingDonations}}
|
{{#if loadingDonations}}
|
||||||
|
<span>{{i18n 'discourse_donations.donations.loading'}}</span>
|
||||||
{{loading-spinner size='small'}}
|
{{loading-spinner size='small'}}
|
||||||
{{else}}
|
{{else}}
|
||||||
|
{{#if currentUser}}
|
||||||
{{#if hasDonations}}
|
{{#if hasDonations}}
|
||||||
{{donation-list charges=charges subscriptions=subscriptions}}
|
{{donation-list charges=charges subscriptions=subscriptions customer=customer}}
|
||||||
{{else}}
|
{{else}}
|
||||||
{{i18n 'discourse_donations.donations.none'}}
|
{{i18n 'discourse_donations.donations.none'}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
{{else}}
|
||||||
|
{{#if hasDonations}}
|
||||||
|
{{donation-list charges=charges subscriptions=subscriptions customer=customer}}
|
||||||
|
{{else}}
|
||||||
|
{{#if hasEmailResult}}
|
||||||
|
{{i18n 'discourse_donations.donations.none_email' email=email}}
|
||||||
|
{{else}}
|
||||||
|
{{input value=email placeholder=(i18n 'email')}}
|
||||||
|
{{d-button action='loadDonations' label='discourse_donations.donations.load' disabled=loadDonationsDisabled}}
|
||||||
|
{{/if}}
|
||||||
|
{{/if}}
|
||||||
|
{{/if}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
|
||||||
|
@ -0,0 +1,11 @@
|
|||||||
|
{{#d-modal-body title='discourse_donations.subscription.cancel.title'}}
|
||||||
|
{{i18n 'discourse_donations.subscription.cancel.description' site=siteSettings.title
|
||||||
|
currency=model.currency
|
||||||
|
amount=model.amount
|
||||||
|
period=model.period}}
|
||||||
|
{{/d-modal-body}}
|
||||||
|
|
||||||
|
<div class="modal-footer">
|
||||||
|
{{d-button action='confirm' label='yes_value' class='btn-primary'}}
|
||||||
|
{{d-button action='cancel' label='no_value'}}
|
||||||
|
</div>
|
@ -61,11 +61,15 @@ div.stripe-errors {
|
|||||||
.donation-list {
|
.donation-list {
|
||||||
.subscription-list, .charge-list {
|
.subscription-list, .charge-list {
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
display: inline-block;
|
|
||||||
|
|
||||||
> ul {
|
> ul {
|
||||||
margin: 10px 0;
|
margin: 10px 0;
|
||||||
list-style: none;
|
list-style: none;
|
||||||
|
|
||||||
|
.spinner {
|
||||||
|
height: 5px;
|
||||||
|
width: 5px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,3 +79,32 @@ div.stripe-errors {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.donation-row {
|
||||||
|
span {
|
||||||
|
line-height: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.canceled {
|
||||||
|
text-decoration: line-through;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.updating {
|
||||||
|
color: $primary-low;
|
||||||
|
}
|
||||||
|
|
||||||
|
.new-flag {
|
||||||
|
color: $tertiary;
|
||||||
|
margin-left: 5px;
|
||||||
|
|
||||||
|
.fa {
|
||||||
|
line-height: 16px;
|
||||||
|
font-size: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
> * {
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -27,11 +27,17 @@ en:
|
|||||||
title: "Make a Donation"
|
title: "Make a Donation"
|
||||||
amount: Amount
|
amount: Amount
|
||||||
card: Card
|
card: Card
|
||||||
submit: Make Payment
|
submit: Donate
|
||||||
submit_with_create_account: Make Payment and Create Account
|
submit_with_create_account: Make Payment and Create Account
|
||||||
invoice: "Invoice"
|
invoice: "invoice"
|
||||||
invoice_prefix: "You gave"
|
invoice_prefix: "You gave"
|
||||||
receipt: "Receipt sent to {{email}}."
|
receipt: "Receipt sent to {{email}}."
|
||||||
|
subscription:
|
||||||
|
cancel:
|
||||||
|
title: "Cancel Recurring Donation"
|
||||||
|
description: >
|
||||||
|
Are you sure you want to cancel your recurring donation to {{site}}
|
||||||
|
of {{currency}} {{amount}} {{period}}?
|
||||||
email_instructions: "Required to send you a receipt. Not used for marketing."
|
email_instructions: "Required to send you a receipt. Not used for marketing."
|
||||||
transaction_fee:
|
transaction_fee:
|
||||||
label: "Include transaction fee of {{currency}} {{transactionFee}}"
|
label: "Include transaction fee of {{currency}} {{transactionFee}}"
|
||||||
@ -52,6 +58,9 @@ en:
|
|||||||
year: "every year on {{anchor}}"
|
year: "every year on {{anchor}}"
|
||||||
donations:
|
donations:
|
||||||
title: "Your Donations"
|
title: "Your Donations"
|
||||||
|
load: "Load Donations"
|
||||||
|
loading: "Loading donations"
|
||||||
charges: "Once Off"
|
charges: "Once Off"
|
||||||
subscriptions: "Recurring"
|
subscriptions: "Recurring"
|
||||||
none: "You haven't made a donation yet."
|
none: "You haven't made a donation yet."
|
||||||
|
none_email: "There are no donations for {{email}}."
|
||||||
|
@ -1,7 +1,11 @@
|
|||||||
en:
|
en:
|
||||||
donations:
|
donations:
|
||||||
recurring: "%{site_title} recurring donation"
|
recurring: "%{site_title} Recurring Donation"
|
||||||
payment:
|
payment:
|
||||||
success: 'Thank you, your donation has been successful.'
|
success: 'Thank you, your donation has been successful.'
|
||||||
receipt_sent: 'A receipt has been sent to %{email}.'
|
receipt_sent: 'A receipt has been sent to %{email}.'
|
||||||
invoice_sent: 'An invoice has been sent to %{email}.'
|
invoice_sent: 'An invoice has been sent to %{email}.'
|
||||||
|
subscription:
|
||||||
|
error:
|
||||||
|
not_found: "Subscription not found."
|
||||||
|
not_cancelled: "Subscription not cancelled."
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
DiscourseDonations::Engine.routes.draw do
|
DiscourseDonations::Engine.routes.draw do
|
||||||
get '/' => 'charges#index'
|
get '/' => 'charges#index'
|
||||||
|
|
||||||
resources :charges, only: [:index, :create]
|
resources :charges, only: [:index, :create]
|
||||||
|
put '/charges/cancel-subscription' => 'charges#cancel_subscription'
|
||||||
|
|
||||||
resources :checkout, only: [:create]
|
resources :checkout, only: [:create]
|
||||||
end
|
end
|
||||||
|
Loading…
x
Reference in New Issue
Block a user