paypal-express 0.0.1

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