From ec4cf4dea9c88a7805afb274324ad46a84798c31 Mon Sep 17 00:00:00 2001
From: Will Bradley
Date: Sat, 24 Aug 2013 02:18:37 -0700
Subject: [PATCH] Adding IPNs
---
Gemfile | 2 +-
Gemfile.lock | 8 ++
app/assets/javascripts/ipn.js.coffee | 3 +
app/assets/stylesheets/ipn.css.scss | 3 +
app/controllers/ipns_controller.rb | 34 +++++
app/helpers/ipn_helper.rb | 2 +
app/models/ipn.rb | 50 +++++++
app/models/payment.rb | 6 +-
app/models/user.rb | 4 +
app/views/ipns/index.html.erb | 26 ++++
app/views/ipns/new.html.erb | 123 ++++++++++++++++++
app/views/ipns/show.html.erb | 17 +++
app/views/payments/_form.html.erb | 4 +
app/views/payments/index.html.erb | 2 +
app/views/payments/show.html.erb | 19 ++-
config/routes.rb | 2 +
db/migrate/20130824062334_create_ipns.rb | 24 ++++
.../20130824072157_add_amount_to_payments.rb | 5 +
db/schema.rb | 25 +++-
test/fixtures/ipns.yml | 7 +
test/functional/ipn_controller_test.rb | 7 +
test/unit/helpers/ipn_helper_test.rb | 4 +
test/unit/ipn_test.rb | 7 +
23 files changed, 379 insertions(+), 5 deletions(-)
create mode 100644 app/assets/javascripts/ipn.js.coffee
create mode 100644 app/assets/stylesheets/ipn.css.scss
create mode 100644 app/controllers/ipns_controller.rb
create mode 100644 app/helpers/ipn_helper.rb
create mode 100644 app/models/ipn.rb
create mode 100644 app/views/ipns/index.html.erb
create mode 100644 app/views/ipns/new.html.erb
create mode 100644 app/views/ipns/show.html.erb
create mode 100644 db/migrate/20130824062334_create_ipns.rb
create mode 100644 db/migrate/20130824072157_add_amount_to_payments.rb
create mode 100644 test/fixtures/ipns.yml
create mode 100644 test/functional/ipn_controller_test.rb
create mode 100644 test/unit/helpers/ipn_helper_test.rb
create mode 100644 test/unit/ipn_test.rb
diff --git a/Gemfile b/Gemfile
index 8ecbde8..6b59f1b 100644
--- a/Gemfile
+++ b/Gemfile
@@ -40,7 +40,7 @@ gem 'bcrypt-ruby', '~> 3.0.0'
# gem 'capistrano'
# To use debugger
-# gem 'ruby-debug'
+gem 'debugger'
#gem "paperclip", "~> 3.0"
gem 'gravtastic'
diff --git a/Gemfile.lock b/Gemfile.lock
index 94abf55..d3be24e 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -39,6 +39,13 @@ GEM
coffee-script-source
execjs
coffee-script-source (1.3.3)
+ columnize (0.3.6)
+ debugger (1.6.0)
+ columnize (>= 0.3.1)
+ debugger-linecache (~> 1.2.0)
+ debugger-ruby_core_source (~> 1.2.1)
+ debugger-linecache (1.2.0)
+ debugger-ruby_core_source (1.2.2)
devise (2.1.1)
bcrypt-ruby (~> 3.0)
orm_adapter (~> 0.1)
@@ -120,6 +127,7 @@ DEPENDENCIES
bcrypt-ruby (~> 3.0.0)
cancan
coffee-rails (~> 3.2.1)
+ debugger
devise
gravtastic
jquery-rails
diff --git a/app/assets/javascripts/ipn.js.coffee b/app/assets/javascripts/ipn.js.coffee
new file mode 100644
index 0000000..7615679
--- /dev/null
+++ b/app/assets/javascripts/ipn.js.coffee
@@ -0,0 +1,3 @@
+# Place all the behaviors and hooks related to the matching controller here.
+# All this logic will automatically be available in application.js.
+# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
diff --git a/app/assets/stylesheets/ipn.css.scss b/app/assets/stylesheets/ipn.css.scss
new file mode 100644
index 0000000..ee38bc7
--- /dev/null
+++ b/app/assets/stylesheets/ipn.css.scss
@@ -0,0 +1,3 @@
+// Place all the styles related to the Ipn controller here.
+// They will automatically be included in application.css.
+// You can use Sass (SCSS) here: http://sass-lang.com/
diff --git a/app/controllers/ipns_controller.rb b/app/controllers/ipns_controller.rb
new file mode 100644
index 0000000..a392dd8
--- /dev/null
+++ b/app/controllers/ipns_controller.rb
@@ -0,0 +1,34 @@
+class IpnsController < ApplicationController
+ load_and_authorize_resource :ipn, :except => "create"
+ before_filter :authenticate_user!
+
+ protect_from_forgery :except => [:create]
+
+ def index
+ @ipns = Ipn.all
+ end
+
+ def show
+ end
+
+ def new
+ end
+
+ def create
+ #TODO: ensure the request is actually from paypal
+ @ipn = Ipn.new_from_dynamic_params(params)
+ @ipn.data = params.to_json
+ @ipn.save
+ render :nothing => true
+ end
+
+ def link
+ result = @ipn.link_payment
+ if result.first
+ redirect_to ipns_url, :notice => 'Payment was successfully linked.'
+ else
+ redirect_to ipns_url, :notice => result.last
+ end
+ end
+
+end
\ No newline at end of file
diff --git a/app/helpers/ipn_helper.rb b/app/helpers/ipn_helper.rb
new file mode 100644
index 0000000..a56c555
--- /dev/null
+++ b/app/helpers/ipn_helper.rb
@@ -0,0 +1,2 @@
+module IpnHelper
+end
diff --git a/app/models/ipn.rb b/app/models/ipn.rb
new file mode 100644
index 0000000..3643066
--- /dev/null
+++ b/app/models/ipn.rb
@@ -0,0 +1,50 @@
+class Ipn < ActiveRecord::Base
+ attr_accessible :data
+ belongs_to :payment
+
+ after_create :create_payment
+
+ def self.new_from_dynamic_params(params)
+ ipn = Ipn.new()
+
+ ipn.attributes.each do |c|
+ unless params[c.first.to_sym].nil?
+ ipn[c.first.to_sym] = params[c.first.to_sym]
+ end
+ end
+
+ return ipn
+ end
+
+ def link_payment
+ create_payment
+ end
+
+ private
+ def create_payment
+ # find user by email, then by payee
+ user = User.find_by_email(self.payer_email)
+ user = User.find_by_payee(self.payer_email) if user.nil? && self.payer_email.present?
+
+ # Only create payments if the amount matches a member level
+ if user.present?
+ if User.member_levels[self.payment_gross.to_i].present?
+ payment = Payment.new
+ payment.date = self.payment_date
+ payment.user_id = user.id
+ payment.amount = self.payment_gross
+ if payment.save
+ self.payment_id = payment.id
+ self.save!
+ else
+ return [false, "Unable to link payment. Payment error: #{payment.errors.full_messages.first}"]
+ end
+ else
+ return [false, "Unable to link payment. Couldn't find membership level '#{self.payment_gross.to_i}'."]
+ end
+ else
+ return [false, "Unable to link payment. Couldn't find user/payee '#{self.payer_email}'."]
+ end
+ end
+
+end
diff --git a/app/models/payment.rb b/app/models/payment.rb
index ef9d681..ea70430 100644
--- a/app/models/payment.rb
+++ b/app/models/payment.rb
@@ -1,10 +1,12 @@
class Payment < ActiveRecord::Base
belongs_to :user
- attr_accessible :date, :user_id, :created_by
+ has_one :ipn
+ attr_accessible :date, :user_id, :created_by, :amount
- validates_presence_of :user_id, :date, :created_by
+ validates_presence_of :user_id, :date, :amount # not created_by
validates_uniqueness_of :date, :scope => :user_id, :message => ' of payment already exists for this user.'
+
def human_date
if date.year < DateTime.now.year
date.strftime("%b %e, %y")
diff --git a/app/models/user.rb b/app/models/user.rb
index 512a0aa..00890e3 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -85,6 +85,10 @@ class User < ActiveRecord::Base
end
end
+ def self.member_levels
+ {25 => "Associate", 50 => "Basic", 100 => "Plus"}
+ end
+
def member_status
case self.member_level.to_i
when 0
diff --git a/app/views/ipns/index.html.erb b/app/views/ipns/index.html.erb
new file mode 100644
index 0000000..9a6afff
--- /dev/null
+++ b/app/views/ipns/index.html.erb
@@ -0,0 +1,26 @@
+
+
+
+ Date |
+ Name |
+ Item |
+ Amount |
+
+ <% @ipns.reverse!.each do |ipn| %>
+
+ <%= ipn.payment_date %> |
+ <%= ipn.first_name %> <%= ipn.last_name %> |
+ <%= ipn.item_name %> |
+ <%= ipn.payment_gross %> |
+
+ <% if ipn.payment.present? %>
+ <%= link_to "Linked Payment", ipn.payment %>
+ <% else %>
+ <%= link_to "Try to link email '#{ipn.payer_email}' at membership level '#{ipn.payment_gross.to_i}'", link_ipn_path(ipn) %>
+ <% end %>
+ |
+ <%= link_to "Details", ipn %> |
+
+ <% end %>
+
+
diff --git a/app/views/ipns/new.html.erb b/app/views/ipns/new.html.erb
new file mode 100644
index 0000000..3322347
--- /dev/null
+++ b/app/views/ipns/new.html.erb
@@ -0,0 +1,123 @@
+ <%= form_tag('/ipns') do |f| %>
+
+
+
+
+ <%= submit_tag %>
+
+
+ <%= label_tag :first_name %>
+ <%= text_field_tag :first_name, "John" %>
+
+
+ <%= label_tag :last_name %>
+ <%= text_field_tag :last_name, "Smith" %>
+
+
+ <%= label_tag :payer_email %>
+ <%= text_field_tag :payer_email, "jsmith@example.com" %>
+
+
+ <%= label_tag :item_name %>
+ <%= text_field_tag :item_name, "Associate Membership" %>
+
+
+ <%= label_tag :payment_gross %>
+ <%= text_field_tag :payment_gross, "25.00" %>
+
+
+ <%= label_tag :transaction_subject %>
+ <%= text_field_tag :transaction_subject, "" %>
+
+
+ <%= label_tag :payment_date %>
+ <%= text_field_tag :payment_date, Date.today.to_s %>
+
+
+ <%= label_tag :txn_type %>
+ <%= text_field_tag :txn_type, "subscr_payment" %>
+
+
+ <%= label_tag :subscr_id %>
+ <%= text_field_tag :subscr_id, "I-1234567890" %>
+
+
+ <%= label_tag :residence_country %>
+ <%= text_field_tag :residence_country, "US" %>
+
+
+ <%= label_tag :mc_currency %>
+ <%= text_field_tag :mc_currency, "USD" %>
+
+
+ <%= label_tag :business %>
+ <%= text_field_tag :business, "hslfinances@gmail.com" %>
+
+
+ <%= label_tag :payment_type %>
+ <%= text_field_tag :payment_type, "instant" %>
+
+
+ <%= label_tag :protection_eligibility %>
+ <%= text_field_tag :protection_eligibility, "Ineligible" %>
+
+
+ <%= label_tag :verify_sign %>
+ <%= text_field_tag :verify_sign, "12ru9021j9f21j90fj1290fj2910fj0219fj0" %>
+
+
+ <%= label_tag :payer_status %>
+ <%= text_field_tag :payer_status, "verified" %>
+
+
+ <%= label_tag :txn_id %>
+ <%= text_field_tag :txn_id, "1234567890" %>
+
+
+ <%= label_tag :receiver_email %>
+ <%= text_field_tag :receiver_email, "hslfinances@gmail.com" %>
+
+
+ <%= label_tag :payer_id %>
+ <%= text_field_tag :payer_id, "V812314914" %>
+
+
+ <%= label_tag :receiver_id %>
+ <%= text_field_tag :receiver_id, "V90R1280182" %>
+
+
+ <%= label_tag :payment_status %>
+ <%= text_field_tag :payment_status, "Completed" %>
+
+
+ <%= label_tag :payment_fee %>
+ <%= text_field_tag :payment_fee, "0.85" %>
+
+
+ <%= label_tag :mc_fee %>
+ <%= text_field_tag :mc_fee, "0.85" %>
+
+
+ <%= label_tag :mc_gross %>
+ <%= text_field_tag :mc_gross, "25.00" %>
+
+
+ <%= label_tag :charset %>
+ <%= text_field_tag :charset, "windows-1252" %>
+
+
+ <%= label_tag :notify_version %>
+ <%= text_field_tag :notify_version, "3.7" %>
+
+
+ <%= label_tag :ipn_track_id %>
+ <%= text_field_tag :ipn_track_id, "9d3d032d9070" %>
+
+
+ <%= submit_tag %>
+
+ <% end %>
\ No newline at end of file
diff --git a/app/views/ipns/show.html.erb b/app/views/ipns/show.html.erb
new file mode 100644
index 0000000..8fdc7ec
--- /dev/null
+++ b/app/views/ipns/show.html.erb
@@ -0,0 +1,17 @@
+IPN Details
+
+<% @ipn.attributes.except("data","payment_id").each do |attr| %>
+
+ <%= attr.first.to_s %>:
+ <%= @ipn.attributes[attr.first] %>
+
+<% end %>
+
+ <% if @ipn.payment.present? %>
+ <%= link_to "Payment", @ipn.payment %>
+ <% else %>
+ Couldn't link '<%= @ipn.payer_email %>' or payment amount '<%= @ipn.payment_gross.to_i %>' not a valid membership level. Please create payment manually.
+ <% end %>
+
+
+<%= link_to "Back", ipns_path %>
\ No newline at end of file
diff --git a/app/views/payments/_form.html.erb b/app/views/payments/_form.html.erb
index dd04aad..72043e4 100644
--- a/app/views/payments/_form.html.erb
+++ b/app/views/payments/_form.html.erb
@@ -19,6 +19,10 @@
<%= f.label :date, "Paid for month beginning" %>
<%= f.date_select :date, :default => (DateTime.now - 1.month) %>
+
+ <%= f.label :amount %>
+ <%= f.number_field :amount %>
+
<%= f.submit %>
diff --git a/app/views/payments/index.html.erb b/app/views/payments/index.html.erb
index 9be4eff..58bbd83 100644
--- a/app/views/payments/index.html.erb
+++ b/app/views/payments/index.html.erb
@@ -6,6 +6,7 @@
User |
Paid for month beginning |
+ Amount |
|
|
|
@@ -15,6 +16,7 @@
<%= link_to payment.user.name_with_payee_and_member_level, payment.user unless payment.user.blank? %> |
<%= payment.human_date %> |
+ <%= payment.amount %> |
<%= link_to 'Details', payment %> |
<%= link_to 'Edit', edit_payment_path(payment) %> |
diff --git a/app/views/payments/show.html.erb b/app/views/payments/show.html.erb
index 59906ce..c735823 100644
--- a/app/views/payments/show.html.erb
+++ b/app/views/payments/show.html.erb
@@ -8,9 +8,19 @@
<%= @payment.date %>
+
+ Amount:
+ <%= @payment.amount %>
+
+
Last Modified by:
- <%= user = @users.find{|u| u.id == @payment.created_by}; link_to user.name, user unless user.blank? %>
+ <% user = @users.find{|u| u.id == @payment.created_by} %>
+ <% if user.blank? %>
+ n/a
+ <% else %>
+ <%= link_to user.name, user %>
+ <% end %>
@@ -23,6 +33,13 @@
<%= @payment.updated_at %>
+<% if @payment.ipn.present? %>
+
+ <%= link_to "Paid via PayPal", @payment.ipn %>
+
+<% end %>
+
+
<%= link_to 'Edit', edit_payment_path(@payment) %> |
<%= link_to 'Destroy', @payment, :confirm => 'Are you sure you want to destroy this payment?', :method => :delete if can? :destroy, @payment %> |
diff --git a/config/routes.rb b/config/routes.rb
index 518a30a..6c64774 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -1,4 +1,6 @@
Dooraccess::Application.routes.draw do
+ resources :ipns
+ match 'ipns/:id/link' => 'ipns#link', :as => :link_ipn
resources :payments
diff --git a/db/migrate/20130824062334_create_ipns.rb b/db/migrate/20130824062334_create_ipns.rb
new file mode 100644
index 0000000..fad43b4
--- /dev/null
+++ b/db/migrate/20130824062334_create_ipns.rb
@@ -0,0 +1,24 @@
+class CreateIpns < ActiveRecord::Migration
+ def change
+ create_table :ipns do |t|
+ t.integer :payment_id
+ t.text :data
+ t.string :txn_id
+ t.string :txn_type
+ t.string :first_name
+ t.string :last_name
+ t.string :payer_business_name
+ t.string :payer_email
+ t.string :payer_id
+ t.string :auth_amount
+ t.string :payment_date
+ t.string :payment_fee
+ t.string :payment_gross
+ t.string :payment_status
+ t.string :item_name
+ t.string :payment_type
+
+ t.timestamps
+ end
+ end
+end
diff --git a/db/migrate/20130824072157_add_amount_to_payments.rb b/db/migrate/20130824072157_add_amount_to_payments.rb
new file mode 100644
index 0000000..37b65e6
--- /dev/null
+++ b/db/migrate/20130824072157_add_amount_to_payments.rb
@@ -0,0 +1,5 @@
+class AddAmountToPayments < ActiveRecord::Migration
+ def change
+ add_column :payments, :amount, :decimal
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 19fced1..915b9a5 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -1,3 +1,4 @@
+# encoding: UTF-8
# This file is auto-generated from the current state of the database. Instead
# of editing this file, please use the migrations feature of Active Record to
# incrementally modify your database, and then regenerate this schema definition.
@@ -10,7 +11,7 @@
#
# It's strongly recommended to check this file into your version control system.
-ActiveRecord::Schema.define(:version => 20130212083412) do
+ActiveRecord::Schema.define(:version => 20130824072157) do
create_table "cards", :force => true do |t|
t.string "card_number"
@@ -35,6 +36,27 @@ ActiveRecord::Schema.define(:version => 20130212083412) do
t.datetime "updated_at", :null => false
end
+ create_table "ipns", :force => true do |t|
+ t.integer "payment_id"
+ t.text "data"
+ t.string "txn_id"
+ t.string "txn_type"
+ t.string "first_name"
+ t.string "last_name"
+ t.string "payer_business_name"
+ t.string "payer_email"
+ t.string "payer_id"
+ t.string "auth_amount"
+ t.string "payment_date"
+ t.string "payment_fee"
+ t.string "payment_gross"
+ t.string "payment_status"
+ t.string "item_name"
+ t.string "payment_type"
+ t.datetime "created_at", :null => false
+ t.datetime "updated_at", :null => false
+ end
+
create_table "mac_logs", :force => true do |t|
t.string "mac"
t.string "ip"
@@ -64,6 +86,7 @@ ActiveRecord::Schema.define(:version => 20130212083412) do
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
t.integer "created_by"
+ t.decimal "amount"
end
add_index "payments", ["user_id"], :name => "index_payments_on_user_id"
diff --git a/test/fixtures/ipns.yml b/test/fixtures/ipns.yml
new file mode 100644
index 0000000..6d2f6e4
--- /dev/null
+++ b/test/fixtures/ipns.yml
@@ -0,0 +1,7 @@
+# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/Fixtures.html
+
+one:
+ data: MyText
+
+two:
+ data: MyText
diff --git a/test/functional/ipn_controller_test.rb b/test/functional/ipn_controller_test.rb
new file mode 100644
index 0000000..0d5ac04
--- /dev/null
+++ b/test/functional/ipn_controller_test.rb
@@ -0,0 +1,7 @@
+require 'test_helper'
+
+class IpnControllerTest < ActionController::TestCase
+ # test "the truth" do
+ # assert true
+ # end
+end
diff --git a/test/unit/helpers/ipn_helper_test.rb b/test/unit/helpers/ipn_helper_test.rb
new file mode 100644
index 0000000..be0cbae
--- /dev/null
+++ b/test/unit/helpers/ipn_helper_test.rb
@@ -0,0 +1,4 @@
+require 'test_helper'
+
+class IpnHelperTest < ActionView::TestCase
+end
diff --git a/test/unit/ipn_test.rb b/test/unit/ipn_test.rb
new file mode 100644
index 0000000..fbdc7fa
--- /dev/null
+++ b/test/unit/ipn_test.rb
@@ -0,0 +1,7 @@
+require 'test_helper'
+
+class IpnTest < ActiveSupport::TestCase
+ # test "the truth" do
+ # assert true
+ # end
+end