funddit_paypal_express 3.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +7 -0
- data/.rspec +2 -0
- data/.travis.yml +13 -0
- data/Gemfile +7 -0
- data/Gemfile.lock +146 -0
- data/MIT-LICENSE +20 -0
- data/README.md +64 -0
- data/Rakefile +38 -0
- data/app/assets/javascripts/catarse_paypal_express.js +6 -0
- data/app/assets/javascripts/catarse_paypal_express/paypal_form.js +19 -0
- data/app/assets/javascripts/catarse_paypal_express/user_document.js +111 -0
- data/app/controllers/.gitkeep +0 -0
- data/app/controllers/catarse_paypal_express/paypal_express_controller.rb +113 -0
- data/app/views/catarse_paypal_express/paypal_express/review.html.slim +20 -0
- data/catarse_paypal_express.gemspec +27 -0
- data/config/initializers/active_merchant.rb +2 -0
- data/config/initializers/register.rb +5 -0
- data/config/locales/en.yml +17 -0
- data/config/locales/pt.yml +17 -0
- data/config/routes.rb +15 -0
- data/lib/catarse_paypal_express.rb +8 -0
- data/lib/catarse_paypal_express/contribution_actions.rb +20 -0
- data/lib/catarse_paypal_express/engine.rb +6 -0
- data/lib/catarse_paypal_express/gateway.rb +36 -0
- data/lib/catarse_paypal_express/payment_engine.rb +25 -0
- data/lib/catarse_paypal_express/version.rb +3 -0
- data/lib/tasks/catarse_paypal_express_tasks.rake +4 -0
- data/script/rails +9 -0
- data/spec/controllers/catarse_paypal_express/paypal_express_controller_spec.rb +363 -0
- data/spec/fixtures/ipn_data.txt +1 -0
- data/spec/lib/catarse_paypal_express/contribution_actions_spec.rb +41 -0
- data/spec/spec_helper.rb +61 -0
- data/spec/support/payment_engines.rb +5 -0
- data/test/dummy/README.rdoc +261 -0
- data/test/dummy/Rakefile +7 -0
- data/test/dummy/app/assets/javascripts/application.js +15 -0
- data/test/dummy/app/assets/stylesheets/application.css +13 -0
- data/test/dummy/app/controllers/application_controller.rb +3 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/app/mailers/.gitkeep +0 -0
- data/test/dummy/app/models/.gitkeep +0 -0
- data/test/dummy/app/views/layouts/application.html.erb +14 -0
- data/test/dummy/config.ru +4 -0
- data/test/dummy/config/application.rb +23 -0
- data/test/dummy/config/boot.rb +10 -0
- data/test/dummy/config/database.yml +50 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +29 -0
- data/test/dummy/config/environments/production.rb +80 -0
- data/test/dummy/config/environments/test.rb +36 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/test/dummy/config/initializers/inflections.rb +16 -0
- data/test/dummy/config/initializers/mime_types.rb +5 -0
- data/test/dummy/config/initializers/secret_token.rb +12 -0
- data/test/dummy/config/initializers/session_store.rb +3 -0
- data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/test/dummy/config/locales/en.yml +5 -0
- data/test/dummy/config/routes.rb +4 -0
- data/test/dummy/lib/assets/.gitkeep +0 -0
- data/test/dummy/log/.gitkeep +0 -0
- data/test/dummy/public/404.html +26 -0
- data/test/dummy/public/422.html +26 -0
- data/test/dummy/public/500.html +25 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/dummy/script/rails +6 -0
- metadata +231 -0
File without changes
|
@@ -0,0 +1,113 @@
|
|
1
|
+
class CatarsePaypalExpress::PaypalExpressController < ApplicationController
|
2
|
+
include ActiveMerchant::Billing::Integrations
|
3
|
+
|
4
|
+
skip_before_filter :force_http
|
5
|
+
SCOPE = "projects.contributions.checkout"
|
6
|
+
layout :false
|
7
|
+
|
8
|
+
def review
|
9
|
+
end
|
10
|
+
|
11
|
+
def ipn
|
12
|
+
if contribution && notification.acknowledge && (contribution.payment_method == 'PayPal' || contribution.payment_method.nil?)
|
13
|
+
process_paypal_message params
|
14
|
+
contribution.update_attributes({
|
15
|
+
:payment_service_fee => params['mc_fee'],
|
16
|
+
:payer_email => params['payer_email']
|
17
|
+
})
|
18
|
+
else
|
19
|
+
return render status: 500, nothing: true
|
20
|
+
end
|
21
|
+
return render status: 200, nothing: true
|
22
|
+
rescue Exception => e
|
23
|
+
return render status: 500, text: e.inspect
|
24
|
+
end
|
25
|
+
|
26
|
+
def pay
|
27
|
+
begin
|
28
|
+
response = gateway.setup_purchase(contribution.price_in_cents, {
|
29
|
+
ip: request.remote_ip,
|
30
|
+
return_url: success_paypal_express_url(id: contribution.id),
|
31
|
+
cancel_return_url: cancel_paypal_express_url(id: contribution.id),
|
32
|
+
currency_code: 'BRL',
|
33
|
+
description: t('paypal_description', scope: SCOPE, :project_name => contribution.project.name, :value => contribution.display_value),
|
34
|
+
notify_url: ipn_paypal_express_index_url
|
35
|
+
})
|
36
|
+
|
37
|
+
process_paypal_message response.params
|
38
|
+
contribution.update_attributes payment_method: 'PayPal', payment_token: response.token
|
39
|
+
|
40
|
+
redirect_to gateway.redirect_url_for(response.token)
|
41
|
+
rescue Exception => e
|
42
|
+
Rails.logger.info "-----> #{e.inspect}"
|
43
|
+
flash[:failure] = t('paypal_error', scope: SCOPE)
|
44
|
+
return redirect_to main_app.new_project_contribution_path(contribution.project)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def success
|
49
|
+
begin
|
50
|
+
purchase = gateway.purchase(contribution.price_in_cents, {
|
51
|
+
ip: request.remote_ip,
|
52
|
+
token: contribution.payment_token,
|
53
|
+
payer_id: params[:PayerID]
|
54
|
+
})
|
55
|
+
|
56
|
+
# we must get the deatils after the purchase in order to get the transaction_id
|
57
|
+
process_paypal_message purchase.params
|
58
|
+
contribution.update_attributes payment_id: purchase.params['transaction_id'] if purchase.params['transaction_id']
|
59
|
+
|
60
|
+
flash[:success] = t('success', scope: SCOPE)
|
61
|
+
redirect_to main_app.project_contribution_path(project_id: contribution.project.id, id: contribution.id)
|
62
|
+
rescue Exception => e
|
63
|
+
Rails.logger.info "-----> #{e.inspect}"
|
64
|
+
flash[:failure] = t('paypal_error', scope: SCOPE)
|
65
|
+
return redirect_to main_app.new_project_contribution_path(contribution.project)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def cancel
|
70
|
+
flash[:failure] = t('paypal_cancel', scope: SCOPE)
|
71
|
+
redirect_to main_app.new_project_contribution_path(contribution.project)
|
72
|
+
end
|
73
|
+
|
74
|
+
def contribution
|
75
|
+
@contribution ||= if params['id']
|
76
|
+
PaymentEngines.find_payment(id: params['id'])
|
77
|
+
elsif params['txn_id']
|
78
|
+
PaymentEngines.find_payment(payment_id: params['txn_id']) || (params['parent_txn_id'] && PaymentEngines.find_payment(payment_id: params['parent_txn_id']))
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def process_paypal_message(data)
|
83
|
+
extra_data = (data['charset'] ? JSON.parse(data.to_json.force_encoding(data['charset']).encode('utf-8')) : data)
|
84
|
+
PaymentEngines.create_payment_notification contribution_id: contribution.id, extra_data: extra_data
|
85
|
+
|
86
|
+
if data["checkout_status"] == 'PaymentActionCompleted'
|
87
|
+
contribution.confirm!
|
88
|
+
elsif data["payment_status"]
|
89
|
+
case data["payment_status"].downcase
|
90
|
+
when 'completed'
|
91
|
+
contribution.confirm!
|
92
|
+
when 'refunded'
|
93
|
+
contribution.refund!
|
94
|
+
when 'canceled_reversal'
|
95
|
+
contribution.cancel!
|
96
|
+
when 'expired', 'denied'
|
97
|
+
contribution.pendent!
|
98
|
+
else
|
99
|
+
contribution.waiting! if contribution.pending?
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def gateway
|
105
|
+
@gateway ||= CatarsePaypalExpress::Gateway.instance
|
106
|
+
end
|
107
|
+
|
108
|
+
protected
|
109
|
+
|
110
|
+
def notification
|
111
|
+
@notification ||= Paypal::Notification.new(request.raw_post)
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
= javascript_include_tag 'catarse_paypal_express'
|
2
|
+
|
3
|
+
#catarse_paypal_express_form
|
4
|
+
= form_tag pay_paypal_express_path(params[:id]) do
|
5
|
+
.w-row
|
6
|
+
.w-col.w-col-6
|
7
|
+
= label_tag 'user_document', t('projects.contributions.review.international.user_document_label'), class: 'field-label fontweight-semibold'
|
8
|
+
= text_field_tag 'user_document', nil, { autocomplete: 'off', class: 'w-input text-field', data: {mask: '999.999.999-99'} }
|
9
|
+
.w-row
|
10
|
+
.w-col.w-col-12
|
11
|
+
.card.card-message.fontsize-small.u-radius.zindex-10.u-marginbottom-30 = t('projects.contributions.review.international.section_title')
|
12
|
+
.w-row
|
13
|
+
.w-col.w-col-push-3.w-col-6
|
14
|
+
.loader.u-text-center.w-col.w-col-12.u-marginbottom-30 = image_tag "catarse_bootstrap/loader.gif"
|
15
|
+
= submit_tag t('projects.contributions.review.international.button'), :class => 'btn btn-large u-marginbottom-20'
|
16
|
+
.fontsize-smallest.u-text-center
|
17
|
+
| Ao apoiar, você concorda com os
|
18
|
+
= link_to 'Termos de Uso', main_app.terms_of_use_path, class: 'alt-link'
|
19
|
+
| e
|
20
|
+
= link_to 'Política de Privacidade', main_app.privacy_policy_path, class: 'alt-link'
|
@@ -0,0 +1,27 @@
|
|
1
|
+
#encoding: utf-8
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
|
4
|
+
# Maintain your gem's version:
|
5
|
+
require "catarse_paypal_express/version"
|
6
|
+
|
7
|
+
# Describe your gem and declare its dependencies:
|
8
|
+
Gem::Specification.new do |s|
|
9
|
+
s.name = "funddit_paypal_express"
|
10
|
+
s.version = CatarsePaypalExpress::VERSION
|
11
|
+
s.authors = ["Joshua Chua"]
|
12
|
+
s.email = ["funddit@gmail.com"]
|
13
|
+
s.homepage = "http://funddit.me"
|
14
|
+
s.summary = "PaypalExpress integration with Funddit"
|
15
|
+
s.description = "PaypalExpress integration with Funddit crowdfunding platform"
|
16
|
+
|
17
|
+
s.files = `git ls-files`.split($\)
|
18
|
+
s.test_files = s.files.grep(%r{^(test|spec|features)/})
|
19
|
+
|
20
|
+
s.add_dependency "rails", "~> 4.0"
|
21
|
+
s.add_dependency "activemerchant", ">= 1.34.0"
|
22
|
+
s.add_dependency "slim-rails"
|
23
|
+
|
24
|
+
s.add_development_dependency "rspec-rails", '~> 2.14.0'
|
25
|
+
s.add_development_dependency "factory_girl_rails"
|
26
|
+
s.add_development_dependency "database_cleaner"
|
27
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
en:
|
2
|
+
projects:
|
3
|
+
contributions:
|
4
|
+
refund:
|
5
|
+
success: 'Pedido de reembolso enviado com sucesso!'
|
6
|
+
error: 'OPS! Ocorreu um erro ao tentar enviar o pedido de reembolso.'
|
7
|
+
review:
|
8
|
+
paypal: 'PayPal'
|
9
|
+
international:
|
10
|
+
section_title: 'You will be directed to the PayPal site to complete payment.'
|
11
|
+
user_document_label: 'CPF / CNPJ (only for Brazilians)'
|
12
|
+
button: 'Redirect to PayPal'
|
13
|
+
checkout:
|
14
|
+
paypal_cancel: "Your PayPal payment was canceled. Please try again."
|
15
|
+
paypal_description: "Back project %{project_name} with %{value} (BRL)"
|
16
|
+
paypal_error: "Ooops. There was an error while sending your payment to PayPal. Please try again."
|
17
|
+
success: "Your back was successfully made. Thanks a lot!"
|
@@ -0,0 +1,17 @@
|
|
1
|
+
pt:
|
2
|
+
projects:
|
3
|
+
contributions:
|
4
|
+
refund:
|
5
|
+
success: 'Pedido de reembolso enviado com sucesso!'
|
6
|
+
error: 'OPS! Ocorreu um erro ao tentar enviar o pedido de reembolso.'
|
7
|
+
review:
|
8
|
+
paypal: 'Pagamento internacional - PayPal'
|
9
|
+
international:
|
10
|
+
section_title: 'Você será direcionado para o site do Paypal para completar o pagamento.'
|
11
|
+
user_document_label: 'CPF / CNPJ (somente números) opcional'
|
12
|
+
button: 'Ir para o paypal'
|
13
|
+
checkout:
|
14
|
+
paypal_cancel: "Seu pagamento no PayPal foi cancelado. Por favor, tente novamente."
|
15
|
+
paypal_description: "Apoio para o projeto %{project_name} no valor de %{value}"
|
16
|
+
paypal_error: "Ooops. Ocorreu um erro ao enviar seu pagamento para o PayPal. Por favor, tente novamente."
|
17
|
+
success: "Seu apoio foi realizado com sucesso. Muito obrigado!"
|
data/config/routes.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
module CatarsePaypalExpress
|
2
|
+
class ContributionActions
|
3
|
+
|
4
|
+
def initialize contribution
|
5
|
+
@contribution = contribution
|
6
|
+
end
|
7
|
+
|
8
|
+
def refund
|
9
|
+
refund_request = gateway.refund(nil, @contribution.payment_id)
|
10
|
+
refund_request.success?
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def gateway
|
16
|
+
@gateway ||= CatarsePaypalExpress::Gateway.instance
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module CatarsePaypalExpress
|
2
|
+
class Gateway
|
3
|
+
include ActiveMerchant::Billing::Integrations
|
4
|
+
|
5
|
+
def initialize configuration = PaymentEngines.configuration
|
6
|
+
@configuration = configuration
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.instance
|
10
|
+
new.setup_gateway
|
11
|
+
rescue Exception => e
|
12
|
+
puts e.message
|
13
|
+
end
|
14
|
+
|
15
|
+
def setup_gateway
|
16
|
+
check_default_configurations!
|
17
|
+
ActiveMerchant::Billing::PaypalExpressGateway.new({
|
18
|
+
login: @configuration[:paypal_username],
|
19
|
+
password: @configuration[:paypal_password],
|
20
|
+
signature: @configuration[:paypal_signature]
|
21
|
+
})
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def check_default_configurations!
|
27
|
+
%i(paypal_username paypal_password paypal_signature).each do |key|
|
28
|
+
raise pending_configuration_message unless @configuration[key].present?
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def pending_configuration_message
|
33
|
+
"[PayPal] An API Certificate or API Signature is required to make requests to PayPal"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module CatarsePaypalExpress
|
2
|
+
class PaymentEngine
|
3
|
+
|
4
|
+
def name
|
5
|
+
'PayPal'
|
6
|
+
end
|
7
|
+
|
8
|
+
def review_path contribution
|
9
|
+
CatarsePaypalExpress::Engine.routes.url_helpers.review_paypal_express_path(contribution)
|
10
|
+
end
|
11
|
+
|
12
|
+
def can_do_refund?
|
13
|
+
true
|
14
|
+
end
|
15
|
+
|
16
|
+
def direct_refund contribution
|
17
|
+
CatarsePaypalExpress::ContributionActions.new(contribution).refund
|
18
|
+
end
|
19
|
+
|
20
|
+
def locale
|
21
|
+
'en'
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
data/script/rails
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application.
|
3
|
+
|
4
|
+
ENGINE_ROOT = File.expand_path('../..', __FILE__)
|
5
|
+
ENGINE_PATH = File.expand_path('../../lib/catarse_paypal_express/engine', __FILE__)
|
6
|
+
|
7
|
+
require 'rails/all'
|
8
|
+
require 'rails/engine/commands'
|
9
|
+
|
@@ -0,0 +1,363 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe CatarsePaypalExpress::PaypalExpressController do
|
6
|
+
SCOPE = CatarsePaypalExpress::PaypalExpressController::SCOPE
|
7
|
+
before do
|
8
|
+
PaymentEngines.stub(:find_payment).and_return(contribution)
|
9
|
+
PaymentEngines.stub(:create_payment_notification)
|
10
|
+
controller.stub(:main_app).and_return(main_app)
|
11
|
+
controller.stub(:current_user).and_return(current_user)
|
12
|
+
controller.stub(:gateway).and_return(gateway)
|
13
|
+
end
|
14
|
+
|
15
|
+
subject{ response }
|
16
|
+
let(:gateway){ double('gateway') }
|
17
|
+
let(:main_app){ double('main_app') }
|
18
|
+
let(:current_user) { double('current_user') }
|
19
|
+
let(:project){ double('project', id: 1, name: 'test project') }
|
20
|
+
let(:contribution){ double('contribution', {
|
21
|
+
id: 1,
|
22
|
+
key: 'contribution key',
|
23
|
+
payment_id: 'payment id',
|
24
|
+
project: project,
|
25
|
+
pending?: true,
|
26
|
+
value: 10,
|
27
|
+
display_value: 'R$ 10,00',
|
28
|
+
price_in_cents: 1000,
|
29
|
+
user: current_user,
|
30
|
+
payer_name: 'foo',
|
31
|
+
payer_email: 'foo@bar.com',
|
32
|
+
payment_token: 'token',
|
33
|
+
address_street: 'test',
|
34
|
+
address_number: '123',
|
35
|
+
address_complement: '123',
|
36
|
+
address_neighbourhood: '123',
|
37
|
+
address_city: '123',
|
38
|
+
address_state: '123',
|
39
|
+
address_zip_code: '123',
|
40
|
+
address_phone_number: '123',
|
41
|
+
payment_method: 'PayPal'
|
42
|
+
}) }
|
43
|
+
|
44
|
+
describe "GET review" do
|
45
|
+
before do
|
46
|
+
get :review, id: contribution.id, use_route: 'catarse_paypal_express'
|
47
|
+
end
|
48
|
+
it{ should render_template(:review) }
|
49
|
+
end
|
50
|
+
|
51
|
+
describe "POST ipn" do
|
52
|
+
let(:ipn_data){ {"mc_gross"=>"50.00", "protection_eligibility"=>"Eligible", "address_status"=>"unconfirmed", "payer_id"=>"S7Q8X88KMGX5S", "tax"=>"0.00", "address_street"=>"Rua Tatui, 40 ap 81\r\nJardins", "payment_date"=>"09:03:01 Nov 05, 2012 PST", "payment_status"=>"Completed", "charset"=>"windows-1252", "address_zip"=>"01409-010", "first_name"=>"Paula", "mc_fee"=>"3.30", "address_country_code"=>"BR", "address_name"=>"Paula Rizzo", "notify_version"=>"3.7", "custom"=>"", "payer_status"=>"verified", "address_country"=>"Brazil", "address_city"=>"Sao Paulo", "quantity"=>"1", "verify_sign"=>"ALBe4QrXe2sJhpq1rIN8JxSbK4RZA.Kfc5JlI9Jk4N1VQVTH5hPYOi2S", "payer_email"=>"paula.rizzo@gmail.com", "txn_id"=>"3R811766V4891372K", "payment_type"=>"instant", "last_name"=>"Rizzo", "address_state"=>"SP", "receiver_email"=>"financeiro@catarse.me", "payment_fee"=>"", "receiver_id"=>"BVUB4EVC7YCWL", "txn_type"=>"express_checkout", "item_name"=>"Back project", "mc_currency"=>"BRL", "item_number"=>"", "residence_country"=>"BR", "handling_amount"=>"0.00", "transaction_subject"=>"Back project", "payment_gross"=>"", "shipping"=>"0.00", "ipn_track_id"=>"5865649c8c27"} }
|
53
|
+
let(:contribution){ double(:contribution, :payment_id => ipn_data['txn_id'], :payment_method => 'PayPal' ) }
|
54
|
+
let(:notification) { double }
|
55
|
+
|
56
|
+
before do
|
57
|
+
controller.stub(:notification).and_return(notification)
|
58
|
+
end
|
59
|
+
|
60
|
+
context "when payment_method is MoIP" do
|
61
|
+
before do
|
62
|
+
params = ipn_data.merge({ use_route: 'catarse_paypal_express' })
|
63
|
+
|
64
|
+
notification.stub(:acknowledge).and_return(true)
|
65
|
+
contribution.stub(:payment_method).and_return('MoIP')
|
66
|
+
|
67
|
+
contribution.should_not_receive(:update_attributes)
|
68
|
+
controller.should_not_receive(:process_paypal_message)
|
69
|
+
|
70
|
+
notification.should_receive(:acknowledge)
|
71
|
+
|
72
|
+
post :ipn, params
|
73
|
+
end
|
74
|
+
|
75
|
+
its(:status){ should == 500 }
|
76
|
+
its(:body){ should == ' ' }
|
77
|
+
end
|
78
|
+
|
79
|
+
context "when is a valid ipn data" do
|
80
|
+
before do
|
81
|
+
params = ipn_data.merge({ use_route: 'catarse_paypal_express' })
|
82
|
+
|
83
|
+
notification.stub(:acknowledge).and_return(true)
|
84
|
+
|
85
|
+
contribution.should_receive(:update_attributes).with({
|
86
|
+
payment_service_fee: ipn_data['mc_fee'],
|
87
|
+
payer_email: ipn_data['payer_email']
|
88
|
+
})
|
89
|
+
controller.should_receive(:process_paypal_message).with(ipn_data.merge({
|
90
|
+
"controller"=>"catarse_paypal_express/paypal_express",
|
91
|
+
"action"=>"ipn"
|
92
|
+
}))
|
93
|
+
|
94
|
+
notification.should_receive(:acknowledge)
|
95
|
+
|
96
|
+
post :ipn, params
|
97
|
+
end
|
98
|
+
|
99
|
+
its(:status){ should == 200 }
|
100
|
+
its(:body){ should == ' ' }
|
101
|
+
end
|
102
|
+
|
103
|
+
context "when is not valid ipn data" do
|
104
|
+
let(:ipn_data){ {"mc_gross"=>"50.00", "payment_status" => 'confirmed', "txn_id" => "3R811766V4891372K", 'payer_email' => 'fake@email.com', 'mc_fee' => '0.0'} }
|
105
|
+
|
106
|
+
before do
|
107
|
+
params = ipn_data.merge({ use_route: 'catarse_paypal_express' })
|
108
|
+
|
109
|
+
notification.stub(:acknowledge).and_return(false)
|
110
|
+
|
111
|
+
contribution.should_receive(:update_attributes).with({
|
112
|
+
payment_service_fee: ipn_data['mc_fee'],
|
113
|
+
payer_email: ipn_data['payer_email']
|
114
|
+
}).never
|
115
|
+
|
116
|
+
controller.should_receive(:process_paypal_message).with(ipn_data.merge({
|
117
|
+
"controller"=>"catarse_paypal_express/paypal_express",
|
118
|
+
"action"=>"ipn"
|
119
|
+
})).never
|
120
|
+
|
121
|
+
notification.should_receive(:acknowledge)
|
122
|
+
|
123
|
+
post :ipn, params
|
124
|
+
end
|
125
|
+
|
126
|
+
its(:status){ should == 500 }
|
127
|
+
its(:body){ should == ' ' }
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
describe "POST pay" do
|
132
|
+
before do
|
133
|
+
set_paypal_response
|
134
|
+
post :pay, { id: contribution.id, locale: 'en', use_route: 'catarse_paypal_express' }
|
135
|
+
end
|
136
|
+
|
137
|
+
|
138
|
+
context 'when response raises a exception' do
|
139
|
+
let(:set_paypal_response) do
|
140
|
+
main_app.should_receive(:new_project_contribution_path).with(contribution.project).and_return('error url')
|
141
|
+
gateway.should_receive(:setup_purchase).and_raise(StandardError)
|
142
|
+
end
|
143
|
+
it 'should assign flash error' do
|
144
|
+
controller.flash[:failure].should == I18n.t('paypal_error', scope: SCOPE)
|
145
|
+
end
|
146
|
+
it{ should redirect_to 'error url' }
|
147
|
+
end
|
148
|
+
|
149
|
+
context 'when successul' do
|
150
|
+
let(:set_paypal_response) do
|
151
|
+
success_response = double('success_response', {
|
152
|
+
token: 'ABCD',
|
153
|
+
params: { 'correlation_id' => '123' }
|
154
|
+
})
|
155
|
+
gateway.should_receive(:setup_purchase).with(
|
156
|
+
contribution.price_in_cents,
|
157
|
+
{
|
158
|
+
ip: request.remote_ip,
|
159
|
+
return_url: 'http://test.host/catarse_paypal_express/payment/paypal_express/1/success',
|
160
|
+
cancel_return_url: 'http://test.host/catarse_paypal_express/payment/paypal_express/1/cancel',
|
161
|
+
currency_code: 'BRL',
|
162
|
+
description: I18n.t('paypal_description', scope: SCOPE, :project_name => contribution.project.name, :value => contribution.display_value),
|
163
|
+
notify_url: 'http://test.host/catarse_paypal_express/payment/paypal_express/ipn'
|
164
|
+
}
|
165
|
+
).and_return(success_response)
|
166
|
+
contribution.should_receive(:update_attributes).with({
|
167
|
+
payment_method: "PayPal",
|
168
|
+
payment_token: "ABCD"
|
169
|
+
})
|
170
|
+
gateway.should_receive(:redirect_url_for).with('ABCD').and_return('success url')
|
171
|
+
end
|
172
|
+
it{ should redirect_to 'success url' }
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
describe "GET cancel" do
|
177
|
+
before do
|
178
|
+
main_app.should_receive(:new_project_contribution_path).with(contribution.project).and_return('new contribution url')
|
179
|
+
get :cancel, { id: contribution.id, locale: 'en', use_route: 'catarse_paypal_express' }
|
180
|
+
end
|
181
|
+
it 'should show for user the flash message' do
|
182
|
+
controller.flash[:failure].should == I18n.t('paypal_cancel', scope: SCOPE)
|
183
|
+
end
|
184
|
+
it{ should redirect_to 'new contribution url' }
|
185
|
+
end
|
186
|
+
|
187
|
+
describe "GET success" do
|
188
|
+
let(:success_details){ double('success_details', params: {'transaction_id' => '12345', "checkout_status" => "PaymentActionCompleted"}) }
|
189
|
+
let(:params){{ id: contribution.id, PayerID: '123', locale: 'en', use_route: 'catarse_paypal_express' }}
|
190
|
+
|
191
|
+
before do
|
192
|
+
gateway.should_receive(:purchase).with(contribution.price_in_cents, {
|
193
|
+
ip: request.remote_ip,
|
194
|
+
token: contribution.payment_token,
|
195
|
+
payer_id: params[:PayerID]
|
196
|
+
}).and_return(success_details)
|
197
|
+
controller.should_receive(:process_paypal_message).with(success_details.params)
|
198
|
+
contribution.should_receive(:update_attributes).with(payment_id: '12345')
|
199
|
+
set_redirect_expectations
|
200
|
+
get :success, params
|
201
|
+
end
|
202
|
+
|
203
|
+
context "when purchase is successful" do
|
204
|
+
let(:set_redirect_expectations) do
|
205
|
+
main_app.
|
206
|
+
should_receive(:project_contribution_path).
|
207
|
+
with(project_id: contribution.project.id, id: contribution.id).
|
208
|
+
and_return('back url')
|
209
|
+
end
|
210
|
+
it{ should redirect_to 'back url' }
|
211
|
+
it 'should assign flash message' do
|
212
|
+
controller.flash[:success].should == I18n.t('success', scope: SCOPE)
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
context 'when paypal purchase raises some error' do
|
217
|
+
let(:set_redirect_expectations) do
|
218
|
+
main_app.
|
219
|
+
should_receive(:project_contribution_path).
|
220
|
+
with(project_id: contribution.project.id, id: contribution.id).
|
221
|
+
and_raise('error')
|
222
|
+
main_app.
|
223
|
+
should_receive(:new_project_contribution_path).
|
224
|
+
with(contribution.project).
|
225
|
+
and_return('new back url')
|
226
|
+
end
|
227
|
+
it 'should assign flash error' do
|
228
|
+
controller.flash[:failure].should == I18n.t('paypal_error', scope: SCOPE)
|
229
|
+
end
|
230
|
+
it{ should redirect_to 'new back url' }
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
describe "#gateway" do
|
235
|
+
before do
|
236
|
+
controller.stub(:gateway).and_call_original
|
237
|
+
PaymentEngines.stub(:configuration).and_return(paypal_config)
|
238
|
+
end
|
239
|
+
subject{ controller.gateway }
|
240
|
+
context "when we have the paypal configuration" do
|
241
|
+
let(:paypal_config) do
|
242
|
+
{ paypal_username: 'username', paypal_password: 'pass', paypal_signature: 'signature' }
|
243
|
+
end
|
244
|
+
before do
|
245
|
+
ActiveMerchant::Billing::PaypalExpressGateway.should_receive(:new).with({
|
246
|
+
login: PaymentEngines.configuration[:paypal_username],
|
247
|
+
password: PaymentEngines.configuration[:paypal_password],
|
248
|
+
signature: PaymentEngines.configuration[:paypal_signature]
|
249
|
+
}).and_return('gateway instance')
|
250
|
+
end
|
251
|
+
it{ should == 'gateway instance' }
|
252
|
+
end
|
253
|
+
|
254
|
+
context "when we do not have the paypal configuration" do
|
255
|
+
let(:paypal_config){ {} }
|
256
|
+
before do
|
257
|
+
ActiveMerchant::Billing::PaypalExpressGateway.should_not_receive(:new)
|
258
|
+
end
|
259
|
+
it{ should be_nil }
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
describe "#contribution" do
|
264
|
+
subject{ controller.contribution }
|
265
|
+
context "when we have an id" do
|
266
|
+
before do
|
267
|
+
controller.stub(:params).and_return({'id' => '1'})
|
268
|
+
PaymentEngines.should_receive(:find_payment).with(id: '1').and_return(contribution)
|
269
|
+
end
|
270
|
+
it{ should == contribution }
|
271
|
+
end
|
272
|
+
|
273
|
+
context "when we have an txn_id that does not return contribution but a parent_txn_id that does" do
|
274
|
+
before do
|
275
|
+
controller.stub(:params).and_return({'txn_id' => '1', 'parent_txn_id' => '2'})
|
276
|
+
PaymentEngines.should_receive(:find_payment).with(payment_id: '1').and_return(nil)
|
277
|
+
PaymentEngines.should_receive(:find_payment).with(payment_id: '2').and_return(contribution)
|
278
|
+
end
|
279
|
+
it{ should == contribution }
|
280
|
+
end
|
281
|
+
|
282
|
+
context "when we do not have any id" do
|
283
|
+
before do
|
284
|
+
controller.stub(:params).and_return({})
|
285
|
+
PaymentEngines.should_not_receive(:find_payment)
|
286
|
+
end
|
287
|
+
it{ should be_nil }
|
288
|
+
end
|
289
|
+
|
290
|
+
context "when we have an txn_id" do
|
291
|
+
before do
|
292
|
+
controller.stub(:params).and_return({'txn_id' => '1'})
|
293
|
+
PaymentEngines.should_receive(:find_payment).with(payment_id: '1').and_return(contribution)
|
294
|
+
end
|
295
|
+
it{ should == contribution }
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
299
|
+
describe "#process_paypal_message" do
|
300
|
+
subject{ controller.process_paypal_message data }
|
301
|
+
let(:data){ {'test_data' => true} }
|
302
|
+
before do
|
303
|
+
controller.stub(:params).and_return({'id' => 1})
|
304
|
+
PaymentEngines.should_receive(:create_payment_notification).with(contribution_id: contribution.id, extra_data: data)
|
305
|
+
end
|
306
|
+
|
307
|
+
context "when data['checkout_status'] == 'PaymentActionCompleted'" do
|
308
|
+
let(:data){ {'checkout_status' => 'PaymentActionCompleted'} }
|
309
|
+
before do
|
310
|
+
contribution.should_receive(:confirm!)
|
311
|
+
end
|
312
|
+
it("should call confirm"){ subject }
|
313
|
+
end
|
314
|
+
|
315
|
+
context "some real data with revert op" do
|
316
|
+
let(:data){ { "mc_gross" => "-150.00","protection_eligibility" => "Eligible","payer_id" => "4DK6S6Q75Z5YS","address_street" => "AV. SAO CARLOS, 2205 - conj 501/502 Centro","payment_date" => "09:55:14 Jun 26, 2013 PDT","payment_status" => "Refunded","charset" => "utf-8","address_zip" => "13560-900","first_name" => "Marcius","mc_fee" => "-8.70","address_country_code" => "BR","address_name" => "Marcius Milori","notify_version" => "3.7","reason_code" => "refund","custom" => "","address_country" => "Brazil","address_city" => "São Carlos","verify_sign" => "AbedXpvDaliC7hltYoQrebkEQft7A.y6bRnDvjPIIB1Mct8-aDGcHkcV","payer_email" => "milorimarcius@gmail.com","parent_txn_id" => "78T862320S496750Y","txn_id" => "9RP43514H84299332","payment_type" => "instant","last_name" => "Milori","address_state" => "São Paulo","receiver_email" => "financeiro@catarse.me","payment_fee" => "","receiver_id" => "BVUB4EVC7YCWL","item_name" => "Apoio para o projeto A Caça (La Chasse) no valor de R$ 150","mc_currency" => "BRL","item_number" => "","residence_country" => "BR","handling_amount" => "0.00","transaction_subject" => "Apoio para o projeto A Caça (La Chasse) no valor de R$ 150","payment_gross" => "","shipping" => "0.00","ipn_track_id" => "18c487e6abca4" } }
|
317
|
+
before do
|
318
|
+
contribution.should_receive(:refund!)
|
319
|
+
end
|
320
|
+
it("should call refund"){ subject }
|
321
|
+
end
|
322
|
+
|
323
|
+
context "when it's a refund message" do
|
324
|
+
let(:data){ {'payment_status' => 'refunded'} }
|
325
|
+
before do
|
326
|
+
contribution.should_receive(:refund!)
|
327
|
+
end
|
328
|
+
it("should call refund"){ subject }
|
329
|
+
end
|
330
|
+
|
331
|
+
context "when it's a completed message" do
|
332
|
+
let(:data){ {'payment_status' => 'Completed'} }
|
333
|
+
before do
|
334
|
+
contribution.should_receive(:confirm!)
|
335
|
+
end
|
336
|
+
it("should call confirm"){ subject }
|
337
|
+
end
|
338
|
+
|
339
|
+
context "when it's a cancelation message" do
|
340
|
+
let(:data){ {'payment_status' => 'canceled_reversal'} }
|
341
|
+
before do
|
342
|
+
contribution.should_receive(:cancel!)
|
343
|
+
end
|
344
|
+
it("should call cancel"){ subject }
|
345
|
+
end
|
346
|
+
|
347
|
+
context "when it's a payment expired message" do
|
348
|
+
let(:data){ {'payment_status' => 'expired'} }
|
349
|
+
before do
|
350
|
+
contribution.should_receive(:pendent!)
|
351
|
+
end
|
352
|
+
it("should call pendent"){ subject }
|
353
|
+
end
|
354
|
+
|
355
|
+
context "all other values of payment_status" do
|
356
|
+
let(:data){ {'payment_status' => 'other'} }
|
357
|
+
before do
|
358
|
+
contribution.should_receive(:waiting!)
|
359
|
+
end
|
360
|
+
it("should call waiting"){ subject }
|
361
|
+
end
|
362
|
+
end
|
363
|
+
end
|