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
data/lib/paypal.rb ADDED
@@ -0,0 +1,56 @@
1
+ require 'logger'
2
+ require 'active_support/core_ext'
3
+ require 'attr_required'
4
+ require 'attr_optional'
5
+ require 'restclient_with_ssl_support'
6
+
7
+ module Paypal
8
+
9
+ API_VERSION = '66.0'
10
+ ENDPOINT = {
11
+ :production => 'https://www.paypal.com/cgi-bin/webscr',
12
+ :sandbox => 'https://www.sandbox.paypal.com/cgi-bin/webscr'
13
+ }
14
+
15
+ def self.log(message, mode = :info)
16
+ self.logger.send mode, message
17
+ end
18
+ def self.logger
19
+ @@logger
20
+ end
21
+ def self.logger=(logger)
22
+ @@logger = logger
23
+ end
24
+ @@logger = Logger.new(STDERR)
25
+ @@logger.progname = 'Paypal::Express'
26
+
27
+ def self.sandbox?
28
+ @@sandbox
29
+ end
30
+ def self.sandbox!
31
+ self.sandbox = true
32
+ end
33
+ def self.sandbox=(boolean)
34
+ @@sandbox = boolean
35
+ end
36
+ self.sandbox = false
37
+
38
+ end
39
+
40
+ require 'paypal/util'
41
+ require 'paypal/exceptions'
42
+ require 'paypal/base'
43
+ require 'paypal/nvp/request'
44
+ require 'paypal/nvp/response'
45
+ require 'paypal/express/request'
46
+ require 'paypal/express/response'
47
+ require 'paypal/payment/request'
48
+ require 'paypal/payment/response'
49
+ require 'paypal/payment/response/amount'
50
+ require 'paypal/payment/response/info'
51
+ require 'paypal/payment/response/payer'
52
+ require 'paypal/payment/response/ship_to'
53
+ require 'paypal/payment/recurring'
54
+ require 'paypal/payment/recurring/activation'
55
+ require 'paypal/payment/recurring/billing'
56
+ require 'paypal/payment/recurring/summary'
@@ -0,0 +1,19 @@
1
+ module Paypal
2
+ class Base
3
+ include AttrRequired, AttrOptional, Util
4
+
5
+ def initialize(attributes = {})
6
+ if attributes.is_a?(Hash)
7
+ (required_attributes + optional_attributes).each do |key|
8
+ value = if numeric_attribute?(key)
9
+ Util.to_numeric(attributes[key])
10
+ else
11
+ attributes[key]
12
+ end
13
+ self.send "#{key}=", value
14
+ end
15
+ end
16
+ attr_missing!
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,21 @@
1
+ module Paypal
2
+
3
+ class Exception < StandardError; end
4
+
5
+ class HttpError < Exception
6
+ attr_accessor :code, :message, :body
7
+ def initialize(code, message, body = '')
8
+ @code = code
9
+ @message = message
10
+ @body = body
11
+ end
12
+ end
13
+
14
+ class APIError < Exception
15
+ attr_accessor :response
16
+ def initialize(response = {})
17
+ @response = response
18
+ end
19
+ end
20
+
21
+ end
@@ -0,0 +1 @@
1
+ require 'paypal'
@@ -0,0 +1,64 @@
1
+ module Paypal
2
+ module Express
3
+ class Request < NVP::Request
4
+ attr_required :return_url, :cancel_url
5
+
6
+ def setup(payment_requests)
7
+ params = {
8
+ :RETURNURL => self.return_url,
9
+ :CANCELURL => self.cancel_url
10
+ }
11
+ Array(payment_requests).each_with_index do |payment_request, index|
12
+ params.merge! payment_request.to_params(index)
13
+ end
14
+ response = self.request :SetExpressCheckout, params
15
+ Response.new response
16
+ end
17
+
18
+ def details(token)
19
+ response = self.request :GetExpressCheckoutDetails, {:TOKEN => token}
20
+ Response.new response
21
+ end
22
+
23
+ def checkout!(token, payer_id, payment_requests)
24
+ params = {
25
+ :TOKEN => token,
26
+ :PAYERID => payer_id
27
+ }
28
+ Array(payment_requests).each_with_index do |payment_request, index|
29
+ params.merge! payment_request.to_params(index)
30
+ end
31
+ response = self.request :DoExpressCheckoutPayment, params
32
+ Response.new response
33
+ end
34
+
35
+ def subscribe!(token, recurring_profile)
36
+ params = {
37
+ :TOKEN => token
38
+ }
39
+ params.merge! recurring_profile.to_params
40
+ response = self.request :CreateRecurringPaymentsProfile, params
41
+ Response.new response
42
+ end
43
+
44
+ def subscription(profile_id)
45
+ params = {
46
+ :PROFILEID => profile_id
47
+ }
48
+ response = self.request :GetRecurringPaymentsProfileDetails, params
49
+ Response.new response
50
+ end
51
+
52
+ def renew!(profile_id, action, note = '')
53
+ params = {
54
+ :PROFILEID => profile_id,
55
+ :ACTION => action,
56
+ :NOTE => note
57
+ }
58
+ response = self.request :ManageRecurringPaymentsProfileStatus, params
59
+ Response.new response
60
+ end
61
+
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,19 @@
1
+ module Paypal
2
+ module Express
3
+ class Response < NVP::Response
4
+ def redirect_uri
5
+ endpoint = if Paypal.sandbox?
6
+ Paypal::ENDPOINT[:sandbox]
7
+ else
8
+ Paypal::ENDPOINT[:production]
9
+ end
10
+ endpoint = URI.parse endpoint
11
+ endpoint.query = {
12
+ :cmd => '_express-checkout',
13
+ :token => self.token
14
+ }.to_query
15
+ endpoint.to_s
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,59 @@
1
+ module Paypal
2
+ module NVP
3
+ class Request < Base
4
+ attr_required :username, :password, :signature
5
+ attr_accessor :version, :endpoint
6
+
7
+ ENDPOINT = {
8
+ :production => 'https://api-3t.paypal.com/nvp',
9
+ :sandbox => 'https://api-3t.sandbox.paypal.com/nvp'
10
+ }
11
+
12
+ def initialize(attributes = {})
13
+ @version = API_VERSION
14
+ @endpoint = if Paypal.sandbox?
15
+ ENDPOINT[:sandbox]
16
+ else
17
+ ENDPOINT[:production]
18
+ end
19
+ super
20
+ end
21
+
22
+ def common_params
23
+ {
24
+ :USER => self.username,
25
+ :PWD => self.password,
26
+ :SIGNATURE => self.signature,
27
+ :VERSION => self.version
28
+ }
29
+ end
30
+
31
+ def request(method, params = {})
32
+ handle_response do
33
+ post(method, params)
34
+ end
35
+ end
36
+
37
+ private
38
+
39
+ def post(method, params)
40
+ RestClient.post(self.endpoint, common_params.merge(params).merge(:METHOD => method))
41
+ end
42
+
43
+ def handle_response
44
+ response = yield
45
+ response = CGI.parse(response).inject({}) do |res, (k, v)|
46
+ res.merge!(k.to_sym => v.first)
47
+ end
48
+ case response[:ACK]
49
+ when 'Success', 'SuccessWithWarning'
50
+ response
51
+ else
52
+ raise APIError.new(response)
53
+ end
54
+ rescue RestClient::Exception => e
55
+ raise HttpError.new(e.http_code, e.message, e.http_body)
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,145 @@
1
+ module Paypal
2
+ module NVP
3
+ class Response < Base
4
+ cattr_reader :attribute_mapping
5
+ @@attribute_mapping = {
6
+ :ACK => :ack,
7
+ :ADDRESSSTATUS => :address_status,
8
+ :BILLINGAGREEMENTACCEPTEDSTATUS => :billing_agreement_accepted_status,
9
+ :BUILD => :build,
10
+ :CHECKOUTSTATUS => :checkout_status,
11
+ :CORRELATIONID => :colleration_id,
12
+ :COUNTRYCODE => :country_code,
13
+ :CURRENCYCODE => :currency_code,
14
+ :TIMESTAMP => :timestamp,
15
+ :TOKEN => :token,
16
+ :VERSION => :version
17
+ }
18
+ attr_accessor *@@attribute_mapping.values
19
+ attr_accessor :shipping_options_is_default, :success_page_redirect_requested, :insurance_option_selected
20
+ attr_accessor :amount, :ship_to, :payer, :recurring, :payment_responses, :payment_info
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
+ @shipping_options_is_default = attrs.delete(:SHIPPINGOPTIONISDEFAULT) == 'true'
28
+ @success_page_redirect_requested = attrs.delete(:SUCCESSPAGEREDIRECTREQUESTED) == 'true'
29
+ @insurance_option_selected = attrs.delete(:INSURANCEOPTIONSELECTED) == 'true'
30
+ @amount = Payment::Response::Amount.new(
31
+ :total => attrs.delete(:AMT),
32
+ :handing => attrs.delete(:HANDLINGAMT),
33
+ :insurance => attrs.delete(:INSURANCEAMT),
34
+ :ship_disc => attrs.delete(:SHIPDISCAMT),
35
+ :shipping => attrs.delete(:SHIPPINGAMT),
36
+ :tax => attrs.delete(:TAXAMT)
37
+ )
38
+ @ship_to = Payment::Response::ShipTo.new(
39
+ :owner => attrs.delete(:SHIPADDRESSOWNER),
40
+ :status => attrs.delete(:SHIPADDRESSSTATUS),
41
+ :name => attrs.delete(:SHIPTONAME),
42
+ :zip => attrs.delete(:SHIPTOZIP),
43
+ :street => attrs.delete(:SHIPTOSTREET),
44
+ :city => attrs.delete(:SHIPTOCITY),
45
+ :state => attrs.delete(:SHIPTOSTATE),
46
+ :country_code => attrs.delete(:SHIPTOCOUNTRYCODE),
47
+ :country_name => attrs.delete(:SHIPTOCOUNTRYNAME)
48
+ )
49
+ if attrs[:PAYERID]
50
+ @payer = Payment::Response::Payer.new(
51
+ :identifier => attrs.delete(:PAYERID),
52
+ :status => attrs.delete(:PAYERSTATUS),
53
+ :first_name => attrs.delete(:FIRSTNAME),
54
+ :last_name => attrs.delete(:LASTNAME),
55
+ :email => attrs.delete(:EMAIL)
56
+ )
57
+ end
58
+ if attrs[:PROFILEID]
59
+ @recurring = Payment::Recurring.new(
60
+ :identifier => attrs.delete(:PROFILEID),
61
+ # NOTE:
62
+ # CreateRecurringPaymentsProfile returns PROFILESTATUS
63
+ # GetRecurringPaymentsProfileDetails returns STATUS
64
+ :description => attrs.delete(:DESC),
65
+ :status => attrs.delete(:STATUS) || attrs.delete(:PROFILESTATUS),
66
+ :start_date => attrs.delete(:PROFILESTARTDATE),
67
+ :name => attrs.delete(:SUBSCRIBERNAME),
68
+ :reference => attrs.delete(:PROFILEREFERENCE),
69
+ :auto_bill => attrs.delete(:AUTOBILLOUTAMT),
70
+ :max_fails => attrs.delete(:MAXFAILEDPAYMENTS),
71
+ :aggregate_amount => attrs.delete(:AGGREGATEAMT),
72
+ :aggregate_optional_amount => attrs.delete(:AGGREGATEOPTIONALAMT),
73
+ :final_payment_date => attrs.delete(:FINALPAYMENTDUEDATE)
74
+ )
75
+ if attrs[:BILLINGPERIOD]
76
+ @recurring.billing = Payment::Recurring::Billing.new(
77
+ :amount => @amount,
78
+ :currency_code => @currency_code,
79
+ :period => attrs.delete(:BILLINGPERIOD),
80
+ :frequency => attrs.delete(:BILLINGFREQUENCY),
81
+ :total_cycles => attrs.delete(:TOTALBILLINGCYCLES),
82
+ :trial_amount_paid => attrs.delete(:TRIALAMTPAID)
83
+ )
84
+ end
85
+ if attrs[:REGULARAMT]
86
+ @recurring.regular_billing = Payment::Recurring::Billing.new(
87
+ :amount => attrs.delete(:REGULARAMT),
88
+ :shipping_amount => attrs.delete(:REGULARSHIPPINGAMT),
89
+ :tax_amount => attrs.delete(:REGULARTAXAMT),
90
+ :currency_code => attrs.delete(:REGULARCURRENCYCODE),
91
+ :period => attrs.delete(:REGULARBILLINGPERIOD),
92
+ :frequency => attrs.delete(:REGULARBILLINGFREQUENCY),
93
+ :total_cycles => attrs.delete(:REGULARTOTALBILLINGCYCLES),
94
+ :paid => attrs.delete(:REGULARAMTPAID)
95
+ )
96
+ @recurring.summary = Payment::Recurring::Summary.new(
97
+ :next_billing_date => attrs.delete(:NEXTBILLINGDATE),
98
+ :cycles_completed => attrs.delete(:NUMCYCLESCOMPLETED),
99
+ :cycles_remaining => attrs.delete(:NUMCYCLESREMAINING),
100
+ :outstanding_balance => attrs.delete(:OUTSTANDINGBALANCE),
101
+ :failed_count => attrs.delete(:FAILEDPAYMENTCOUNT),
102
+ :last_payment_date => attrs.delete(:LASTPAYMENTDATE),
103
+ :last_payment_amount => attrs.delete(:LASTPAYMENTAMT)
104
+ )
105
+ end
106
+ end
107
+
108
+ # payment_responses
109
+ payment_responses = []
110
+ attrs.keys.each do |attribute|
111
+ prefix, index, key = attribute.to_s.split('_')
112
+ case prefix
113
+ when 'PAYMENTREQUEST', 'PAYMENTREQUESTINFO'
114
+ payment_responses[index.to_i] ||= {}
115
+ payment_responses[index.to_i][key.to_sym] = attrs.delete(attribute)
116
+ end
117
+ end
118
+ @payment_responses = payment_responses.collect do |_attrs_|
119
+ Payment::Response.new _attrs_
120
+ end
121
+
122
+ # payment_info
123
+ payment_info = []
124
+ attrs.keys.each do |_attr_|
125
+ prefix, index, key = _attr_.to_s.split('_')
126
+ if prefix == 'PAYMENTINFO'
127
+ payment_info[index.to_i] ||= {}
128
+ payment_info[index.to_i][key.to_sym] = attrs.delete(_attr_)
129
+ end
130
+ end
131
+ @payment_info = payment_info.collect do |_attrs_|
132
+ Payment::Response::Info.new _attrs_
133
+ end
134
+
135
+ # remove duplicated parameters
136
+ attrs.delete(:SHIPTOCOUNTRY) # NOTE: Same with SHIPTOCOUNTRYCODE
137
+
138
+ # warn ignored attrs
139
+ attrs.each do |key, value|
140
+ Paypal.log "Ignored Parameter (#{self.class}): #{key}=#{value}", :warn
141
+ end
142
+ end
143
+ end
144
+ end
145
+ end
@@ -0,0 +1,43 @@
1
+ module Paypal
2
+ module Payment
3
+ class Recurring < Base
4
+ attr_optional :start_date, :description, :identifier, :status, :name, :reference, :max_fails, :auto_bill, :aggregate_amount, :aggregate_optional_amount, :final_payment_date
5
+ attr_accessor :activation, :billing, :regular_billing, :summary
6
+
7
+ def initialize(attributes = {})
8
+ super
9
+ @activation = Activation.new attributes[:activation] if attributes[:activation]
10
+ @billing = Billing.new attributes[:billing] if attributes[:billing]
11
+ @regular_billing = Billing.new attributes[:regular_billing] if attributes[:regular_billing]
12
+ @summary = Summary.new attributes[:summary] if attributes[:summary]
13
+ end
14
+
15
+ def to_params
16
+ params = [
17
+ self.billing,
18
+ self.activation
19
+ ].compact.inject({}) do |params, attribute|
20
+ params.merge! attribute.to_params
21
+ end
22
+ if self.start_date.is_a?(Time)
23
+ self.start_date = self.start_date.to_s(:db)
24
+ end
25
+ params.merge!(
26
+ :DESC => self.description,
27
+ :MAXFAILEDPAYMENTS => self.max_fails,
28
+ :AUTOBILLAMT => self.auto_bill,
29
+ :PROFILESTARTDATE => self.start_date,
30
+ :SUBSCRIBERNAME => self.name,
31
+ :PROFILEREFERENCE => self.reference
32
+ )
33
+ params.delete_if do |k, v|
34
+ v.blank?
35
+ end
36
+ end
37
+
38
+ def numeric_attribute?(key)
39
+ super || [:max_fails, :failed_count].include?(key)
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,14 @@
1
+ module Paypal
2
+ module Payment
3
+ class Recurring::Activation < Base
4
+ attr_optional :initial_amount, :failed_action
5
+
6
+ def to_params
7
+ {
8
+ :INITAMT => Util.formatted_amount(self.initial_amount),
9
+ :FAILEDINITAMTACTION => self.failed_action
10
+ }
11
+ end
12
+ end
13
+ end
14
+ end