funddit_paypal_express 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|