catarse_paypal_express 0.1.0 → 1.0.0
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 +0 -2
- data/.rspec +2 -0
- data/.travis.yml +13 -0
- data/Gemfile +2 -126
- data/Gemfile.lock +88 -477
- data/README.md +3 -12
- 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/catarse_paypal_express/paypal_express_controller.rb +113 -0
- data/app/views/catarse_paypal_express/paypal_express/review.html.slim +13 -0
- data/catarse_paypal_express.gemspec +4 -3
- data/config/initializers/active_merchant.rb +2 -0
- data/config/initializers/register.rb +5 -0
- data/config/locales/en.yml +7 -1
- data/config/locales/pt.yml +6 -0
- data/config/routes.rb +11 -5
- data/lib/catarse_paypal_express/version.rb +1 -1
- data/spec/controllers/catarse_paypal_express/paypal_express_controller_spec.rb +304 -0
- data/spec/fixtures/ipn_data.txt +1 -0
- data/spec/spec_helper.rb +12 -26
- data/spec/support/payment_engines.rb +3 -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 +58 -0
- data/test/dummy/config/boot.rb +10 -0
- data/test/dummy/config/database.yml +52 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +37 -0
- data/test/dummy/config/environments/production.rb +67 -0
- data/test/dummy/config/environments/test.rb +37 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/inflections.rb +15 -0
- data/test/dummy/config/initializers/mime_types.rb +5 -0
- data/test/dummy/config/initializers/secret_token.rb +7 -0
- data/test/dummy/config/initializers/session_store.rb +8 -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 +112 -34
- data/app/controllers/catarse_paypal_express/payment/paypal_express_controller.rb +0 -119
- data/lib/catarse_paypal_express/processors.rb +0 -5
- data/lib/catarse_paypal_express/processors/paypal.rb +0 -26
- data/spec/controllers/catarse_paypal_express/payment/paypal_express_controller_spec.rb +0 -177
- data/spec/lib/processors/paypal_spec.rb +0 -33
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# CatarsePaypalExpress
|
1
|
+
# CatarsePaypalExpress [](https://travis-ci.org/catarse/catarse_paypal_express)
|
2
2
|
|
3
3
|
Catarse paypal express integration with [Catarse](http://github.com/danielweinmann/catarse) crowdfunding platform
|
4
4
|
|
@@ -38,22 +38,13 @@ Clone the repository:
|
|
38
38
|
|
39
39
|
Add the catarse code into test/dummy:
|
40
40
|
|
41
|
-
$ git submodule
|
42
|
-
|
43
|
-
Copy the Catarse's gems to Gemfile:
|
44
|
-
|
45
|
-
$ cat test/dummy/Gemfile >> Gemfile
|
41
|
+
$ git submodule init
|
42
|
+
$ git submodule update
|
46
43
|
|
47
44
|
And then execute:
|
48
45
|
|
49
46
|
$ bundle
|
50
47
|
|
51
|
-
## Troubleshooting in development environment
|
52
|
-
|
53
|
-
Remove the admin folder from test/dummy application to prevent a weird active admin bug:
|
54
|
-
|
55
|
-
$ rm -rf test/dummy/app/admin
|
56
|
-
|
57
48
|
## Contributing
|
58
49
|
|
59
50
|
1. Fork it
|
@@ -0,0 +1,19 @@
|
|
1
|
+
App.addChild('PayPalForm', _.extend({
|
2
|
+
el: '#catarse_paypal_express_form',
|
3
|
+
|
4
|
+
events: {
|
5
|
+
'click input[type=submit]': 'onSubmitToPayPal',
|
6
|
+
'keyup #user_document' : 'onUserDocumentKeyup'
|
7
|
+
},
|
8
|
+
|
9
|
+
activate: function() {
|
10
|
+
this.loader = $('.loader');
|
11
|
+
this.parent.backerId = $('input#backer_id').val();
|
12
|
+
this.parent.projectId = $('input#project_id').val();
|
13
|
+
},
|
14
|
+
|
15
|
+
onSubmitToPayPal: function(e) {
|
16
|
+
$(e.currentTarget).hide();
|
17
|
+
this.loader.show();
|
18
|
+
}
|
19
|
+
}, window.PayPal.UserDocument));
|
@@ -0,0 +1,111 @@
|
|
1
|
+
var PayPal = window.PayPal = { UserDocument: {
|
2
|
+
onContentClick: function(e){
|
3
|
+
window.setTimeout(function(){
|
4
|
+
this.moipForm.checkoutSuccessful({'StatusPagamento': 'Success'});
|
5
|
+
}, 2000);
|
6
|
+
},
|
7
|
+
|
8
|
+
onUserDocumentKeyup: function(e){
|
9
|
+
var $documentField = $(e.currentTarget);
|
10
|
+
|
11
|
+
var documentNumber = $documentField.val();
|
12
|
+
$documentField.prop('maxlength', 18);
|
13
|
+
var resultCpf = this.validateCpf(documentNumber);
|
14
|
+
var resultCnpj = this.validateCnpj(documentNumber.replace(/[\/.\-\_ ]/g, ''));
|
15
|
+
var numberLength = documentNumber.replace(/[.\-\_ ]/g, '').length
|
16
|
+
if(numberLength > 10) {
|
17
|
+
if($documentField.attr('id') != 'payment_card_cpf'){
|
18
|
+
if(numberLength == 11) {$documentField.mask('999.999.999-99?999'); }//CPF
|
19
|
+
else if(numberLength == 14 ){$documentField.mask('99.999.999/9999-99');}//CNPJ
|
20
|
+
if(numberLength != 14 || numberLength != 11){ $documentField.unmask()}
|
21
|
+
}
|
22
|
+
|
23
|
+
if(resultCpf || resultCnpj) {
|
24
|
+
$documentField.addClass('ok').removeClass('error');
|
25
|
+
|
26
|
+
$.post('/projects/' + this.parent.projectId + '/backers/' + this.parent.backerId + '/update_info', {
|
27
|
+
backer: { payer_document: documentNumber }
|
28
|
+
});
|
29
|
+
|
30
|
+
} else {
|
31
|
+
$documentField.addClass('error').removeClass('ok');
|
32
|
+
}
|
33
|
+
}
|
34
|
+
else{
|
35
|
+
$documentField.addClass('error').removeClass('ok');
|
36
|
+
}
|
37
|
+
|
38
|
+
},
|
39
|
+
|
40
|
+
validateCpf: function(cpfString){
|
41
|
+
var product = 0, i, digit;
|
42
|
+
cpfString = cpfString.replace(/[.\-\_ ]/g, '');
|
43
|
+
var aux = Math.floor(parseFloat(cpfString) / 100);
|
44
|
+
var cpf = aux * 100;
|
45
|
+
var quotient;
|
46
|
+
|
47
|
+
for(i=0; i<=8; i++){
|
48
|
+
product += (aux % 10) * (i+2);
|
49
|
+
aux = Math.floor(aux / 10);
|
50
|
+
}
|
51
|
+
digit = product % 11 < 2 ? 0 : 11 - (product % 11);
|
52
|
+
cpf += (digit * 10);
|
53
|
+
product = 0;
|
54
|
+
aux = Math.floor(cpf / 10);
|
55
|
+
for(i=0; i<=9; i++){
|
56
|
+
product += (aux % 10) * (i+2);
|
57
|
+
aux = Math.floor(aux / 10);
|
58
|
+
}
|
59
|
+
digit = product % 11 < 2 ? 0 : 11 - (product % 11);
|
60
|
+
cpf += digit;
|
61
|
+
return parseFloat(cpfString) === cpf;
|
62
|
+
},
|
63
|
+
|
64
|
+
validateCnpj: function(cnpj) {
|
65
|
+
var numeros, digitos, soma, i, resultado, pos, tamanho, digitos_iguais;
|
66
|
+
digitos_iguais = 1;
|
67
|
+
if (cnpj.length < 14 && cnpj.length < 15)
|
68
|
+
return false;
|
69
|
+
for (i = 0; i < cnpj.length - 1; i++)
|
70
|
+
if (cnpj.charAt(i) != cnpj.charAt(i + 1))
|
71
|
+
{
|
72
|
+
digitos_iguais = 0;
|
73
|
+
break;
|
74
|
+
}
|
75
|
+
if (!digitos_iguais)
|
76
|
+
{
|
77
|
+
tamanho = cnpj.length - 2
|
78
|
+
numeros = cnpj.substring(0,tamanho);
|
79
|
+
digitos = cnpj.substring(tamanho);
|
80
|
+
soma = 0;
|
81
|
+
pos = tamanho - 7;
|
82
|
+
for (i = tamanho; i >= 1; i--)
|
83
|
+
{
|
84
|
+
soma += numeros.charAt(tamanho - i) * pos--;
|
85
|
+
if (pos < 2)
|
86
|
+
pos = 9;
|
87
|
+
}
|
88
|
+
resultado = soma % 11 < 2 ? 0 : 11 - soma % 11;
|
89
|
+
if (resultado != digitos.charAt(0))
|
90
|
+
return false;
|
91
|
+
tamanho = tamanho + 1;
|
92
|
+
numeros = cnpj.substring(0,tamanho);
|
93
|
+
soma = 0;
|
94
|
+
pos = tamanho - 7;
|
95
|
+
for (i = tamanho; i >= 1; i--)
|
96
|
+
{
|
97
|
+
soma += numeros.charAt(tamanho - i) * pos--;
|
98
|
+
if (pos < 2)
|
99
|
+
pos = 9;
|
100
|
+
}
|
101
|
+
resultado = soma % 11 < 2 ? 0 : 11 - soma % 11;
|
102
|
+
if (resultado != digitos.charAt(1))
|
103
|
+
return false;
|
104
|
+
return true;
|
105
|
+
}
|
106
|
+
else
|
107
|
+
return false;
|
108
|
+
}
|
109
|
+
}};
|
110
|
+
|
111
|
+
|
@@ -0,0 +1,113 @@
|
|
1
|
+
class CatarsePaypalExpress::PaypalExpressController < ApplicationController
|
2
|
+
skip_before_filter :force_http
|
3
|
+
SCOPE = "projects.backers.checkout"
|
4
|
+
layout :false
|
5
|
+
|
6
|
+
def review
|
7
|
+
end
|
8
|
+
|
9
|
+
def ipn
|
10
|
+
if backer
|
11
|
+
process_paypal_message params
|
12
|
+
backer.update_attributes({
|
13
|
+
:payment_service_fee => params['mc_fee'],
|
14
|
+
:payer_email => params['payer_email']
|
15
|
+
})
|
16
|
+
else
|
17
|
+
return render status: 500, text: e.inspect
|
18
|
+
end
|
19
|
+
return render status: 200, nothing: true
|
20
|
+
rescue Exception => e
|
21
|
+
return render status: 500, text: e.inspect
|
22
|
+
end
|
23
|
+
|
24
|
+
def pay
|
25
|
+
begin
|
26
|
+
response = gateway.setup_purchase(backer.price_in_cents, {
|
27
|
+
ip: request.remote_ip,
|
28
|
+
return_url: success_paypal_expres_url(id: backer.id),
|
29
|
+
cancel_return_url: cancel_paypal_expres_url(id: backer.id),
|
30
|
+
currency_code: 'BRL',
|
31
|
+
description: t('paypal_description', scope: SCOPE, :project_name => backer.project.name, :value => backer.display_value),
|
32
|
+
notify_url: ipn_paypal_express_url
|
33
|
+
})
|
34
|
+
|
35
|
+
process_paypal_message response.params
|
36
|
+
backer.update_attributes payment_method: 'PayPal', payment_token: response.token
|
37
|
+
|
38
|
+
redirect_to gateway.redirect_url_for(response.token)
|
39
|
+
rescue Exception => e
|
40
|
+
Rails.logger.info "-----> #{e.inspect}"
|
41
|
+
flash[:failure] = t('paypal_error', scope: SCOPE)
|
42
|
+
return redirect_to main_app.new_project_backer_path(backer.project)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def success
|
47
|
+
begin
|
48
|
+
purchase = gateway.purchase(backer.price_in_cents, {
|
49
|
+
ip: request.remote_ip,
|
50
|
+
token: backer.payment_token,
|
51
|
+
payer_id: params[:PayerID]
|
52
|
+
})
|
53
|
+
|
54
|
+
# we must get the deatils after the purchase in order to get the transaction_id
|
55
|
+
process_paypal_message purchase.params
|
56
|
+
backer.update_attributes payment_id: purchase.params['transaction_id'] if purchase.params['transaction_id']
|
57
|
+
|
58
|
+
flash[:success] = t('success', scope: SCOPE)
|
59
|
+
redirect_to main_app.project_backer_path(project_id: backer.project.id, id: backer.id)
|
60
|
+
rescue Exception => e
|
61
|
+
Rails.logger.info "-----> #{e.inspect}"
|
62
|
+
flash[:failure] = t('paypal_error', scope: SCOPE)
|
63
|
+
return redirect_to main_app.new_project_backer_path(backer.project)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def cancel
|
68
|
+
flash[:failure] = t('paypal_cancel', scope: SCOPE)
|
69
|
+
redirect_to main_app.new_project_backer_path(backer.project)
|
70
|
+
end
|
71
|
+
|
72
|
+
def backer
|
73
|
+
@backer ||= if params['id']
|
74
|
+
PaymentEngines.find_payment(id: params['id'])
|
75
|
+
elsif params['txn_id']
|
76
|
+
PaymentEngines.find_payment(payment_id: params['txn_id']) || (params['parent_txn_id'] && PaymentEngines.find_payment(payment_id: params['parent_txn_id']))
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def process_paypal_message(data)
|
81
|
+
extra_data = (data['charset'] ? JSON.parse(data.to_json.force_encoding(data['charset']).encode('utf-8')) : data)
|
82
|
+
PaymentEngines.create_payment_notification backer_id: backer.id, extra_data: extra_data
|
83
|
+
|
84
|
+
if data["checkout_status"] == 'PaymentActionCompleted'
|
85
|
+
backer.confirm!
|
86
|
+
elsif data["payment_status"]
|
87
|
+
case data["payment_status"].downcase
|
88
|
+
when 'completed'
|
89
|
+
backer.confirm!
|
90
|
+
when 'refunded'
|
91
|
+
backer.refund!
|
92
|
+
when 'canceled_reversal'
|
93
|
+
backer.cancel!
|
94
|
+
when 'expired', 'denied'
|
95
|
+
backer.pendent!
|
96
|
+
else
|
97
|
+
backer.waiting! if backer.pending?
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def gateway
|
103
|
+
if PaymentEngines.configuration[:paypal_username] and PaymentEngines.configuration[:paypal_password] and PaymentEngines.configuration[:paypal_signature]
|
104
|
+
@gateway ||= ActiveMerchant::Billing::PaypalExpressGateway.new({
|
105
|
+
login: PaymentEngines.configuration[:paypal_username],
|
106
|
+
password: PaymentEngines.configuration[:paypal_password],
|
107
|
+
signature: PaymentEngines.configuration[:paypal_signature]
|
108
|
+
})
|
109
|
+
else
|
110
|
+
puts "[PayPal] An API Certificate or API Signature is required to make requests to PayPal"
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
= javascript_include_tag 'catarse_paypal_express'
|
2
|
+
|
3
|
+
h3= t('projects.backers.review.international.section_title')
|
4
|
+
|
5
|
+
#catarse_paypal_express_form
|
6
|
+
= form_tag pay_paypal_expres_path(params[:id]) do
|
7
|
+
.clearfix
|
8
|
+
.bootstrap-twitter
|
9
|
+
= label_tag 'user_document', t('projects.backers.review.international.user_document_label')
|
10
|
+
.clearfix
|
11
|
+
= text_field_tag 'user_document', nil, { autocomplete: 'off' }
|
12
|
+
.loader.hide= image_tag 'loading.gif'
|
13
|
+
= submit_tag t('projects.backers.review.international.button'), :class => 'btn btn-primary btn-large'
|
@@ -8,8 +8,8 @@ require "catarse_paypal_express/version"
|
|
8
8
|
Gem::Specification.new do |s|
|
9
9
|
s.name = "catarse_paypal_express"
|
10
10
|
s.version = CatarsePaypalExpress::VERSION
|
11
|
-
s.authors = ["Antônio Roberto Silva"]
|
12
|
-
s.email = ["forevertonny@gmail.com"]
|
11
|
+
s.authors = ["Antônio Roberto Silva", "Diogo Biazus", "Josemar Davi Luedke"]
|
12
|
+
s.email = ["forevertonny@gmail.com", "diogob@gmail.com", "josemarluedke@gmail.com"]
|
13
13
|
s.homepage = "http://github.com/devton/catarse_paypal_express"
|
14
14
|
s.summary = "PaypalExpress integration with Catarse"
|
15
15
|
s.description = "PaypalExpress integration with Catarse crowdfunding platform"
|
@@ -18,7 +18,8 @@ Gem::Specification.new do |s|
|
|
18
18
|
s.test_files = s.files.grep(%r{^(test|spec|features)/})
|
19
19
|
|
20
20
|
s.add_dependency "rails", "~> 3.2.6"
|
21
|
-
s.add_dependency "activemerchant", "
|
21
|
+
s.add_dependency "activemerchant", ">= 1.17.0"
|
22
|
+
s.add_dependency "slim-rails"
|
22
23
|
|
23
24
|
s.add_development_dependency "rspec-rails"
|
24
25
|
s.add_development_dependency "factory_girl_rails"
|
data/config/locales/en.yml
CHANGED
@@ -1,8 +1,14 @@
|
|
1
1
|
en:
|
2
2
|
projects:
|
3
3
|
backers:
|
4
|
+
review:
|
5
|
+
paypal: 'PayPal'
|
6
|
+
international:
|
7
|
+
section_title: 'You will be directed to the PayPal site to complete payment.'
|
8
|
+
user_document_label: 'CPF / CNPJ (only for Brazilians)'
|
9
|
+
button: 'Redirect to PayPal'
|
4
10
|
checkout:
|
5
11
|
paypal_cancel: "Your PayPal payment was canceled. Please try again."
|
6
|
-
paypal_description: "Back project"
|
12
|
+
paypal_description: "Back project %{project_name} with %{value} (BRL)"
|
7
13
|
paypal_error: "Ooops. There was an error while sending your payment to PayPal. Please try again."
|
8
14
|
success: "Your back was successfully made. Thanks a lot!"
|
data/config/locales/pt.yml
CHANGED
@@ -1,6 +1,12 @@
|
|
1
1
|
pt:
|
2
2
|
projects:
|
3
3
|
backers:
|
4
|
+
review:
|
5
|
+
paypal: 'Pagamento internacional - PayPal'
|
6
|
+
international:
|
7
|
+
section_title: 'Você será direcionado para o site do Paypal para completar o pagamento.'
|
8
|
+
user_document_label: 'CPF / CNPJ (somente números) opcional'
|
9
|
+
button: 'Ir para o paypal'
|
4
10
|
checkout:
|
5
11
|
paypal_cancel: "Seu pagamento no PayPal foi cancelado. Por favor, tente novamente."
|
6
12
|
paypal_description: "Apoio para o projeto %{project_name} no valor de %{value}"
|
data/config/routes.rb
CHANGED
@@ -1,9 +1,15 @@
|
|
1
1
|
CatarsePaypalExpress::Engine.routes.draw do
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
2
|
+
resources :paypal_express, only: [], path: 'payment/paypal_express' do
|
3
|
+
collection do
|
4
|
+
post :ipn
|
5
|
+
end
|
6
|
+
|
7
|
+
member do
|
8
|
+
get :review
|
9
|
+
match :pay
|
10
|
+
match :success
|
11
|
+
match :cancel
|
12
|
+
end
|
7
13
|
end
|
8
14
|
end
|
9
15
|
|
@@ -0,0 +1,304 @@
|
|
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(backer)
|
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(:backer){ double('backer', {
|
21
|
+
id: 1,
|
22
|
+
key: 'backer 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
|
+
}) }
|
42
|
+
|
43
|
+
describe "GET review" do
|
44
|
+
before do
|
45
|
+
get :review, id: backer.id, use_route: 'catarse_paypal_express'
|
46
|
+
end
|
47
|
+
it{ should render_template(:review) }
|
48
|
+
end
|
49
|
+
|
50
|
+
describe "POST ipn" do
|
51
|
+
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"} }
|
52
|
+
|
53
|
+
let(:backer){ double(:backer, :payment_id => ipn_data['txn_id'] ) }
|
54
|
+
|
55
|
+
before do
|
56
|
+
params = ipn_data.merge({ use_route: 'catarse_paypal_express' })
|
57
|
+
backer.should_receive(:update_attributes).with({
|
58
|
+
payment_service_fee: ipn_data['mc_fee'],
|
59
|
+
payer_email: ipn_data['payer_email']
|
60
|
+
})
|
61
|
+
controller.should_receive(:process_paypal_message).with(ipn_data.merge({
|
62
|
+
"controller"=>"catarse_paypal_express/paypal_express",
|
63
|
+
"action"=>"ipn"
|
64
|
+
}))
|
65
|
+
post :ipn, params
|
66
|
+
end
|
67
|
+
|
68
|
+
its(:status){ should == 200 }
|
69
|
+
its(:body){ should == ' ' }
|
70
|
+
end
|
71
|
+
|
72
|
+
describe "GET pay" do
|
73
|
+
before do
|
74
|
+
set_paypal_response
|
75
|
+
get :pay, { id: backer.id, locale: 'en', use_route: 'catarse_paypal_express' }
|
76
|
+
end
|
77
|
+
|
78
|
+
|
79
|
+
context 'when response raises a exception' do
|
80
|
+
let(:set_paypal_response) do
|
81
|
+
main_app.should_receive(:new_project_backer_path).with(backer.project).and_return('error url')
|
82
|
+
gateway.should_receive(:setup_purchase).and_raise(StandardError)
|
83
|
+
end
|
84
|
+
it 'should assign flash error' do
|
85
|
+
controller.flash[:failure].should == I18n.t('paypal_error', scope: SCOPE)
|
86
|
+
end
|
87
|
+
it{ should redirect_to 'error url' }
|
88
|
+
end
|
89
|
+
|
90
|
+
context 'when successul' do
|
91
|
+
let(:set_paypal_response) do
|
92
|
+
success_response = double('success_response', {
|
93
|
+
token: 'ABCD',
|
94
|
+
params: { 'correlation_id' => '123' }
|
95
|
+
})
|
96
|
+
gateway.should_receive(:setup_purchase).with(
|
97
|
+
backer.price_in_cents,
|
98
|
+
{
|
99
|
+
ip: request.remote_ip,
|
100
|
+
return_url: 'http://test.host/catarse_paypal_express/payment/paypal_express/1/success',
|
101
|
+
cancel_return_url: 'http://test.host/catarse_paypal_express/payment/paypal_express/1/cancel',
|
102
|
+
currency_code: 'BRL',
|
103
|
+
description: I18n.t('paypal_description', scope: SCOPE, :project_name => backer.project.name, :value => backer.display_value),
|
104
|
+
notify_url: 'http://test.host/catarse_paypal_express/payment/paypal_express/ipn'
|
105
|
+
}
|
106
|
+
).and_return(success_response)
|
107
|
+
backer.should_receive(:update_attributes).with({
|
108
|
+
payment_method: "PayPal",
|
109
|
+
payment_token: "ABCD"
|
110
|
+
})
|
111
|
+
gateway.should_receive(:redirect_url_for).with('ABCD').and_return('success url')
|
112
|
+
end
|
113
|
+
it{ should redirect_to 'success url' }
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
describe "GET cancel" do
|
118
|
+
before do
|
119
|
+
main_app.should_receive(:new_project_backer_path).with(backer.project).and_return('new backer url')
|
120
|
+
get :cancel, { id: backer.id, locale: 'en', use_route: 'catarse_paypal_express' }
|
121
|
+
end
|
122
|
+
it 'should show for user the flash message' do
|
123
|
+
controller.flash[:failure].should == I18n.t('paypal_cancel', scope: SCOPE)
|
124
|
+
end
|
125
|
+
it{ should redirect_to 'new backer url' }
|
126
|
+
end
|
127
|
+
|
128
|
+
describe "GET success" do
|
129
|
+
let(:success_details){ double('success_details', params: {'transaction_id' => '12345', "checkout_status" => "PaymentActionCompleted"}) }
|
130
|
+
let(:params){{ id: backer.id, PayerID: '123', locale: 'en', use_route: 'catarse_paypal_express' }}
|
131
|
+
|
132
|
+
before do
|
133
|
+
gateway.should_receive(:purchase).with(backer.price_in_cents, {
|
134
|
+
ip: request.remote_ip,
|
135
|
+
token: backer.payment_token,
|
136
|
+
payer_id: params[:PayerID]
|
137
|
+
}).and_return(success_details)
|
138
|
+
controller.should_receive(:process_paypal_message).with(success_details.params)
|
139
|
+
backer.should_receive(:update_attributes).with(payment_id: '12345')
|
140
|
+
set_redirect_expectations
|
141
|
+
get :success, params
|
142
|
+
end
|
143
|
+
|
144
|
+
context "when purchase is successful" do
|
145
|
+
let(:set_redirect_expectations) do
|
146
|
+
main_app.
|
147
|
+
should_receive(:project_backer_path).
|
148
|
+
with(project_id: backer.project.id, id: backer.id).
|
149
|
+
and_return('back url')
|
150
|
+
end
|
151
|
+
it{ should redirect_to 'back url' }
|
152
|
+
it 'should assign flash message' do
|
153
|
+
controller.flash[:success].should == I18n.t('success', scope: SCOPE)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
context 'when paypal purchase raises some error' do
|
158
|
+
let(:set_redirect_expectations) do
|
159
|
+
main_app.
|
160
|
+
should_receive(:project_backer_path).
|
161
|
+
with(project_id: backer.project.id, id: backer.id).
|
162
|
+
and_raise('error')
|
163
|
+
main_app.
|
164
|
+
should_receive(:new_project_backer_path).
|
165
|
+
with(backer.project).
|
166
|
+
and_return('new back url')
|
167
|
+
end
|
168
|
+
it 'should assign flash error' do
|
169
|
+
controller.flash[:failure].should == I18n.t('paypal_error', scope: SCOPE)
|
170
|
+
end
|
171
|
+
it{ should redirect_to 'new back url' }
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
describe "#gateway" do
|
176
|
+
before do
|
177
|
+
controller.stub(:gateway).and_call_original
|
178
|
+
PaymentEngines.stub(:configuration).and_return(paypal_config)
|
179
|
+
end
|
180
|
+
subject{ controller.gateway }
|
181
|
+
context "when we have the paypal configuration" do
|
182
|
+
let(:paypal_config) do
|
183
|
+
{ paypal_username: 'username', paypal_password: 'pass', paypal_signature: 'signature' }
|
184
|
+
end
|
185
|
+
before do
|
186
|
+
ActiveMerchant::Billing::PaypalExpressGateway.should_receive(:new).with({
|
187
|
+
login: PaymentEngines.configuration[:paypal_username],
|
188
|
+
password: PaymentEngines.configuration[:paypal_password],
|
189
|
+
signature: PaymentEngines.configuration[:paypal_signature]
|
190
|
+
}).and_return('gateway instance')
|
191
|
+
end
|
192
|
+
it{ should == 'gateway instance' }
|
193
|
+
end
|
194
|
+
|
195
|
+
context "when we do not have the paypal configuration" do
|
196
|
+
let(:paypal_config){ {} }
|
197
|
+
before do
|
198
|
+
ActiveMerchant::Billing::PaypalExpressGateway.should_not_receive(:new)
|
199
|
+
end
|
200
|
+
it{ should be_nil }
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
describe "#backer" do
|
205
|
+
subject{ controller.backer }
|
206
|
+
context "when we have an id" do
|
207
|
+
before do
|
208
|
+
controller.stub(:params).and_return({'id' => '1'})
|
209
|
+
PaymentEngines.should_receive(:find_payment).with(id: '1').and_return(backer)
|
210
|
+
end
|
211
|
+
it{ should == backer }
|
212
|
+
end
|
213
|
+
|
214
|
+
context "when we have an txn_id that does not return backer but a parent_txn_id that does" do
|
215
|
+
before do
|
216
|
+
controller.stub(:params).and_return({'txn_id' => '1', 'parent_txn_id' => '2'})
|
217
|
+
PaymentEngines.should_receive(:find_payment).with(payment_id: '1').and_return(nil)
|
218
|
+
PaymentEngines.should_receive(:find_payment).with(payment_id: '2').and_return(backer)
|
219
|
+
end
|
220
|
+
it{ should == backer }
|
221
|
+
end
|
222
|
+
|
223
|
+
context "when we do not have any id" do
|
224
|
+
before do
|
225
|
+
controller.stub(:params).and_return({})
|
226
|
+
PaymentEngines.should_not_receive(:find_payment)
|
227
|
+
end
|
228
|
+
it{ should be_nil }
|
229
|
+
end
|
230
|
+
|
231
|
+
context "when we have an txn_id" do
|
232
|
+
before do
|
233
|
+
controller.stub(:params).and_return({'txn_id' => '1'})
|
234
|
+
PaymentEngines.should_receive(:find_payment).with(payment_id: '1').and_return(backer)
|
235
|
+
end
|
236
|
+
it{ should == backer }
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
describe "#process_paypal_message" do
|
241
|
+
subject{ controller.process_paypal_message data }
|
242
|
+
let(:data){ {'test_data' => true} }
|
243
|
+
before do
|
244
|
+
controller.stub(:params).and_return({'id' => 1})
|
245
|
+
PaymentEngines.should_receive(:create_payment_notification).with(backer_id: backer.id, extra_data: data)
|
246
|
+
end
|
247
|
+
|
248
|
+
context "when data['checkout_status'] == 'PaymentActionCompleted'" do
|
249
|
+
let(:data){ {'checkout_status' => 'PaymentActionCompleted'} }
|
250
|
+
before do
|
251
|
+
backer.should_receive(:confirm!)
|
252
|
+
end
|
253
|
+
it("should call confirm"){ subject }
|
254
|
+
end
|
255
|
+
|
256
|
+
context "some real data with revert op" do
|
257
|
+
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" } }
|
258
|
+
before do
|
259
|
+
backer.should_receive(:refund!)
|
260
|
+
end
|
261
|
+
it("should call refund"){ subject }
|
262
|
+
end
|
263
|
+
|
264
|
+
context "when it's a refund message" do
|
265
|
+
let(:data){ {'payment_status' => 'refunded'} }
|
266
|
+
before do
|
267
|
+
backer.should_receive(:refund!)
|
268
|
+
end
|
269
|
+
it("should call refund"){ subject }
|
270
|
+
end
|
271
|
+
|
272
|
+
context "when it's a completed message" do
|
273
|
+
let(:data){ {'payment_status' => 'Completed'} }
|
274
|
+
before do
|
275
|
+
backer.should_receive(:confirm!)
|
276
|
+
end
|
277
|
+
it("should call confirm"){ subject }
|
278
|
+
end
|
279
|
+
|
280
|
+
context "when it's a cancelation message" do
|
281
|
+
let(:data){ {'payment_status' => 'canceled_reversal'} }
|
282
|
+
before do
|
283
|
+
backer.should_receive(:cancel!)
|
284
|
+
end
|
285
|
+
it("should call cancel"){ subject }
|
286
|
+
end
|
287
|
+
|
288
|
+
context "when it's a payment expired message" do
|
289
|
+
let(:data){ {'payment_status' => 'expired'} }
|
290
|
+
before do
|
291
|
+
backer.should_receive(:pendent!)
|
292
|
+
end
|
293
|
+
it("should call pendent"){ subject }
|
294
|
+
end
|
295
|
+
|
296
|
+
context "all other values of payment_status" do
|
297
|
+
let(:data){ {'payment_status' => 'other'} }
|
298
|
+
before do
|
299
|
+
backer.should_receive(:waiting!)
|
300
|
+
end
|
301
|
+
it("should call waiting"){ subject }
|
302
|
+
end
|
303
|
+
end
|
304
|
+
end
|