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.
Files changed (61) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +0 -2
  3. data/.rspec +2 -0
  4. data/.travis.yml +13 -0
  5. data/Gemfile +2 -126
  6. data/Gemfile.lock +88 -477
  7. data/README.md +3 -12
  8. data/app/assets/javascripts/catarse_paypal_express.js +6 -0
  9. data/app/assets/javascripts/catarse_paypal_express/paypal_form.js +19 -0
  10. data/app/assets/javascripts/catarse_paypal_express/user_document.js +111 -0
  11. data/app/controllers/catarse_paypal_express/paypal_express_controller.rb +113 -0
  12. data/app/views/catarse_paypal_express/paypal_express/review.html.slim +13 -0
  13. data/catarse_paypal_express.gemspec +4 -3
  14. data/config/initializers/active_merchant.rb +2 -0
  15. data/config/initializers/register.rb +5 -0
  16. data/config/locales/en.yml +7 -1
  17. data/config/locales/pt.yml +6 -0
  18. data/config/routes.rb +11 -5
  19. data/lib/catarse_paypal_express/version.rb +1 -1
  20. data/spec/controllers/catarse_paypal_express/paypal_express_controller_spec.rb +304 -0
  21. data/spec/fixtures/ipn_data.txt +1 -0
  22. data/spec/spec_helper.rb +12 -26
  23. data/spec/support/payment_engines.rb +3 -0
  24. data/test/dummy/README.rdoc +261 -0
  25. data/test/dummy/Rakefile +7 -0
  26. data/test/dummy/app/assets/javascripts/application.js +15 -0
  27. data/test/dummy/app/assets/stylesheets/application.css +13 -0
  28. data/test/dummy/app/controllers/application_controller.rb +3 -0
  29. data/test/dummy/app/helpers/application_helper.rb +2 -0
  30. data/test/dummy/app/mailers/.gitkeep +0 -0
  31. data/test/dummy/app/models/.gitkeep +0 -0
  32. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  33. data/test/dummy/config.ru +4 -0
  34. data/test/dummy/config/application.rb +58 -0
  35. data/test/dummy/config/boot.rb +10 -0
  36. data/test/dummy/config/database.yml +52 -0
  37. data/test/dummy/config/environment.rb +5 -0
  38. data/test/dummy/config/environments/development.rb +37 -0
  39. data/test/dummy/config/environments/production.rb +67 -0
  40. data/test/dummy/config/environments/test.rb +37 -0
  41. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  42. data/test/dummy/config/initializers/inflections.rb +15 -0
  43. data/test/dummy/config/initializers/mime_types.rb +5 -0
  44. data/test/dummy/config/initializers/secret_token.rb +7 -0
  45. data/test/dummy/config/initializers/session_store.rb +8 -0
  46. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  47. data/test/dummy/config/locales/en.yml +5 -0
  48. data/test/dummy/config/routes.rb +4 -0
  49. data/test/dummy/lib/assets/.gitkeep +0 -0
  50. data/test/dummy/log/.gitkeep +0 -0
  51. data/test/dummy/public/404.html +26 -0
  52. data/test/dummy/public/422.html +26 -0
  53. data/test/dummy/public/500.html +25 -0
  54. data/test/dummy/public/favicon.ico +0 -0
  55. data/test/dummy/script/rails +6 -0
  56. metadata +112 -34
  57. data/app/controllers/catarse_paypal_express/payment/paypal_express_controller.rb +0 -119
  58. data/lib/catarse_paypal_express/processors.rb +0 -5
  59. data/lib/catarse_paypal_express/processors/paypal.rb +0 -26
  60. data/spec/controllers/catarse_paypal_express/payment/paypal_express_controller_spec.rb +0 -177
  61. data/spec/lib/processors/paypal_spec.rb +0 -33
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # CatarsePaypalExpress
1
+ # CatarsePaypalExpress [![Build Status](https://travis-ci.org/catarse/catarse_paypal_express.png)](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 add git://github.com/danielweinmann/catarse.git test/dummy
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,6 @@
1
+ //= require ./catarse_paypal_express/user_document
2
+ //= require_tree ./catarse_paypal_express
3
+
4
+ $(function(){
5
+ app.createViewGetters();
6
+ });
@@ -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", "~> 1.17.0"
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"
@@ -0,0 +1,2 @@
1
+ ActiveMerchant::Billing::PaypalExpressGateway.default_currency = 'BRL'
2
+ ActiveMerchant::Billing::Base.mode = :test if (PaymentEngines.configuration[:paypal_test] == 'true' rescue nil)
@@ -0,0 +1,5 @@
1
+ begin
2
+ PaymentEngines.register({name: 'paypal', review_path: ->(backer){ CatarsePaypalExpress::Engine.routes.url_helpers.review_paypal_expres_path(backer) }, locale: 'en'})
3
+ rescue Exception => e
4
+ puts "Error while registering payment engine: #{e}"
5
+ end
@@ -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!"
@@ -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
- namespace :payment do
3
- match '/paypal_express/:id/notifications' => 'paypal_express#notifications', :as => 'notifications_paypal_express'
4
- match '/paypal_express/:id/pay' => 'paypal_express#pay', :as => 'pay_paypal_express'
5
- match '/paypal_express/:id/success' => 'paypal_express#success', :as => 'success_paypal_express'
6
- match '/paypal_express/:id/cancel' => 'paypal_express#cancel', :as => 'cancel_paypal_express'
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
 
@@ -1,3 +1,3 @@
1
1
  module CatarsePaypalExpress
2
- VERSION = "0.1.0"
2
+ VERSION = "1.0.0"
3
3
  end
@@ -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