diff --git a/README.md b/README.md deleted file mode 100644 index a01b4ff..0000000 --- a/README.md +++ /dev/null @@ -1,59 +0,0 @@ -# Discourse Donations - -[![Build Status](https://travis-ci.org/rimian/discourse-donations.svg?branch=master)](https://travis-ci.org/rimian/discourse-donations) - -Accept donations from visitors to your [Discourse](https://www.discourse.org/) application. Integrates with [Stripe](https://stripe.com). - -## Installation - -* 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 - -1. Click **Donate**: -![Menu Link](doc/menulink.png) - -1. Enter card details: -![Enter card details](doc/pre-payment.png) - -1. Click **Make Payment**: -![Enter card details](doc/post-payment.png) - -## Creating new user accounts - -**This is an experimental feature.** A user can create a new account if they makes a successful donation. Enable this in settings. When a user is not logged in, they will be asked to enter details for a new user account. This feature doesn't support mandatory custom user fields yet. - -## Testing - -These commands should run: - -* ```yarn prettier --list-different 'assets/**/*.scss' '**/*.es6'``` - -## Tested Credit Card Numbers - -These numbers can be used in test mode to simulate a transaction. For more information see the [Stripe docs](https://stripe.com/docs/testing). - -Card numbers in **bold** have been tested. - -* **4000 0000 0000 0077** Charge succeeds and funds will be added directly to your available balance (bypassing your pending balance). -* **4000 0000 0000 0093** Charge succeeds and domestic pricing is used (other test cards use international pricing). This card is only significant in countries with split pricing. -* **4000 0000 0000 0010** The address_line1_check and address_zip_check verifications fail. If your account is blocking payments that fail postal code validation, the charge is declined. -* **4000 0000 0000 0028** Charge succeeds but the address_line1_check verification fails. -* **4000 0000 0000 0036** The address_zip_check verification fails. If your account is blocking payments that fail postal code validation, the charge is declined. -* **4000 0000 0000 0044** Charge succeeds but the address_zip_check and address_line1_check verifications are both unavailable. -* **4000 0000 0000 0101** If a CVC number is provided, the cvc_check fails. If your account is blocking payments that fail CVC code validation, the charge is declined. -* **4000 0000 0000 0341** Attaching this card to a Customer object succeeds, but attempts to charge the customer fail. -* **4000 0000 0000 9235** Charge succeeds with a risk_level of elevated and placed into review. -* **4000 0000 0000 0002** Charge is declined with a card_declined code. -* **4100 0000 0000 0019** Charge is declined with a card_declined code and a fraudulent reason. -* **4000 0000 0000 0127** Charge is declined with an incorrect_cvc code. -* **4000 0000 0000 0069** Charge is declined with an expired_card code. -* **4000 0000 0000 0119** Charge is declined with a processing_error code. -* **4242 4242 4242 4241** Charge is declined with an incorrect_number code as the card number fails the Luhn check. - -## Warranty - -This software comes with no warranty of any kind. diff --git a/app/controllers/controllers.rb b/app/controllers/controllers.rb deleted file mode 100644 index 46b553e..0000000 --- a/app/controllers/controllers.rb +++ /dev/null @@ -1,4 +0,0 @@ -# frozen_string_literal: true - -load File.expand_path('../discourse_donations/charges_controller.rb', __FILE__) -load File.expand_path('../discourse_donations/checkout_controller.rb', __FILE__) diff --git a/app/controllers/discourse_donations/charges_controller.rb b/app/controllers/discourse_donations/charges_controller.rb deleted file mode 100644 index d182ce0..0000000 --- a/app/controllers/discourse_donations/charges_controller.rb +++ /dev/null @@ -1,190 +0,0 @@ -# frozen_string_literal: true - -module DiscourseDonations - class ChargesController < ::ApplicationController - 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_email, only: [:index, :create, :cancel_subscription] - - def index - 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 - - render json: success_json.merge(result) - end - - def create - Rails.logger.info user_params.inspect - - output = { 'messages' => [], 'rewards' => [] } - - if create_account - if !@email.present? || !user_params[:username].present? - output['messages'] << I18n.t('login.missing_user_field') - end - if user_params[:password] && user_params[:password].length > User.max_password_length - output['messages'] << I18n.t('login.password_too_long') - end - if user_params[:username] && ::User.reserved_username?(user_params[:username]) - output['messages'] << I18n.t('login.reserved_username') - end - end - - if output['messages'].present? - render(json: output.merge(success: false)) && (return) - end - - Rails.logger.debug "Creating a Stripe payment" - stripe = DiscourseDonations::Stripe.new(secret_key, stripe_options) - result = {} - - begin - Rails.logger.debug "Creating a Stripe charge for #{user_params[:amount]}" - opts = { - cause: user_params[:cause], - email: @email, - token: user_params[:stripeToken], - amount: user_params[:amount] - } - - if user_params[:type] === 'once' - result[:charge] = stripe.charge(@user, opts) - else - opts[:type] = user_params[:type] - - 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 - - rescue ::Stripe::CardError => e - err = e.json_body[:error] - - output['messages'] << "There was an error (#{err[:type]})." - output['messages'] << "Error code: #{err[:code]}" if err[:code] - output['messages'] << "Decline code: #{err[:decline_code]}" if err[:decline_code] - output['messages'] << "Message: #{err[:message]}" if err[:message] - - render(json: output) && (return) - end - - if (result[:charge] && result[:charge]['paid'] == true) || - (result[:subscription] && result[:subscription][:subscription] && - result[:subscription][:subscription]['status'] === 'active') - - output['messages'] << I18n.t('donations.payment.success') - - 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)}" - end - - output['charge'] = result[:charge] if result[:charge] - output['subscription'] = result[:subscription] if result[:subscription] - - output['rewards'] << { type: :group, name: group_name } if group_name - output['rewards'] << { type: :badge, name: badge_name } if badge_name - - if create_account && @email.present? - args = user_params.to_h.slice(:email, :username, :password, :name).merge(rewards: output['rewards']) - Jobs.enqueue(:donation_user, args) - end - - if SiteSetting.discourse_donations_cause_category - Jobs.enqueue(:update_category_donation_statistics) - end - end - - render json: output - 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 - - def create_account - user_params[:create_account] == 'true' && SiteSetting.discourse_donations_enable_create_accounts - end - - def reward?(payment) - payment.present? && payment.successful? - end - - def group_name - SiteSetting.discourse_donations_reward_group_name - end - - def badge_name - SiteSetting.discourse_donations_reward_badge_name - end - - def secret_key - SiteSetting.discourse_donations_secret_key - end - - def stripe_options - { - description: SiteSetting.discourse_donations_description, - currency: SiteSetting.discourse_donations_currency - } - end - - def user_params - params.permit(:user_id, :name, :username, :email, :password, :stripeToken, :cause, :type, :amount, :create_account) - end - - def set_user - user = current_user - - if user_params[:user_id].present? - if record = User.find_by(user_params[:user_id]) - user = record - end - end - - @user = user - end - - def set_email - email = nil - - if user_params[:email].present? - email = user_params[:email] - elsif @user - email = @user.try(:email) - end - - @email = email - end - end -end diff --git a/app/controllers/discourse_donations/checkout_controller.rb b/app/controllers/discourse_donations/checkout_controller.rb deleted file mode 100644 index 24206e1..0000000 --- a/app/controllers/discourse_donations/checkout_controller.rb +++ /dev/null @@ -1,89 +0,0 @@ -# frozen_string_literal: true - -require_dependency 'discourse' - -module DiscourseDonations - class CheckoutController < ApplicationController - - skip_before_action :verify_authenticity_token, only: [:create] - - def create - Rails.logger.debug params.inspect - Rails.logger.debug user_params.inspect - - output = { 'messages' => [], 'rewards' => [] } - payment = DiscourseDonations::Stripe.new(secret_key, stripe_options) - user = current_user || nil - - begin - charge = payment.checkoutCharge(user, user_params[:stripeEmail], user_params[:stripeToken], user_params[:amount]) - rescue ::Stripe::CardError => e - err = e.json_body[:error] - - output['messages'] << "There was an error (#{err[:type]})." - output['messages'] << "Error code: #{err[:code]}" if err[:code] - output['messages'] << "Decline code: #{err[:decline_code]}" if err[:decline_code] - output['messages'] << "Message: #{err[:message]}" if err[:message] - - render(json: output) && (return) - end - - if charge['paid'] - output['messages'] << I18n.l(Time.now(), format: :long) + ': ' + I18n.t('donations.payment.success') - output['rewards'] << { type: :group, name: group_name } if group_name - output['rewards'] << { type: :badge, name: badge_name } if badge_name - end - - render json: output - end - - private - - def reward?(payment) - payment.present? && payment.successful? - end - - def group_name - SiteSetting.discourse_donations_reward_group_name - end - - def badge_name - SiteSetting.discourse_donations_reward_badge_name - end - - def secret_key - SiteSetting.discourse_donations_secret_key - end - - def user_params - params.permit(:amount, - :email, - :stripeToken, - :stripeTokenType, - :stripeEmail, - :stripeCustomerId, - :stripeBillingName, - :stripeBillingAddressLine1, - :stripeBillingAddressZip, - :stripeBillingAddressState, - :stripeBillingAddressCity, - :stripeBillingAddressCountry, - :stripeBillingAddressCountryCode, - :stripeShippingName, - :stripeShippingAddressLine1, - :stripeShippingAddressZip, - :stripeShippingAddressState, - :stripeShippingAddressCity, - :stripeShippingAddressCountry, - :stripeShippingAddressCountryCode - ) - end - - def stripe_options - { - description: SiteSetting.discourse_donations_description, - currency: SiteSetting.discourse_donations_currency - } - end - end -end diff --git a/app/jobs/jobs.rb b/app/jobs/jobs.rb deleted file mode 100644 index 5c38931..0000000 --- a/app/jobs/jobs.rb +++ /dev/null @@ -1,4 +0,0 @@ -# frozen_string_literal: true - -load File.expand_path('../regular/donation_user.rb', __FILE__) -load File.expand_path('../scheduled/update_category_donation_statistics.rb', __FILE__) diff --git a/app/jobs/regular/donation_user.rb b/app/jobs/regular/donation_user.rb deleted file mode 100644 index 88ea75d..0000000 --- a/app/jobs/regular/donation_user.rb +++ /dev/null @@ -1,19 +0,0 @@ -# frozen_string_literal: true - -module Jobs - class DonationUser < ::Jobs::Base - def execute(args) - user = User.create!(args.slice(:username, :password, :name, :email)) - return unless user.persisted? - Jobs.enqueue( - :critical_user_email, - type: :signup, user_id: user.id, email_token: user.email_tokens.first.token - ) - rewards = DiscourseDonations::Rewards.new(user) - args[:rewards].to_a.each do |reward| - rewards.grant_badge(reward[:name]) if reward[:type] == 'badge' - rewards.add_to_group(reward[:name]) if reward[:type] == 'group' - end - end - end -end diff --git a/app/jobs/scheduled/update_category_donation_statistics.rb b/app/jobs/scheduled/update_category_donation_statistics.rb deleted file mode 100644 index 5046656..0000000 --- a/app/jobs/scheduled/update_category_donation_statistics.rb +++ /dev/null @@ -1,71 +0,0 @@ -# frozen_string_literal: true - -module Jobs - class UpdateCategoryDonationStatistics < ::Jobs::Scheduled - every 1.day - - def execute(args) - return unless SiteSetting.discourse_donations_cause_category - - ::Stripe.api_key = SiteSetting.discourse_donations_secret_key - totals = {} - backers = {} - categories = [] - - raw_charges = ::Stripe::Charge.list( - expand: ['data.invoice.subscription', 'data.customer'] - ) - raw_charges = raw_charges.is_a?(Object) ? raw_charges['data'] : [] - - raw_charges.each do |c| - cause_base = c['invoice'] && c['invoice']['subscription'] ? c['invoice']['subscription'] : c - category_id = cause_base['metadata']['discourse_cause'].to_i - - backer_base = c['customer'] - backer_user_id = backer_base['metadata']['discourse_user_id'].to_i - backer_email = backer_base['email'] - - if category_id > 0 && Category.exists?(id: category_id) - categories.push(category_id) - - current = totals[category_id] || {} - amount = c['amount'].to_i - date = Time.at(c['created']).to_datetime - - totals[category_id] ||= {} - totals[category_id][:total] ||= 0 - totals[category_id][:month] ||= 0 - - totals[category_id][:total] += amount - - if date.month == Date.today.month - totals[category_id][:month] += amount - end - - backers[category_id] ||= [] - - if backer_user_id > 0 && User.exists?(id: backer_user_id) - backers[category_id].push(backer_user_id) unless backers[category_id].include? backer_user_id - elsif user = User.find_by_email(backer_email) - backers[category_id].push(user.id) unless backers[category_id].include? user.id - end - end - end - - categories.each do |category_id| - category = Category.find(category_id) - - if totals[category_id] - category.custom_fields['donations_total'] = totals[category_id][:total] - category.custom_fields['donations_month'] = totals[category_id][:month] - end - - if backers[category_id] - category.custom_fields['donations_backers'] = backers[category_id] - end - - category.save_custom_fields(true) - end - end - end -end diff --git a/app/services/discourse_donations/rewards.rb b/app/services/discourse_donations/rewards.rb deleted file mode 100644 index 93596c4..0000000 --- a/app/services/discourse_donations/rewards.rb +++ /dev/null @@ -1,32 +0,0 @@ -# frozen_string_literal: true - -module DiscourseDonations - class Rewards - attr_reader :user - - def initialize(user) - @user = user - end - - def add_to_group(name) - grp = ::Group.find_by_name(name) - return if grp.nil? - log_group_add(grp) - grp.add(user) - end - - def grant_badge(name) - return unless SiteSetting.enable_badges - badge = ::Badge.find_by_name(name) - return if badge.nil? - BadgeGranter.grant(badge, user) - end - - private - - def log_group_add(grp) - system_user = User.find(-1) - GroupActionLogger.new(system_user, grp).log_add_user_to_group(user) - end - end -end diff --git a/app/services/discourse_donations/stripe.rb b/app/services/discourse_donations/stripe.rb deleted file mode 100644 index 199d59e..0000000 --- a/app/services/discourse_donations/stripe.rb +++ /dev/null @@ -1,273 +0,0 @@ -# frozen_string_literal: true - -module DiscourseDonations - class Stripe - attr_reader :charge, :currency, :description - - def initialize(secret_key, opts) - ::Stripe.api_key = secret_key - @description = opts[:description] - @currency = opts[:currency] - end - - def checkoutCharge(user = nil, email, token, amount) - customer = customer(user, - email: email, - source: token, - create: true - ) - - return if !customer - - charge = ::Stripe::Charge.create( - customer: customer.id, - amount: amount, - description: @description, - currency: @currency - ) - - charge - end - - def charge(user = nil, opts) - customer = customer(user, - email: opts[:email], - source: opts[:token], - create: true - ) - - return if !customer - - metadata = { - discourse_cause: opts[:cause] - } - - if (user) - metadata[:discourse_user_id] = user.id - end - - @charge = ::Stripe::Charge.create( - customer: customer.id, - amount: opts[:amount], - description: @description, - currency: @currency, - receipt_email: customer.email, - metadata: metadata - ) - - @charge - end - - def subscribe(user = nil, opts) - customer = customer(user, - email: opts[:email], - source: opts[:token], - create: true - ) - - return if !customer - - type = opts[:type] - amount = opts[:amount] - - plans = ::Stripe::Plan.list - plan_id = create_plan_id(type, amount) - - unless plans.data && plans.data.any? { |p| p['id'] === plan_id } - result = create_plan(type, amount) - - plan_id = result['id'] - end - - ::Stripe::Subscription.create( - customer: customer.id, - items: [{ - plan: plan_id - }], - metadata: { - discourse_cause: opts[:cause], - discourse_user_id: user.id - } - ) - end - - def list(user, opts = {}) - customer = customer(user, opts) - - return if !customer - - result = { customer: customer } - - raw_invoices = ::Stripe::Invoice.list(customer: customer.id) - raw_invoices = raw_invoices.is_a?(Object) ? raw_invoices['data'] : [] - - raw_charges = ::Stripe::Charge.list(customer: customer.id) - raw_charges = raw_charges.is_a?(Object) ? raw_charges['data'] : [] - - if raw_invoices.any? - raw_subscriptions = ::Stripe::Subscription.list(customer: customer.id, status: 'all') - raw_subscriptions = raw_subscriptions.is_a?(Object) ? raw_subscriptions['data'] : [] - - if raw_subscriptions.any? - subscriptions = [] - - raw_subscriptions.each do |subscription| - invoices = raw_invoices.select do |invoice| - invoice['subscription'] === subscription['id'] - end - - subscriptions.push( - subscription: subscription, - invoices: invoices - ) - end - - result[:subscriptions] = subscriptions - end - - ## filter out any charges related to subscriptions - raw_invoice_ids = raw_invoices.map { |i| i['id'] } - raw_charges = raw_charges.select { |c| raw_invoice_ids.exclude?(c['invoice']) } - end - - if raw_charges.any? - result[:charges] = raw_charges - end - - result - end - - def invoices_for_subscription(user, opts) - customer = customer(user, - 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 - { 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_opts = { - email: opts[:email], - source: opts[:source] - } - - if user - customer_opts[:metadata] = { - discourse_user_id: user.id - } - end - - customer = ::Stripe::Customer.create(customer_opts) - - if user - user.custom_fields['stripe_customer_id'] = customer.id - user.save_custom_fields(true) - end - end - - customer - end - - def successful? - @charge[:paid] - end - - def create_plan(type, amount) - id = create_plan_id(type, amount) - nickname = id.gsub(/_/, ' ').titleize - - products = ::Stripe::Product.list(type: 'service') - - if products['data'] && products['data'].any? { |p| p['id'] === product_id } - product = product_id - else - result = create_product - product = result['id'] - end - - ::Stripe::Plan.create( - id: id, - nickname: nickname, - interval: type, - currency: @currency, - product: product, - amount: amount.to_i - ) - end - - def create_product - ::Stripe::Product.create( - id: product_id, - name: product_name, - type: 'service' - ) - end - - def product_id - @product_id ||= "#{SiteSetting.title}_recurring_donation".freeze - end - - def product_name - @product_name ||= I18n.t('donations.recurring', site_title: SiteSetting.title) - end - - def create_plan_id(type, amount) - "discourse_donation_recurring_#{type}_#{amount}".freeze - end - end -end diff --git a/app/services/services.rb b/app/services/services.rb deleted file mode 100644 index 73924a0..0000000 --- a/app/services/services.rb +++ /dev/null @@ -1,4 +0,0 @@ -# frozen_string_literal: true - -load File.expand_path('../discourse_donations/rewards.rb', __FILE__) -load File.expand_path('../discourse_donations/stripe.rb', __FILE__) diff --git a/assets/javascripts/discourse/components/donation-form.es6 b/assets/javascripts/discourse/components/donation-form.es6 deleted file mode 100644 index 84e446d..0000000 --- a/assets/javascripts/discourse/components/donation-form.es6 +++ /dev/null @@ -1 +0,0 @@ -export default Ember.Component.extend({}); diff --git a/assets/javascripts/discourse/components/donation-list.js.es6 b/assets/javascripts/discourse/components/donation-list.js.es6 deleted file mode 100644 index b9fcea7..0000000 --- a/assets/javascripts/discourse/components/donation-list.js.es6 +++ /dev/null @@ -1,5 +0,0 @@ -export default Ember.Component.extend({ - classNames: "donation-list", - hasSubscriptions: Ember.computed.notEmpty("subscriptions"), - hasCharges: Ember.computed.notEmpty("charges") -}); diff --git a/assets/javascripts/discourse/components/donation-row.js.es6 b/assets/javascripts/discourse/components/donation-row.js.es6 deleted file mode 100644 index fb2ab1a..0000000 --- a/assets/javascripts/discourse/components/donation-row.js.es6 +++ /dev/null @@ -1,99 +0,0 @@ -import { ajax } from "discourse/lib/ajax"; -import { popupAjaxError } from "discourse/lib/ajax-error"; -import { formatAnchor, formatAmount } from "../lib/donation-utilities"; -import { default as computed } 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() - } - }); - } - } -}); diff --git a/assets/javascripts/discourse/components/stripe-card.js.es6 b/assets/javascripts/discourse/components/stripe-card.js.es6 deleted file mode 100644 index 3af07c6..0000000 --- a/assets/javascripts/discourse/components/stripe-card.js.es6 +++ /dev/null @@ -1,286 +0,0 @@ -import { ajax } from "discourse/lib/ajax"; -import { formatAnchor, zeroDecimalCurrencies } from "../lib/donation-utilities"; -import { default as computed } from "ember-addons/ember-computed-decorators"; -import { emailValid as emailValidHelper } from "discourse/lib/utilities"; - -export default Ember.Component.extend({ - result: [], - stripe: null, - transactionInProgress: null, - settings: null, - showTransactionFeeDescription: false, - includeTransactionFee: true, - - init() { - this._super(...arguments); - - const user = this.get("currentUser"); - const settings = Discourse.SiteSettings; - - this.setProperties({ - create_accounts: - !user && settings.discourse_donations_enable_create_accounts, - stripe: Stripe(settings.discourse_donations_public_key), - color: jQuery("body").css("color") - }); - - const types = settings.discourse_donations_types.split("|") || []; - const amounts = this.get("donateAmounts"); - - this.setProperties({ - types, - type: types[0], - amount: amounts[0].value - }); - }, - - @computed - causes() { - const categoryEnabled = - Discourse.SiteSettings.discourse_donations_cause_category; - - if (categoryEnabled) { - let categoryIds = Discourse.SiteSettings.discourse_donations_causes_categories.split( - "|" - ); - - if (categoryIds.length) { - categoryIds = categoryIds.map(Number); - return this.site - .get("categoriesList") - .filter(c => { - return categoryIds.indexOf(c.id) > -1; - }) - .map(c => { - return { - id: c.id, - name: c.name - }; - }); - } else { - return []; - } - } else { - const causes = Discourse.SiteSettings.discourse_donations_causes; - return causes ? causes.split("|") : []; - } - }, - - @computed("types") - donationTypes(types) { - return types.map(type => { - return { - id: type, - name: I18n.t(`discourse_donations.types.${type}`) - }; - }); - }, - - @computed("type") - period(type) { - return I18n.t(`discourse_donations.period.${type}`, { - anchor: formatAnchor(type) - }); - }, - - @computed - donateAmounts() { - const setting = Discourse.SiteSettings.discourse_donations_amounts.split( - "|" - ); - if (setting.length) { - return setting.map(amount => { - return { - value: parseInt(amount, 10), - name: `${amount}.00` - }; - }); - } else { - return []; - } - }, - - @computed("stripe") - card(stripe) { - const color = this.get("color"); - const hidePostalCode = !Discourse.SiteSettings.discourse_donations_zip_code; - const elements = stripe.elements(); - - const style = { - base: { - color, - iconColor: color, - "::placeholder": { color } - } - }; - - const card = elements.create("card", { style, hidePostalCode }); - - card.addEventListener("change", event => { - if (event.error) { - this.set("stripeError", event.error.message); - } else { - this.set("stripeError", ""); - } - - if (event.elementType === "card" && event.complete) { - this.set("stripeReady", true); - } - }); - - return card; - }, - - @computed("amount") - transactionFee(amount) { - const fixed = - Discourse.SiteSettings.discourse_donations_transaction_fee_fixed; - const percent = - Discourse.SiteSettings.discourse_donations_transaction_fee_percent; - const fee = (amount + fixed) / (1 - percent) - amount; - return Math.round(fee * 100) / 100; - }, - - @computed("amount", "transactionFee", "includeTransactionFee") - totalAmount(amount, fee, include) { - if (include) return amount + fee; - return amount; - }, - - @computed("email") - emailValid(email) { - return emailValidHelper(email); - }, - - @computed("email", "emailValid") - showEmailError(email, emailValid) { - return email && email.length > 3 && !emailValid; - }, - - @computed("currentUser", "emailValid") - userReady(currentUser, emailValid) { - return currentUser || emailValid; - }, - - @computed("cause") - causeValid(cause) { - return cause || !Discourse.SiteSettings.discourse_donations_cause_required; - }, - - @computed("userReady", "stripeReady", "causeValid") - formIncomplete(userReady, stripeReady, causeValid) { - return !userReady || !stripeReady || !causeValid; - }, - - @computed("transactionInProgress", "formIncomplete") - disableSubmit(transactionInProgress, formIncomplete) { - return transactionInProgress || formIncomplete; - }, - - didInsertElement() { - this._super(); - this.get("card").mount("#card-element"); - jQuery(document).on("click", Ember.run.bind(this, this.documentClick)); - }, - - willDestroyElement() { - jQuery(document).off("click", Ember.run.bind(this, this.documentClick)); - }, - - documentClick(e) { - let $element = jQuery(".transaction-fee-description"); - let $target = jQuery(e.target); - if ($target.closest($element).length < 1 && this._state !== "destroying") { - this.set("showTransactionFeeDescription", false); - } - }, - - setSuccess() { - this.set("paymentSuccess", true); - }, - - endTranscation() { - this.set("transactionInProgress", false); - }, - - concatMessages(messages) { - this.set("result", this.get("result").concat(messages)); - }, - - actions: { - toggleTransactionFeeDescription() { - this.toggleProperty("showTransactionFeeDescription"); - }, - - submitStripeCard() { - let self = this; - this.set("transactionInProgress", true); - - this.get("stripe") - .createToken(this.get("card")) - .then(data => { - self.set("result", []); - - if (data.error) { - this.setProperties({ - stripeError: data.error.message, - stripeReady: false - }); - self.endTranscation(); - } else { - const settings = Discourse.SiteSettings; - - const transactionFeeEnabled = - settings.discourse_donations_enable_transaction_fee; - let amount = transactionFeeEnabled - ? this.get("totalAmount") - : this.get("amount"); - - if ( - zeroDecimalCurrencies.indexOf( - settings.discourse_donations_currency - ) === -1 - ) { - amount = amount * 100; - } - - let params = { - stripeToken: data.token.id, - cause: self.get("cause"), - type: self.get("type"), - amount, - email: self.get("email"), - username: self.get("username"), - create_account: self.get("create_accounts") - }; - - if (!self.get("paymentSuccess")) { - ajax("/donate/charges", { - data: params, - method: "post" - }).then(result => { - if (result.subscription) { - let subscription = $.extend({}, result.subscription, { - new: true - }); - this.get("subscriptions").unshiftObject(subscription); - } - - if (result.charge) { - let charge = $.extend({}, result.charge, { - new: true - }); - this.get("charges").unshiftObject(charge); - } - - self.concatMessages(result.messages); - - self.endTranscation(); - self.onCompleteTransation(); - }); - } - } - }); - } - } -}); diff --git a/assets/javascripts/discourse/connectors/below-site-header/donations-category-header-container.hbs b/assets/javascripts/discourse/connectors/below-site-header/donations-category-header-container.hbs deleted file mode 100644 index 6e2c7bb..0000000 --- a/assets/javascripts/discourse/connectors/below-site-header/donations-category-header-container.hbs +++ /dev/null @@ -1,3 +0,0 @@ -{{#if siteSettings.discourse_donations_cause_category}} - {{mount-widget widget="category-header-widget" args=(hash currentPath=currentPath)}} -{{/if}} diff --git a/assets/javascripts/discourse/connectors/category-custom-settings/donations_category_settings.hbs b/assets/javascripts/discourse/connectors/category-custom-settings/donations_category_settings.hbs deleted file mode 100644 index c448fec..0000000 --- a/assets/javascripts/discourse/connectors/category-custom-settings/donations_category_settings.hbs +++ /dev/null @@ -1,36 +0,0 @@ -{{#if siteSettings.discourse_donations_cause_category}} -
- {{input type="checkbox" checked=category.custom_fields.donations_show_amounts}} - {{i18n 'discourse_donations.cause.amounts.setting_label'}} -
- -
- - {{text-field value=category.custom_fields.donations_github placeholderKey="discourse_donations.cause.github.setting_placeholder"}} -
- -
- - {{text-field value=category.custom_fields.donations_meta placeholderKey="discourse_donations.cause.meta.setting_placeholder"}} -
- -
- - {{user-selector usernames=category.custom_fields.donations_maintainers}} -
- -
- - {{input value=category.custom_fields.donations_maintainers_label}} -
- -
- - {{input value=category.custom_fields.donations_release_latest}} -
- -
- - {{input value=category.custom_fields.donations_release_oldest}} -
-{{/if}} diff --git a/assets/javascripts/discourse/connectors/extra-nav-item/donate.hbs b/assets/javascripts/discourse/connectors/extra-nav-item/donate.hbs deleted file mode 100644 index 69bb4ec..0000000 --- a/assets/javascripts/discourse/connectors/extra-nav-item/donate.hbs +++ /dev/null @@ -1,5 +0,0 @@ -{{#if siteSettings.discourse_donations_enabled}} - - {{i18n 'discourse_donations.nav_item'}} - -{{/if}} diff --git a/assets/javascripts/discourse/controllers/cancel-subscription.js.es6 b/assets/javascripts/discourse/controllers/cancel-subscription.js.es6 deleted file mode 100644 index c6949b0..0000000 --- a/assets/javascripts/discourse/controllers/cancel-subscription.js.es6 +++ /dev/null @@ -1,12 +0,0 @@ -export default Ember.Controller.extend({ - actions: { - confirm() { - this.get("model.confirm")(); - this.send("closeModal"); - }, - - cancel() { - this.send("closeModal"); - } - } -}); diff --git a/assets/javascripts/discourse/controllers/donate.js.es6 b/assets/javascripts/discourse/controllers/donate.js.es6 deleted file mode 100644 index 8cba1ae..0000000 --- a/assets/javascripts/discourse/controllers/donate.js.es6 +++ /dev/null @@ -1,61 +0,0 @@ -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"; -import { emailValid } from "discourse/lib/utilities"; - -export default Ember.Controller.extend({ - loadingDonations: false, - loadDonationsDisabled: Ember.computed.not("emailVaild"), - - @computed("charges.[]", "subscriptions.[]") - hasDonations(charges, subscriptions) { - return ( - (charges && charges.length > 0) || - (subscriptions && subscriptions.length > 0) - ); - }, - - @computed("email") - emailVaild(email) { - return emailValid(email); - }, - - actions: { - stripeTransationCompleteCtr() {}, - - 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"); - } - } -}); diff --git a/assets/javascripts/discourse/discourse-patrons-map.js b/assets/javascripts/discourse/discourse-patrons-map.js new file mode 100644 index 0000000..26de642 --- /dev/null +++ b/assets/javascripts/discourse/discourse-patrons-map.js @@ -0,0 +1,12 @@ + +export default function() { + const { disabled_plugins = [] } = this.site; + + if (disabled_plugins.indexOf("discourse-patrons") !== -1) { + return; + } + + this.route("patrons", function() { + this.route("show", { path: ":payment_id" }); + }); +} diff --git a/assets/javascripts/discourse/discourse-payments-route-map.js.es6 b/assets/javascripts/discourse/discourse-payments-route-map.js.es6 deleted file mode 100644 index dbf4000..0000000 --- a/assets/javascripts/discourse/discourse-payments-route-map.js.es6 +++ /dev/null @@ -1,3 +0,0 @@ -export default function() { - this.route("donate"); -} diff --git a/assets/javascripts/discourse/initializers/donations-edits.js.es6 b/assets/javascripts/discourse/initializers/donations-edits.js.es6 deleted file mode 100644 index 9741855..0000000 --- a/assets/javascripts/discourse/initializers/donations-edits.js.es6 +++ /dev/null @@ -1,56 +0,0 @@ -import { withPluginApi } from "discourse/lib/plugin-api"; - -export default { - name: "donations-edits", - initialize(container) { - const siteSettings = container.lookup("site-settings:main"); - - withPluginApi("0.8.12", api => { - api.decorateCooked( - $post => { - const $form = $post.find(".stripe-checkout"); - if ($form.length) { - const $input = $form.find("input"); - var s = document.createElement("script"); - s.src = "https://checkout.stripe.com/checkout.js"; - s.setAttribute("class", "stripe-button"); - s.setAttribute( - "data-key", - siteSettings.discourse_donations_public_key - ); - s.setAttribute("data-amount", $input.attr("amount")); - s.setAttribute( - "data-name", - siteSettings.discourse_donations_shop_name - ); - s.setAttribute("data-description", $form.attr("content")); - s.setAttribute("data-image", $form.attr("image") || ""); - s.setAttribute("data-locale", "auto"); - s.setAttribute( - "data-zip-code", - siteSettings.discourse_donations_zip_code - ); - s.setAttribute( - "data-billing-address", - siteSettings.discourse_donations_billing_address - ); - s.setAttribute( - "data-currency", - siteSettings.discourse_donations_currency - ); - $form.append(s); - } - }, - { id: "discourse-donations" } - ); - - if (siteSettings.discourse_donations_cause_category) { - api.decorateWidget("category-header-widget:after", helper => { - helper.widget.appEvents.on("page:changed", () => { - helper.widget.scheduleRerender(); - }); - }); - } - }); - } -}; diff --git a/assets/javascripts/discourse/lib/donation-utilities.js.es6 b/assets/javascripts/discourse/lib/donation-utilities.js.es6 deleted file mode 100644 index 619fde4..0000000 --- a/assets/javascripts/discourse/lib/donation-utilities.js.es6 +++ /dev/null @@ -1,48 +0,0 @@ -const formatAnchor = function(type = null, time = moment()) { - let format; - - switch (type) { - case "once": - format = "Do MMMM YYYY"; - break; - case "week": - format = "dddd"; - break; - case "month": - format = "Do"; - break; - case "year": - format = "MMMM D"; - break; - default: - format = "dddd"; - } - - return moment(time).format(format); -}; - -const zeroDecimalCurrencies = [ - "MGA", - "BIF", - "CLP", - "PYG", - "DFJ", - "RWF", - "GNF", - "UGX", - "JPY", - "VND", - "VUV", - "XAF", - "KMF", - "KRW", - "XOF", - "XPF" -]; - -const formatAmount = function(amount, currency) { - let zeroDecimal = zeroDecimalCurrencies.indexOf(currency) > -1; - return zeroDecimal ? amount : (amount / 100).toFixed(2); -}; - -export { formatAnchor, formatAmount, zeroDecimalCurrencies }; diff --git a/assets/javascripts/discourse/routes/donate.js.es6 b/assets/javascripts/discourse/routes/donate.js.es6 deleted file mode 100644 index d9ed51a..0000000 --- a/assets/javascripts/discourse/routes/donate.js.es6 +++ /dev/null @@ -1,40 +0,0 @@ -import DiscourseRoute from "discourse/routes/discourse"; -import DiscourseURL from "discourse/lib/url"; -import { popupAjaxError } from "discourse/lib/ajax-error"; -import { ajax } from "discourse/lib/ajax"; - -export default DiscourseRoute.extend({ - redirect() { - if (!Discourse.SiteSettings.discourse_donations_enabled) { - DiscourseURL.routeTo("/"); - return; - } - }, - - setupController(controller) { - let charges = []; - let subscriptions = []; - let customer = {}; - - controller.set("loadingDonations", true); - - ajax("/donate/charges") - .then(result => { - if (result) { - charges = result.charges; - subscriptions = result.subscriptions; - customer = result.customer; - } - - controller.setProperties({ - charges: Ember.A(charges), - subscriptions: Ember.A(subscriptions), - customer - }); - }) - .catch(popupAjaxError) - .finally(() => { - controller.set("loadingDonations", false); - }); - } -}); diff --git a/assets/javascripts/discourse/templates/components/donation-form.hbs b/assets/javascripts/discourse/templates/components/donation-form.hbs deleted file mode 100644 index 2449ad2..0000000 --- a/assets/javascripts/discourse/templates/components/donation-form.hbs +++ /dev/null @@ -1,9 +0,0 @@ -
-
- {{stripe-card - charges=charges - subscriptions=subscriptions - onCompleteTransation=onCompleteTransation - }} -
-
diff --git a/assets/javascripts/discourse/templates/components/donation-list.hbs b/assets/javascripts/discourse/templates/components/donation-list.hbs deleted file mode 100644 index 4a23964..0000000 --- a/assets/javascripts/discourse/templates/components/donation-list.hbs +++ /dev/null @@ -1,28 +0,0 @@ -{{#if hasSubscriptions}} -
-
{{i18n 'discourse_donations.donations.subscriptions'}}
- -
-{{/if}} - -{{#if hasCharges}} -
-
{{i18n 'discourse_donations.donations.charges'}}
- -
-{{/if}} diff --git a/assets/javascripts/discourse/templates/components/donation-row.hbs b/assets/javascripts/discourse/templates/components/donation-row.hbs deleted file mode 100644 index 0e6d17a..0000000 --- a/assets/javascripts/discourse/templates/components/donation-row.hbs +++ /dev/null @@ -1,41 +0,0 @@ -{{#if includePrefix}} - {{i18n 'discourse_donations.invoice_prefix'}} -{{/if}} - -{{currency}} - -{{amount}} - -{{period}} - -{{#if invoice}} - ({{i18n 'discourse_donations.invoice'}}) -{{/if}} - -{{#if currentUser}} - {{#if subscription}} - - {{#if updating}} - {{loading-spinner size='small'}} - {{else}} - {{#unless canceled}} - - {{i18n 'cancel'}} - - {{/unless}} - {{/if}} - - {{/if}} -{{/if}} - -{{#if receiptSent}} - - {{i18n 'discourse_donations.receipt' email=customer.email}} -{{/if}} - -{{#if new}} - - {{d-icon 'circle'}} - {{i18n 'new_item'}} - -{{/if}} diff --git a/assets/javascripts/discourse/templates/components/stripe-card.hbs b/assets/javascripts/discourse/templates/components/stripe-card.hbs deleted file mode 100644 index a682436..0000000 --- a/assets/javascripts/discourse/templates/components/stripe-card.hbs +++ /dev/null @@ -1,118 +0,0 @@ -
- -
- {{combo-box content=donationTypes value=type}} -
-
- -
- -
- {{combo-box valueAttribute="value" content=donateAmounts value=amount}} -
-
- -{{#if siteSettings.discourse_donations_enable_transaction_fee}} -
-
- {{input type="checkbox" checked=includeTransactionFee}} - {{i18n 'discourse_donations.transaction_fee.label' transactionFee=transactionFee currency=siteSettings.discourse_donations_currency}} -
- {{d-icon 'info-circle'}} - {{#if showTransactionFeeDescription}} -
- {{i18n 'discourse_donations.transaction_fee.description'}} -
- {{/if}} -
-
-
- -
- -
- {{siteSettings.discourse_donations_currency}} - {{totalAmount}} - {{period}} -
-
-{{/if}} - -
- -
-
- {{#if stripeError}} -
{{stripeError}}
- {{/if}} -
-
- -{{#unless currentUser}} -
- -
- {{text-field value=email}} - {{#if showEmailError}} -
{{i18n 'user.email.invalid'}}
- {{else}} -
{{i18n 'discourse_donations.email_instructions'}}
- {{/if}} -
-
- - {{#if create_accounts}} -
- -
- {{text-field value=username}} -
-
- -
- -
- {{text-field value=name}} -
-
- -
- -
- {{input type="password" value=password}} -
-
- {{/if}} -{{/unless}} - -
-
- - {{#d-button action="submitStripeCard" class="btn btn-primary btn-payment"}} - {{#if create_accounts}} - {{i18n 'discourse_donations.submit_with_create_account'}} - {{else}} - {{i18n 'discourse_donations.submit'}} - {{/if}} - {{/d-button}} - - {{#if transactionInProgress}} - {{loading-spinner size="small"}} - {{/if}} - - {{#each result as |message|}} -

{{{message}}}

- {{/each}} - - {{#if success}} -

{{i18n 'discourse_donations.messages.success'}}

- {{/if}} -
-
diff --git a/assets/javascripts/discourse/templates/donate.hbs b/assets/javascripts/discourse/templates/donate.hbs deleted file mode 100644 index 14df9a4..0000000 --- a/assets/javascripts/discourse/templates/donate.hbs +++ /dev/null @@ -1,40 +0,0 @@ -

{{i18n 'discourse_donations.title' site_name=siteSettings.title}}

- -
- {{cook-text siteSettings.discourse_donations_page_description}} -
- -
- {{donation-form - charges=charges - subscriptions=subscriptions - onCompleteTransation=(action "stripeTransationCompleteCtr") - }} -
- -
-

{{i18n 'discourse_donations.donations.title'}}

- {{#if loadingDonations}} - {{i18n 'discourse_donations.donations.loading'}} - {{loading-spinner size='small'}} - {{else}} - {{#if currentUser}} - {{#if hasDonations}} - {{donation-list charges=charges subscriptions=subscriptions customer=customer}} - {{else}} - {{i18n 'discourse_donations.donations.none'}} - {{/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}} -
diff --git a/assets/javascripts/discourse/templates/modal/cancel-subscription.hbs b/assets/javascripts/discourse/templates/modal/cancel-subscription.hbs deleted file mode 100644 index b67cb78..0000000 --- a/assets/javascripts/discourse/templates/modal/cancel-subscription.hbs +++ /dev/null @@ -1,11 +0,0 @@ -{{#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}} - - diff --git a/assets/javascripts/discourse/templates/patrons/index.hbs b/assets/javascripts/discourse/templates/patrons/index.hbs new file mode 100644 index 0000000..5eea065 --- /dev/null +++ b/assets/javascripts/discourse/templates/patrons/index.hbs @@ -0,0 +1,2 @@ + +index diff --git a/assets/javascripts/discourse/templates/patrons/show.hbs b/assets/javascripts/discourse/templates/patrons/show.hbs new file mode 100644 index 0000000..a133b77 --- /dev/null +++ b/assets/javascripts/discourse/templates/patrons/show.hbs @@ -0,0 +1,3 @@ + + +show diff --git a/assets/javascripts/discourse/widgets/donations-category-header-widget.js.es6 b/assets/javascripts/discourse/widgets/donations-category-header-widget.js.es6 deleted file mode 100644 index 9f345cb..0000000 --- a/assets/javascripts/discourse/widgets/donations-category-header-widget.js.es6 +++ /dev/null @@ -1,196 +0,0 @@ -import { createWidget } from "discourse/widgets/widget"; -import { h } from "virtual-dom"; -import { avatarFor } from "discourse/widgets/post"; -import { userPath } from "discourse/lib/url"; - -function donationDisplay(amount, type) { - return h(`div.donations-${type}`, [ - h("span", I18n.t(`discourse_donations.cause.category.${type}`)), - h("span", `$${(amount / 100).toFixed(2)}`) - ]); -} - -createWidget("category-header-widget", { - tagName: "span", - - html(args) { - const controller = this.register.lookup("controller:navigation/category"); - const category = controller.get("category"); - - if ( - args.currentPath.toLowerCase().indexOf("category") > -1 && - category && - category.donations_cause - ) { - $("body").addClass("donations-category"); - - let contents = [ - h("div.donations-category-contents", [ - h("h1", category.name), - h("div.category-title-description", h("p", category.description_text)) - ]) - ]; - - let metadata = []; - - if (category.donations_total !== undefined) { - metadata.push(donationDisplay(category.donations_total || 0, "total")); - - if (Discourse.SiteSettings.discourse_donations_cause_month) { - metadata.push( - donationDisplay(category.donations_month || 0, "month") - ); - } - } - - if (category.donations_github) { - metadata.push( - h( - "div.donations-github", - this.attach("link", { - icon: "github", - label: "discourse_donations.cause.github.label", - href: category.donations_github - }) - ) - ); - } - - if (category.donations_meta) { - metadata.push( - h( - "div.donations-meta", - this.attach("link", { - href: category.donations_meta, - contents: () => { - return [ - h("img.meta-icon", { - attributes: { - src: - "https://discourse-meta.s3.dualstack.us-west-1.amazonaws.com/original/3X/b/1/b19ba793155a785bbd9707bc0cabbd3a987fa126.png?v=6" - } - }), - h("span", I18n.t("discourse_donations.cause.meta.label")) - ]; - } - }) - ) - ); - } - - if (category.donations_release_oldest) { - let releaseArray = category.donations_release_oldest.split("/"); - let label = releaseArray[releaseArray.length - 1]; - metadata.push( - h("div.donations-release-oldest", [ - h("span", ">="), - this.attach("link", { - href: category.donations_release_oldest, - icon: "tag", - rawLabel: label, - omitSpan: true, - attributes: { - target: "_blank" - } - }) - ]) - ); - } - - if (category.donations_release_latest) { - let releaseArray = category.donations_release_latest.split("/"); - let label = releaseArray[releaseArray.length - 1]; - metadata.push( - h("div.donations-release-latest", [ - h("span", "<="), - this.attach("link", { - href: category.donations_release_latest, - icon: "tag", - rawLabel: label, - omitSpan: true, - attributes: { - target: "_blank" - } - }) - ]) - ); - } - - if (metadata.length) { - contents.push(h("div.donations-category-metadata", metadata)); - } - - let users = []; - - if (category.donations_backers.length) { - users.push( - h("div.donations-backers", [ - h( - "div.donations-backers-title", - I18n.t("discourse_donations.cause.backers.label") - ), - category.donations_backers.map(user => { - if (user) { - return avatarFor("medium", { - template: user.avatar_template, - username: user.username, - name: user.name, - url: userPath(user.username), - className: "backer-avatar" - }); - } else { - return; - } - }) - ]) - ); - } - - if (category.donations_maintainers.length) { - let maintainersLabel = - category.donations_maintainers_label || - I18n.t("discourse_donations.cause.maintainers.label"); - - users.push( - h("div.donations-maintainers", [ - h("div.donations-maintainers-title", maintainersLabel), - category.donations_maintainers.map(user => { - if (user) { - return avatarFor("medium", { - template: user.avatar_template, - username: user.username, - name: user.name, - url: userPath(user.username), - className: "maintainer-avatar" - }); - } else { - return; - } - }) - ]) - ); - } - - if (users.length) { - contents.push(h("div.donations-category-users", users)); - } - - return h( - "div.donations-category-header", - { - attributes: { - style: - "background-color: #" + - category.color + - "; color: #" + - category.text_color + - ";" - } - }, - contents - ); - } else { - $("body").removeClass("donations-category"); - } - } -}); diff --git a/assets/javascripts/lib/discourse-markdown/stripe.js.es6 b/assets/javascripts/lib/discourse-markdown/stripe.js.es6 deleted file mode 100644 index 3ce2bee..0000000 --- a/assets/javascripts/lib/discourse-markdown/stripe.js.es6 +++ /dev/null @@ -1,104 +0,0 @@ -function validationErrors(tagInfo, content, siteSettings) { - let errors = []; - if (!siteSettings.discourse_donations_public_key) { - errors.push("missing key (site setting)"); - } - if (!siteSettings.discourse_donations_currency) { - errors.push("missing currency (site setting)"); - } - if (!siteSettings.discourse_donations_shop_name) { - errors.push("missing name (site setting)"); - } - if (!siteSettings.discourse_donations_zip_code) { - errors.push("missing zip code toggle (site setting)"); - } - if (!siteSettings.discourse_donations_billing_address) { - errors.push("missing billing address toggle (site setting)"); - } - if (!tagInfo.attrs["amount"]) { - errors.push("missing amount"); - } - if (!content) { - errors.push("missing description"); - } - return errors; -} - -function replaceWithStripeOrError(siteSettings) { - return function(state, tagInfo, content) { - let errors = validationErrors(tagInfo, content, siteSettings); - if (errors.length) { - displayErrors(state, errors); - } else { - insertCheckout(state, tagInfo, content); - } - return true; - }; -} - -function displayErrors(state, errors) { - let token = state.push("div-open", "div", 1); - token.attrs = [["class", "stripe-errors"]]; - token = state.push("html_inline", "", 0); - token.content = "Stripe checkout can't be rendered: " + errors.join(", "); - state.push("div-close", "div", -1); -} - -function insertCheckout(state, tagInfo, content) { - let token = state.push("stripe-checkout-form-open", "form", 1); - token.attrs = [ - ["method", "POST"], - ["action", "/checkout"], - ["content", content], - ["image", tagInfo.attrs["image"]], - ["class", "stripe-checkout"] - ]; - - token = state.push("stripe-checkout-form-amount", "input", 0); - token.attrs = [ - ["type", "hidden"], - ["name", "amount"], - ["value", tagInfo.attrs["amount"]] - ]; - - state.push("stripe-checkout-form-close", "form", -1); -} - -function setupMarkdownIt(helper, siteSettings) { - helper.registerPlugin(md => { - md.inline.bbcode.ruler.push("stripe-checkout", { - tag: "stripe", - replace: replaceWithStripeOrError(siteSettings) - }); - }); -} - -export function setup(helper) { - helper.registerOptions((opts, siteSettings) => { - helper.whiteList([ - "div[class]", - "form[method]", - "form[action]", - "form[class]", - "form[content]", - "form[image]", - "input[type]", - "input[name]", - "input[value]", - "script[class]", - "script[src]", - "script[data-key]", - "script[data-amount]", - "script[data-name]", - "script[data-description]", - "script[data-image]", - "script[data-zip-code]", - "script[data-billing-address]", - "script[data-currency]", - "script[data-locale]" - ]); - if (helper.markdownIt) { - setupMarkdownIt(helper, siteSettings); - } - }); -} diff --git a/assets/stylesheets/common/discourse-donations.scss b/assets/stylesheets/common/discourse-donations.scss deleted file mode 100644 index 4b2264b..0000000 --- a/assets/stylesheets/common/discourse-donations.scss +++ /dev/null @@ -1,218 +0,0 @@ -div.stripe-errors { - border: 1px solid #c33; - border-radius: 5px; - color: #600; - background-color: #fdd; - padding: 5px 10px; -} - -.donations-page-description { - max-width: 700px; - font-size: 1.1em; - line-height: 24px; -} - -.donations-page-payment { - padding: 30px 0; - - #payment-form { - .control-label { - margin: 0 6.5px; - } - - .select-kit ul { - margin: 0; - } - - input[type="checkbox"] { - margin: 0; - } - - .error, - .stripe-error { - margin-top: 5px; - color: $danger; - } - } -} - -.transaction-fee-description { - position: relative; - display: inline-block; - margin-left: 5px; - cursor: pointer; -} - -.transaction-fee-description-modal { - display: block; - position: absolute; - top: -30px; - left: 20px; - background-color: $secondary; - border: 1px solid $primary-low; - padding: 10px; - box-shadow: 0 2px 2px rgba(0, 0, 0, 0.4); - width: 400px; - z-index: 1; -} - -.body-page .donations-page-donations { - margin-bottom: 20px; - - .donation-list { - .subscription-list, - .charge-list { - margin-bottom: 10px; - - > ul { - margin: 10px 0; - list-style: none; - - .spinner { - height: 5px; - width: 5px; - } - } - } - - .underline { - border-bottom: 1px solid $primary-medium; - display: inline-block; - } - } -} - -.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; - } - } -} - -.donations-category-header { - padding-top: 60px; - text-align: center; - - .donations-category-contents { - max-width: 500px; - margin: 0 auto; - padding: 30px 40px 10px; - text-align: center; - - i { - margin-right: 0.25em; - font-size: 1.5em; - } - - h1 { - text-transform: capitalize; - display: inline; - } - - .category-title-description { - font-size: 1.2rem; - padding: 10px 0; - - p { - margin: 0; - } - } - } - - .donations-category-metadata { - max-width: 700px; - margin: 0 auto; - padding-bottom: 20px; - display: flex; - justify-content: space-around; - font-size: 1.2rem; - - .donations-total span:first-of-type, - .donations-month span:first-of-type { - margin-right: 5px; - } - - .donations-github a, - .donations-meta a { - color: inherit; - } - - .donations-meta { - .widget-link { - display: flex; - align-items: center; - } - img { - width: 17px; - height: 17px; - margin-right: 4px; - } - } - - .donations-release-latest, - .donations-release-oldest { - display: flex; - align-items: center; - - span:first-of-type { - margin-right: 10px; - } - - a { - color: white; - } - } - } - - .donations-category-users { - max-width: 500px; - margin: 0 auto; - font-size: 1.2rem; - padding-bottom: 30px; - display: flex; - align-items: center; - - .donations-backers, - .donations-maintainers { - flex: 1 1 auto; - } - - .backer-avatar, - .maintainer-avatar { - margin: 0 5px; - } - - .donations-backers-title, - .donations-maintainers-title { - padding-bottom: 10px; - } - } -} - -.donations-category { - #main-outlet { - padding-top: 20px; - } -} diff --git a/assets/stylesheets/mobile/discourse-donations.scss b/assets/stylesheets/mobile/discourse-donations.scss deleted file mode 100644 index b99f3da..0000000 --- a/assets/stylesheets/mobile/discourse-donations.scss +++ /dev/null @@ -1,8 +0,0 @@ -.donations-category-header .donations-category-metadata { - flex-flow: wrap; - padding: 0 10px; - - div { - padding-bottom: 10px; - } -} diff --git a/config/locales/client.de.yml b/config/locales/client.de.yml deleted file mode 100644 index 92fed55..0000000 --- a/config/locales/client.de.yml +++ /dev/null @@ -1,10 +0,0 @@ -en: - js: - discourse_donations: - title: Spenden - nav_item: Spenden - amount: Betrag - card: Kreditkarte oder Bankkarte - submit: Spende bezahlen - messages: - success: Thank you for your donation! diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml deleted file mode 100644 index 2f796f3..0000000 --- a/config/locales/client.en.yml +++ /dev/null @@ -1,96 +0,0 @@ -en: - site_settings: - discourse_donations_enabled: "Enable the Discourse Donations plugin." - discourse_donations_enable_create_accounts: "EXPERIMENTAL: Enable anonymous users to create accounts after successful payment" - discourse_donations_secret_key: "Stripe Secret Key" - discourse_donations_public_key: "Stripe Public Key" - discourse_donations_shop_name: "Shop Name shown in Stripe Checkout form" - discourse_donations_description: "Description shown in Stripe Checkout form" - discourse_donations_currency: "Currency Code" - discourse_donations_zip_code: "Show Zip Code" - discourse_donations_billing_address: "Collect billing address" - discourse_donations_reward_badge_name: "Grant this badge to user when a payment is successful" - discourse_donations_reward_group_name: "Add the user to this group when a payment is successful" - discourse_donations_page_description: "Text to be added to /donate page. Markdown is supported." - discourse_donations_enable_transaction_fee: "Give the user the option of including the Stripe transaction fee in their donation." - discourse_donations_transaction_fee_fixed: "Fixed part of Stripe transaction fee (changes per region). See Stripe's pricing for your region and Stripe's explaination of passing fees onto customers." - discourse_donations_transaction_fee_percent: "Percent part of Stripe transaction fee (changes per region). See Stripe's pricing for your region and Stripe's explaination of passing fees onto customers." - discourse_donations_amounts: "Donation amounts available to user. First listed will be the default." - discourse_donations_custom_amount: "Allow custom donation amount" - discourse_donations_types: "Donation types. First listed will be the default." - discourse_donations_causes: "Custom causes a user can donate to." - discourse_donations_causes_categories: "Categories of cause a user can donate do." - discourse_donations_cause_category: "Category causes enabled." - discourse_donations_cause_required: "Require user to select a cause when donating." - discourse_donations_cause_month: "Show monthly total of donations to each cause." - errors: - discourse_donations_amount_must_be_number: "Amounts must be numbers" - - js: - discourse_donations: - nav_item: Donate - title: "Make a Donation" - amount: Amount - card: Card - submit: Donate - submit_with_create_account: Make Payment and Create Account - invoice: "invoice" - invoice_prefix: "You gave" - receipt: "Receipt sent to {{email}}." - cause: - label: "Cause" - placeholder: "Select a cause" - category: - total: "Total" - month: "Month" - backers: - label: "Backers" - github: - label: "Repository" - setting_label: "Github" - setting_placeholder: "repoistory url" - meta: - label: "Discussion" - setting_label: "Meta" - setting_placeholder: "topic url" - maintainers: - label: "Maintainers" - setting_label: "Maintainers label" - amounts: - setting_label: "Show donation amounts" - release_latest: - label: "Latest Release Supported" - release_oldest: - label: "Oldest Release Supported" - 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." - transaction_fee: - label: "Include transaction fee of {{currency}} {{transactionFee}}" - description: "When you make a donation we get charged a transaction fee. If you would like to help us out with this fee, check this box and it will be included in your donation." - total: "Total" - messages: - success: Thank you for your donation! - type: "Type" - types: - once: "Once" - week: "Weekly" - month: "Monthly" - year: "Yearly" - period: - once: "on {{anchor}}" - week: "every week on {{anchor}}" - month: "on the {{anchor}} of every month" - year: "every year on {{anchor}}" - donations: - title: "Your Donations" - load: "Load Donations" - loading: "Loading donations" - charges: "Once Off" - subscriptions: "Recurring" - none: "You haven't made a donation yet." - none_email: "There are no donations for {{email}}." diff --git a/config/locales/client.fi.yml b/config/locales/client.fi.yml deleted file mode 100644 index 351f1cd..0000000 --- a/config/locales/client.fi.yml +++ /dev/null @@ -1,15 +0,0 @@ -fi: - site_settings: - discourse_donations_enabled: Ota käyttöön lahjoituslisäosa. - discourse_donations_secret_key: Stripen Secret Key -salausavain - discourse_donations_public_key: Stripen Public Key -tunnistusavain - discourse_donations_currency: Valuuttakoodi - js: - discourse_donations: - title: Lahjoita - nav_item: Lahjoita - amount: Määrä - card: Credit- vai debit-kortti - submit: Maksa - messages: - success: Kiitos lahjoituksestasi! diff --git a/config/locales/client.it.yml b/config/locales/client.it.yml deleted file mode 100644 index fe95322..0000000 --- a/config/locales/client.it.yml +++ /dev/null @@ -1,19 +0,0 @@ -it: - site_settings: - discourse_donations_enabled: Abilita il plugin per le donazioni. - discourse_donations_enable_create_accounts: "SPERIMENTALE: Permetti agli utenti anonimi la creazione di un account dopo un pagamento effettuato con successo" - discourse_donations_secret_key: Stripe Secret Key - discourse_donations_public_key: Stripe Public Key - discourse_donations_currency: Codice Valuta - discourse_donations_reward_badge_name: Assegna questo distintivo all'utente quando un pagamento viene effettuato con successo - discourse_donations_reward_group_name: Aggiungi l'utente a questo gruppo quando un pagamento viene effettuato con successo - js: - discourse_donations: - title: Donazione - nav_item: Donazione - amount: Importo - card: Carta di credito o debito - submit: Effettua il pagamento - submit_with_create_account: Effettua il Pagamento e Crea un Account - messages: - success: Grazie per la tua donazione! diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml deleted file mode 100644 index c72bc8f..0000000 --- a/config/locales/server.en.yml +++ /dev/null @@ -1,11 +0,0 @@ -en: - donations: - recurring: "%{site_title} Recurring Donation" - payment: - success: 'Thank you, your donation has been successful.' - receipt_sent: 'A receipt 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." diff --git a/config/locales/server.it.yml b/config/locales/server.it.yml deleted file mode 100644 index 059a90b..0000000 --- a/config/locales/server.it.yml +++ /dev/null @@ -1,4 +0,0 @@ -it: - donations: - payment: - success: 'Grazie. La tua donazione è stata effettuata con successo' diff --git a/config/routes.rb b/config/routes.rb index 5314102..6367814 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,10 +1,6 @@ # frozen_string_literal: true -DiscourseDonations::Engine.routes.draw do - get '/' => 'charges#index' - - resources :charges, only: [:index, :create] - put '/charges/cancel-subscription' => 'charges#cancel_subscription' - - resources :checkout, only: [:create] +DiscoursePatrons::Engine.routes.draw do + get '/' => 'patrons#index' + get '/:id' => 'patrons#show' end diff --git a/config/settings.yml b/config/settings.yml index 898c70e..de4e71f 100644 --- a/config/settings.yml +++ b/config/settings.yml @@ -1,78 +1,21 @@ plugins: - discourse_donations_enabled: + discourse_patrons_enabled: default: false client: true - discourse_donations_secret_key: + discourse_patrons_secret_key: default: '' client: false - discourse_donations_public_key: + discourse_patrons_public_key: default: '' client: true - discourse_donations_enable_create_accounts: + discourse_patrons_currency: client: true - default: false - discourse_donations_description: - client: true - default: '' - discourse_donations_shop_name: - client: true - default: '' - discourse_donations_currency: - client: true - default: 'USD' - discourse_donations_zip_code: - default: false - client: true - discourse_donations_billing_address: - default: true - client: true - discourse_donations_reward_badge_name: - client: false - default: 'Donation' - discourse_donations_reward_group_name: - client: false - default: 'Donation' - discourse_donations_page_description: - client: true - default: '' - discourse_donations_enable_transaction_fee: - client: true - default: false - discourse_donations_transaction_fee_fixed: - client: true - default: 0.3 - discourse_donations_transaction_fee_percent: - client: true - default: 0.029 - discourse_donations_amounts: - client: true - type: list - default: '1|2|5|10|20|50' - regex: "^[0-9\\|]+$" - regex_error: "site_settings.errors.discourse_donations_amount_must_be_number" - discourse_donations_types: - client: true - type: list - default: 'once|month' + default: "USD" + type: enum choices: - - year - - month - - week - - once - discourse_donations_causes: - client: true - type: list - default: '' - discourse_donations_causes_categories: - client: true - type: category_list - default: '' - discourse_donations_cause_category: - client: true - default: false - discourse_donations_cause_required: - client: true - default: false - discourse_donations_cause_month: - client: true - default: false + - AUD + - CAD + - EUR + - JPY + - GBP + - USD diff --git a/doc/menulink.png b/doc/menulink.png deleted file mode 100644 index ba820af..0000000 Binary files a/doc/menulink.png and /dev/null differ diff --git a/doc/post-payment.png b/doc/post-payment.png deleted file mode 100644 index 7a152e1..0000000 Binary files a/doc/post-payment.png and /dev/null differ diff --git a/doc/pre-payment.png b/doc/pre-payment.png deleted file mode 100644 index e07c072..0000000 Binary files a/doc/pre-payment.png and /dev/null differ diff --git a/lib/discourse_donations/engine.rb b/lib/discourse_donations/engine.rb deleted file mode 100644 index 41a41c5..0000000 --- a/lib/discourse_donations/engine.rb +++ /dev/null @@ -1,8 +0,0 @@ -# frozen_string_literal: true - -module ::DiscourseDonations - class Engine < ::Rails::Engine - engine_name 'discourse-donations' - isolate_namespace DiscourseDonations - end -end diff --git a/lib/discourse_patrons/engine.rb b/lib/discourse_patrons/engine.rb new file mode 100644 index 0000000..37bc3e0 --- /dev/null +++ b/lib/discourse_patrons/engine.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +module ::DiscoursePatrons + PLUGIN_NAME = "discourse-patrons" + + class Engine < ::Rails::Engine + engine_name DiscoursePatrons::PLUGIN_NAME + isolate_namespace DiscoursePatrons + end +end diff --git a/plugin.rb b/plugin.rb index d643306..4e281ed 100644 --- a/plugin.rb +++ b/plugin.rb @@ -1,172 +1,15 @@ # frozen_string_literal: true -# name: discourse-donations -# about: Integrates Stripe into Discourse to allow visitors to make donations -# version: 1.12.2 -# url: https://github.com/rimian/discourse-donations -# authors: Rimian Perkins, Chris Beach, Angus McLeod +# name: discourse-patrons +# about: Integrates Stripe into Discourse to allow visitors to make payments +# version: 1.0.0 +# url: https://github.com/rimian/discourse-patrons +# authors: Rimian Perkins -gem 'stripe', '5.1.0' +enabled_site_setting :discourse_patrons_enabled -register_asset "stylesheets/common/discourse-donations.scss" -register_asset "stylesheets/mobile/discourse-donations.scss" +load File.expand_path('../lib/discourse_patrons/engine.rb', __FILE__) -enabled_site_setting :discourse_donations_enabled - -register_html_builder('server:before-head-close') do - "" -end - -extend_content_security_policy( - script_src: ['https://js.stripe.com/v3/'] -) - -after_initialize do - load File.expand_path('../lib/discourse_donations/engine.rb', __FILE__) - load File.expand_path('../config/routes.rb', __FILE__) - load File.expand_path('../app/controllers/controllers.rb', __FILE__) - load File.expand_path('../app/jobs/jobs.rb', __FILE__) - load File.expand_path('../app/services/services.rb', __FILE__) - - Discourse::Application.routes.append do - mount ::DiscourseDonations::Engine, at: 'donate' - end - - class ::User - def stripe_customer_id - if custom_fields['stripe_customer_id'] - custom_fields['stripe_customer_id'].to_s - else - nil - end - end - end - - Category.register_custom_field_type('donations_show_amounts', :boolean) - - class ::Category - def donations_cause - SiteSetting.discourse_donations_causes_categories.split('|').include? self.id.to_s - end - - def donations_total - if custom_fields['donations_total'] - custom_fields['donations_total'] - else - 0 - end - end - - def donations_show_amounts - if custom_fields['donations_show_amounts'] != nil - custom_fields['donations_show_amounts'] - else - false - end - end - - def donations_month - if custom_fields['donations_month'] - custom_fields['donations_month'] - else - 0 - end - end - - def donations_backers - if custom_fields['donations_backers'] - [*custom_fields['donations_backers']].map do |user_id| - User.find_by(id: user_id.to_i) - end - else - [] - end - end - - def donations_maintainers - if custom_fields['donations_maintainers'] - custom_fields['donations_maintainers'].split(',').map do |username| - User.find_by(username: username) - end - else - [] - end - end - - def donations_maintainers_label - if custom_fields['donations_maintainers_label'] - custom_fields['donations_maintainers_label'] - else - nil - end - end - - def donations_github - if custom_fields['donations_github'] - custom_fields['donations_github'] - else - nil - end - end - - def donations_meta - if custom_fields['donations_meta'] - custom_fields['donations_meta'] - else - nil - end - end - - def donations_release_latest - if custom_fields['donations_release_latest'] - custom_fields['donations_release_latest'] - else - nil - end - end - - def donations_release_oldest - if custom_fields['donations_release_oldest'] - custom_fields['donations_release_oldest'] - else - nil - end - end - end - - [ - 'donations_cause', - 'donations_total', - 'donations_month', - 'donations_backers', - 'donations_show_amounts', - 'donations_maintainers', - 'donations_maintainers_label', - 'donations_github', - 'donations_meta', - 'donations_release_latest', - 'donations_release_oldest' - ].each do |key| - Site.preloaded_category_custom_fields << key if Site.respond_to? :preloaded_category_custom_fields - end - - add_to_serializer(:basic_category, :donations_cause) { object.donations_cause } - add_to_serializer(:basic_category, :donations_total) { object.donations_total } - add_to_serializer(:basic_category, :include_donations_total?) { object.donations_show_amounts } - add_to_serializer(:basic_category, :donations_month) { object.donations_month } - add_to_serializer(:basic_category, :include_donations_month?) { object.donations_show_amounts && SiteSetting.discourse_donations_cause_month } - add_to_serializer(:basic_category, :donations_backers) { - ActiveModel::ArraySerializer.new(object.donations_backers, each_serializer: BasicUserSerializer).as_json - } - add_to_serializer(:basic_category, :donations_maintainers) { - ActiveModel::ArraySerializer.new(object.donations_maintainers, each_serializer: BasicUserSerializer).as_json - } - add_to_serializer(:basic_category, :donations_maintainers_label) { object.donations_maintainers_label } - add_to_serializer(:basic_category, :include_donations_maintainers_label?) { object.donations_maintainers_label.present? } - add_to_serializer(:basic_category, :donations_github) { object.donations_github } - add_to_serializer(:basic_category, :donations_meta) { object.donations_meta } - add_to_serializer(:basic_category, :donations_release_latest) { object.donations_release_latest } - add_to_serializer(:basic_category, :donations_release_oldest) { object.donations_release_oldest } - - DiscourseEvent.trigger(:donations_ready) +Discourse::Application.routes.append do + mount ::DiscoursePatrons::Engine, at: '/patrons' end diff --git a/spec/controllers/discourse_donations/charges_controller_spec.rb b/spec/controllers/discourse_donations/charges_controller_spec.rb deleted file mode 100644 index edd70f8..0000000 --- a/spec/controllers/discourse_donations/charges_controller_spec.rb +++ /dev/null @@ -1,125 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -shared_examples 'failure response' do |message_key| - let(:body) { JSON.parse(response.body) } - - it 'has status 200' do expect(response).to have_http_status(200) end - it 'has an error message' do expect(body['messages']).to include(I18n.t(message_key)) end - it 'is not successful' do expect(body['success']).to eq false end - it 'does not create a payment' do DiscourseDonations::Stripe.expects(:new).never end - it 'does not create rewards' do DiscourseDonations::Rewards.expects(:new).never end - it 'does not queue up any jobs' do ::Jobs.expects(:enqueue).never end -end - -module DiscourseDonations - RSpec.describe ChargesController, type: :controller do - routes { DiscourseDonations::Engine.routes } - let(:body) { JSON.parse(response.body) } - let(:current_user) { Fabricate(:user) } - # Workaround for rails-5 issue. See https://github.com/thoughtbot/shoulda-matchers/issues/1018#issuecomment-315876453 - # let(:allowed_params) { { create_account: 'true', email: 'email@example.com', password: 'secret', username: 'mr-pink', name: 'kirsten', amount: 100, stripeToken: 'rrurrrurrrrr' } } - - before do - SiteSetting.stubs(:disable_discourse_narrative_bot_welcome_post).returns(true) - SiteSetting.stubs(:discourse_donations_secret_key).returns('secret-key-yo') - SiteSetting.stubs(:discourse_donations_description).returns('charity begins at discourse plugin') - SiteSetting.stubs(:discourse_donations_currency).returns('AUD') - - customer = Fabricate(:stripe_customer).to_json - - stub_request(:get, /v1\/customers/).to_return(status: 200, body: customer) - - plans = Fabricate(:stripe_plans).to_json - - stub_request(:get, "https://api.stripe.com/v1/plans").to_return(status: 200, body: plans) - stub_request(:post, "https://api.stripe.com/v1/plans").to_return(status: 200, body: plans) - - products = Fabricate(:stripe_products).to_json - - stub_request(:get, "https://api.stripe.com/v1/products?type=service").to_return(status: 200, body: products) - stub_request(:post, "https://api.stripe.com/v1/products").to_return(status: 200, body: products) - stub_request(:post, "https://api.stripe.com/v1/customers").to_return(status: 200, body: customer) - - subscription = Fabricate(:stripe_subscription).to_json - - stub_request(:post, "https://api.stripe.com/v1/subscriptions").to_return(status: 200, body: subscription) - - invoices = Fabricate(:stripe_invoices).to_json - - stub_request(:get, "https://api.stripe.com/v1/invoices?customer=cus_FhHJDzf0OxYtb8&subscription=sub_8epEF0PuRhmltU") - .to_return(status: 200, body: invoices) - end - - xit 'whitelists the params' do - should permit(:name, :username, :email, :password, :create_account). - for(:create, params: { params: allowed_params }) - end - - it 'responds ok for anonymous users' do - controller.expects(:current_user).at_least(1).returns(current_user) - - post :create, params: { email: 'foobar@example.com' }, format: :json - - aggregate_failures do - expect(response).to have_http_status(200) - expect(body['messages'][0]).to end_with(I18n.t('donations.payment.success')) - end - end - - it 'does not expect a username or email if accounts are not being created' do - charge = Fabricate(:stripe_charge).to_json - stub_request(:post, "https://api.stripe.com/v1/charges").to_return(status: 200, body: charge) - - post :create, params: { create_account: 'false', type: 'once' }, format: :json - - aggregate_failures do - expect(response).to have_http_status(200) - expect(body['messages'][0]).to end_with(I18n.t('donations.payment.success')) - end - end - - describe 'create accounts' do - describe 'create acccount disabled' do - let(:params) { { amount: 100, stripeToken: 'rrurrrurrrrr-rrruurrrr' } } - - before do - SiteSetting.stubs(:discourse_donations_enable_create_accounts).returns(false) - ::Jobs.expects(:enqueue).never - end - - it 'does not create user accounts' do - controller.expects(:current_user).at_least(1).returns(current_user) - - post :create, params: params, format: :json - end - - it 'does not create user accounts if the user is logged in' do - log_in :coding_horror - post :create, params: params, format: :json - end - - it 'does not create user accounts when settings are disabled and params are not' do - log_in :coding_horror - post :create, params: params.merge(create_account: true, email: 'email@example.com', password: 'secret', username: 'mr-brown', name: 'hacker-guy') - end - end - - describe 'creating an account enabled' do - let(:params) { { create_account: 'true', email: 'email@example.com', password: 'secret', username: 'mr-pink', amount: 100, stripeToken: 'rrurrrurrrrr-rrruurrrr' } } - - before do - SiteSetting.stubs(:discourse_donations_enable_create_accounts).returns(true) - Jobs.expects(:enqueue).with(:donation_user, anything) - end - - it 'enqueues the user account create' do - controller.expects(:current_user).at_least(1).returns(current_user) - - post :create, params: params, format: :json - end - end - end - end -end diff --git a/spec/fabricators/charge_fabricator.rb b/spec/fabricators/charge_fabricator.rb deleted file mode 100644 index 9a08a61..0000000 --- a/spec/fabricators/charge_fabricator.rb +++ /dev/null @@ -1,111 +0,0 @@ -# frozen_string_literal: true - -Fabricator(:stripe_charge, from: "DiscourseDonations::StripeResponse") do - response = { - "id": "ch_1FBxEe2eZvKYlo2CAWyww6QM", - "object": "charge", - "amount": 100, - "amount_refunded": 0, - "application": "null", - "application_fee": "null", - "application_fee_amount": "null", - "balance_transaction": "txn_19XJJ02eZvKYlo2ClwuJ1rbA", - "billing_details": { - "address": { - "city": "null", - "country": "null", - "line1": "null", - "line2": "null", - "postal_code": "null", - "state": "null" - }, - "email": "null", - "name": "null", - "phone": "null" - }, - "captured": false, - "created": 1566883732, - "currency": "usd", - "customer": "null", - "description": "My First Test Charge (created for API docs)", - "destination": "null", - "dispute": "null", - "failure_code": "null", - "failure_message": "null", - "fraud_details": {}, - "invoice": "null", - "livemode": false, - "metadata": {}, - "on_behalf_of": "null", - "order": "null", - "outcome": "null", - "paid": true, - "payment_intent": "null", - "payment_method": "card_103Z0w2eZvKYlo2CyzMjT1R1", - "payment_method_details": { - "card": { - "brand": "visa", - "checks": { - "address_line1_check": "null", - "address_postal_code_check": "null", - "cvc_check": "unchecked" - }, - "country": "US", - "exp_month": 2, - "exp_year": 2015, - "fingerprint": "Xt5EWLLDS7FJjR1c", - "funding": "credit", - "last4": "4242", - "three_d_secure": "null", - "wallet": "null" - }, - "type": "card" - }, - "receipt_email": "null", - "receipt_number": "null", - "receipt_url": "https://pay.stripe.com/receipts/acct_1032D82eZvKYlo2C/ch_1FBxEe2eZvKYlo2CAWyww6QM/rcpt_FhLw6tME6cvwGXWoL0Hn3f65Gkvyocg", - "refunded": false, - "refunds": { - "object": "list", - "data": [], - "has_more": false, - "total_count": 0, - "url": "/v1/charges/ch_1FBxEe2eZvKYlo2CAWyww6QM/refunds" - }, - "review": "null", - "shipping": "null", - "source": { - "id": "card_103Z0w2eZvKYlo2CyzMjT1R1", - "object": "card", - "address_city": "null", - "address_country": "null", - "address_line1": "null", - "address_line1_check": "null", - "address_line2": "null", - "address_state": "null", - "address_zip": "null", - "address_zip_check": "null", - "brand": "Visa", - "country": "US", - "customer": "null", - "cvc_check": "unchecked", - "dynamic_last4": "null", - "exp_month": 2, - "exp_year": 2015, - "fingerprint": "Xt5EWLLDS7FJjR1c", - "funding": "credit", - "last4": "4242", - "metadata": {}, - "name": "null", - "tokenization_method": "null" - }, - "source_transfer": "null", - "statement_descriptor": "null", - "statement_descriptor_suffix": "null", - "status": "succeeded", - "transfer_data": "null", - "transfer_group": "null" - }.to_json - - to_json response -end diff --git a/spec/fabricators/customer_fabricator.rb b/spec/fabricators/customer_fabricator.rb deleted file mode 100644 index 6222092..0000000 --- a/spec/fabricators/customer_fabricator.rb +++ /dev/null @@ -1,56 +0,0 @@ -# frozen_string_literal: true - -Fabricator(:stripe_customer, from: "DiscourseDonations::StripeResponse") do - response = { - "id": "cus_FhHJDzf0OxYtb8", - "object": "customer", - "account_balance": 0, - "address": "null", - "balance": 0, - "created": 1566866533, - "currency": "usd", - "default_source": "null", - "delinquent": false, - "description": "null", - "discount": "null", - "email": "null", - "invoice_prefix": "0BBF354", - "invoice_settings": { - "custom_fields": "null", - "default_payment_method": "null", - "footer": "null" - }, - "livemode": false, - "metadata": {}, - "name": "null", - "phone": "null", - "preferred_locales": [], - "shipping": "null", - "sources": { - "object": "list", - "data": [], - "has_more": false, - "total_count": 0, - "url": "/v1/customers/cus_FhHJDzf0OxYtb8/sources" - }, - "subscriptions": { - "object": "list", - "data": [], - "has_more": false, - "total_count": 0, - "url": "/v1/customers/cus_FhHJDzf0OxYtb8/subscriptions" - }, - "tax_exempt": "none", - "tax_ids": { - "object": "list", - "data": [], - "has_more": false, - "total_count": 0, - "url": "/v1/customers/cus_FhHJDzf0OxYtb8/tax_ids" - }, - "tax_info": "null", - "tax_info_verification": "null" - }.to_json - - to_json response -end diff --git a/spec/fabricators/invoices_fabricator.rb b/spec/fabricators/invoices_fabricator.rb deleted file mode 100644 index d4c823c..0000000 --- a/spec/fabricators/invoices_fabricator.rb +++ /dev/null @@ -1,128 +0,0 @@ -# frozen_string_literal: true - -Fabricator(:stripe_invoices, from: "DiscourseDonations::StripeResponse") do - response = { - "object": "list", - "url": "/v1/invoices", - "has_more": false, - "data": [ - { - "id": "in_1Cc9wc2eZvKYlo2ClBzJbDQz", - "object": "invoice", - "account_country": "US", - "account_name": "Stripe.com", - "amount_due": 20, - "amount_paid": 0, - "amount_remaining": 20, - "application_fee_amount": "null", - "attempt_count": 0, - "attempted": false, - "auto_advance": false, - "billing": "send_invoice", - "billing_reason": "subscription_update", - "charge": "null", - "collection_method": "send_invoice", - "created": 1528800106, - "currency": "usd", - "custom_fields": "null", - "customer": "cus_FhHJDzf0OxYtb8", - "customer_address": "null", - "customer_email": "ziad+123@elysian.team", - "customer_name": "null", - "customer_phone": "null", - "customer_shipping": "null", - "customer_tax_exempt": "none", - "customer_tax_ids": [], - "default_payment_method": "null", - "default_source": "null", - "default_tax_rates": [], - "description": "null", - "discount": "null", - "due_date": 1529059306, - "ending_balance": "null", - "footer": "null", - "hosted_invoice_url": "null", - "invoice_pdf": "null", - "lines": { - "data": [ - { - "id": "sli_42e8bf79bec714", - "object": "line_item", - "amount": 999, - "currency": "usd", - "description": "1 × Ivory Freelance (at $9.99 / month)", - "discountable": true, - "livemode": false, - "metadata": {}, - "period": { - "end": 1521326190, - "start": 1518906990 - }, - "plan": { - "id": "ivory-freelance-040", - "object": "plan", - "active": true, - "aggregate_usage": "null", - "amount": 999, - "amount_decimal": "999", - "billing_scheme": "per_unit", - "created": 1466202980, - "currency": "usd", - "interval": "month", - "interval_count": 1, - "livemode": false, - "metadata": {}, - "nickname": "null", - "product": "prod_BUthVRQ7KdFfa7", - "tiers": "null", - "tiers_mode": "null", - "transform_usage": "null", - "trial_period_days": "null", - "usage_type": "licensed" - }, - "proration": false, - "quantity": 1, - "subscription": "sub_8epEF0PuRhmltU", - "subscription_item": "si_18NVZi2eZvKYlo2CUtBNGL9x", - "tax_amounts": [], - "tax_rates": [], - "type": "subscription" - } - ], - "has_more": false, - "object": "list", - "url": "/v1/invoices/in_1Cc9wc2eZvKYlo2ClBzJbDQz/lines" - }, - "livemode": false, - "metadata": {}, - "next_payment_attempt": "null", - "number": "8B36FE9-0005", - "paid": false, - "payment_intent": "null", - "period_end": 1528800106, - "period_start": 1528800106, - "post_payment_credit_notes_amount": 0, - "pre_payment_credit_notes_amount": 0, - "receipt_number": "null", - "starting_balance": 10, - "statement_descriptor": "null", - "status": "draft", - "status_transitions": { - "finalized_at": "null", - "marked_uncollectible_at": "null", - "paid_at": "null", - "voided_at": "null" - }, - "subscription": "sub_D2ECXpuEnnXkWU", - "subtotal": 10, - "tax": "null", - "tax_percent": "null", - "total": 10, - "total_tax_amounts": [], - "webhooks_delivered_at": 1528800106 - }, - ] - }.to_json - - to_json response -end diff --git a/spec/fabricators/plans_fabricator.rb b/spec/fabricators/plans_fabricator.rb deleted file mode 100644 index cd4effd..0000000 --- a/spec/fabricators/plans_fabricator.rb +++ /dev/null @@ -1,35 +0,0 @@ -# frozen_string_literal: true - -Fabricator(:stripe_plans, from: "DiscourseDonations::StripeResponse") do - response = { - "object": "list", - "url": "/v1/plans", - "has_more": false, - "data": [ - { - "id": "plan_EeE4ns3bvb34ZP", - "object": "plan", - "active": true, - "aggregate_usage": "null", - "amount": 3000, - "amount_decimal": "3000", - "billing_scheme": "per_unit", - "created": 1551862832, - "currency": "usd", - "interval": "month", - "interval_count": 1, - "livemode": false, - "metadata": {}, - "nickname": "Pro Plan", - "product": "prod_BT942zL7VcClrn", - "tiers": "null", - "tiers_mode": "null", - "transform_usage": "null", - "trial_period_days": "null", - "usage_type": "licensed" - }, - ] - }.to_json - - to_json response -end diff --git a/spec/fabricators/products_fabricator.rb b/spec/fabricators/products_fabricator.rb deleted file mode 100644 index ff9cbcf..0000000 --- a/spec/fabricators/products_fabricator.rb +++ /dev/null @@ -1,34 +0,0 @@ -# frozen_string_literal: true - -Fabricator(:stripe_products, from: "DiscourseDonations::StripeResponse") do - response = { - "object": "list", - "url": "/v1/products", - "has_more": false, - "data": [ - { - "id": "prod_FhGJ7clA2xMxGI", - "object": "product", - "active": true, - "attributes": [], - "caption": "null", - "created": 1566862775, - "deactivate_on": [], - "description": "null", - "images": [], - "livemode": false, - "metadata": {}, - "name": "Sapphire Personal", - "package_dimensions": "null", - "shippable": "null", - "statement_descriptor": "null", - "type": "service", - "unit_label": "null", - "updated": 1566862775, - "url": "null" - }, - ] - }.to_json - - to_json response -end diff --git a/spec/fabricators/stripe_response.rb b/spec/fabricators/stripe_response.rb deleted file mode 100644 index 6649fad..0000000 --- a/spec/fabricators/stripe_response.rb +++ /dev/null @@ -1,11 +0,0 @@ -# frozen_string_literal: true - -# This is for building http responses with Fabricate -# Usage: Fabricate(:customer).to_json -# See: https://stripe.com/docs/api - -module DiscourseDonations - class StripeResponse - attr_accessor :to_json - end -end diff --git a/spec/fabricators/subscription_fabricator.rb b/spec/fabricators/subscription_fabricator.rb deleted file mode 100644 index 75a716d..0000000 --- a/spec/fabricators/subscription_fabricator.rb +++ /dev/null @@ -1,102 +0,0 @@ -# frozen_string_literal: true - -Fabricator(:stripe_subscription, from: "DiscourseDonations::StripeResponse") do - response = { - "id": "sub_8epEF0PuRhmltU", - "object": "subscription", - "application_fee_percent": "null", - "billing": "charge_automatically", - "billing_cycle_anchor": 1466202990, - "billing_thresholds": "null", - "cancel_at": "null", - "cancel_at_period_end": false, - "canceled_at": 1517528245, - "collection_method": "charge_automatically", - "created": 1466202990, - "current_period_end": 1518906990, - "current_period_start": 1516228590, - "customer": "cus_FhHJDzf0OxYtb8", - "days_until_due": "null", - "default_payment_method": "null", - "default_source": "null", - "default_tax_rates": [], - "discount": "null", - "ended_at": 1517528245, - "items": { - "object": "list", - "data": [ - { - "id": "si_18NVZi2eZvKYlo2CUtBNGL9x", - "object": "subscription_item", - "billing_thresholds": "null", - "created": 1466202990, - "metadata": {}, - "plan": { - "id": "ivory-freelance-040", - "object": "plan", - "active": true, - "aggregate_usage": "null", - "amount": 999, - "amount_decimal": "999", - "billing_scheme": "per_unit", - "created": 1466202980, - "currency": "usd", - "interval": "month", - "interval_count": 1, - "livemode": false, - "metadata": {}, - "nickname": "null", - "product": "prod_BUthVRQ7KdFfa7", - "tiers": "null", - "tiers_mode": "null", - "transform_usage": "null", - "trial_period_days": "null", - "usage_type": "licensed" - }, - "quantity": 1, - "subscription": "sub_8epEF0PuRhmltU", - "tax_rates": [] - } - ], - "has_more": false, - "total_count": 1, - "url": "/v1/subscription_items?subscription=sub_8epEF0PuRhmltU" - }, - "latest_invoice": "null", - "livemode": false, - "metadata": {}, - "pending_setup_intent": "null", - "plan": { - "id": "ivory-freelance-040", - "object": "plan", - "active": true, - "aggregate_usage": "null", - "amount": 999, - "amount_decimal": "999", - "billing_scheme": "per_unit", - "created": 1466202980, - "currency": "usd", - "interval": "month", - "interval_count": 1, - "livemode": false, - "metadata": {}, - "nickname": "null", - "product": "prod_BUthVRQ7KdFfa7", - "tiers": "null", - "tiers_mode": "null", - "transform_usage": "null", - "trial_period_days": "null", - "usage_type": "licensed" - }, - "quantity": 1, - "schedule": "null", - "start": 1466202990, - "start_date": 1466202990, - "status": "active", - "tax_percent": "null", - "trial_end": "null", - "trial_start": "null" - }.to_json - - to_json response -end diff --git a/spec/jobs/regular/donation_user_spec.rb b/spec/jobs/regular/donation_user_spec.rb deleted file mode 100644 index fa09fea..0000000 --- a/spec/jobs/regular/donation_user_spec.rb +++ /dev/null @@ -1,94 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -RSpec.describe Jobs::DonationUser, type: :job do - let(:args) { { email: 'captain-sensible@example.com', username: 'wot', name: 'captain', password: 'secret121321' } } - - before do - SiteSetting.stubs(:enable_badges).returns(true) - end - - it 'creates a new user with no rewards' do - aggregate_failures do - expect { subject.execute(args) }.to change { User.count }.by(1) - user = User.find_by_email(args[:email]) - expect(user.badges).to be_empty - expect(user.groups).to be_empty - end - end - - describe 'sending the signup email' do - let(:user) { User.find_by_email(args[:email]) } - - it 'has an email token' do - subject.execute(args) - expect(user.email_tokens).not_to be_empty - end - - it 'enqueues the signup email' do - User.expects(:create!).returns(Fabricate(:user, args)) - Jobs.expects(:enqueue).with( - :critical_user_email, - type: :signup, user_id: user.id, email_token: user.email_tokens.first.token - ) - subject.execute(args) - end - end - - describe 'rewards' do - describe 'create user with rewards' do - let(:user) { Fabricate(:user) } - - it 'does not create the rewards if the user does not persist' do - User.expects(:create!).returns(user) - user.expects(:persisted?).returns(false) - DiscourseDonations::Rewards.expects(:new).never - subject.execute(args) - end - - it 'creates a User object without rewards' do - User.expects(:create!).with(args).returns(user) - subject.execute(args.merge(rewards: [], otherthing: nil)) - end - end - - describe 'User rewards' do - let(:user) { Fabricate(:user) } - let(:badge) { Fabricate(:badge) } - let(:grp) { Fabricate(:group) } - - before do - User.stubs(:create!).returns(user) - end - - it 'grants the user a badge' do - subject.execute(args.merge(rewards: [{ type: 'badge', name: badge.name }])) - aggregate_failures do - expect(user.badges).to include(badge) - expect(user.groups).to be_empty - end - end - - it 'adds the user to the group' do - subject.execute(args.merge(rewards: [{ type: 'group', name: grp.name }])) - aggregate_failures do - expect(user.badges).to be_empty - expect(user.groups).to include(grp) - end - end - - it 'has no collisions in badges' do - Fabricate(:badge, name: 'weiner_schitzel') - subject.execute(args.merge(rewards: [{ type: 'group', name: 'weiner_schitzel' }])) - expect(user.badges).to be_empty - end - - it 'has no collisions in groups' do - Fabricate(:group, name: 'dude_ranch') - subject.execute(args.merge(rewards: [{ type: 'badge', name: 'dude_ranch' }])) - expect(user.groups).to be_empty - end - end - end -end diff --git a/spec/plugin_helper.rb b/spec/plugin_helper.rb deleted file mode 100644 index aa3fb30..0000000 --- a/spec/plugin_helper.rb +++ /dev/null @@ -1,9 +0,0 @@ -# frozen_string_literal: true - -require_relative './fabricators/charge_fabricator.rb' -require_relative './fabricators/customer_fabricator.rb' -require_relative './fabricators/invoices_fabricator.rb' -require_relative './fabricators/plans_fabricator.rb' -require_relative './fabricators/products_fabricator.rb' -require_relative './fabricators/stripe_response.rb' -require_relative './fabricators/subscription_fabricator.rb' diff --git a/spec/services/discourse_donations/rewards_spec.rb b/spec/services/discourse_donations/rewards_spec.rb deleted file mode 100644 index f39956b..0000000 --- a/spec/services/discourse_donations/rewards_spec.rb +++ /dev/null @@ -1,53 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' - -module DiscourseDonations - RSpec.describe DiscourseDonations::Rewards do - let(:grp) { Fabricate(:group) } - let(:user) { Fabricate(:user) } - subject { described_class.new(user) } - - it 'adds the user to a group' do - Group.expects(:find_by_name).with(grp.name).returns(grp) - grp.expects(:add).with(user) - subject.expects(:log_group_add).once - subject.add_to_group(grp.name) - end - - it 'does not add the user to a group' do - Group.expects(:find_by_name).with(grp.name).returns(nil) - grp.expects(:add).never - subject.expects(:log_group_add).never - expect(subject.add_to_group(grp.name)).to be_falsy - end - - it 'logs the group add' do - GroupActionLogger.any_instance.expects(:log_add_user_to_group) - subject.add_to_group(grp.name) - end - - describe '.grant_badge' do - let(:badge) { Fabricate(:badge) } - - before { SiteSetting.stubs(:enable_badges).returns(true) } - - it 'grants the user a badge' do - BadgeGranter.expects(:grant).with(badge, user) - subject.grant_badge(badge.name) - end - - it 'does not grant the user a badge when the badge does not exist' do - Badge.stubs(:find_by_name).returns(nil) - BadgeGranter.expects(:grant).never - expect(subject.grant_badge('does not exist')).to be_falsy - end - - it 'does not grant the user a badge when badges are disabled' do - SiteSetting.stubs(:enable_badges).returns(false) - BadgeGranter.expects(:grant).never - subject.grant_badge(badge.name) - end - end - end -end diff --git a/spec/services/discourse_donations/stripe_spec.rb b/spec/services/discourse_donations/stripe_spec.rb deleted file mode 100644 index f49e1fe..0000000 --- a/spec/services/discourse_donations/stripe_spec.rb +++ /dev/null @@ -1,91 +0,0 @@ -# frozen_string_literal: true - -require 'rails_helper' -require_relative '../../support/dd_helper' - -module DiscourseDonations - RSpec.describe DiscourseDonations::Stripe do - before { SiteSetting.stubs(:discourse_donations_secret_key).returns('secret-key-yo') } - - let(:stripe_options) { { description: 'hi there', currency: 'AUD' } } - let(:email) { 'ray-zintoast@example.com' } - let(:customer) { stub(id: 1, email: email) } - let!(:subject) { described_class.new('secret-key-yo', stripe_options) } - - it 'sets the api key' do - expect(::Stripe.api_key).to eq 'secret-key-yo' - end - - describe 'subscribe' do - let(:params) { { email: email, stripeToken: 'stripe-token', plan: 'subscription-plan-1234', other: 'redundant param' } } - - xit 'creates a customer and a subscription' do - # todo - - ::Stripe::Customer.expects(:create).with( - email: email, - source: nil - ).returns(customer) - - ::Stripe::Customer.expects(:list) - - subject.subscribe(params) - end - end - - describe 'charge' do - let(:params) { { email: email, stripeToken: 'stripe-token', amount: '1234', other: 'redundant param' } } - - xit 'creates a customer and charges them an amount' do - ::Stripe::Customer.expects(:create).with( - email: email, - source: 'stripe-token' - ).returns(customer) - ::Stripe::Charge.expects(:create).with( - customer: customer.id, - amount: params[:amount], - description: stripe_options[:description], - currency: stripe_options[:currency] - ).returns( - paid: true, - outcome: { seller_message: 'yay!' } - ) - subject.charge(nil, params) - end - end - - describe '.successful?' do - let(:params) { { email: email, stripeToken: 'stripe-token', amount: '1234', other: 'redundant param' } } - let(:charge_options) do - { - customer: customer.id, - amount: params[:amount], - description: stripe_options[:description], - currency: stripe_options[:currency], - receipt_email: customer.email, - metadata: { discourse_cause: nil } - } - end - - before do - ::Stripe::Customer.expects(:create).returns(customer) - end - - it 'is successful' do - ::Stripe::Charge.expects(:create).with(charge_options).returns(paid: true) - - ::Stripe::Customer.expects(:list).returns(data: []) - - subject.charge(nil, params) - expect(subject).to be_successful - end - - it 'is not successful' do - ::Stripe::Charge.expects(:create).with(charge_options).returns(paid: false) - ::Stripe::Customer.expects(:list).returns(data: []) - subject.charge(nil, params) - expect(subject).not_to be_successful - end - end - end -end diff --git a/spec/support/dd_helper.rb b/spec/support/dd_helper.rb deleted file mode 100644 index 051d6fb..0000000 --- a/spec/support/dd_helper.rb +++ /dev/null @@ -1,139 +0,0 @@ -# frozen_string_literal: true - -require 'fakeweb' - -#TODO register some fixtures - -FakeWeb.register_uri(:post, 'https://api.stripe.com/v1/customers', - body: '{ - "id": "cus_AJqrL4OU1sffPl", - "object": "customer", - "account_balance": 0, - "created": 1489965018, - "currency": "aud", - "default_source": "card_19zDADEfVxQsvRbHVooMYHqg", - "delinquent": false, - "description": null, - "discount": null, - "email": "jo@example.com", - "livemode": false, - "metadata": { - }, - "shipping": null, - "sources": { - "object": "list", - "data": [ - { - "id": "card_19zDADEfVxQsvRbHVooMYHqg", - "object": "card", - "address_city": null, - "address_country": null, - "address_line1": null, - "address_line1_check": null, - "address_line2": null, - "address_state": null, - "address_zip": null, - "address_zip_check": null, - "brand": "MasterCard", - "country": "US", - "customer": "cus_AJqrL4OU1sffPl", - "cvc_check": "pass", - "dynamic_last4": null, - "exp_month": 11, - "exp_year": 2022, - "funding": "credit", - "last4": "4444", - "metadata": { - }, - "name": null, - "tokenization_method": null - } - ], - "has_more": false, - "total_count": 1, - "url": "/v1/customers/cus_AJqrL4OU1sffPl/sources" - } - }', - status: ['200', 'OK'] -) - -FakeWeb.register_uri(:post, 'https://api.stripe.com/v1/charges', - body: '{ - "id": "ch_19zDAFEfVxQsvRbHtAwsCvV0", - "object": "charge", - "amount": 100, - "amount_refunded": 0, - "application": null, - "application_fee": null, - "balance_transaction": "txn_19wkkaEfVxQsvRbH8rnq3SAK", - "captured": true, - "created": 1489965019, - "currency": "aud", - "customer": "cus_AJqrL4OU1sffPl", - "description": "Donation", - "destination": null, - "dispute": null, - "failure_code": null, - "failure_message": null, - "fraud_details": { - }, - "invoice": null, - "livemode": false, - "metadata": { - }, - "on_behalf_of": null, - "order": null, - "outcome": { - "network_status": "approved_by_network", - "reason": null, - "risk_level": "normal", - "seller_message": "Payment complete.", - "type": "authorized" - }, - "paid": true, - "receipt_email": null, - "receipt_number": null, - "refunded": false, - "refunds": { - "object": "list", - "data": [ - - ], - "has_more": false, - "total_count": 0, - "url": "/v1/charges/ch_19zDAFEfVxQsvRbHtAwsCvV0/refunds" - }, - "review": null, - "shipping": null, - "source": { - "id": "card_19zDADEfVxQsvRbHVooMYHqg", - "object": "card", - "address_city": null, - "address_country": null, - "address_line1": null, - "address_line1_check": null, - "address_line2": null, - "address_state": null, - "address_zip": null, - "address_zip_check": null, - "brand": "MasterCard", - "country": "US", - "customer": "cus_AJqrL4OU1sffPl", - "cvc_check": "pass", - "dynamic_last4": null, - "exp_month": 11, - "exp_year": 2022, - "funding": "credit", - "last4": "4444", - "metadata": { - }, - "name": null, - "tokenization_method": null - }, - "source_transfer": null, - "statement_descriptor": null, - "status": "succeeded", - "transfer_group": null - }', - status: ['200', 'OK'] -) diff --git a/test/javascripts/components/donation-form-test.es6 b/test/javascripts/components/donation-form-test.es6 deleted file mode 100644 index 3ce0b0d..0000000 --- a/test/javascripts/components/donation-form-test.es6 +++ /dev/null @@ -1,22 +0,0 @@ -import componentTest from "helpers/component-test"; - -moduleForComponent("donation-form", { integration: true }); - -componentTest("donation form has content", { - template: `{{donation-form}}`, - - beforeEach() { - this.registry.register( - "component:stripe-card", - Ember.Component.extend({ tagName: "dummy-component-tag" }) - ); - }, - - async test(assert) { - assert.ok(find("#payment-form").length, "The form renders"); - assert.ok( - find("dummy-component-tag").length, - "The stripe component renders" - ); - } -}); diff --git a/test/javascripts/components/donation-row-test.js.es6 b/test/javascripts/components/donation-row-test.js.es6 deleted file mode 100644 index 045dcc5..0000000 --- a/test/javascripts/components/donation-row-test.js.es6 +++ /dev/null @@ -1,33 +0,0 @@ -import componentTest from "helpers/component-test"; - -moduleForComponent("donation-row", { integration: true }); - -componentTest("donation-row", { - template: `{{donation-row currency=3 amount=21 period='monthly'}}`, - - test(assert) { - assert.equal(find(".donation-row-currency").text(), "3", "It has currency"); - assert.equal(find(".donation-row-amount").text(), "21", "It has an amount"); - assert.equal( - find(".donation-row-period").text(), - "monthly", - "It has a period" - ); - } -}); - -componentTest("donation-row cancels subscription", { - template: `{{donation-row currentUser=currentUser subscription=subscription}}`, - - beforeEach() { - this.set("currentUser", true); - this.set("subscription", true); - }, - - async test(assert) { - assert.ok( - find(".donation-row-subscription").length, - "It has a subscription" - ); - } -}); diff --git a/test/javascripts/components/stripe-card-test.js.es6 b/test/javascripts/components/stripe-card-test.js.es6 deleted file mode 100644 index 0dc1e47..0000000 --- a/test/javascripts/components/stripe-card-test.js.es6 +++ /dev/null @@ -1,33 +0,0 @@ -import componentTest from "helpers/component-test"; - -moduleForComponent("stripe-card", { integration: true }); - -window.Stripe = function() { - return { - elements: function() { - return { - create: function() { - return { - mount: function() {}, - card: function() {} - }; - } - }; - } - }; -}; - -componentTest("stripe card", { - template: `{{stripe-card donateAmounts=donateAmounts}}`, - - skip: true, - - beforeEach() { - Discourse.SiteSettings.discourse_donations_types = ""; - this.set("donateAmounts", [{ value: 2 }]); - }, - - test(assert) { - assert.ok(true); - } -});