Skip to content

Commit

Permalink
Refactored payment to use authenticate/authorise for the initial paym…
Browse files Browse the repository at this point in the history
…ent,

and then create repeat payments for subsequent payments.
  • Loading branch information
John Cinnamond committed Jun 23, 2009
1 parent 741b154 commit 3cb4b5e
Show file tree
Hide file tree
Showing 12 changed files with 213 additions and 68 deletions.
4 changes: 2 additions & 2 deletions app/controllers/payment_methods_controller.rb
Expand Up @@ -35,14 +35,14 @@ def get_payment_method
def new_payment_method
@payment_method = @current_user.payment_methods.
build(params[:payment_method])
@payment_method.organisation =
@payment_method.organisation =
@current_user.organisations.find(params[:organisation_id])

@payment_method.build_billing_address :country => "United Kingdom"

if (params[:payment_method] &&
params[:payment_method][:billing_address_attributes])
@payment_method.billing_address.attributes =
@payment_method.billing_address.attributes =
params[:payment_method][:billing_address_attributes]
end
end
Expand Down
3 changes: 2 additions & 1 deletion app/models/authorisation.rb
Expand Up @@ -10,7 +10,8 @@ def before_create
self.response = gateway.authorize(
amount,
payment_method.credit_card,
:order_id => payment.vendor_tx_code
:order_id => payment.vendor_tx_code,
:authenticate => true
)

self.status = response.params['Status']
Expand Down
2 changes: 1 addition & 1 deletion app/models/capture.rb
Expand Up @@ -7,7 +7,7 @@ class Capture < ActiveRecord::Base
belongs_to :payment

def before_create
self.response = gateway.capture(amount, authorization)
self.response = gateway.capture(amount, authorization, :authenticate => true)

self.status = response.params['Status']
self.status_detail = response.params['StatusDetail']
Expand Down
15 changes: 10 additions & 5 deletions app/models/organisation.rb
Expand Up @@ -64,14 +64,19 @@ def take_payment
end
raise NoPaymentMethod unless has_valid_payment_method?

repeat = Repeat.create!(
:authorization => payment_method.repeat_payment_token,
payment = payment_method.initial_payment.capture_or_repeat(
:amount => payment_plan.total * 100,
:description => name,
:organisation => self
:description => name
)

if repeat.successful?
# repeat = Repeat.create!(
# :authorization => payment_method.initial_payment.reference,
# :amount => payment_plan.total * 100,
# :description => name,
# :organisation => self
# )

if payment.successful?
self.update_attribute(:next_payment_date, next_payment_date >> 1)
else
payment_method.update_attribute(:has_failed, true)
Expand Down
13 changes: 13 additions & 0 deletions app/models/payment.rb
Expand Up @@ -12,4 +12,17 @@ def before_create
def reference
[vendor_tx_code, vpstxid, tx_auth_no, security_key].join(';')
end

def capture_or_repeat(params)
if capture.nil?
Capture.create!(
params.delete_if{ |k,v| k == :description }.merge(:payment => self)
)
else
Repeat.create(params.merge(
:organisation => organisation,
:authorization => reference
))
end
end
end
8 changes: 4 additions & 4 deletions app/models/payment_method.rb
Expand Up @@ -8,6 +8,7 @@ class PaymentMethod < ActiveRecord::Base
accepts_nested_attributes_for :billing_address
belongs_to :user
belongs_to :organisation
belongs_to :initial_payment, :class_name => 'Payment'

CARD_TYPES = ['mastercard', 'visa']
SHARED_ATTRIBUTES = [
Expand Down Expand Up @@ -49,7 +50,7 @@ def validate

def before_create
set_last_four_digits
test_payment
authorise_initial_payment
end

def year=(value)
Expand Down Expand Up @@ -91,15 +92,14 @@ def create_credit_card
card
end

def test_payment
def authorise_initial_payment
authorisation = Authorisation.create!(:payment_method => self,
:amount => 100)
if ! authorisation.successful?
errors.add_to_base(:credit_card_rejected)
return false
end
self.repeat_payment_token = authorisation.payment.reference
authorisation.void
self.initial_payment = authorisation.payment
end

protected
Expand Down
@@ -0,0 +1,11 @@
class ReplaceRepeatPaymentTokenWithInitialPaymentInPaymentMethods < ActiveRecord::Migration
def self.up
remove_column :payment_methods, :repeat_payment_token
add_column :payment_methods, :initial_payment_id, :integer
end

def self.down
add_column :payment_methods, :repeat_payment_token, :text
remove_column :payment_methods, :initial_payment_id
end
end
7 changes: 7 additions & 0 deletions spec/models/authorisation_spec.rb
Expand Up @@ -62,6 +62,13 @@ def do_protx_action
do_protx_action
end

it "should set authorization type to authenticate" do
@gateway.should_receive(:authorize) do |amount, card, args|
args[:authenticate].should be_true
end
do_protx_action
end

it "should store the status" do
do_protx_action
@authorisation.status.should == "OK"
Expand Down
11 changes: 9 additions & 2 deletions spec/models/capture_spec.rb
Expand Up @@ -26,19 +26,26 @@ def do_protx_action
end

it "should pass in the amount" do
@gateway.should_receive(:capture) do |amount, authorization|
@gateway.should_receive(:capture) do |amount, authorization, options|
amount.should == @amount
end
do_protx_action
end

it "should pass in the authorization" do
@gateway.should_receive(:capture) do |amount, authorization|
@gateway.should_receive(:capture) do |amount, authorization, options|
authorization.should == @authorization
end
do_protx_action
end

it "should set authenticate to true" do
@gateway.should_receive(:capture) do |amount, authorization, options|
options[:authenticate].should be_true
end
do_protx_action
end

it "should store the status" do
do_protx_action
@capture.status.should == "OK"
Expand Down
77 changes: 42 additions & 35 deletions spec/models/organisation_spec.rb
Expand Up @@ -182,47 +182,54 @@
:verification_value => '123',
:cardholder_name => 'Joe Bloggs',
:month => Date.today.month,
:year => Date.today.year + 1,
:repeat_payment_token => @token
:year => Date.today.year + 1
)
end

it "should create a new repeat payment" do
repeat = Repeat.new
Repeat.should_receive(:new).and_return(repeat)
@organisation.take_payment
@initial_payment = @payment_method.initial_payment
end

it "should pass the repeat payment token to the Repeat payment" do
repeat = Repeat.new
Repeat.should_receive(:new).with do |params|
params[:authorization].should == @payment_method.repeat_payment_token
end.and_return(repeat)
@organisation.take_payment
end
describe "when no capture exists for the initial payment" do
it "should capture the initial payment" do
capture = Capture.new
Capture.should_receive(:new).and_return(capture)
@organisation.take_payment
end

it "should pass in the amount from the plan" do
repeat = Repeat.new
Repeat.should_receive(:new).with do |params|
params[:amount].should == @payment_plan.total * 100
end.and_return(repeat)
@organisation.take_payment
it "should pass in the amount from the plan" do
capture = Capture.new
Capture.should_receive(:new).with do |params|
params[:amount].should == @payment_plan.total * 100
end.and_return(capture)
@organisation.take_payment
end
end

it "should pass in a description" do
repeat = Repeat.new
Repeat.should_receive(:new).with do |params|
params[:description].should == @organisation.name
end.and_return(repeat)
@organisation.take_payment
end
describe "when a capture exists for the initial payment" do
before :each do
Capture.create!(:payment => @initial_payment)
end

it "should pass in itself as organisation" do
repeat = Repeat.new
Repeat.should_receive(:new).
with(hash_including(:organisation => @organisation)).
and_return(repeat)
@organisation.take_payment
it "should create a new repeat payment" do
repeat = Repeat.new
Repeat.should_receive(:new).and_return(repeat)
@organisation.take_payment
end

it "should pass in the amount from the plan" do
repeat = Repeat.new
Repeat.should_receive(:new).with do |params|
params[:amount].should == @payment_plan.total * 100
end.and_return(repeat)
@organisation.take_payment
end

it "should pass in a description" do
repeat = Repeat.new
Repeat.should_receive(:new).with do |params|
params[:description].should == @organisation.name
end.and_return(repeat)
@organisation.take_payment
end
end

it "should set the next payment date to next month" do
Expand Down Expand Up @@ -282,8 +289,8 @@

describe "failure to take payment" do
before :each do
@repeat = mock_model(Repeat, :successful? => false)
Repeat.stub!(:create!).and_return(@repeat)
@capture = mock_model(Capture, :successful? => false)
Capture.stub!(:create!).and_return(@capture)
@user = Users.create_user!
@payment_method.user = @user
@payment_method.save!
Expand Down
33 changes: 15 additions & 18 deletions spec/models/payment_method_spec.rb
Expand Up @@ -51,6 +51,10 @@
it "should have one organisation" do
PaymentMethod.should belong_to(:organisation)
end

it "should belong to an inital payment" do
PaymentMethod.should belong_to(:initial_payment)
end
end

describe "has_expired?" do
Expand Down Expand Up @@ -95,28 +99,21 @@
end
end

describe "test_payment" do
describe "authorise_initial_payment" do
before :each do
@payment_method = PaymentMethod.new
end

it "should create a new authorization" do
authorisation_count = Authorisation.count
@payment_method.test_payment
Authorisation.count.should == authorisation_count + 1
end

it "should store the repeat_payment_token" do
@payment_method.test_payment
last_payment = Payment.find(:last, :order => 'id')
@payment_method.repeat_payment_token.should ==
last_payment.reference
it "should create a new authorisation" do
lambda {
@payment_method.authorise_initial_payment
}.should change(Authorisation, :count).by(1)
end

it "should create a new void" do
void_count = Void.count
@payment_method.test_payment
Void.count.should == void_count + 1
it "should store the ID of the initial payment" do
@payment_method.authorise_initial_payment
last_authorisation = Authorisation.last
@payment_method.initial_payment.should == last_authorisation.payment
end

describe "authorization failure" do
Expand All @@ -125,11 +122,11 @@
end

it "should return false" do
@payment_method.test_payment.should == false
@payment_method.authorise_initial_payment.should == false
end

it "should set an error on the base" do
@payment_method.test_payment
@payment_method.authorise_initial_payment
@payment_method.errors.should be_invalid(:base)
end
end
Expand Down

0 comments on commit 3cb4b5e

Please sign in to comment.