paypal-express 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. data/.document +5 -0
  2. data/.gitignore +22 -0
  3. data/.rspec +2 -0
  4. data/Gemfile +2 -0
  5. data/LICENSE +20 -0
  6. data/README.rdoc +17 -0
  7. data/Rakefile +20 -0
  8. data/VERSION +1 -0
  9. data/lib/cert +3509 -0
  10. data/lib/paypal.rb +56 -0
  11. data/lib/paypal/base.rb +19 -0
  12. data/lib/paypal/exceptions.rb +21 -0
  13. data/lib/paypal/express.rb +1 -0
  14. data/lib/paypal/express/request.rb +64 -0
  15. data/lib/paypal/express/response.rb +19 -0
  16. data/lib/paypal/nvp/request.rb +59 -0
  17. data/lib/paypal/nvp/response.rb +145 -0
  18. data/lib/paypal/payment/recurring.rb +43 -0
  19. data/lib/paypal/payment/recurring/activation.rb +14 -0
  20. data/lib/paypal/payment/recurring/billing.rb +37 -0
  21. data/lib/paypal/payment/recurring/summary.rb +11 -0
  22. data/lib/paypal/payment/request.rb +24 -0
  23. data/lib/paypal/payment/response.rb +36 -0
  24. data/lib/paypal/payment/response/amount.rb +11 -0
  25. data/lib/paypal/payment/response/info.rb +40 -0
  26. data/lib/paypal/payment/response/payer.rb +7 -0
  27. data/lib/paypal/payment/response/ship_to.rb +7 -0
  28. data/lib/paypal/util.rb +28 -0
  29. data/lib/restclient_with_ssl_support.rb +16 -0
  30. data/paypal-express.gemspec +24 -0
  31. data/spec/fake_response/CreateRecurringPaymentsProfile/failure.txt +1 -0
  32. data/spec/fake_response/CreateRecurringPaymentsProfile/success.txt +1 -0
  33. data/spec/fake_response/DoExpressCheckoutPayment/failure.txt +1 -0
  34. data/spec/fake_response/DoExpressCheckoutPayment/success.txt +1 -0
  35. data/spec/fake_response/GetExpressCheckoutDetails/failure.txt +1 -0
  36. data/spec/fake_response/GetExpressCheckoutDetails/success.txt +1 -0
  37. data/spec/fake_response/GetRecurringPaymentsProfileDetails/failure.txt +1 -0
  38. data/spec/fake_response/GetRecurringPaymentsProfileDetails/success.txt +1 -0
  39. data/spec/fake_response/ManageRecurringPaymentsProfileStatus/failure.txt +1 -0
  40. data/spec/fake_response/ManageRecurringPaymentsProfileStatus/success.txt +1 -0
  41. data/spec/fake_response/SetExpressCheckout/failure.txt +1 -0
  42. data/spec/fake_response/SetExpressCheckout/success.txt +1 -0
  43. data/spec/helpers/fake_response_helper.rb +27 -0
  44. data/spec/paypal/exception_spec.rb +17 -0
  45. data/spec/paypal/express/request_spec.rb +223 -0
  46. data/spec/paypal/express/response_spec.rb +33 -0
  47. data/spec/paypal/nvp/request_spec.rb +88 -0
  48. data/spec/paypal/payment/recurring_spec.rb +114 -0
  49. data/spec/paypal/payment/request_spec.rb +55 -0
  50. data/spec/paypal/payment/response/amount_spec.rb +36 -0
  51. data/spec/paypal/payment/response/info_spec.rb +88 -0
  52. data/spec/paypal/payment/response/payer_spec.rb +26 -0
  53. data/spec/paypal/payment/response/ship_to_spec.rb +26 -0
  54. data/spec/paypal/util_spec.rb +32 -0
  55. data/spec/spec_helper.rb +22 -0
  56. metadata +267 -0
@@ -0,0 +1,37 @@
1
+ module Paypal
2
+ module Payment
3
+ class Recurring::Billing < Base
4
+ attr_optional :period, :frequency, :paid, :currency_code, :total_cycles, :trial_period, :trial_frequency, :trial_total_cycles, :trial_amount, :trial_amount_paid
5
+ attr_accessor :amount
6
+
7
+ def initialize(attributes = {})
8
+ @amount = if attributes[:amount].is_a?(Response::Amount)
9
+ attributes[:amount]
10
+ else
11
+ Response::Amount.new(
12
+ :total => attributes[:amount],
13
+ :tax => attributes[:tax_amount],
14
+ :shipping => attributes[:shipping_amount]
15
+ )
16
+ end
17
+ super
18
+ end
19
+
20
+ def to_params
21
+ {
22
+ :BILLINGPERIOD => self.period,
23
+ :BILLINGFREQUENCY => self.frequency,
24
+ :TOTALBILLINGCYCLES => self.total_cycles,
25
+ :AMT => Util.formatted_amount(self.amount.total),
26
+ :TRIALBILLINGPERIOD => self.trial_period,
27
+ :TRIALBILLINGFREQUENCY => self.trial_frequency,
28
+ :TRIALTOTALBILLINGCYCLES => self.trial_total_cycles,
29
+ :TRIALAMT => Util.formatted_amount(self.trial_amount),
30
+ :CURRENCYCODE => self.currency_code,
31
+ :SHIPPINGAMT => Util.formatted_amount(self.amount.shipping),
32
+ :TAXAMT => Util.formatted_amount(self.amount.tax)
33
+ }
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,11 @@
1
+ module Paypal
2
+ module Payment
3
+ class Recurring::Summary < Base
4
+ attr_optional :next_billing_date, :cycles_completed, :cycles_remaining, :outstanding_balance, :failed_count, :last_payment_date, :last_payment_amount
5
+
6
+ def numeric_attribute?(key)
7
+ super || [:outstanding_balance, :failed_count].include?(key)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,24 @@
1
+ module Paypal
2
+ module Payment
3
+ class Request < Base
4
+ attr_optional :amount, :action, :currency_code, :description, :notify_url, :billing_type, :billing_agreement_description
5
+
6
+ def to_params(index = 0)
7
+ {
8
+ :"PAYMENTREQUEST_#{index}_PAYMENTACTION" => self.action,
9
+ :"PAYMENTREQUEST_#{index}_AMT" => Util.formatted_amount(self.amount),
10
+ :"PAYMENTREQUEST_#{index}_CURRENCYCODE" => self.currency_code,
11
+ :"PAYMENTREQUEST_#{index}_DESC" => self.description,
12
+ # NOTE:
13
+ # notify_url works only when DoExpressCheckoutPayment called.
14
+ # recurring payment doesn't support dynamic notify_url.
15
+ :"PAYMENTREQUEST_#{index}_NOTIFYURL" => self.notify_url,
16
+ :"L_BILLINGTYPE#{index}" => self.billing_type,
17
+ :"L_BILLINGAGREEMENTDESCRIPTION#{index}" => self.billing_agreement_description
18
+ }.delete_if do |k, v|
19
+ v.blank?
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,36 @@
1
+ module Paypal
2
+ module Payment
3
+ class Response < Base
4
+ attr_accessor :amount, :ship_to, :insurance_option_offered, :currency_code, :error_code
5
+
6
+ def initialize(attributes = {})
7
+ attrs = attributes.dup
8
+ @amount = Amount.new(
9
+ :total => attrs.delete(:AMT),
10
+ :handing => attrs.delete(:HANDLINGAMT),
11
+ :insurance => attrs.delete(:INSURANCEAMT),
12
+ :ship_disc => attrs.delete(:SHIPDISCAMT),
13
+ :shipping => attrs.delete(:SHIPPINGAMT),
14
+ :tax => attrs.delete(:TAXAMT)
15
+ )
16
+ @ship_to = Payment::Response::ShipTo.new(
17
+ :name => attrs.delete(:SHIPTONAME),
18
+ :zip => attrs.delete(:SHIPTOZIP),
19
+ :street => attrs.delete(:SHIPTOSTREET),
20
+ :city => attrs.delete(:SHIPTOCITY),
21
+ :state => attrs.delete(:SHIPTOSTATE),
22
+ :country_code => attrs.delete(:SHIPTOCOUNTRYCODE),
23
+ :country_name => attrs.delete(:SHIPTOCOUNTRYNAME)
24
+ )
25
+ @insurance_option_offered = attrs.delete(:INSURANCEOPTIONOFFERED) == 'true'
26
+ @currency_code = attrs.delete(:CURRENCYCODE)
27
+ @error_code = attrs.delete(:ERRORCODE)
28
+
29
+ # warn ignored params
30
+ attrs.each do |key, value|
31
+ Paypal.log "Ignored Parameter (#{self.class}): #{key}=#{value}", :warn
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,11 @@
1
+ module Paypal
2
+ module Payment
3
+ class Response::Amount < Base
4
+ attr_optional :total, :fee, :handing, :insurance, :ship_disc, :shipping, :tax
5
+
6
+ def numeric_attribute?(key)
7
+ true
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,40 @@
1
+ module Paypal
2
+ module Payment
3
+ class Response::Info < Base
4
+ cattr_reader :attribute_mapping
5
+ @@attribute_mapping = {
6
+ :ACK => :ack,
7
+ :CURRENCYCODE => :currency_code,
8
+ :ERRORCODE => :error_code,
9
+ :ORDERTIME => :order_time,
10
+ :PAYMENTSTATUS => :payment_status,
11
+ :PAYMENTTYPE => :payment_type,
12
+ :PENDINGREASON => :pending_reason,
13
+ :PROTECTIONELIGIBILITY => :protection_eligibility,
14
+ :PROTECTIONELIGIBILITYTYPE => :protection_eligibility_type,
15
+ :REASONCODE => :reason_code,
16
+ :TRANSACTIONID => :transaction_id,
17
+ :TRANSACTIONTYPE => :transaction_type
18
+ }
19
+ attr_accessor *@@attribute_mapping.values
20
+ attr_accessor :amount
21
+
22
+ def initialize(attributes = {})
23
+ attrs = attributes.dup
24
+ @@attribute_mapping.each do |key, value|
25
+ self.send "#{value}=", attrs.delete(key)
26
+ end
27
+ @amount = Response::Amount.new(
28
+ :total => attrs.delete(:AMT),
29
+ :fee => attrs.delete(:FEEAMT),
30
+ :tax => attrs.delete(:TAXAMT)
31
+ )
32
+
33
+ # warn ignored params
34
+ attrs.each do |key, value|
35
+ Paypal.log "Ignored Parameter (#{self.class}): #{key}=#{value}", :warn
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,7 @@
1
+ module Paypal
2
+ module Payment
3
+ class Response::Payer < Base
4
+ attr_optional :identifier, :status, :first_name, :last_name, :email
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ module Paypal
2
+ module Payment
3
+ class Response::ShipTo < Base
4
+ attr_optional :owner, :status, :name, :zip, :street, :city, :state, :country_code, :country_name
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,28 @@
1
+ module Paypal
2
+ module Util
3
+
4
+ def self.formatted_amount(x)
5
+ # Thanks @nahi ;)
6
+ sprintf "%0.2f", BigDecimal.new(x.to_s).truncate(2)
7
+ end
8
+
9
+ def self.to_numeric(x)
10
+ if x.to_f == x.to_i
11
+ x.to_i
12
+ else
13
+ x.to_f
14
+ end
15
+ end
16
+
17
+ def numeric_attribute?(key)
18
+ !!(key.to_s =~ /(amount|frequency|cycles|paid)/)
19
+ end
20
+
21
+ def ==(other)
22
+ instance_variables.all? do |key|
23
+ instance_variable_get(key) == other.instance_variable_get(key)
24
+ end
25
+ end
26
+
27
+ end
28
+ end
@@ -0,0 +1,16 @@
1
+ require 'restclient'
2
+
3
+ module RestClient
4
+
5
+ def self.ssl_settings
6
+ {
7
+ :verify_ssl => OpenSSL::SSL::VERIFY_PEER,
8
+ :ssl_ca_file => File.join(File.dirname(__FILE__), 'cert')
9
+ }
10
+ end
11
+
12
+ def self.post(url, payload, headers={}, &block)
13
+ Request.execute(ssl_settings.merge(:method => :post, :url => url, :payload => payload, :headers => headers), &block)
14
+ end
15
+
16
+ end
@@ -0,0 +1,24 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = "paypal-express"
3
+ s.version = File.read(File.join(File.dirname(__FILE__), "VERSION"))
4
+ s.required_rubygems_version = Gem::Requirement.new(">= 1.3.6") if s.respond_to? :required_rubygems_version=
5
+ s.authors = ["nov matake"]
6
+ s.description = %q{Rugy Gem for PayPal Express Checkout API}
7
+ s.summary = %q{PayPal Express Checkout API Client Supporting Both Instant and Recurring Payment}
8
+ s.email = "nov@matake.jp"
9
+ s.extra_rdoc_files = ["LICENSE", "README.rdoc"]
10
+ s.rdoc_options = ["--charset=UTF-8"]
11
+ s.homepage = "http://github.com/nov/paypal-express"
12
+ s.require_paths = ["lib"]
13
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
14
+ s.files = `git ls-files`.split("\n")
15
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
16
+ s.add_dependency "activesupport", ">= 2.3"
17
+ s.add_dependency "i18n"
18
+ s.add_dependency "rest-client", ">= 1.4"
19
+ s.add_dependency "attr_required", ">= 0.0.3"
20
+ s.add_development_dependency "rake", ">= 0.8"
21
+ s.add_development_dependency "rcov", ">= 0.9"
22
+ s.add_development_dependency "rspec", ">= 2"
23
+ s.add_development_dependency "fakeweb", ">= 1.3.0"
24
+ end
@@ -0,0 +1 @@
1
+ TIMESTAMP=2011%2d02%2d08T07%3a18%3a29Z&CORRELATIONID=fde1527b25b4f&ACK=Failure&VERSION=66%2e0&BUILD=1704252&L_ERRORCODE0=11502&L_SHORTMESSAGE0=Invalid%20Token&L_LONGMESSAGE0=The%20token%20is%20invalid&L_SEVERITYCODE0=Error
@@ -0,0 +1 @@
1
+ PROFILEID=I%2dL8N58XFUCET3&PROFILESTATUS=ActiveProfile&TIMESTAMP=2011%2d02%2d08T07%3a17%3a51Z&CORRELATIONID=7d2e125f5ca63&ACK=Success&VERSION=66%2e0&BUILD=1704252
@@ -0,0 +1 @@
1
+ TIMESTAMP=2011%2d02%2d08T07%3a15%3a19Z&CORRELATIONID=5dd0308345382&ACK=Failure&VERSION=66%2e0&BUILD=1721431&L_ERRORCODE0=10410&L_SHORTMESSAGE0=Invalid%20token&L_LONGMESSAGE0=Invalid%20token%2e&L_SEVERITYCODE0=Error
@@ -0,0 +1 @@
1
+ TOKEN=EC-9E2743126S4330617&SUCCESSPAGEREDIRECTREQUESTED=false&TIMESTAMP=2011-02-08T03:23:55Z&CORRELATIONID=15b93874c358c&ACK=Success&VERSION=66.0&BUILD=1721431&INSURANCEOPTIONSELECTED=false&SHIPPINGOPTIONISDEFAULT=false&PAYMENTINFO_0_TRANSACTIONID=8NC65222871997739&PAYMENTINFO_0_TRANSACTIONTYPE=expresscheckout&PAYMENTINFO_0_PAYMENTTYPE=instant&PAYMENTINFO_0_ORDERTIME=2011-02-08T03:23:54Z&PAYMENTINFO_0_AMT=14.00&PAYMENTINFO_0_FEEAMT=0.85&PAYMENTINFO_0_TAXAMT=0.00&PAYMENTINFO_0_CURRENCYCODE=USD&PAYMENTINFO_0_PAYMENTSTATUS=Completed&PAYMENTINFO_0_PENDINGREASON=None&PAYMENTINFO_0_REASONCODE=None&PAYMENTINFO_0_PROTECTIONELIGIBILITY=Ineligible&PAYMENTINFO_0_PROTECTIONELIGIBILITYTYPE=None&PAYMENTINFO_0_ERRORCODE=0&PAYMENTINFO_0_ACK=Success
@@ -0,0 +1 @@
1
+ TOKEN=EC%2d9E2743126S4330617&CHECKOUTSTATUS=PaymentActionCompleted&TIMESTAMP=2011%2d02%2d08T07%3a10%3a26Z&CORRELATIONID=8f5fddf13ed10&ACK=Failure&VERSION=66%2e0&BUILD=1721431&L_ERRORCODE0=10411&L_SHORTMESSAGE0=This%20Express%20Checkout%20session%20has%20expired%2e&L_LONGMESSAGE0=This%20Express%20Checkout%20session%20has%20expired%2e%20%20Token%20value%20is%20no%20longer%20valid%2e&L_SEVERITYCODE0=Error&EMAIL=valid_1296805890_per%40matake%2ejp&PAYERID=PRT3TZ6MCBCNC&PAYERSTATUS=verified&FIRSTNAME=Test&LASTNAME=User&COUNTRYCODE=US&SHIPTONAME=Test%20User&SHIPTOSTREET=1%20Main%20St&SHIPTOCITY=San%20Jose&SHIPTOSTATE=CA&SHIPTOZIP=95131&SHIPTOCOUNTRYCODE=US&SHIPTOCOUNTRYNAME=United%20States&ADDRESSSTATUS=Confirmed&CURRENCYCODE=USD&AMT=14%2e00&SHIPPINGAMT=0%2e00&HANDLINGAMT=0%2e00&TAXAMT=0%2e00&INSURANCEAMT=0%2e00&SHIPDISCAMT=0%2e00&PAYMENTREQUEST_0_CURRENCYCODE=USD&PAYMENTREQUEST_0_AMT=14%2e00&PAYMENTREQUEST_0_SHIPPINGAMT=0%2e00&PAYMENTREQUEST_0_HANDLINGAMT=0%2e00&PAYMENTREQUEST_0_TAXAMT=0%2e00&PAYMENTREQUEST_0_INSURANCEAMT=0%2e00&PAYMENTREQUEST_0_SHIPDISCAMT=0%2e00&PAYMENTREQUEST_0_TRANSACTIONID=8NC65222871997739&PAYMENTREQUEST_0_INSURANCEOPTIONOFFERED=false&PAYMENTREQUEST_0_SHIPTONAME=Test%20User&PAYMENTREQUEST_0_SHIPTOSTREET=1%20Main%20St&PAYMENTREQUEST_0_SHIPTOCITY=San%20Jose&PAYMENTREQUEST_0_SHIPTOSTATE=CA&PAYMENTREQUEST_0_SHIPTOZIP=95131&PAYMENTREQUEST_0_SHIPTOCOUNTRYCODE=US&PAYMENTREQUEST_0_SHIPTOCOUNTRYNAME=United%20States&PAYMENTREQUESTINFO_0_TRANSACTIONID=8NC65222871997739&PAYMENTREQUESTINFO_0_ERRORCODE=0
@@ -0,0 +1 @@
1
+ TOKEN=EC%2d99G324856K8277846&CHECKOUTSTATUS=PaymentActionNotInitiated&TIMESTAMP=2011%2d02%2d08T07%3a14%3a29Z&CORRELATIONID=e4e63a7a94b02&ACK=Success&VERSION=66%2e0&BUILD=1721431&EMAIL=valid_1296805890_per%40matake%2ejp&PAYERID=PRT3TZ6MCBCNC&PAYERSTATUS=verified&FIRSTNAME=Test&LASTNAME=User&COUNTRYCODE=US&SHIPTONAME=Test%20User&SHIPTOSTREET=1%20Main%20St&SHIPTOCITY=San%20Jose&SHIPTOSTATE=CA&SHIPTOZIP=95131&SHIPTOCOUNTRYCODE=US&SHIPTOCOUNTRYNAME=United%20States&ADDRESSSTATUS=Confirmed&CURRENCYCODE=USD&AMT=14%2e00&SHIPPINGAMT=0%2e00&HANDLINGAMT=0%2e00&TAXAMT=0%2e00&INSURANCEAMT=0%2e00&SHIPDISCAMT=0%2e00&PAYMENTREQUEST_0_CURRENCYCODE=USD&PAYMENTREQUEST_0_AMT=14%2e00&PAYMENTREQUEST_0_SHIPPINGAMT=0%2e00&PAYMENTREQUEST_0_HANDLINGAMT=0%2e00&PAYMENTREQUEST_0_TAXAMT=0%2e00&PAYMENTREQUEST_0_INSURANCEAMT=0%2e00&PAYMENTREQUEST_0_SHIPDISCAMT=0%2e00&PAYMENTREQUEST_0_INSURANCEOPTIONOFFERED=false&PAYMENTREQUEST_0_SHIPTONAME=Test%20User&PAYMENTREQUEST_0_SHIPTOSTREET=1%20Main%20St&PAYMENTREQUEST_0_SHIPTOCITY=San%20Jose&PAYMENTREQUEST_0_SHIPTOSTATE=CA&PAYMENTREQUEST_0_SHIPTOZIP=95131&PAYMENTREQUEST_0_SHIPTOCOUNTRYCODE=US&PAYMENTREQUEST_0_SHIPTOCOUNTRYNAME=United%20States&PAYMENTREQUESTINFO_0_ERRORCODE=0
@@ -0,0 +1 @@
1
+ PROFILEID=I%2dK1VFLRN3VVG0x&TIMESTAMP=2011%2d02%2d08T07%3a19%3a43Z&CORRELATIONID=edc954f1c34c5&ACK=Failure&VERSION=66%2e0&BUILD=1704252&L_ERRORCODE0=11552&L_SHORTMESSAGE0=Invalid%20profile%20ID&L_LONGMESSAGE0=The%20profile%20ID%20is%20invalid&L_SEVERITYCODE0=Error
@@ -0,0 +1 @@
1
+ PROFILEID=I%2dK1VFLRN3VVG0&STATUS=Active&AUTOBILLOUTAMT=NoAutoBill&DESC=iKnow%21%20Subscription&MAXFAILEDPAYMENTS=0&SUBSCRIBERNAME=Test%20User&PROFILESTARTDATE=2011%2d02%2d03T15%3a00%3a00Z&NEXTBILLINGDATE=2011%2d03%2d04T10%3a00%3a00Z&NUMCYCLESCOMPLETED=1&NUMCYCLESREMAINING=18446744073709551615&OUTSTANDINGBALANCE=0&FAILEDPAYMENTCOUNT=0&LASTPAYMENTDATE=2011%2d02%2d04T10%3a50%3a56Z&LASTPAYMENTAMT=1000&TRIALAMTPAID=0&REGULARAMTPAID=1000&AGGREGATEAMT=1000&AGGREGATEOPTIONALAMT=0&FINALPAYMENTDUEDATE=1970%2d01%2d01T00%3a00%3a00Z&TIMESTAMP=2011%2d02%2d08T07%3a19%3a25Z&CORRELATIONID=8e18dd6545045&ACK=Success&VERSION=66%2e0&BUILD=1704252&SHIPTOSTREET=1%20Main%20St&SHIPTOCITY=San%20Jose&SHIPTOSTATE=CA&SHIPTOZIP=95131&SHIPTOCOUNTRYCODE=US&SHIPTOCOUNTRY=US&SHIPTOCOUNTRYNAME=United%20States&SHIPADDRESSOWNER=PayPal&SHIPADDRESSSTATUS=Unconfirmed&BILLINGPERIOD=Month&BILLINGFREQUENCY=1&TOTALBILLINGCYCLES=0&CURRENCYCODE=JPY&AMT=1000&SHIPPINGAMT=0&TAXAMT=0&REGULARBILLINGPERIOD=Month&REGULARBILLINGFREQUENCY=1&REGULARTOTALBILLINGCYCLES=0&REGULARCURRENCYCODE=JPY&REGULARAMT=1000&REGULARSHIPPINGAMT=0&REGULARTAXAMT=0
@@ -0,0 +1 @@
1
+ TIMESTAMP=2011%2d02%2d08T07%3a21%3a08Z&CORRELATIONID=aa160cad77481&ACK=Failure&VERSION=66%2e0&BUILD=1704252&L_ERRORCODE0=11556&L_SHORTMESSAGE0=Invalid%20profile%20status%20for%20cancel%20action%3b%20profile%20should%20be%20active%20or%20suspended&L_LONGMESSAGE0=Invalid%20profile%20status%20for%20cancel%20action%3b%20profile%20should%20be%20active%20or%20suspended&L_SEVERITYCODE0=Error
@@ -0,0 +1 @@
1
+ PROFILEID=I%2dK1VFLRN3VVG0&TIMESTAMP=2011%2d02%2d08T07%3a20%3a44Z&CORRELATIONID=86bde487ccba6&ACK=Success&VERSION=66%2e0&BUILD=1704252
@@ -0,0 +1 @@
1
+ TIMESTAMP=2011%2d02%2d02T02%3a16%3a50Z&CORRELATIONID=379d1b7f97afb&ACK=Failure&L_ERRORCODE0=10001&L_SHORTMESSAGE0=Internal%20Error&L_LONGMESSAGE0=Timeout%20processing%20request
@@ -0,0 +1 @@
1
+ ACK=Success&BUILD=1721431&CORRELATIONID=5549ea3a78af1&TIMESTAMP=2011-02-02T02%3A02%3A18Z&TOKEN=EC-5YJ90598G69065317&VERSION=66.0
@@ -0,0 +1,27 @@
1
+ require 'fakeweb'
2
+
3
+ module FakeResponseHelper
4
+
5
+ def fake_response(file_path, options = {})
6
+ Paypal::NVP::Request::ENDPOINT.each do |env, endpoint|
7
+ FakeWeb.register_uri(
8
+ :post,
9
+ endpoint,
10
+ options.merge(
11
+ :body => File.read(File.join(File.dirname(__FILE__), '../fake_response', "#{file_path}.txt"))
12
+ )
13
+ )
14
+ end
15
+ end
16
+
17
+ def request_to(endpoint, method = :get)
18
+ raise_error(
19
+ FakeWeb::NetConnectNotAllowedError,
20
+ "Real HTTP connections are disabled. Unregistered request: #{method.to_s.upcase} #{endpoint}"
21
+ )
22
+ end
23
+
24
+ end
25
+
26
+ FakeWeb.allow_net_connect = false
27
+ include FakeResponseHelper
@@ -0,0 +1,17 @@
1
+ require 'spec_helper.rb'
2
+
3
+ describe Paypal::HttpError do
4
+ it 'should have code, message and body' do
5
+ error = Paypal::HttpError.new(400, 'BadRequest', 'You are bad man!')
6
+ error.code.should == 400
7
+ error.message.should == 'BadRequest'
8
+ error.body.should == 'You are bad man!'
9
+ end
10
+ end
11
+
12
+ describe Paypal::APIError do
13
+ it 'should have raw response' do
14
+ error = Paypal::APIError.new({:error => 'ERROR!!'})
15
+ error.response.should == {:error => 'ERROR!!'}
16
+ end
17
+ end
@@ -0,0 +1,223 @@
1
+ require 'spec_helper.rb'
2
+
3
+ describe Paypal::Express::Request do
4
+ class Paypal::Express::Request
5
+ attr_accessor :_sent_params_, :_method_
6
+ def post_with_logging(method, params)
7
+ self._method_ = method
8
+ self._sent_params_ = params
9
+ post_without_logging method, params
10
+ end
11
+ alias_method_chain :post, :logging
12
+ end
13
+
14
+ let :attributes do
15
+ {
16
+ :username => 'nov',
17
+ :password => 'password',
18
+ :signature => 'sig',
19
+ :return_url => 'http://example.com/success',
20
+ :cancel_url => 'http://example.com/cancel'
21
+ }
22
+ end
23
+
24
+ let :instance do
25
+ Paypal::Express::Request.new attributes
26
+ end
27
+
28
+ let :instant_payment_request do
29
+ Paypal::Payment::Request.new(
30
+ :amount => 1000,
31
+ :description => 'Instant Payment Request'
32
+ )
33
+ end
34
+
35
+ let :recurring_payment_request do
36
+ Paypal::Payment::Request.new(
37
+ :billing_type => :RecurringPayments,
38
+ :billing_agreement_description => 'Recurring Payment Request'
39
+ )
40
+ end
41
+
42
+ let :recurring_profile do
43
+ Paypal::Payment::Recurring.new(
44
+ :start_date => Time.utc(2011, 2, 8, 9, 0, 0),
45
+ :description => 'Recurring Profile',
46
+ :billing => {
47
+ :period => :Month,
48
+ :frequency => 1,
49
+ :amount => 1000
50
+ }
51
+ )
52
+ end
53
+
54
+ describe '.new' do
55
+ context 'when any required parameters are missing' do
56
+ it 'should raise AttrMissing exception' do
57
+ attributes.keys.each do |missing_key|
58
+ insufficient_attributes = attributes.reject do |key, value|
59
+ key == missing_key
60
+ end
61
+ lambda do
62
+ Paypal::Express::Request.new insufficient_attributes
63
+ end.should raise_error AttrRequired::AttrMissing
64
+ end
65
+ end
66
+ end
67
+
68
+ context 'when all required parameters are given' do
69
+ it 'should succeed' do
70
+ lambda do
71
+ Paypal::Express::Request.new attributes
72
+ end.should_not raise_error AttrRequired::AttrMissing
73
+ end
74
+ end
75
+ end
76
+
77
+ describe '#setup' do
78
+ it 'should return Paypal::Express::Response' do
79
+ fake_response 'SetExpressCheckout/success'
80
+ response = instance.setup recurring_payment_request
81
+ response.should be_instance_of(Paypal::Express::Response)
82
+ end
83
+
84
+ context 'when instance payment request given' do
85
+ it 'should call SetExpressCheckout' do
86
+ lambda do
87
+ instance.setup instant_payment_request
88
+ end.should request_to 'https://api-3t.paypal.com/nvp', :post
89
+ instance._method_.should == :SetExpressCheckout
90
+ instance._sent_params_.should == {
91
+ :PAYMENTREQUEST_0_DESC => 'Instant Payment Request',
92
+ :RETURNURL => 'http://example.com/success',
93
+ :CANCELURL => 'http://example.com/cancel',
94
+ :PAYMENTREQUEST_0_AMT => '1000.00'
95
+ }
96
+ end
97
+ end
98
+
99
+ context 'when recurring payment request given' do
100
+ it 'should call SetExpressCheckout' do
101
+ lambda do
102
+ instance.setup recurring_payment_request
103
+ end.should request_to 'https://api-3t.paypal.com/nvp', :post
104
+ instance._method_.should == :SetExpressCheckout
105
+ instance._sent_params_.should == {
106
+ :L_BILLINGTYPE0 => :RecurringPayments,
107
+ :L_BILLINGAGREEMENTDESCRIPTION0 => 'Recurring Payment Request',
108
+ :RETURNURL => 'http://example.com/success',
109
+ :CANCELURL => 'http://example.com/cancel',
110
+ :PAYMENTREQUEST_0_AMT => '0.00'
111
+ }
112
+ end
113
+ end
114
+ end
115
+
116
+ describe '#details' do
117
+ it 'should return Paypal::Express::Response' do
118
+ fake_response 'GetExpressCheckoutDetails/success'
119
+ response = instance.details 'token'
120
+ response.should be_instance_of(Paypal::Express::Response)
121
+ end
122
+
123
+ it 'should call GetExpressCheckoutDetails' do
124
+ lambda do
125
+ instance.details 'token'
126
+ end.should request_to 'https://api-3t.paypal.com/nvp', :post
127
+ instance._method_.should == :GetExpressCheckoutDetails
128
+ instance._sent_params_.should == {
129
+ :TOKEN => 'token'
130
+ }
131
+ end
132
+ end
133
+
134
+ describe '#checkout!' do
135
+ it 'should return Paypal::Express::Response' do
136
+ fake_response 'DoExpressCheckoutPayment/success'
137
+ response = instance.checkout! 'token', 'payer_id', instant_payment_request
138
+ response.should be_instance_of(Paypal::Express::Response)
139
+ end
140
+
141
+ it 'should call DoExpressCheckoutPayment' do
142
+ lambda do
143
+ instance.checkout! 'token', 'payer_id', instant_payment_request
144
+ end.should request_to 'https://api-3t.paypal.com/nvp', :post
145
+ instance._method_.should == :DoExpressCheckoutPayment
146
+ instance._sent_params_.should == {
147
+ :PAYERID => 'payer_id',
148
+ :TOKEN => 'token',
149
+ :PAYMENTREQUEST_0_DESC => 'Instant Payment Request',
150
+ :PAYMENTREQUEST_0_AMT => '1000.00'
151
+ }
152
+ end
153
+ end
154
+
155
+ describe '#subscribe!' do
156
+ it 'should return Paypal::Express::Response' do
157
+ fake_response 'CreateRecurringPaymentsProfile/success'
158
+ response = instance.subscribe! 'token', recurring_profile
159
+ response.should be_instance_of(Paypal::Express::Response)
160
+ end
161
+
162
+ it 'should call CreateRecurringPaymentsProfile' do
163
+ lambda do
164
+ instance.subscribe! 'token', recurring_profile
165
+ end.should request_to 'https://api-3t.paypal.com/nvp', :post
166
+ instance._method_.should == :CreateRecurringPaymentsProfile
167
+ instance._sent_params_.should == {
168
+ :DESC => 'Recurring Profile',
169
+ :TRIALAMT => '0.00',
170
+ :TOKEN => 'token',
171
+ :SHIPPINGAMT => '0.00',
172
+ :AMT => '1000.00',
173
+ :TRIALTOTALBILLINGCYCLES => 0,
174
+ :BILLINGFREQUENCY => 1,
175
+ :MAXFAILEDPAYMENTS => 0,
176
+ :TRIALBILLINGFREQUENCY => 0,
177
+ :BILLINGPERIOD => :Month,
178
+ :TAXAMT => '0.00',
179
+ :PROFILESTARTDATE => '2011-02-08 09:00:00',
180
+ :TOTALBILLINGCYCLES => 0
181
+ }
182
+ end
183
+ end
184
+
185
+ describe '#subscription' do
186
+ it 'should return Paypal::Express::Response' do
187
+ fake_response 'GetRecurringPaymentsProfileDetails/success'
188
+ response = instance.subscription 'profile_id'
189
+ response.should be_instance_of(Paypal::Express::Response)
190
+ end
191
+
192
+ it 'should call GetRecurringPaymentsProfileDetails' do
193
+ lambda do
194
+ instance.subscription 'profile_id'
195
+ end.should request_to 'https://api-3t.paypal.com/nvp', :post
196
+ instance._method_.should == :GetRecurringPaymentsProfileDetails
197
+ instance._sent_params_.should == {
198
+ :PROFILEID => 'profile_id'
199
+ }
200
+ end
201
+ end
202
+
203
+ describe '#renew!' do
204
+ it 'should return Paypal::Express::Response' do
205
+ fake_response 'ManageRecurringPaymentsProfileStatus/success'
206
+ response = instance.renew! 'profile_id', :Cancel
207
+ response.should be_instance_of(Paypal::Express::Response)
208
+ end
209
+
210
+ it 'should call ManageRecurringPaymentsProfileStatus' do
211
+ lambda do
212
+ instance.renew! 'profile_id', :Cancel
213
+ end.should request_to 'https://api-3t.paypal.com/nvp', :post
214
+ instance._method_.should == :ManageRecurringPaymentsProfileStatus
215
+ instance._sent_params_.should == {
216
+ :ACTION => :Cancel,
217
+ :PROFILEID => 'profile_id',
218
+ :NOTE => ''
219
+ }
220
+ end
221
+ end
222
+
223
+ end