summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.ruby-version3
-rw-r--r--Gemfile3
-rw-r--r--Gemfile.lock11
-rw-r--r--app/assets/stylesheets/leap.scss3
-rw-r--r--app/controllers/sessions_controller.rb9
-rw-r--r--app/controllers/users_controller.rb6
-rw-r--r--app/models/anonymous_user.rb5
-rw-r--r--app/models/invite_code.rb1
-rw-r--r--app/models/invite_code_validator.rb7
-rw-r--r--app/models/user.rb7
-rw-r--r--app/views/layouts/_footer.html.haml1
-rw-r--r--app/views/layouts/_navigation.html.haml6
-rw-r--r--app/views/users/_user.html.haml34
-rw-r--r--app/views/users/index.html.haml3
-rw-r--r--config/locales/cs.yml1
-rw-r--r--config/locales/de.yml1
-rw-r--r--config/locales/en/footer.en.yml1
-rw-r--r--config/locales/en/generic.en.yml6
-rw-r--r--config/locales/en/users.en.yml11
-rw-r--r--config/locales/es.yml40
-rw-r--r--config/locales/fr.yml1
-rw-r--r--config/locales/it.yml3
-rw-r--r--config/locales/pt.yml1
-rw-r--r--engines/billing/README.md61
-rw-r--r--engines/billing/app/controllers/billing_admin_controller.rb3
-rw-r--r--engines/billing/app/controllers/billing_base_controller.rb3
-rw-r--r--engines/billing/app/controllers/credit_card_info_controller.rb35
-rw-r--r--engines/billing/app/controllers/customer_controller.rb64
-rw-r--r--engines/billing/app/controllers/payments_controller.rb60
-rw-r--r--engines/billing/app/controllers/subscriptions_controller.rb97
-rw-r--r--engines/billing/app/helpers/billing_helper.rb6
-rw-r--r--engines/billing/app/helpers/braintree_form_helper.rb1
-rw-r--r--engines/billing/app/helpers/braintree_helper.rb2
-rw-r--r--engines/billing/app/models/customer.rb58
-rw-r--r--engines/billing/app/views/billing_admin/show.html.haml6
-rw-r--r--engines/billing/app/views/credit_card_info/confirm.html.haml5
-rw-r--r--engines/billing/app/views/credit_card_info/edit.html.haml16
-rw-r--r--engines/billing/app/views/customer/_customer_data.html.haml16
-rw-r--r--engines/billing/app/views/customer/_transaction.html.haml0
-rw-r--r--engines/billing/app/views/customer/confirm.html.haml14
-rw-r--r--engines/billing/app/views/customer/edit.html.haml23
-rw-r--r--engines/billing/app/views/customer/new.html.haml24
-rw-r--r--engines/billing/app/views/customer/show.html.haml27
-rw-r--r--engines/billing/app/views/payments/_customer_form.html.haml10
-rw-r--r--engines/billing/app/views/payments/_non_customer_fields.html.haml16
-rw-r--r--engines/billing/app/views/payments/_transaction_details.html.haml15
-rw-r--r--engines/billing/app/views/payments/confirm.html.haml26
-rw-r--r--engines/billing/app/views/payments/index.html.haml3
-rw-r--r--engines/billing/app/views/payments/new.html.haml37
-rw-r--r--engines/billing/app/views/subscriptions/_customer_form.html.haml10
-rw-r--r--engines/billing/app/views/subscriptions/_subscription_details.html.haml3
-rw-r--r--engines/billing/app/views/subscriptions/create.html.haml9
-rw-r--r--engines/billing/app/views/subscriptions/destroy.html.haml7
-rw-r--r--engines/billing/app/views/subscriptions/index.html.haml37
-rw-r--r--engines/billing/app/views/subscriptions/new.html.haml15
-rw-r--r--engines/billing/app/views/subscriptions/show.html.haml26
-rw-r--r--engines/billing/config/locales/en.yml30
-rw-r--r--engines/billing/config/routes.rb34
-rw-r--r--engines/billing/questions.md22
-rw-r--r--engines/billing/test/factories.rb3
-rw-r--r--engines/billing/test/functional/customer_controller_test.rb124
-rw-r--r--engines/billing/test/functional/customers_controller_test.rb62
-rw-r--r--engines/billing/test/functional/payments_controller_test.rb69
-rw-r--r--engines/billing/test/functional/subscriptions_controller_test.rb81
-rw-r--r--engines/billing/test/test_helper.rb1
-rw-r--r--engines/billing/test/unit/customer_test.rb38
-rw-r--r--engines/billing/test/unit/customer_with_payment_info_test.rb40
-rw-r--r--lib/tasks/invite_code.rake9
-rw-r--r--test/functional/users_controller_test.rb1
-rw-r--r--test/support/browser_integration_test.rb1
-rw-r--r--test/unit/invite_code_test.rb42
-rw-r--r--test/unit/invite_code_validator_test.rb56
72 files changed, 660 insertions, 852 deletions
diff --git a/.ruby-version b/.ruby-version
index f3a9c9a..54b50ff 100644
--- a/.ruby-version
+++ b/.ruby-version
@@ -1 +1,2 @@
-1.9.3-p194
+2.1.5
+
diff --git a/Gemfile b/Gemfile
index fdd63f5..62f8519 100644
--- a/Gemfile
+++ b/Gemfile
@@ -45,7 +45,7 @@ group :production do
# this must not be included in development mode, or js
# will get included twice.
gem 'therubyracer', "~> 0.12.2", :platforms => :ruby
- # ^^ See https://github.com/sstephenson/execjs#readme
+ # ^^ See https://github.com/sstephenson/execjs#readme
# for list of supported runtimes.
end
@@ -78,6 +78,7 @@ end
group :test, :development do
gem 'thin'
gem 'i18n-missing_translations'
+ gem 'pry'
end
group :production do
diff --git a/Gemfile.lock b/Gemfile.lock
index 3c9a211..da4df87 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -63,7 +63,7 @@ GEM
debug_inspector (>= 0.0.1)
bootstrap-sass (2.3.2.2)
sass (~> 3.2)
- braintree (2.38.0)
+ braintree (2.48.1)
builder (>= 2.0.0)
builder (3.0.4)
capybara (2.4.4)
@@ -121,7 +121,7 @@ GEM
factory_girl_rails (4.5.0)
factory_girl (~> 4.5.0)
railties (>= 3.0.0)
- fake_braintree (0.6.0)
+ fake_braintree (0.7.0)
activesupport
braintree (~> 2.32)
capybara (>= 2.0.3)
@@ -158,6 +158,7 @@ GEM
mime-types (~> 1.16)
treetop (~> 1.4.8)
metaclass (0.0.4)
+ method_source (0.8.2)
mime-types (1.25.1)
mini_portile (0.6.1)
minitest-stub-const (0.2)
@@ -175,6 +176,10 @@ GEM
multi_json (~> 1.0)
websocket-driver (>= 0.2.0)
polyglot (0.3.5)
+ pry (0.10.1)
+ coderay (~> 1.1.0)
+ method_source (~> 0.8.1)
+ slop (~> 3.4)
quiet_assets (1.0.3)
railties (>= 3.1, < 5.0)
rack (1.4.5)
@@ -226,6 +231,7 @@ GEM
rack (~> 1.4)
rack-protection (~> 1.4)
tilt (~> 1.3, >= 1.3.4)
+ slop (3.6.0)
sprockets (2.2.3)
hike (~> 1.2)
multi_json (~> 1.0)
@@ -293,6 +299,7 @@ DEPENDENCIES
mocha (~> 0.13.0)
phantomjs-binaries
poltergeist
+ pry
quiet_assets
rails (~> 3.2.21)
rails-i18n
diff --git a/app/assets/stylesheets/leap.scss b/app/assets/stylesheets/leap.scss
index dddcc1a..9fe2195 100644
--- a/app/assets/stylesheets/leap.scss
+++ b/app/assets/stylesheets/leap.scss
@@ -307,3 +307,6 @@ html, body {
margin: 0 2px;
}
}
+.modal-footer form{
+ margin: 0;
+}
diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb
index 66eba40..34d4f53 100644
--- a/app/controllers/sessions_controller.rb
+++ b/app/controllers/sessions_controller.rb
@@ -32,4 +32,13 @@ class SessionsController < ApplicationController
# throw :warden, response.finish
#end
+ Warden::Manager.after_set_user do |user, auth, opts|
+ scope = opts[:scope]
+ unless user.enabled?
+ auth.logout(scope)
+ throw(:warden, scope: scope, reason: "User not active")
+ end
+ end
+
+
end
diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb
index 3943afc..446b726 100644
--- a/app/controllers/users_controller.rb
+++ b/app/controllers/users_controller.rb
@@ -49,13 +49,15 @@ class UsersController < ApplicationController
def deactivate
@user.enabled = false
@user.save
- respond_with @user
+ flash[:notice] = I18n.t("actions.user_disabled_message", username: @user.username)
+ redirect_to :back
end
def enable
@user.enabled = true
@user.save
- respond_with @user
+ flash[:notice] = I18n.t("actions.user_enabled_message", username: @user.username)
+ redirect_to :back
end
def destroy
diff --git a/app/models/anonymous_user.rb b/app/models/anonymous_user.rb
index 0c1f540..73e95e5 100644
--- a/app/models/anonymous_user.rb
+++ b/app/models/anonymous_user.rb
@@ -12,6 +12,10 @@ class AnonymousUser < Object
def id
nil
end
+
+ def has_payment_info?
+ false
+ end
def email
nil
@@ -32,4 +36,5 @@ class AnonymousUser < Object
def is_anonymous?
true
end
+
end
diff --git a/app/models/invite_code.rb b/app/models/invite_code.rb
index 6fcc427..5666a4f 100644
--- a/app/models/invite_code.rb
+++ b/app/models/invite_code.rb
@@ -5,6 +5,7 @@ class InviteCode < CouchRest::Model::Base
use_database 'invite_codes'
property :invite_code, String, :read_only => true
property :invite_count, Integer, :default => 0, :accessible => true
+ property :max_uses, Integer, :default => 1
timestamps!
diff --git a/app/models/invite_code_validator.rb b/app/models/invite_code_validator.rb
index f96ca4a..676e4fa 100644
--- a/app/models/invite_code_validator.rb
+++ b/app/models/invite_code_validator.rb
@@ -1,4 +1,5 @@
class InviteCodeValidator < ActiveModel::Validator
+
def validate(user)
user_invite_code = InviteCode.find_by_invite_code user.invite_code
@@ -6,7 +7,7 @@ class InviteCodeValidator < ActiveModel::Validator
if not_existent?(user_invite_code)
add_error_to_user("This is not a valid code", user)
- elsif count_greater_than_zero?(user_invite_code)
+ elsif has_no_uses_left?(user_invite_code)
add_error_to_user("This code has already been used", user)
end
end
@@ -16,8 +17,8 @@ class InviteCodeValidator < ActiveModel::Validator
code == nil
end
- def count_greater_than_zero?(code)
- code.invite_count > 0
+ def has_no_uses_left?(code)
+ code.invite_count >= code.max_uses
end
def add_error_to_user(error, user)
diff --git a/app/models/user.rb b/app/models/user.rb
index 3daee0f..4bb1e79 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -9,6 +9,9 @@ class User < CouchRest::Model::Base
property :contact_email, String, :accessible => true
property :contact_email_key, String, :accessible => true
property :invite_code, String, :accessible => true
+ property :braintree_customer_id, Integer, :accessible => true
+ property :subscription_id, String, :accessible => true
+
property :enabled, TrueClass, :default => true
# these will be null by default but we shouldn't ever pull them directly, but only via the methods that will return the full ServiceLevel
@@ -177,6 +180,10 @@ class User < CouchRest::Model::Base
end
+ def has_payment_info?
+ braintree_customer_id
+ end
+
protected
##
diff --git a/app/views/layouts/_footer.html.haml b/app/views/layouts/_footer.html.haml
index fa523d4..ff84dff 100644
--- a/app/views/layouts/_footer.html.haml
+++ b/app/views/layouts/_footer.html.haml
@@ -15,3 +15,4 @@
= link_to icon('comment') + t(:contact), contact_path
- if paid_service_level?
= link_to icon('shopping-cart') + t(:pricing), pricing_path
+ = link_to icon('barcode') + t(:Donations), new_payment_path if APP_CONFIG[:payment].present?
diff --git a/app/views/layouts/_navigation.html.haml b/app/views/layouts/_navigation.html.haml
index dccba0c..63a361a 100644
--- a/app/views/layouts/_navigation.html.haml
+++ b/app/views/layouts/_navigation.html.haml
@@ -7,6 +7,8 @@
= link_to_navigation ".tickets", auto_tickets_path,
active: controller?(:tickets)
- if APP_CONFIG[:billing]
- = link_to_navigation :billing_settings, billing_top_link(@user),
- active: controller?(:customer, :payments, :subscriptions, :credit_card_info)
+ = link_to_navigation :donations, new_payment_path,
+ active: (controller?(:donations) and action?(:new))
+ = link_to_navigation :subscriptions, billing_top_link(@braintree_customer_id),
+ active: controller?(:subscriptions)
= link_to_navigation :logout, logout_path, method: :delete
diff --git a/app/views/users/_user.html.haml b/app/views/users/_user.html.haml
index 583d22f..1cabcf5 100644
--- a/app/views/users/_user.html.haml
+++ b/app/views/users/_user.html.haml
@@ -1,4 +1,32 @@
%tr
- %td= link_to user.login, user
- %td= l(user.created_at, :format => :short)
- %td= l(user.updated_at, :format => :short)
+ %td
+ = link_to user.username, user
+ %td
+ = user.created_at.strftime("%d %b, %Y, %H:%M")
+ %td
+ = user.updated_at.strftime("%d %b, %Y, %H:%M")
+ %td
+ - if user.enabled
+ %button.btn.btn-default{:"data-toggle" => "modal", :"data-target" => "#user-form-#{user.id}", :type => "button"}
+ = t("actions.disable_user")
+ .modal.fade.hide{:id => "user-form-#{user.id}"}
+ .modal-dialog
+ .modal-content
+ .modal-header
+ %button.close{:"data-dismiss" => "modal"} ×
+ = t("actions.confirm_user_deactivation", username: user.username)
+ .modal-footer
+ = form_tag deactivate_user_path(user) do
+ %input.btn.btn-default.btn-danger{:type => "submit", :value => "#{t("actions.disable_user")}"}
+ - else
+ %button.btn.btn-default{:"data-toggle" => "modal", :"data-target" => "#user-form-#{user.id}", :type => "button"}
+ = t("actions.enable_user")
+ .modal.fade.hide{:id => "user-form-#{user.id}"}
+ .modal-dialog
+ .modal-content
+ .modal-header
+ %button.close{:"data-dismiss" => "modal"} ×
+ = t("actions.confirm_user_activation", username: user.username)
+ .modal-footer
+ = form_tag enable_user_path(user) do
+ %input.btn.btn-default.btn-danger{:type => "submit", :value => "#{t("actions.enable_user")}"}
diff --git a/app/views/users/index.html.haml b/app/views/users/index.html.haml
index 3ed8835..e1136d8 100644
--- a/app/views/users/index.html.haml
+++ b/app/views/users/index.html.haml
@@ -1,4 +1,5 @@
- @show_navigation = false
= search :users
-= table @users, %w(username, created, updated)
+= table @users, %w(username, created, updated, actions.toggle_user)
+
diff --git a/config/locales/cs.yml b/config/locales/cs.yml
index 08e1321..32b0f03 100644
--- a/config/locales/cs.yml
+++ b/config/locales/cs.yml
@@ -23,6 +23,7 @@ cs:
pricing: Stanovéní cen
about: O nás
contact: Kontakt
+ donations: Donations
signup: Registrovat
login: Přihlásit
logout: Odhlásit
diff --git a/config/locales/de.yml b/config/locales/de.yml
index 76ecc1d..1f78b88 100644
--- a/config/locales/de.yml
+++ b/config/locales/de.yml
@@ -23,6 +23,7 @@ de:
pricing: Kosten
about: über uns
contact: Kontakt
+ donations: Spende
signup: Registrieren
login: Anmelden
logout: Abmelden
diff --git a/config/locales/en/footer.en.yml b/config/locales/en/footer.en.yml
index 65f8ab2..7569070 100644
--- a/config/locales/en/footer.en.yml
+++ b/config/locales/en/footer.en.yml
@@ -5,3 +5,4 @@ en:
pricing: Pricing
about: About Us
contact: Contact
+ donations: Donations
diff --git a/config/locales/en/generic.en.yml b/config/locales/en/generic.en.yml
index f268bff..be62a40 100644
--- a/config/locales/en/generic.en.yml
+++ b/config/locales/en/generic.en.yml
@@ -2,9 +2,9 @@ en:
signup: "Sign Up"
login: "Log In"
logout: "Log Out"
-
+
cancel: "Cancel"
-
+
created: "Created"
created_by_on: "Created by %{user} on %{time}"
updated: "Updated"
@@ -25,3 +25,5 @@ en:
create_thing: "Create %{thing}"
overview: "Overview"
+ choose: "Choose"
+ enter_amount: "Enter amount"
diff --git a/config/locales/en/users.en.yml b/config/locales/en/users.en.yml
index 4c6bbc0..f2e60af 100644
--- a/config/locales/en/users.en.yml
+++ b/config/locales/en/users.en.yml
@@ -5,6 +5,8 @@ en:
identities: "Usernames"
tickets: "Tickets"
user_control_panel: "user control panel"
+ donations: "Donations"
+ subscriptions: "Subscriptions"
account_settings: "Account Settings"
username: "Username"
password: "Password"
@@ -57,6 +59,14 @@ en:
tickets: "Create and check support tickets."
email: "Modify email settings."
account: "Destroy your account."
+ actions:
+ toggle_user: "Enable / Disable User"
+ enable_user: "Enable User"
+ disable_user: "Disable User"
+ confirm_user_deactivation: "Are you sure you want to deactive user with username '%{username}'?"
+ confirm_user_activation: "Are you sure you want to activate user with username '%{username}'?"
+ user_disabled_message: "User with username '%{username}' has been disabled."
+ user_enabled_message: "User with username '%{username}' has been enabled."
#
# rails
@@ -78,4 +88,3 @@ en:
placeholders:
user:
email_forward: "my_other_email@domain.net"
-
diff --git a/config/locales/es.yml b/config/locales/es.yml
index 2a36b6e..1742be5 100644
--- a/config/locales/es.yml
+++ b/config/locales/es.yml
@@ -23,6 +23,8 @@ es:
pricing: Tarifas
about: Sobre nosotros
contact: Contacto
+ donations: "Donaciones"
+ subscriptions: "Subscripciones"
signup: Registrarse
login: Iniciar sesión
logout: Cerrar sesión
@@ -110,6 +112,14 @@ es:
user:
one: Usuario
other: '%{count} usuarios'
+ actions:
+ toggle_user: "Activar / Desactivar Usuario"
+ enable_user: "Activar Usuario"
+ disable_user: "Desactivar Usuario"
+ confirm_user_deactivation: "Estás seguro que quieres desactivar al usuario con nombre '%{username}'?"
+ confirm_user_activation: "Estás seguro que quieres activar al usuario con nombre '%{username}'?"
+ user_disabled_message: "Usuario con nombre '%{username}' ha sido desactivado."
+ user_enabled_message: "Usuario con nombre '%{username}' ha sido activado."
create_new_customer: Crear un nuevo Cliente de Braintree
must_create_customer: Tiene que almacenar un cliente en Braintree antes de suscribirse a un plan
subscribe: Suscribirse
@@ -120,6 +130,11 @@ es:
description: Descripción
cost: Coste
free: Gratuito
+ new_donation: "Nueva Donación"
+ donation_info: "Por favor, llena los detalles de tu donación (esta no será cargada a tu cuenta, si tienes una):"
+ donation_amount: "Ingrese el monto"
+ donate: "Donar"
+ personal_info: "Por favor, ingresa tu información personal"
support_tickets: Soporte
email_notice_text: Ha sido añadido un nuevo comentario a esta instancia de ayuda.
email_no_reply_text: No responda a este correo.
@@ -193,3 +208,28 @@ es:
hints:
ticket:
email: Proporcione una dirección de correo electrónico para que se le notifique cuando se actualice esta instancia.
+
+#Payment (except for some donation, which is in user, mostly)
+#donations
+ donation_sucess: "¡Felicitaciones! Tu donación ha sido exitosa"
+ donation_not_sucess: "Algo malo sucedió al procesar tu donación. Por favor, intenta de nuevo"
+#subscriptions
+ subscription_sucess: "¡Felicitaciones! Ahora estás suscrito"
+ subscription_not_sucess: "Algo malo sucedió al procesar tu subscripción. Por favor, intenta de nuevo"
+ unsubscription_sucess: "Tu subscripción ha sido cancelada"
+ unsubscription_not_sucess: "Algo malo sucedió. Por favor, intenta de nuevo"
+ subscriptions: "Subscripciones"
+ lastestsubs: "Últimas Subscripciones:"
+ date: "Fecha"
+ unsubscribe_from: "Cancelar la subscrición a"
+ no_subs: "Ninguna Sucripción"
+ choose_subs: "Elige una Subscripción:"
+ choose_button: "Elegir"
+ new_subs: "Nueva Subscripción"
+ #common
+ first_name: "Nombre"
+ last_name: "Apellido"
+ company: "Compañía"
+ phone: "Teléfono"
+ personal_info: "Por favor, ingresa tu información personal:"
+ pay_details: "Por favor, ingresa tus detalles de pago"
diff --git a/config/locales/fr.yml b/config/locales/fr.yml
index fac4c32..2e0043b 100644
--- a/config/locales/fr.yml
+++ b/config/locales/fr.yml
@@ -24,6 +24,7 @@ fr:
about: À propos de nous
contact: Contact
signup: S'enregistrer
+ donations: Donation
login: Connexion
logout: Déconnexion
cancel: Annuler
diff --git a/config/locales/it.yml b/config/locales/it.yml
index 9e318fc..e6d746c 100644
--- a/config/locales/it.yml
+++ b/config/locales/it.yml
@@ -23,6 +23,7 @@ it:
pricing: Prezzario
about: A proposito di
contact: Contatti
+ donations: Donazioni
signup: Registrati
login: Accedi
logout: Log Out
@@ -32,7 +33,7 @@ it:
updated: Aggiornata
none: Nessuno
unknown: Sconosciuto
- admin: Admin
+ admin: Administratore
anonymous: Anonimo
save: Salva
add: Aggiungi
diff --git a/config/locales/pt.yml b/config/locales/pt.yml
index 256653e..3cf8820 100644
--- a/config/locales/pt.yml
+++ b/config/locales/pt.yml
@@ -23,6 +23,7 @@ pt:
pricing: Preço
about: Sobre Nós
contact: Contato
+ donations: Doações
signup: Cadastrar
login: Entrar
logout: Sair
diff --git a/engines/billing/README.md b/engines/billing/README.md
index 3ef6153..d1e4d60 100644
--- a/engines/billing/README.md
+++ b/engines/billing/README.md
@@ -1,13 +1,14 @@
Billing Engine
====================
-Currently, this engine support billing via Braintree. More backends to come later.
+Currently, this engine support billing via Braintree. Braintree provides three
+options for payments: Pay Pal, Bitcoin and Credit Cards.
Configuration
----------------------------------
Start with a sandbox account, which you can get here: https://www.braintreepayments.com/get-started
-
+:q
Once you have registered for the sandbox, logging in will show you three important variables you will need to configure:
* merchantId
@@ -46,4 +47,58 @@ You also will want to add a Plan to your Sandbox. Within the Braintree Sandbox,
Here are credit cared numbers to try in the Sandbox:
-https://www.braintreepayments.com/docs/ruby/reference/sandbox \ No newline at end of file
+https://www.braintreepayments.com/docs/ruby/reference/sandbox
+
+How does it works
+--------------------------------
+
+The new implementation of Braintree uses its new API called 'v.zero'. It
+consists of complementary client and server SDKs:
+
+1. The JS client SDK enables you to collect payment method (e.g. credit card,
+PayPal) details on your website
+2. The server SDKs manage all requests to the Braintree gateway.
+They represent the Client-side Encryption solution that combines Braintree’s
+traditional Server-to-Server (S2S) approach and Transparent Redirect (TR)
+solution. It can be described as following:
+
+1. The application server generates a client token for each customer (data blob)
+using the Ruby SDK for the frontend that initializes the JavaScript SDK
+using that client token.
+2. The Braintree-provided JavaScript library encrypts sensitive data using the
+public key and communicates with Braintree before the form is ever posted to
+your server.
+3. Once the data reaches Braintree’s servers, it is decrypted using the keypair’s
+private key, then returns a payment method nonce to your client code. Your code
+relays this nonce to your server.
+4. Your server-side code provides the payment method nonce to the Ruby SDK to
+perform Braintree operations (in this case either donations or subcriptions).
+
+What is included
+--------------------------------
+
+Current implementation with 'v.zero' provides:
+1. Donations and subscriptions.
+
+2. Three payment methods: Bitcoin, Pay Pal and Credit Cards.
+
+3. Creation and storage of customers (stored in 'The Vault')
+
+4. Ability to donate as anonymous user.
+
+5. Subscription or unsubscriptions to plans.
+
+6. Recurring billing.
+
+7. Storing Multiple Credit Cards.
+
+Bitcoin
+--------------------------------
+
+In order for Bitcoin to work, you need to write Braintree's community and ask
+them to allow that payment method. Bitcoin is implemented via Coinbase.
+
+Learn about this here:
+https://developers.braintreepayments.com/javascript+ruby/guides/coinbase/configuration
+
+Contact: coinbase@braintreepayments.com
diff --git a/engines/billing/app/controllers/billing_admin_controller.rb b/engines/billing/app/controllers/billing_admin_controller.rb
index e11d4ee..23740d6 100644
--- a/engines/billing/app/controllers/billing_admin_controller.rb
+++ b/engines/billing/app/controllers/billing_admin_controller.rb
@@ -1,6 +1,9 @@
class BillingAdminController < BillingBaseController
before_filter :require_admin
+ #not sure if this controller is still needed. Admin can easly acess
+ #braintree's dashboard and check subscriptions. Don't know if everything
+ #should be 'self contained' in web_app""
def show
br_atleast_90_days = Braintree::Subscription.search do |search|
diff --git a/engines/billing/app/controllers/billing_base_controller.rb b/engines/billing/app/controllers/billing_base_controller.rb
index 0453677..c343938 100644
--- a/engines/billing/app/controllers/billing_base_controller.rb
+++ b/engines/billing/app/controllers/billing_base_controller.rb
@@ -13,6 +13,9 @@ class BillingBaseController < ApplicationController
elsif params[:id]
@user = User.find(params[:id])
else
+ #not sure if this is still needed. Donations work with either customer or
+ #anonymous_user. Subscriptions work with customer. Customer belongs to
+ #user.
# TODO
# hacky, what are cases where @user hasn't yet been set? certainly some cases with subscriptions and payments
@user = current_user
diff --git a/engines/billing/app/controllers/credit_card_info_controller.rb b/engines/billing/app/controllers/credit_card_info_controller.rb
deleted file mode 100644
index fbaa6f1..0000000
--- a/engines/billing/app/controllers/credit_card_info_controller.rb
+++ /dev/null
@@ -1,35 +0,0 @@
-class CreditCardInfoController < ApplicationController
- before_filter :require_login, :set_user
-
- def edit
- @credit_card = Braintree::CreditCard.find(params[:id])
- customer = Customer.find_by_user_id(@user.id)
- if customer and customer.braintree_customer_id == @credit_card.customer_id
- @tr_data = Braintree::TransparentRedirect.
- update_credit_card_data(:redirect_url => confirm_credit_card_info_url,
- :payment_method_token => @credit_card.token)
- else
- access_denied
- end
-
- end
-
- def confirm
- @result = Braintree::TransparentRedirect.confirm(request.query_string)
- if @result.success?
- render :action => "confirm"
- else
- @credit_card = Braintree::CreditCard.find(@result.params[:payment_method_token])
- render :action => "edit"
- end
- end
-
-
- private
-
- def set_user
- # this assumes anybody, even an admin, will not access for another user.
- @user = current_user
- end
-
-end
diff --git a/engines/billing/app/controllers/customer_controller.rb b/engines/billing/app/controllers/customer_controller.rb
deleted file mode 100644
index 6cbcb44..0000000
--- a/engines/billing/app/controllers/customer_controller.rb
+++ /dev/null
@@ -1,64 +0,0 @@
-class CustomerController < BillingBaseController
- before_filter :require_login, :fetch_customer
-
- def show
- if @customer
- @customer.with_braintree_data!
- @default_cc = @customer.default_credit_card
- @active_subscription = @customer.subscriptions
- @transactions = @customer.braintree_customer.transactions
- end
- end
-
- def new
- if @customer.has_payment_info?
- redirect_to edit_customer_path(@user), :notice => 'Here is your saved customer data'
- else
- fetch_new_transparent_redirect_data
- end
- end
-
- def edit
- fetch_edit_transparent_redirect_data
- end
-
- def confirm
- @result = Braintree::TransparentRedirect.confirm(request.query_string)
- if @result.success?
- @customer.braintree_customer = @result.customer
- @customer.save
- render :action => "confirm"
- elsif @customer.has_payment_info?
- fetch_edit_transparent_redirect_data
- render :action => "edit"
- else
- fetch_new_transparent_redirect_data
- render :action => "new"
- end
- end
-
- protected
-
- def fetch_new_transparent_redirect_data
- access_denied unless @user == current_user # admins cannot do this for others
- @tr_data = Braintree::TransparentRedirect.
- create_customer_data(:redirect_url => confirm_customer_url)
- end
-
- def fetch_edit_transparent_redirect_data
- access_denied unless @user == current_user # admins cannot do this for others
- @customer.with_braintree_data!
- @default_cc = @customer.default_credit_card
- @tr_data = Braintree::TransparentRedirect.
- update_customer_data(:redirect_url => confirm_customer_url,
- :customer_id => @customer.braintree_customer_id) ##??
- end
-
- def fetch_customer
- @customer = Customer.find_by_user_id(@user.id)
- if @user == current_user
- @customer ||= Customer.new(user: @user)
- end
- access_denied unless (@customer and (@customer.user == current_user)) or admin?
- end
-end
diff --git a/engines/billing/app/controllers/payments_controller.rb b/engines/billing/app/controllers/payments_controller.rb
index fce6570..871f1b4 100644
--- a/engines/billing/app/controllers/payments_controller.rb
+++ b/engines/billing/app/controllers/payments_controller.rb
@@ -2,19 +2,14 @@ class PaymentsController < BillingBaseController
before_filter :require_login, :only => [:index]
def new
- fetch_transparent_redirect
- end
-
- def confirm
- @result = Braintree::TransparentRedirect.confirm(request.query_string)
- if @result.success?
- render :action => "confirm"
+ if current_user.has_payment_info?
+ @client_token = Braintree::ClientToken.generate(customer_id: current_user.braintree_customer_id)
else
- fetch_transparent_redirect
- render :action => "new"
- end
+ @client_token = Braintree::ClientToken.generate
+ end
end
+# not sure if this should be kept
def index
access_denied unless admin? or (@user == current_user)
customer = Customer.find_by_user_id(@user.id)
@@ -23,12 +18,49 @@ class PaymentsController < BillingBaseController
@transactions = braintree_data.transactions
end
- protected
+ def confirm
+ make_transaction
+ if @result.success?
+ flash[:success] = I18n.t(:donation_sucess)
+ else
+ flash[:error] = I18n.t(:donation_not_sucess)
+ end
+ redirect_to action: :new, locale: params[:locale]
+ end
- def fetch_transparent_redirect
- @tr_data = Braintree::TransparentRedirect.transaction_data redirect_url: confirm_payment_url,
- transaction: { type: "sale", options: {submit_for_settlement: true } }
+ private
+ def make_transaction
+ if current_user.has_payment_info?
+ transact_without_user_info
+ elsif current_user.is_anonymous?
+ transact_without_user_info
+ else
+ transact_with_user_info
+ end
end
+ def transact_with_user_info
+ @result = Braintree::Transaction.sale(
+ amount: params[:amount],
+ payment_method_nonce: params[:payment_method_nonce],
+ customer: {
+ first_name: params[:first_name],
+ last_name: params[:last_name],
+ company: params[:company],
+ email: current_user.email,
+ phone: params[:phone]
+ },
+ options: {
+ store_in_vault: true
+ })
+ current_user.update_attributes(braintree_customer_id: @result.transaction.customer_details.id) if @result.success?
+ end
+
+ def transact_without_user_info
+ @result = Braintree::Transaction.sale(
+ amount: params[:amount],
+ payment_method_nonce: params[:payment_method_nonce],
+ )
+ end
end
diff --git a/engines/billing/app/controllers/subscriptions_controller.rb b/engines/billing/app/controllers/subscriptions_controller.rb
index f066b3c..1d29cac 100644
--- a/engines/billing/app/controllers/subscriptions_controller.rb
+++ b/engines/billing/app/controllers/subscriptions_controller.rb
@@ -1,63 +1,72 @@
class SubscriptionsController < BillingBaseController
before_filter :require_login
- before_filter :fetch_subscription, :only => [:show, :destroy]
- before_filter :confirm_cancel_subscription, :only => [:destroy]
- before_filter :confirm_self_or_admin, :only => [:index]
- before_filter :confirm_no_pending_active_pastdue_subscription, :only => [:new, :create]
- # for now, admins cannot create or destroy subscriptions for others:
- before_filter :confirm_self, :only => [:new, :create]
+ before_filter :assign_user
+ before_filter :confirm_cancel_subscription, only: [:destroy]
+ before_filter :generate_client_token, only: [:show]
+ before_filter :get_braintree_customer, only: [:subscribe]
- def new
- # don't show link to subscribe if they are already subscribed?
- credit_card = @customer.default_credit_card #safe to assume default?
- @payment_method_token = credit_card.token
- @plans = Braintree::Plan.all
+ def index
+ if @user.subscription_id
+ @subscription = Braintree::Subscription.find @user.subscription_id
+ @plan = Braintree::Plan.all.select{ |plan| plan.id == @subscription.plan_id }.first
+ else
+ @subscriptions = Braintree::Plan.all
+ end
end
- # show has no content, so not needed at this point.
-
- def create
- @result = Braintree::Subscription.create( :payment_method_token => params[:payment_method_token], :plan_id => params[:plan_id] )
- #if you want to test pastdue, can add :price => '2001', :trial_period => true,:trial_duration => 1,:trial_duration_unit => "day" and then wait a day
+ def show
+ @plan = Braintree::Plan.all.select{ |plan| plan.id == params[:id] }.first
end
- def destroy
- @result = Braintree::Subscription.cancel params[:id]
+ def subscribe
+ @result = Braintree::Subscription.create(payment_method_token: @customer.payment_methods.first.token,
+ plan_id: params[:id])
+ if @result.success?
+ @user.update_attributes subscription_id: @result.subscription.id
+ flash[:success] = I18n.t(:subscription_sucess)
+ else
+ flash[:error] = I18n.t(:subscription_not_sucess)
+ end
+ redirect_to action: :index, locale: params[:locale]
end
- def index
- customer = Customer.find_by_user_id(@user.id)
- @subscriptions = customer.subscriptions(nil, false)
+ def unsubscribe
+ @result = Braintree::Subscription.cancel(@user.subscription_id)
+ if @result.success?
+ @user.update_attributes subscription_id: nil
+ flash[:success] = I18n.t(:unsubscription_sucess)
+ else
+ flash[:error] = I18n.t(:unsubscription_not_sucess)
+ end
+ redirect_to action: :index, locale: params[:locale]
end
private
-
- def fetch_subscription
- @subscription = Braintree::Subscription.find params[:id]
- @credit_card = Braintree::CreditCard.find @subscription.payment_method_token
- @subscription_customer_id = @credit_card.customer_id
- current_user_customer = Customer.find_by_user_id(current_user.id)
- access_denied unless admin? or (current_user_customer and current_user_customer.braintree_customer_id == @subscription_customer_id)
-
- end
-
- def confirm_cancel_subscription
- access_denied unless view_context.allow_cancel_subscription(@subscription)
+ def assign_user
+ @user = current_user
end
- def confirm_no_pending_active_pastdue_subscription
- @customer = Customer.find_by_user_id(@user.id)
- if subscription = @customer.subscriptions # will return pending, active or pastdue subscription, if it exists
- redirect_to user_subscription_path(@user, subscription.id), :notice => 'You already have a subscription'
+ def generate_client_token
+ if current_user.braintree_customer_id
+ @client_token = Braintree::ClientToken.generate(customer_id: current_user.braintree_customer_id)
+ else
+ @client_token = Braintree::ClientToken.generate
end
end
- def confirm_self
- @user == current_user
- end
-
- def confirm_self_or_admin
- access_denied unless confirm_self or admin?
+ def get_braintree_customer
+ if current_user.braintree_customer_id
+ @customer = Braintree::Customer.find(current_user.braintree_customer_id)
+ else
+ @customer = Braintree::Customer.create(
+ payment_method_nonce: params[:payment_method_nonce],
+ first_name: params[:first_name],
+ last_name: params[:last_name],
+ company: params[:company],
+ email: current_user.email,
+ phone: params[:phone]
+ ).customer
+ current_user.update_attributes braintree_customer_id: @customer.id
+ end
end
-
end
diff --git a/engines/billing/app/helpers/billing_helper.rb b/engines/billing/app/helpers/billing_helper.rb
index b9e5e2e..6d1df5a 100644
--- a/engines/billing/app/helpers/billing_helper.rb
+++ b/engines/billing/app/helpers/billing_helper.rb
@@ -1,5 +1,6 @@
module BillingHelper
+ #deprecated.. erase?
def braintree_form_for(object, options = {}, &block)
options.reverse_merge! params: @result && @result.params[object],
errors: @result && @result.errors.for(object),
@@ -14,10 +15,11 @@ module BillingHelper
if (admin? and user == current_user)
billing_admin_path
else
- show_or_new_customer_link(user)
+ subscriptions_path
end
end
+ #deprecated.. erase?
def show_or_new_customer_link(user)
# Link to show if user is admin viewing another user, or user is already a customer.
# Otherwise link to create a new customer.
@@ -28,6 +30,7 @@ module BillingHelper
end
end
+ #deprecated.. erase?
# a bit strange to put here, but we don't have a subscription model
def user_for_subscription(subscription)
@@ -44,6 +47,7 @@ module BillingHelper
end
+ #customer needs to see active or pending?
def allow_cancel_subscription(subscription)
['Active', 'Pending'].include? subscription.status or (admin? and subscription.status == 'Past Due')
end
diff --git a/engines/billing/app/helpers/braintree_form_helper.rb b/engines/billing/app/helpers/braintree_form_helper.rb
index cb322fa..31ea373 100644
--- a/engines/billing/app/helpers/braintree_form_helper.rb
+++ b/engines/billing/app/helpers/braintree_form_helper.rb
@@ -3,6 +3,7 @@ module BraintreeFormHelper
include ActionView::Helpers::AssetTagHelper
include ActionView::Helpers::TagHelper
+ #check if needed
def initialize(object_name, object, template, options, proc)
super
@braintree_params = @options[:params]
diff --git a/engines/billing/app/helpers/braintree_helper.rb b/engines/billing/app/helpers/braintree_helper.rb
index 2d18b6c..a6322c5 100644
--- a/engines/billing/app/helpers/braintree_helper.rb
+++ b/engines/billing/app/helpers/braintree_helper.rb
@@ -1,5 +1,5 @@
module BraintreeHelper
-
+ #check if needed
end
diff --git a/engines/billing/app/models/customer.rb b/engines/billing/app/models/customer.rb
deleted file mode 100644
index 1acc7a5..0000000
--- a/engines/billing/app/models/customer.rb
+++ /dev/null
@@ -1,58 +0,0 @@
-class Customer < CouchRest::Model::Base
-
- FIELDS = [:first_name, :last_name, :phone, :website, :company, :fax, :addresses, :credit_cards, :custom_fields]
- attr_accessor *FIELDS
-
- use_database "customers"
- belongs_to :user
- belongs_to :braintree_customer
-
- # Braintree::Customer - stored on braintrees servers - we only have the id.
- def braintree_customer
- @braintree_customer ||= Braintree::Customer.find(braintree_customer_id)
- end
-
- validates :user, presence: true
-
- design do
- view :by_user_id
- view :by_braintree_customer_id
- end
-
- def has_payment_info?
- !!braintree_customer_id
- end
-
- # from braintree_ruby_examples/rails3_tr_devise and should be tweaked
- def with_braintree_data!
- return self unless has_payment_info?
-
- FIELDS.each do |field|
- send(:"#{field}=", braintree_customer.send(field))
- end
- self
- end
-
- def default_credit_card
- return unless has_payment_info?
-
- credit_cards.find { |cc| cc.default? }
- end
-
- # based on 2nd parameter, either returns the single active subscription (or nil if there isn't one), or an array of all subsciptions
- def subscriptions(braintree_data=nil, only_pending_active_pastdue=true)
- self.with_braintree_data!
- return unless has_payment_info?
-
- subscriptions = []
- self.default_credit_card.subscriptions.each do |sub|
- if only_pending_active_pastdue and ['Pending', 'Active','Past Due'].include? sub.status
- return sub
- else
- subscriptions << sub
- end
- end
- only_pending_active_pastdue ? nil : subscriptions
- end
-
-end
diff --git a/engines/billing/app/views/billing_admin/show.html.haml b/engines/billing/app/views/billing_admin/show.html.haml
index 0382cf0..11f3928 100644
--- a/engines/billing/app/views/billing_admin/show.html.haml
+++ b/engines/billing/app/views/billing_admin/show.html.haml
@@ -1,7 +1,9 @@
+/ same concern as in controller
%legend= t(:more_than_90_days_past_due)
= render(:partial => "subscriptions/subscription_details", :collection => @past_due_atleast_90_days, :as => 'subscription', :locals => {:show_user => true}) || t(:none)
%legend= t(:all_past_due)
= render(:partial => "subscriptions/subscription_details", :collection => @all_past_due, :as => 'subscription', :locals => {:show_user => true}) || t(:none)
-%legend= t(:your_settings)
-= link_to 'view own billing settings', show_or_new_customer_link(current_user) \ No newline at end of file
+/needed?
+/%legend= t(:your_settings)
+/= link_to 'view own billing settings', show_or_new_customer_link(current_user)
diff --git a/engines/billing/app/views/credit_card_info/confirm.html.haml b/engines/billing/app/views/credit_card_info/confirm.html.haml
deleted file mode 100644
index 9dd8176..0000000
--- a/engines/billing/app/views/credit_card_info/confirm.html.haml
+++ /dev/null
@@ -1,5 +0,0 @@
-%h1 Payment Info Confirmation
-%p Your payment information was successfully saved.
-%dl
- %dt Credit Card
- %dd= @result.credit_card.masked_number
diff --git a/engines/billing/app/views/credit_card_info/edit.html.haml b/engines/billing/app/views/credit_card_info/edit.html.haml
deleted file mode 100644
index 9e44344..0000000
--- a/engines/billing/app/views/credit_card_info/edit.html.haml
+++ /dev/null
@@ -1,16 +0,0 @@
-%h1 Change Credit Card
-- if @result
- #total-errors{:style => "color:red;"}
- = h(@result.errors.size)
- error(s)
-= braintree_form_for :credit_card, :existing => @credit_card do |f|
- = field_set_tag "Credit Card" do
- %dl
- %dt= f.label :number, 'Number'
- %dd= f.text_field :number
- %dt= f.label :expiration_date, 'Expiration Date (MM/YY)'
- %dd= f.text_field :expiration_date
- %dt= f.label :cvv, 'CVV'
- %dd= f.text_field :cvv
- = hidden_field_tag :tr_data, @tr_data
- = f.button :wrapped, 'Save Payment Info', cancel: edit_customer_path(@user.id)
diff --git a/engines/billing/app/views/customer/_customer_data.html.haml b/engines/billing/app/views/customer/_customer_data.html.haml
deleted file mode 100644
index 439ae5c..0000000
--- a/engines/billing/app/views/customer/_customer_data.html.haml
+++ /dev/null
@@ -1,16 +0,0 @@
-%legend= t(:customer_information)
-%dl
- %dt First Name
- %dd= @customer.first_name
- %dt Last Name
- %dd= @customer.last_name
- %dt Phone
- %dd= @customer.phone
-%legend= t(:credit_card_information)
-%dl
- %dt Number
- %dd= @default_cc.masked_number
- %dt Expiration Date
- %dd= @default_cc.expiration_date
- - if current_user == @user
- = btn t(:edit_saved_data), edit_customer_path(@user.id)
diff --git a/engines/billing/app/views/customer/_transaction.html.haml b/engines/billing/app/views/customer/_transaction.html.haml
deleted file mode 100644
index e69de29..0000000
--- a/engines/billing/app/views/customer/_transaction.html.haml
+++ /dev/null
diff --git a/engines/billing/app/views/customer/confirm.html.haml b/engines/billing/app/views/customer/confirm.html.haml
deleted file mode 100644
index eab9616..0000000
--- a/engines/billing/app/views/customer/confirm.html.haml
+++ /dev/null
@@ -1,14 +0,0 @@
-%h1 Payment Info Confirmation
-%p Your payment information was successfully saved.
-%dl
- %dt First Name
- %dd= @result.customer.first_name
- %dt Last Name
- %dd= @result.customer.last_name
- %dt Phone
- %dd= @result.customer.phone
- %dt Credit Card
- - @result.customer.credit_cards.each do |cc|
- %dd= cc.masked_number
-- customer = Customer.find_by_user_id(@user.id)
-= btn 'View Customer Info', show_customer_path(@user.id)
diff --git a/engines/billing/app/views/customer/edit.html.haml b/engines/billing/app/views/customer/edit.html.haml
deleted file mode 100644
index f461fcc..0000000
--- a/engines/billing/app/views/customer/edit.html.haml
+++ /dev/null
@@ -1,23 +0,0 @@
-- if @result
- #total-errors{:style => "color:red;"}
- = h(@result.errors.size)
- error(s)
-= braintree_form_for :customer, existing: @customer do |f|
- = field_set_tag "Customer" do
- %dl
- %dt= f.label :first_name, 'First Name'
- %dd= f.text_field :first_name
- %dt= f.label :last_name, 'Last Name'
- %dd= f.text_field :last_name
- %dt= f.label :phone, 'Phone'
- %dd= f.text_field :phone
- - if @default_cc
- = # todo, as they will need a credit card, so not sure about conditional?
- %dt= t(:stored_credit_card)
- %dd
- = @default_cc.masked_number
- = btn t(:change_credit_card), edit_credit_card_info_path(:id => @default_cc.token)
- = hidden_field_tag :tr_data, @tr_data
- .form-actions
- = f.submit t(:save_customer_info), :class => 'btn btn-primary'
- = btn t(:cancel), show_customer_path(@user)
diff --git a/engines/billing/app/views/customer/new.html.haml b/engines/billing/app/views/customer/new.html.haml
deleted file mode 100644
index e1f5ba9..0000000
--- a/engines/billing/app/views/customer/new.html.haml
+++ /dev/null
@@ -1,24 +0,0 @@
-- if @result
- #total-errors{:style => "color:red;"}
- = h(@result.errors.size)
- error(s)
-= braintree_form_for :customer do |f|
- = field_set_tag "Customer" do
- %dl
- %dt= f.label :first_name, 'First Name'
- %dd= f.text_field :first_name
- %dt= f.label :last_name, 'Last Name'
- %dd= f.text_field :last_name
- %dt= f.label :phone, 'Phone'
- %dd= f.text_field :phone
- = field_set_tag "Credit Card" do
- - f.fields_for :credit_card do |cc|
- %dl
- %dt= cc.label :number, 'Number'
- %dd= cc.text_field :number
- %dt= cc.label :expiration_date, 'Expiration Date (MM/YY)'
- %dd= cc.text_field :expiration_date
- %dt= cc.label :cvv, 'CVV'
- %dd= cc.text_field :cvv
- = hidden_field_tag :tr_data, @tr_data
- = f.submit 'Save Payment Info'
diff --git a/engines/billing/app/views/customer/show.html.haml b/engines/billing/app/views/customer/show.html.haml
deleted file mode 100644
index ce4d01a..0000000
--- a/engines/billing/app/views/customer/show.html.haml
+++ /dev/null
@@ -1,27 +0,0 @@
-- if admin? and !@customer
- = t(:no_saved_customer)
-- else
- = render :partial => 'customer_data'
- %legend= t(:last_three_transactions)
- - counter = 0
- = # these will be ordered with most recently created first, per http://stackoverflow.com/questions/16425475/
- - @transactions.each do |t|
- - break if counter > 2 # not ruby-like, but object is a Braintree::ResourceCollection so limited methods available
- = render :partial => "payments/transaction_details", :locals => {:transaction => t}
- - counter += 1
- = btn :transaction_history, user_payments_path(@user)
- %legend= t(:subscriptions)
- - if @active_subscription
- = render :partial => "subscriptions/subscription_details", :locals => {:subscription => @active_subscription}
- - else
- %p
- = t(:no_relevant_subscription)
- - if current_user == @user
- %p
- .form-actions
- = btn :subscribe_to_plan, new_subscription_path
- %p
- = link_to t(:all_subscriptions), user_subscriptions_path(@user)
-
-.form-actions
- = btn :make_donation, new_payment_path, :type => 'primary'
diff --git a/engines/billing/app/views/payments/_customer_form.html.haml b/engines/billing/app/views/payments/_customer_form.html.haml
new file mode 100644
index 0000000..70b9b97
--- /dev/null
+++ b/engines/billing/app/views/payments/_customer_form.html.haml
@@ -0,0 +1,10 @@
+%p
+ = t(:personal_info)
+%div
+ = text_field_tag :first_name, "",placeholder: "#{t(:first_name)}", class: "radius"
+%div
+ = text_field_tag :last_name, "",placeholder: "#{t(:last_name)}", class: "radius"
+%div
+ = text_field_tag :company, "",placeholder: "#{t(:company)}", class: "radius"
+%div
+ = text_field_tag :phone, "",placeholder: "#{t(:phone)}", class: "radius"
diff --git a/engines/billing/app/views/payments/_non_customer_fields.html.haml b/engines/billing/app/views/payments/_non_customer_fields.html.haml
deleted file mode 100644
index 77cfe95..0000000
--- a/engines/billing/app/views/payments/_non_customer_fields.html.haml
+++ /dev/null
@@ -1,16 +0,0 @@
-= field_set_tag "Personal Information" do
- = f.fields_for :customer do |c|
- %div= c.label :first_name, "First Name"
- %div= c.text_field :first_name
- %div= c.label :last_name, "Last Name"
- %div= c.text_field :last_name
- %div= c.label :email, "Email"
- %div= c.text_field :email
-= field_set_tag "Credit Card" do
- = f.fields_for :credit_card do |c|
- %div= c.label :number, "Number"
- %div= c.text_field :number
- %div= c.label :expiration_date, "Expiration Date (MM/YY)"
- %div= c.text_field :expiration_date
- %div= c.label :cvv, "CVV"
- %div= c.text_field :cvv \ No newline at end of file
diff --git a/engines/billing/app/views/payments/_transaction_details.html.haml b/engines/billing/app/views/payments/_transaction_details.html.haml
deleted file mode 100644
index 85e4f6a..0000000
--- a/engines/billing/app/views/payments/_transaction_details.html.haml
+++ /dev/null
@@ -1,15 +0,0 @@
-%p
- = transaction.id
- Type:
- = transaction.type
- Amount:
- = number_to_currency(transaction.amount)
- Status:
- = transaction.status
- Date
- = transaction.created_at.strftime("%Y-%m-%d")
- - if sub_start = transaction.subscription_details.billing_period_start_date
- From subscription which started
- = sub_start
- - else # should not have any of these
- Not paid as part of subscription \ No newline at end of file
diff --git a/engines/billing/app/views/payments/confirm.html.haml b/engines/billing/app/views/payments/confirm.html.haml
deleted file mode 100644
index 45af3c9..0000000
--- a/engines/billing/app/views/payments/confirm.html.haml
+++ /dev/null
@@ -1,26 +0,0 @@
-%h1 Payment Result
-%div Thank you for your donation.
-%h2 Transaction Details
-%table
- %tr
- %td Amount
- %td
- $#{@result.transaction.amount}
- %tr
- %td Transaction ID:
- %td= @result.transaction.id
- %tr
- %td First Name:
- %td= h @result.transaction.customer_details.first_name
- %tr
- %td Last Name:
- %td= h @result.transaction.customer_details.last_name
- %tr
- %td Email:
- %td= h @result.transaction.customer_details.email
- %tr
- %td Credit Card:
- %td= h @result.transaction.credit_card_details.masked_number
- %tr
- %td Card Type:
- %td= h @result.transaction.credit_card_details.card_type \ No newline at end of file
diff --git a/engines/billing/app/views/payments/index.html.haml b/engines/billing/app/views/payments/index.html.haml
index 7a89917..01aa660 100644
--- a/engines/billing/app/views/payments/index.html.haml
+++ b/engines/billing/app/views/payments/index.html.haml
@@ -1,5 +1,6 @@
+/ check if necessary
%h2=t :transaction_history
- if (@transactions.count == 0)
= t(:no_transaction_history)
- @transactions.each do |t|
- = render :partial => "transaction_details", :locals => {:transaction => t} \ No newline at end of file
+ = render :partial => "transaction_details", :locals => {:transaction => t}
diff --git a/engines/billing/app/views/payments/new.html.haml b/engines/billing/app/views/payments/new.html.haml
index e9a8273..67018b2 100644
--- a/engines/billing/app/views/payments/new.html.haml
+++ b/engines/billing/app/views/payments/new.html.haml
@@ -1,17 +1,20 @@
-%h1
- = t(:Donation)
-- if logged_in?
- = t(:donation_not_payment)
-- if @result and @result.errors.size > 0
- %div{:style => "color: red;"}
- = h @result.errors.size
- error(s)
-- if @result and @result.transaction and @result.transaction.status != 'success'
- %div{:style => "color: red;"}
- = t(:processor_declined)
-= braintree_form_for :transaction, :html => {:autocomplete => "off"} do |f|
- = f.label :amount, t(:amount)
- = f.text_field :amount
- = render :partial => 'non_customer_fields', :locals => {:f => f}
- = hidden_field_tag :tr_data, @tr_data
- = f.submit "Submit Donation", :class => 'btn btn-primary'
+%h2.mbs
+ = t(:new_donation)
+%br/
+= form_tag confirm_payment_path, id: "checkout-form" do
+ - if current_user and !current_user.has_payment_info?
+ = render 'customer_form' unless current_user.is_anonymous?
+ %p
+ = t(:donation_info)
+ %div{:id => "payment-form" }
+ %div{:id => "coinbase-container-id" }
+ %input{:name => "amount", :placeholder => "#{t(:donation_amount)}", :type => "text"}
+ %input.btn.btn-primary{:type => "submit", :value => "#{t(:donate)}"}
+%script{:src => "https://js.braintreegateway.com/v2/braintree.js"}
+:javascript
+ var clientToken = "#{@client_token}";
+ braintree.setup(clientToken, "dropin", {
+ container: "payment-form",
+ form: "checkout-form",
+ coinbase: { container: "coinbase-container-id" }
+ });
diff --git a/engines/billing/app/views/subscriptions/_customer_form.html.haml b/engines/billing/app/views/subscriptions/_customer_form.html.haml
new file mode 100644
index 0000000..70b9b97
--- /dev/null
+++ b/engines/billing/app/views/subscriptions/_customer_form.html.haml
@@ -0,0 +1,10 @@
+%p
+ = t(:personal_info)
+%div
+ = text_field_tag :first_name, "",placeholder: "#{t(:first_name)}", class: "radius"
+%div
+ = text_field_tag :last_name, "",placeholder: "#{t(:last_name)}", class: "radius"
+%div
+ = text_field_tag :company, "",placeholder: "#{t(:company)}", class: "radius"
+%div
+ = text_field_tag :phone, "",placeholder: "#{t(:phone)}", class: "radius"
diff --git a/engines/billing/app/views/subscriptions/_subscription_details.html.haml b/engines/billing/app/views/subscriptions/_subscription_details.html.haml
index 6145c95..e6cf87d 100644
--- a/engines/billing/app/views/subscriptions/_subscription_details.html.haml
+++ b/engines/billing/app/views/subscriptions/_subscription_details.html.haml
@@ -1,3 +1,4 @@
+/needed?
%p
- if local_assigns[:show_user]
User:
@@ -23,4 +24,4 @@
Status:
%font{:color => color}
= subscription.status
- - # would be good to get plan name but not sure if that is possible? \ No newline at end of file
+ - # would be good to get plan name but not sure if that is possible?
diff --git a/engines/billing/app/views/subscriptions/create.html.haml b/engines/billing/app/views/subscriptions/create.html.haml
deleted file mode 100644
index 2b6c5e9..0000000
--- a/engines/billing/app/views/subscriptions/create.html.haml
+++ /dev/null
@@ -1,9 +0,0 @@
-- if @result.success?
- %h1
- Subscription Status
- = @result.subscription.status
- = render :partial => "subscription_details", :locals => {:subscription => @result.subscription}
-- else
- %h1
- Error:
- = @result.message \ No newline at end of file
diff --git a/engines/billing/app/views/subscriptions/destroy.html.haml b/engines/billing/app/views/subscriptions/destroy.html.haml
deleted file mode 100644
index e6e8578..0000000
--- a/engines/billing/app/views/subscriptions/destroy.html.haml
+++ /dev/null
@@ -1,7 +0,0 @@
-- if @result.success?
- Subscription destroyed
-- else
- Error:
- = @result.message
-%p
- = btn 'Customer Information', show_customer_path(@user)
diff --git a/engines/billing/app/views/subscriptions/index.html.haml b/engines/billing/app/views/subscriptions/index.html.haml
index 1e3fb25..70fbf8d 100644
--- a/engines/billing/app/views/subscriptions/index.html.haml
+++ b/engines/billing/app/views/subscriptions/index.html.haml
@@ -1,8 +1,29 @@
-%h2=t :all_subscriptions
-- pending_active_pastdue = false
-- @subscriptions.each do |s|
- - if ['Pending', 'Active','Past Due'].include? s.status
- - pending_active_pastdue = true
- = render :partial => "subscription_details", :locals => {:subscription => s}
-- if !pending_active_pastdue and @user == current_user
- = btn 'subscribe to plan', new_subscription_path
+%h2.mbs
+ = t(:subscriptions)
+%h4{ :style => "line-height: 300%;" }
+ .last
+ = t(:lastestsubs)
+ - if @user.subscription_id
+ %ul
+ - @subscription.transactions.each do |transaction|
+ %li
+ %p{ :style => "font-size: 14px; font-weight: normal;" }
+ #{t(:date)} #{transaction.created_at}
+ %ul
+ = link_to "#{t(:unsubscribe_from)} #{@plan.name}", unsubscribe_subscription_path(@subscription.plan_id), method: :delete, class: "btn btn-danger"
+ %br
+ - else
+ %p{ :style => "font-size: 14px; font-weight: normal;"}
+ =t(:no_subs)
+ %h4
+ =t(:choose_subs)
+ %br
+ %ul.nav.nav-tabs.nav-stacked
+ - @subscriptions.each do |subscription|
+ .well
+ = subscription.name
+ = "$" + subscription.price.to_s
+ %div{ :style => "line-height: 300%;" }
+ = link_to t(:choose_button), subscription_path(subscription.id), class: "btn btn-info"
+
+
diff --git a/engines/billing/app/views/subscriptions/new.html.haml b/engines/billing/app/views/subscriptions/new.html.haml
deleted file mode 100644
index 4183458..0000000
--- a/engines/billing/app/views/subscriptions/new.html.haml
+++ /dev/null
@@ -1,15 +0,0 @@
-- if @payment_method_token
- %h1
- Subscribe to plan
- = #currently just one plan
- = @plans[0].name
- = number_to_currency(@plans[0].price)
- = simple_form_for :subscription, :url => :subscriptions do |f|
- = hidden_field_tag :payment_method_token, @payment_method_token
- = hidden_field_tag :plan_id, @plans[0].id
- .form-actions
- = f.submit t(:subscribe), :class => 'btn btn-primary'
-- else
- = t(:must_create_customer)
- %p
- = link_to t(:create_new_customer), new_customer_path
diff --git a/engines/billing/app/views/subscriptions/show.html.haml b/engines/billing/app/views/subscriptions/show.html.haml
index 246ebf0..45c54cc 100644
--- a/engines/billing/app/views/subscriptions/show.html.haml
+++ b/engines/billing/app/views/subscriptions/show.html.haml
@@ -1,7 +1,19 @@
-%h1
- - if @subscription.status == 'Active'
- Current
- Subscription
-= render :partial => "subscription_details", :locals => {:subscription => @subscription}
-- if allow_cancel_subscription(@subscription)
- = destroy_btn :cancel_subscription, user_subscription_path(@user, @subscription.id), type: 'danger'
+%script{:src => "https://js.braintreegateway.com/v2/braintree.js"}
+%h2.mbs
+ = t(:new_subs)
+= simple_form_for :subscription, :url => subscribe_subscription_path(@plan.id), :id => "checkout-form" do |f|
+ - if current_user and !current_user.has_payment_info?
+ = render 'customer_form'
+ %br/
+ %p
+ =t(:pay_details)
+ #payment-form
+ #coinbase-container-id
+ .form-actions
+ = f.submit t(:subscribe), :class => 'btn btn-primary'
+ :javascript
+ var clientToken = "#{@client_token}";
+ braintree.setup(clientToken, "dropin", {
+ container: "payment-form",
+ coinbase: { container: "coinbase-container-id" }
+ });
diff --git a/engines/billing/config/locales/en.yml b/engines/billing/config/locales/en.yml
index 1300958..0bc167f 100644
--- a/engines/billing/config/locales/en.yml
+++ b/engines/billing/config/locales/en.yml
@@ -8,4 +8,32 @@ en:
plan: "Plan"
description: "Description"
cost: "Cost"
- free: "Free" \ No newline at end of file
+ free: "Free"
+ #payments (all have spanish trans)
+ #donations
+ new_donation: "New Donation"
+ donation_info: "Please enter your donation details (this is a donation and will not be applied towards your account, if you have one):"
+ donation_amount: "Enter amount"
+ donate: "Donate"
+ donation_sucess: "Congratulations! Your transaction has been successfull!"
+ donation_not_sucess: "Something went wrong while processing your donation. Please try again!"
+ #subscriptions
+ subscription_sucess: "Congratulations! Your are now subscribed"
+ subscription_not_sucess: "Something went wrong while processing your subscription. Please try again"
+ unsubscription_sucess: "You have been unsubscribed!"
+ unsubscription_not_sucess: "Something went wrong. Please try again!"
+ subscriptions: "Subscriptions"
+ lastestsubs: "Lastest Subscriptions:"
+ date: "Date"
+ unsubscribe_from: "Unsuscribe from"
+ no_subs: "No Subscriptions"
+ choose_subs: "Choose Subscription:"
+ choose_button: "Choose"
+ new_subs: "New Subscription"
+ #common
+ first_name: "First Name"
+ last_name: "Last Name"
+ company: "Company"
+ phone: "Phone"
+ personal_info: "Please enter your personal info:"
+ pay_details: "Please enter payment details"
diff --git a/engines/billing/config/routes.rb b/engines/billing/config/routes.rb
index 6bbe501..357c55b 100644
--- a/engines/billing/config/routes.rb
+++ b/engines/billing/config/routes.rb
@@ -1,25 +1,27 @@
Rails.application.routes.draw do
scope "(:locale)", :locale => CommonLanguages.match_available do
- match 'payments/new' => 'payments#new', :as => :new_payment
- match 'payments/confirm' => 'payments#confirm', :as => :confirm_payment
- resources :users do
- resources :payments, :only => [:index]
- resources :subscriptions, :only => [:index, :show, :destroy]
- end
- resources :customer, :only => [:new, :edit]
- resources :credit_card_info, :only => [:edit]
+ get 'payments/new' => 'payments#new', :as => :new_payment
+ post 'payments/confirm' => 'payments#confirm', :as => :confirm_payment
+ # match 'payments/new' => 'payments#new', :as => :new_payment
+ # match 'payments/confirm' => 'payments#confirm', :as => :confirm_payment
+ #resources :users do
+ # resources :payments, :only => [:new, :confirm]
+ # resources :subscriptions, :only => [:index, :destroy]
+ #end
+ resources :subscriptions, :only => [:index, :show] do
+ member do
+ post 'subscribe'
+ delete 'unsubscribe'
+ end
+ end
- match 'customer/confirm/' => 'customer#confirm', :as => :confirm_customer
- match 'customer/show/:id' => 'customer#show', :as => :show_customer
- match 'credit_card_info/confirm' => 'credit_card_info#confirm', :as => :confirm_credit_card_info
+ resources :customer, :only => [:new, :edit]
- resources :subscriptions, :only => [:new, :create, :update] # index, show & destroy are within users path
- match 'billing_admin' => 'billing_admin#show', :as => :billing_admin
+ match 'customer/confirm/' => 'customer#confirm', :as => :confirm_customer
+ match 'customer/show/:id' => 'customer#show', :as => :show_customer
- #match 'transactions/:product_id/new' => 'transactions#new', :as => :new_transaction
- #match 'transactions/confirm/:product_id' => 'transactions#confirm', :as => :confirm_transaction
+ match 'billing_admin' => 'billing_admin#show', :as => :billing_admin
end
-
end
diff --git a/engines/billing/questions.md b/engines/billing/questions.md
new file mode 100644
index 0000000..41da41f
--- /dev/null
+++ b/engines/billing/questions.md
@@ -0,0 +1,22 @@
+Questions
+====================
+
+1. Should admin be able to see subscriptions?
+
+2. Should user be able to upgrade or downgrade subscriptions?
+
+3. Should confirmations emails be allowed?
+
+See here:
+https://articles.braintreepayments.com/guides/recurring-billing/email-notifications
+https://articles.braintreepayments.com/control-panel/transactions/email-receipts
+
+4. Braintree uses JS for dropin. I'm not sure but this may not work in browsers
+such as Tor. Is there a problem?
+JS can be improved and changed to 'Hosted Fields'. See here:
+https://developers.braintreepayments.com/javascript+ruby/guides/hosted-fields/overview
+
+5. There is another payment method that can be allowed: SEPA Direct Debit (on
+ beta). Should it be allowed?
+
+
diff --git a/engines/billing/test/factories.rb b/engines/billing/test/factories.rb
index 87543b2..6352211 100644
--- a/engines/billing/test/factories.rb
+++ b/engines/billing/test/factories.rb
@@ -14,7 +14,8 @@ FactoryGirl.define do
first_name 'Big'
last_name 'Spender'
credit_card number: TEST_CC_NUMBER, expiration_date: '04/2016'
- initialize_with { Braintree::Customer.create(attributes).customer }
+ initialize_with { Braintree::Configuration.environment = :sandbox
+ Braintree::Customer.create(attributes).customer }
skip_create
factory :broken_customer do
diff --git a/engines/billing/test/functional/customer_controller_test.rb b/engines/billing/test/functional/customer_controller_test.rb
deleted file mode 100644
index d943e23..0000000
--- a/engines/billing/test/functional/customer_controller_test.rb
+++ /dev/null
@@ -1,124 +0,0 @@
-require 'test_helper'
-require 'fake_braintree'
-
-class CustomerControllerTest < ActionController::TestCase
- include CustomerTestHelper
-
- test "new assigns redirect url" do
- login
- get :new
-
- assert_response :success
- assert assigns(:tr_data)
- tr_data = Braintree::Util.parse_query_string(assigns(:tr_data))
- assert_equal confirm_customer_url, tr_data[:redirect_url]
- end
-
- test "new requires login" do
- get :new
-
- assert_response :redirect
- assert_redirected_to login_path
- end
-
- test "edit uses params[:id]" do
- customer = stub_customer
- login customer.user
- get :edit, id: customer.user.id
-
- assert_response :success
- assert assigns(:tr_data)
- tr_data = Braintree::Util.parse_query_string(assigns(:tr_data))
- assert_equal customer.braintree_customer_id, tr_data[:customer_id]
- assert_equal confirm_customer_url, tr_data[:redirect_url]
- end
-
- test "confirm customer creation" do
- login
- Braintree::TransparentRedirect.expects(:confirm).returns(success_response)
- # to_confirm = prepare_confirmation :create_customer_data,
- # customer: FactoryGirl.attributes_for(:braintree_customer),
- # redirect_url: confirm_customer_url
-
- assert_difference("Customer.count") do
- post :confirm, braintree: :query
- end
-
- assert_response :success
- assert result = assigns(:result)
- assert result.success?
- assert result.customer.id
- end
-
- test "customer update" do
- customer = stub_customer
- customer.expects(:save)
- login customer.user
- Braintree::TransparentRedirect.expects(:confirm).
- returns(success_response(customer))
-
- assert_no_difference("Customer.count") do
- post :confirm, query: :from_braintree
- end
-
- assert_response :success
- assert result = assigns(:result)
- assert result.success?
- assert_equal customer.braintree_customer, result.customer
- end
-
- test "failed customer creation" do
- skip "can't get customer creation to fail"
- login
- FakeBraintree.decline_all_cards!
- # what is prepare_confirmation ?? this method isn't found
- to_confirm = prepare_confirmation :create_customer_data,
- customer: FactoryGirl.attributes_for(:broken_customer),
- redirect_url: confirm_customer_url
- post :confirm, to_confirm
-
- FakeBraintree.clear!
- assert_response :success
- assert result = assigns(:result)
- assert !result.success?
- end
-
- test "failed customer creation with stubbing" do
- login
- Braintree::TransparentRedirect.expects(:confirm).returns(failure_response)
- post :confirm, bla: :blub
-
- assert_response :success
- assert_template :new
- end
-
- test "failed customer update with stubbing" do
- customer = stub_customer
- login customer.user
- Braintree::TransparentRedirect.expects(:confirm).returns(failure_response)
- post :confirm, bla: :blub
-
- assert_response :success
- assert_template :edit
- end
-
- def failure_response
- stub success?: false,
- errors: stub(for: nil, size: 0),
- params: {}
- end
-
- def success_response(customer = nil)
- stub success?: true,
- customer: braintree_customer(customer)
- end
-
- def braintree_customer(customer)
- if customer
- customer.braintree_customer
- else
- FactoryGirl.build :braintree_customer
- end
- end
-
-end
diff --git a/engines/billing/test/functional/customers_controller_test.rb b/engines/billing/test/functional/customers_controller_test.rb
deleted file mode 100644
index 4d84fb0..0000000
--- a/engines/billing/test/functional/customers_controller_test.rb
+++ /dev/null
@@ -1,62 +0,0 @@
-require 'test_helper'
-require 'fake_braintree'
-
-class CustomersControllerTest < ActionController::TestCase
- tests CustomerController
-
- setup do
- InviteCodeValidator.any_instance.stubs(:validate)
- @user = FactoryGirl.create :user
- @other_user = FactoryGirl.create :user
- #FakeBraintree.clear!
- #FakeBraintree.verify_all_cards!
- testid = 'testid'
- #this wasn't actually being used
- #FakeBraintree::Customer.new({:credit_cards => [{:number=>"5105105105105100", :expiration_date=>"05/2013"}]}, {:id => testid, :merchant_id => Braintree::Configuration.merchant_id})
- # any reason to call the create instance method on the FakeBraintree::Customer ?
- @customer = Customer.new(:user_id => @other_user.id)
- @customer.braintree_customer_id = testid
- @customer.save
-
- end
-
- teardown do
- @user.destroy
- @other_user.destroy
- @customer.destroy
- end
-
- test "no access if not logged in" do
- get :new
- assert_login_required
- get :show, :id => @customer.braintree_customer_id
- assert_login_required
- get :edit, :id => @customer.braintree_customer_id
- assert_login_required
- end
-
-
- test "should get new if logged in and not customer" do
- login @user
- get :new
- assert_not_nil assigns(:tr_data)
- assert_response :success
- end
-
- test "new should direct edit if user is already a customer" do
- login @other_user
- get :new
- assert_response :redirect
- assert_equal edit_customer_url(@customer.user), response.header['Location']
- end
-
-
- test "show" do
- skip "show customer"
- login @other_user
- # Below will fail, as when we go to fetch the customer data, Braintree::Customer.find(params[:id]) won't find the customer as it is a FakeBraintree customer.
- #get :show, :id => @customer.braintree_customer_id
-
- end
-
-end
diff --git a/engines/billing/test/functional/payments_controller_test.rb b/engines/billing/test/functional/payments_controller_test.rb
index 90b7582..e015a07 100644
--- a/engines/billing/test/functional/payments_controller_test.rb
+++ b/engines/billing/test/functional/payments_controller_test.rb
@@ -4,47 +4,56 @@ require 'fake_braintree'
class PaymentsControllerTest < ActionController::TestCase
include CustomerTestHelper
- test "payment when unauthorized" do
- get :new
- assert_not_nil assigns(:tr_data)
- assert_response :success
+ def setup
+ FakeBraintree.activate!
end
- test "successful confirmation renders confirm" do
- Braintree::TransparentRedirect.expects(:confirm).returns(success_response)
- get :confirm
-
- assert_response :success
- assert_template :confirm
+ def teardown
+ FakeBraintree.clear!
end
- test "failed confirmation renders new" do
- Braintree::TransparentRedirect.expects(:confirm).returns(failure_response)
- get :confirm
+ test "payment new" do
+ get :new
+ assert_not_nil assigns(:client_token)
assert_response :success
- assert_not_nil assigns(:tr_data)
- assert_template :new
end
- def failure_response
- stub success?: false,
- errors: stub(for: nil, size: 0),
- params: {},
- transaction: stub(status: nil)
+ test "sucess confirmation" do
+ #already included with FakeBraintree
+ #Braintree::Transaction.sale.expects(:confirm).returns(success_response)
+ post :confirm, {
+ amount: "100",
+ payment_method_nonce: "fake-valid-nonce",
+ customer: {
+ first_name: "Test",
+ last_name: "Testing",
+ company: "RGSoC",
+ email: "any@email.com",
+ phone: "555-888-1234" }
+ }
+
+ assert assigns(:result).success?
+ assert_not_nil flash[:success]
end
- def success_response
- stub success?: true,
- transaction: stub_transaction
+ test "failed confirmation renders new" do
+ FakeBraintree.decline_all_cards!
+ post :confirm, {
+ amount: "100",
+ payment_method_nonce: "fake-valid-nonce",
+ customer: {
+ first_name: "Test",
+ last_name: "Testing",
+ company: "RGSoC",
+ email: "any@email.com",
+ phone: "555-888-1234" }
+ }
+
+ assert !assigns(:result).success?
+ assert_not_nil flash[:error]
+ FakeBraintree.clear!
end
# that's what you get when not following the law of demeter...
- def stub_transaction
- stub amount: "100.00",
- id: "ASDF",
- customer_details: FactoryGirl.build(:braintree_customer),
- credit_card_details: FactoryGirl.build(:braintree_customer).credit_cards.first
- end
-
end
diff --git a/engines/billing/test/functional/subscriptions_controller_test.rb b/engines/billing/test/functional/subscriptions_controller_test.rb
index a6a1057..1e98eff 100644
--- a/engines/billing/test/functional/subscriptions_controller_test.rb
+++ b/engines/billing/test/functional/subscriptions_controller_test.rb
@@ -4,13 +4,78 @@ require 'fake_braintree'
class SubscriptionsControllerTest < ActionController::TestCase
include CustomerTestHelper
- test "destroy cancels subscription" do
- customer = stub_customer
- login customer.user
- result = Braintree::Subscription.create plan_id: 'my_plan',
- payment_method_token: customer.braintree_customer.credit_cards.first.token
- subscription = result.subscription
- delete :destroy, id: subscription.id, user_id: customer.user.id
- assert_equal "Canceled", Braintree::Subscription.find(subscription.id).status
+ def setup
+ FakeBraintree.activate!
end
+
+ def teardown
+ FakeBraintree.clear!
+ end
+
+ test "get all subscriptions when the user doesn't have an active subscription" do
+ user = find_record :user
+ login user
+ plans = [stub(:id => 1, :name => "First Plan", :price => 10), stub(:id => 2, :name => "Other Plan", :price => 30)]
+ Braintree::Plan.expects(:all).returns(plans)
+
+ get :index
+
+ assert assigns(:subscriptions)
+ assert_response :success
+ end
+
+ test "get subscriptions when user has an active subscription" do
+ user = find_record :user
+ login user
+ plans = [stub(:id => 1, :name => "First Plan", :price => 10), stub(:id => 2, :name => "Other Plan", :price => 30)]
+ Braintree::Plan.expects(:all).returns(plans)
+ result = Braintree::Subscription.create(payment_method_token: 'user_token', plan_id: 1)
+ user.subscription_id = result.subscription.id
+
+ get :index
+
+ assert assigns(:subscription)
+ assert assigns(:plan)
+ assert_response :success
+ end
+
+ test "subscriptions show" do
+ user = find_record :user
+ login user
+ plans = [stub(:id => "1", :name => "First Plan", :price => 10), stub(:id => "2", :name => "Other Plan", :price => 30)]
+ Braintree::Plan.expects(:all).returns(plans)
+
+ get :show, :id => "1"
+
+ assert assigns(:plan)
+ assert_response :success
+ end
+
+ test "subscribe creates subscription" do
+ user = find_record :user
+ user.expects(:save).returns(true)
+ login user
+ payment_methods = [stub(:token => 'user_token')]
+ Braintree::Customer.any_instance.stubs(:payment_methods).returns(payment_methods)
+ user.expects(:save).returns(true)
+
+ post :subscribe, :id => "1", :first_name => "Test", :last_name => "Testing", :company => "RGSoC", :email => "any@email.com", :phone => "555-888-1234"
+
+ assert assigns(:result).success?
+ assert_not_nil flash[:success]
+ end
+
+ test "unsubscribe cancels subscription" do
+ user = find_record :user
+ user.expects(:save).returns(true)
+ result = Braintree::Subscription.create(payment_method_token: 'user_token', plan_id: '1')
+ user.subscription_id = result.subscription.id
+ login user
+
+ delete :unsubscribe, :id => "1"
+
+ assert assigns(:result).success?
+ assert_not_nil flash[:success]
+ end
+
end
diff --git a/engines/billing/test/test_helper.rb b/engines/billing/test/test_helper.rb
index 57cdd63..14e453e 100644
--- a/engines/billing/test/test_helper.rb
+++ b/engines/billing/test/test_helper.rb
@@ -2,6 +2,7 @@
ENV["RAILS_ENV"] = "test"
require "rails/test_help"
+require 'mocha/setup'
Rails.backtrace_cleaner.remove_silencers!
diff --git a/engines/billing/test/unit/customer_test.rb b/engines/billing/test/unit/customer_test.rb
deleted file mode 100644
index 6156f87..0000000
--- a/engines/billing/test/unit/customer_test.rb
+++ /dev/null
@@ -1,38 +0,0 @@
-require 'test_helper'
-
-class CustomerTest < ActiveSupport::TestCase
- include StubRecordHelper
-
- setup do
- @user = find_record :user
- @customer = FactoryGirl.build(:customer, user: @user)
- end
-
- test "test set of attributes should be valid" do
- @customer.valid?
- assert_equal Hash.new, @customer.errors.messages
- end
-
- test "customer belongs to user" do
- assert_equal User, @customer.user.class
- end
-
- test "user validation" do
- @customer.user = nil
- assert !@customer.valid?
- end
-
- test "has no payment info" do
- assert !@customer.braintree_customer_id
- assert !@customer.has_payment_info?
- end
-
- test "with no braintree data" do
- assert_equal @customer, @customer.with_braintree_data!
- end
-
- test "without default credit card" do
- assert_nil @customer.default_credit_card
- end
-
-end
diff --git a/engines/billing/test/unit/customer_with_payment_info_test.rb b/engines/billing/test/unit/customer_with_payment_info_test.rb
deleted file mode 100644
index 0589a59..0000000
--- a/engines/billing/test/unit/customer_with_payment_info_test.rb
+++ /dev/null
@@ -1,40 +0,0 @@
-require 'test_helper'
-require 'fake_braintree'
-
-class CustomerWithPaymentInfoTest < ActiveSupport::TestCase
- include StubRecordHelper
-
- setup do
- @user = find_record :user
- @customer = FactoryGirl.build(:customer_with_payment_info, user: @user)
- end
-
- test "has payment_info" do
- assert @customer.braintree_customer_id
- assert @customer.has_payment_info?
- end
-
- test "constructs customer with braintree data" do
- @customer.with_braintree_data!
- assert_equal 'Big', @customer.first_name
- assert_equal 'Spender', @customer.last_name
- assert_equal 1, @customer.credit_cards.size
- assert_equal Hash.new, @customer.custom_fields
- end
-
- test "can access braintree_customer after reload" do
- @customer.save
- @customer = Customer.find_by_user_id(@customer.user_id)
- @customer.with_braintree_data!
- assert_equal 'Big', @customer.first_name
- assert_equal 'Spender', @customer.last_name
- assert_equal 1, @customer.credit_cards.size
- assert_equal Hash.new, @customer.custom_fields
- @customer.destroy
- end
-
- test "sets default_credit_card" do
- @customer.with_braintree_data!
- assert_equal @customer.credit_cards.first, @customer.default_credit_card
- end
-end
diff --git a/lib/tasks/invite_code.rake b/lib/tasks/invite_code.rake
index 3d608ce..d6e2b49 100644
--- a/lib/tasks/invite_code.rake
+++ b/lib/tasks/invite_code.rake
@@ -1,11 +1,18 @@
+
+
desc "Generate a batch of invite codes"
-task :generate_invites, [:n] => :environment do |task, args|
+task :generate_invites, [:n, :u] => :environment do |task, args|
codes = args.n
codes = codes.to_i
+ if args.u != nil
+ max_uses = args.u
+ end
+
codes.times do |x|
x = InviteCode.new
+ x.max_uses = max_uses
x.save
puts x.invite_code
end
diff --git a/test/functional/users_controller_test.rb b/test/functional/users_controller_test.rb
index 7d1745c..70f483e 100644
--- a/test/functional/users_controller_test.rb
+++ b/test/functional/users_controller_test.rb
@@ -158,6 +158,7 @@ class UsersControllerTest < ActionController::TestCase
login :is_admin? => true
+ @request.env['HTTP_REFERER'] = 'http://test.com/sessions/new'
post :deactivate, :id => user.id
assert !assigns(:user).enabled?
end
diff --git a/test/support/browser_integration_test.rb b/test/support/browser_integration_test.rb
index 35887cc..950a395 100644
--- a/test/support/browser_integration_test.rb
+++ b/test/support/browser_integration_test.rb
@@ -30,7 +30,6 @@ class BrowserIntegrationTest < ActionDispatch::IntegrationTest
Capybara.javascript_driver = :poltergeist
Capybara.default_wait_time = 5
-
# Make the Capybara DSL available
include Capybara::DSL
diff --git a/test/unit/invite_code_test.rb b/test/unit/invite_code_test.rb
index b17d1bc..fd93f2f 100644
--- a/test/unit/invite_code_test.rb
+++ b/test/unit/invite_code_test.rb
@@ -21,47 +21,5 @@ class InviteCodeTest < ActiveSupport::TestCase
end
- test "Invite count >0 is not accepted for new account signup" do
- validator = InviteCodeValidator.new nil
-
- user_code = InviteCode.new
- user_code.invite_count = 1
- user_code.save
-
- user = FactoryGirl.build :user
- user.invite_code = user_code.invite_code
-
- validator.validate(user)
-
- assert_equal ["This code has already been used"], user.errors[:invite_code]
-
- end
-
- test "Invite count 0 is accepted for new account signup" do
- validator = InviteCodeValidator.new nil
-
- user_code = InviteCode.create
-
- user = FactoryGirl.build :user
- user.invite_code = user_code.invite_code
-
- validator.validate(user)
-
- assert_equal [], user.errors[:invite_code]
- end
-
- test "There is an error message if the invite code does not exist" do
- validator = InviteCodeValidator.new nil
-
- user = FactoryGirl.build :user
- user.invite_code = "wrongcode"
-
- validator.validate(user)
-
- assert_equal ["This is not a valid code"], user.errors[:invite_code]
-
- end
-
-
end
diff --git a/test/unit/invite_code_validator_test.rb b/test/unit/invite_code_validator_test.rb
index ee8f1b3..62eeae6 100644
--- a/test/unit/invite_code_validator_test.rb
+++ b/test/unit/invite_code_validator_test.rb
@@ -27,4 +27,60 @@ class InviteCodeValidatorTest < ActiveSupport::TestCase
assert_equal errors, invalid_user.errors.messages
end
end
+
+
+ test "Invite count >= invite max uses is not accepted for new account signup" do
+ validator = InviteCodeValidator.new nil
+
+ user_code = InviteCode.new
+ user_code.invite_count = 1
+ user_code.save
+
+ user = FactoryGirl.build :user
+ user.invite_code = user_code.invite_code
+
+ validator.validate(user)
+
+ assert_equal ["This code has already been used"], user.errors[:invite_code]
+
+ end
+
+ test "Invite count < invite max uses is accepted for new account signup" do
+ validator = InviteCodeValidator.new nil
+
+ user_code = InviteCode.create
+ user_code.save
+
+ user = FactoryGirl.build :user
+ user.invite_code = user_code.invite_code
+
+ validator.validate(user)
+
+ assert_equal [], user.errors[:invite_code]
+ end
+
+ test "Invite count 0 is accepted for new account signup" do
+ validator = InviteCodeValidator.new nil
+
+ user_code = InviteCode.create
+
+ user = FactoryGirl.build :user
+ user.invite_code = user_code.invite_code
+
+ validator.validate(user)
+
+ assert_equal [], user.errors[:invite_code]
+ end
+
+ test "There is an error message if the invite code does not exist" do
+ validator = InviteCodeValidator.new nil
+
+ user = FactoryGirl.build :user
+ user.invite_code = "wrongcode"
+
+ validator.validate(user)
+
+ assert_equal ["This is not a valid code"], user.errors[:invite_code]
+ end
+
end \ No newline at end of file