catarse_paypal_express 0.1.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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