didil-paypal-recurring 1.1.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 (70) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +18 -0
  3. data/.rspec +1 -0
  4. data/Gemfile +2 -0
  5. data/Gemfile.lock +46 -0
  6. data/README.rdoc +144 -0
  7. data/Rakefile +5 -0
  8. data/docs/PP_NVPAPI_DeveloperGuide.pdf +0 -0
  9. data/docs/PP_WPP_IntegrationGuide.pdf +0 -0
  10. data/lib/paypal-recurring.rb +1 -0
  11. data/lib/paypal/recurring.rb +110 -0
  12. data/lib/paypal/recurring/base.rb +286 -0
  13. data/lib/paypal/recurring/cacert.pem +3987 -0
  14. data/lib/paypal/recurring/notification.rb +84 -0
  15. data/lib/paypal/recurring/request.rb +211 -0
  16. data/lib/paypal/recurring/response.rb +29 -0
  17. data/lib/paypal/recurring/response/base.rb +62 -0
  18. data/lib/paypal/recurring/response/checkout.rb +11 -0
  19. data/lib/paypal/recurring/response/details.rb +26 -0
  20. data/lib/paypal/recurring/response/manage_profile.rb +12 -0
  21. data/lib/paypal/recurring/response/payment.rb +24 -0
  22. data/lib/paypal/recurring/response/profile.rb +71 -0
  23. data/lib/paypal/recurring/response/refund.rb +23 -0
  24. data/lib/paypal/recurring/utils.rb +26 -0
  25. data/lib/paypal/recurring/version.rb +10 -0
  26. data/paypal-recurring.gemspec +27 -0
  27. data/spec/fixtures/checkout/failure.yml +38 -0
  28. data/spec/fixtures/checkout/success.yml +38 -0
  29. data/spec/fixtures/create_profile/failure.yml +38 -0
  30. data/spec/fixtures/create_profile/success.yml +38 -0
  31. data/spec/fixtures/details/cancelled.yml +38 -0
  32. data/spec/fixtures/details/failure.yml +38 -0
  33. data/spec/fixtures/details/success.yml +38 -0
  34. data/spec/fixtures/ipn/express_checkout.json +35 -0
  35. data/spec/fixtures/ipn/recurring_payment.json +44 -0
  36. data/spec/fixtures/ipn/recurring_payment_profile_created.json +31 -0
  37. data/spec/fixtures/ipn/recurring_payment_profile_created_with_initial_amount.json +33 -0
  38. data/spec/fixtures/ipn/recurring_payment_skipped.json +30 -0
  39. data/spec/fixtures/ipn/recurring_payment_with_initial_amount.json +44 -0
  40. data/spec/fixtures/notification/failure.yml +50 -0
  41. data/spec/fixtures/notification/success.yml +50 -0
  42. data/spec/fixtures/payment/failure.yml +38 -0
  43. data/spec/fixtures/payment/success.yml +38 -0
  44. data/spec/fixtures/profile/cancel/failure.yml +38 -0
  45. data/spec/fixtures/profile/cancel/success.yml +38 -0
  46. data/spec/fixtures/profile/failure.yml +38 -0
  47. data/spec/fixtures/profile/reactivate/failure.yml +38 -0
  48. data/spec/fixtures/profile/reactivate/success.yml +38 -0
  49. data/spec/fixtures/profile/success.yml +38 -0
  50. data/spec/fixtures/profile/suspend/failure.yml +38 -0
  51. data/spec/fixtures/profile/suspend/success.yml +38 -0
  52. data/spec/fixtures/refund/failure.yml +38 -0
  53. data/spec/fixtures/refund/success.yml +38 -0
  54. data/spec/fixtures/update_profile/failure.yml +38 -0
  55. data/spec/fixtures/update_profile/profile.yml +38 -0
  56. data/spec/fixtures/update_profile/success.yml +38 -0
  57. data/spec/paypal/notification_spec.rb +106 -0
  58. data/spec/paypal/recurring_spec.rb +81 -0
  59. data/spec/paypal/request_spec.rb +136 -0
  60. data/spec/paypal/response/base_spec.rb +19 -0
  61. data/spec/paypal/response/checkout_details_spec.rb +50 -0
  62. data/spec/paypal/response/checkout_spec.rb +32 -0
  63. data/spec/paypal/response/create_recurring_profile_spec.rb +41 -0
  64. data/spec/paypal/response/manage_profile_spec.rb +62 -0
  65. data/spec/paypal/response/profile_spec.rb +43 -0
  66. data/spec/paypal/response/refund_spec.rb +33 -0
  67. data/spec/paypal/response/request_payment_spec.rb +35 -0
  68. data/spec/paypal/response/update_recurring_profile_spec.rb +48 -0
  69. data/spec/spec_helper.rb +27 -0
  70. metadata +209 -0
@@ -0,0 +1,84 @@
1
+ module PayPal
2
+ module Recurring
3
+ class Notification
4
+ extend PayPal::Recurring::Utils
5
+
6
+ attr_reader :params
7
+
8
+ mapping({
9
+ :type => :txn_type,
10
+ :transaction_id => :txn_id,
11
+ :fee => [:mc_fee, :payment_fee],
12
+ :reference => [:rp_invoice_id, :custom, :invoice],
13
+ :payment_id => :recurring_payment_id,
14
+ :amount => [:amount, :mc_gross, :payment_gross],
15
+ :currency => :mc_currency,
16
+ :status => :payment_status,
17
+ :pending_reason => :pending_reason,
18
+ :profile_status => :profile_status,
19
+ :payment_date => :payment_date,
20
+ :time_created => :time_created,
21
+ :seller_id => :receiver_id,
22
+ :email => :receiver_email,
23
+ :initial_amount => :initial_payment_amount,
24
+ :payer_email => :payer_email
25
+ })
26
+
27
+ def initialize(params = {})
28
+ self.params = params
29
+ end
30
+
31
+ def params=(params)
32
+ @params = params.inject({}) do |buffer, (name,value)|
33
+ buffer.merge(name.to_sym => value)
34
+ end
35
+ end
36
+
37
+ def express_checkout?
38
+ type == "express_checkout"
39
+ end
40
+
41
+ def recurring_payment?
42
+ type == "recurring_payment"
43
+ end
44
+
45
+ def recurring_payment_profile?
46
+ type == "recurring_payment_profile_created"
47
+ end
48
+
49
+ def request
50
+ @request ||= PayPal::Recurring::Request.new.tap do |request|
51
+ request.uri = URI.parse("#{PayPal::Recurring.site_endpoint}?cmd=_notify-validate")
52
+ end
53
+ end
54
+
55
+ def response
56
+ @response ||= request.post(params.merge(:cmd => "_notify-validate"))
57
+ end
58
+
59
+ def valid?
60
+ completed? && verified? && email == PayPal::Recurring.email && seller_id == PayPal::Recurring.seller_id
61
+ end
62
+
63
+ def completed?
64
+ status == "Completed"
65
+ end
66
+
67
+ def next_payment_date
68
+ self.class.convert_to_time(params[:next_payment_date]) if params[:next_payment_date]
69
+ end
70
+
71
+ def paid_at
72
+ self.class.convert_to_time(payment_date) if payment_date
73
+ end
74
+
75
+ def created_at
76
+ self.class.convert_to_time(time_created) if time_created
77
+ end
78
+
79
+ def verified?
80
+ response.body == "VERIFIED"
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,211 @@
1
+ module PayPal
2
+ module Recurring
3
+ class Request
4
+ METHODS = {
5
+ :checkout => "SetExpressCheckout",
6
+ :payment => "DoExpressCheckoutPayment",
7
+ :details => "GetExpressCheckoutDetails",
8
+ :create_profile => "CreateRecurringPaymentsProfile",
9
+ :profile => "GetRecurringPaymentsProfileDetails",
10
+ :manage_profile => "ManageRecurringPaymentsProfileStatus",
11
+ :update_profile => "UpdateRecurringPaymentsProfile",
12
+ :refund => "RefundTransaction"
13
+ }
14
+
15
+ INITIAL_AMOUNT_ACTIONS = {
16
+ :cancel => "CancelOnFailure",
17
+ :continue => "ContinueOnFailure"
18
+ }
19
+
20
+ ACTIONS = {
21
+ :cancel => "Cancel",
22
+ :suspend => "Suspend",
23
+ :reactivate => "Reactivate"
24
+ }
25
+
26
+ PERIOD = {
27
+ :daily => "Day",
28
+ :weekly => "Weekly",
29
+ :monthly => "Month",
30
+ :yearly => "Year"
31
+ }
32
+
33
+ TRIAL_PERIOD = {
34
+ :daily => "Day",
35
+ :weekly => "Weekly",
36
+ :monthly => "Month",
37
+ :yearly => "Year"
38
+ }
39
+
40
+ OUTSTANDING = {
41
+ :next_billing => "AddToNextBilling",
42
+ :no_auto => "NoAutoBill"
43
+ }
44
+
45
+ REFUND_TYPE = {
46
+ :full => "Full",
47
+ :partial => "Partial",
48
+ :external => "ExternalDispute",
49
+ :other => "Other"
50
+ }
51
+
52
+ ATTRIBUTES = {
53
+ :action => "ACTION",
54
+ :amount => ["PAYMENTREQUEST_0_AMT", "AMT"],
55
+ :billing_type => "L_BILLINGTYPE0",
56
+ :cancel_url => "CANCELURL",
57
+ :currency => ["PAYMENTREQUEST_0_CURRENCYCODE", "CURRENCYCODE"],
58
+ :description => ["DESC", "PAYMENTREQUEST_0_DESC", "L_BILLINGAGREEMENTDESCRIPTION0"],
59
+ :note => "NOTE",
60
+ :item_category => "L_PAYMENTREQUEST_0_ITEMCATEGORY0",
61
+ :item_name => "L_PAYMENTREQUEST_0_NAME0",
62
+ :item_amount => "L_PAYMENTREQUEST_0_AMT0",
63
+ :item_quantity => "L_PAYMENTREQUEST_0_QTY0",
64
+ :email => "EMAIL",
65
+ :failed => "MAXFAILEDPAYMENTS",
66
+ :frequency => "BILLINGFREQUENCY",
67
+ :initial_amount => "INITAMT",
68
+ :initial_amount_action => "FAILEDINITAMTACTION",
69
+ :ipn_url => ["PAYMENTREQUEST_0_NOTIFYURL", "NOTIFYURL"],
70
+ :locale => "LOCALECODE",
71
+ :method => "METHOD",
72
+ :no_shipping => "NOSHIPPING",
73
+ :outstanding => "AUTOBILLOUTAMT",
74
+ :password => "PWD",
75
+ :payer_id => "PAYERID",
76
+ :payment_action => "PAYMENTREQUEST_0_PAYMENTACTION",
77
+ :period => "BILLINGPERIOD",
78
+ :profile_id => "PROFILEID",
79
+ :reference => ["PROFILEREFERENCE", "PAYMENTREQUEST_0_CUSTOM", "PAYMENTREQUEST_0_INVNUM"],
80
+ :refund_type => "REFUNDTYPE",
81
+ :return_url => "RETURNURL",
82
+ :signature => "SIGNATURE",
83
+ :start_at => "PROFILESTARTDATE",
84
+ :token => "TOKEN",
85
+ :transaction_id => "TRANSACTIONID",
86
+ :trial_amount => "TRIALAMT",
87
+ :trial_frequency => "TRIALBILLINGFREQUENCY",
88
+ :trial_length => "TRIALTOTALBILLINGCYCLES",
89
+ :trial_period => "TRIALBILLINGPERIOD",
90
+ :username => "USER",
91
+ :version => "VERSION",
92
+ :brand_name => "BRANDNAME",
93
+ :page_style => "PAGESTYLE",
94
+ }
95
+
96
+ CA_FILE = File.dirname(__FILE__) + "/cacert.pem"
97
+
98
+ attr_accessor :uri
99
+
100
+ # Do a POST request to PayPal API.
101
+ # The +method+ argument is the name of the API method you want to invoke.
102
+ # For instance, if you want to request a new checkout token, you may want
103
+ # to do something like:
104
+ #
105
+ # response = request.run(:express_checkout)
106
+ #
107
+ # We normalize the methods name. For a list of what's being covered, refer to
108
+ # PayPal::Recurring::Request::METHODS constant.
109
+ #
110
+ # The params hash can use normalized names. For a list, check the
111
+ # PayPal::Recurring::Request::ATTRIBUTES constant.
112
+ #
113
+ def run(method, params = {})
114
+ params = prepare_params(params.merge(:method => METHODS.fetch(method, method.to_s)))
115
+ response = post(params)
116
+ Response.process(method, response)
117
+ end
118
+
119
+ #
120
+ #
121
+ def request
122
+ @request ||= Net::HTTP::Post.new(uri.request_uri).tap do |http|
123
+ http["User-Agent"] = "PayPal::Recurring/#{PayPal::Recurring::Version::STRING}"
124
+ end
125
+ end
126
+
127
+ #
128
+ #
129
+ def post(params = {})
130
+ request.form_data = params
131
+ client.request(request)
132
+ end
133
+
134
+ # Join params and normalize attribute names.
135
+ #
136
+ def prepare_params(params) # :nodoc:
137
+ normalize_params default_params.merge(params)
138
+ end
139
+
140
+ # Parse current API url.
141
+ #
142
+ def uri # :nodoc:
143
+ @uri ||= URI.parse(PayPal::Recurring.api_endpoint)
144
+ end
145
+
146
+ def client
147
+ @client ||= begin
148
+ Net::HTTP.new(uri.host, uri.port).tap do |http|
149
+ http.use_ssl = true
150
+ http.verify_mode = OpenSSL::SSL::VERIFY_PEER
151
+ http.ca_file = CA_FILE
152
+ end
153
+ end
154
+ end
155
+
156
+ def default_params
157
+ {
158
+ :username => PayPal::Recurring.username,
159
+ :password => PayPal::Recurring.password,
160
+ :signature => PayPal::Recurring.signature,
161
+ :version => PayPal::Recurring.api_version
162
+ }
163
+ end
164
+
165
+ def normalize_params(params)
166
+ params.inject({}) do |buffer, (name, value)|
167
+ attr_names = [ATTRIBUTES[name.to_sym]].flatten.compact
168
+ attr_names << name if attr_names.empty?
169
+
170
+ attr_names.each do |attr_name|
171
+ buffer[attr_name.to_sym] = respond_to?("build_#{name}") ? send("build_#{name}", value) : value
172
+ end
173
+
174
+ buffer
175
+ end
176
+ end
177
+
178
+ def build_period(value) # :nodoc:
179
+ PERIOD.fetch(value.to_sym, value) if value
180
+ end
181
+
182
+ def build_trial_period(value)
183
+ TRIAL_PERIOD.fetch(value.to_sym, value) if value
184
+ end
185
+
186
+ def build_start_at(value) # :nodoc:
187
+ value.respond_to?(:strftime) ? value.strftime("%Y-%m-%dT%H:%M:%SZ") : value
188
+ end
189
+
190
+ def build_outstanding(value) # :nodoc:
191
+ OUTSTANDING.fetch(value.to_sym, value) if value
192
+ end
193
+
194
+ def build_refund_type(value) # :nodoc:
195
+ REFUND_TYPE.fetch(value.to_sym, value) if value
196
+ end
197
+
198
+ def build_action(value) # :nodoc:
199
+ ACTIONS.fetch(value.to_sym, value) if value
200
+ end
201
+
202
+ def build_initial_amount_action(value) # :nodoc:
203
+ INITIAL_AMOUNT_ACTIONS.fetch(value.to_sym, value) if value
204
+ end
205
+
206
+ def build_locale(value) # :nodoc:
207
+ value.to_s.upcase
208
+ end
209
+ end
210
+ end
211
+ end
@@ -0,0 +1,29 @@
1
+ module PayPal
2
+ module Recurring
3
+ module Response
4
+ autoload :Base, "paypal/recurring/response/base"
5
+ autoload :Checkout, "paypal/recurring/response/checkout"
6
+ autoload :Details, "paypal/recurring/response/details"
7
+ autoload :Payment, "paypal/recurring/response/payment"
8
+ autoload :ManageProfile, "paypal/recurring/response/manage_profile"
9
+ autoload :Profile, "paypal/recurring/response/profile"
10
+ autoload :Refund, "paypal/recurring/response/refund"
11
+
12
+ RESPONDERS = {
13
+ :checkout => "Checkout",
14
+ :details => "Details",
15
+ :payment => "Payment",
16
+ :profile => "Profile",
17
+ :create_profile => "ManageProfile",
18
+ :manage_profile => "ManageProfile",
19
+ :update_profile => "ManageProfile",
20
+ :refund => "Refund"
21
+ }
22
+
23
+ def self.process(method, response)
24
+ response_class = PayPal::Recurring::Response.const_get(RESPONDERS[method])
25
+ response_class.new(response)
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,62 @@
1
+ module PayPal
2
+ module Recurring
3
+ module Response
4
+ class Base
5
+ extend PayPal::Recurring::Utils
6
+
7
+ attr_accessor :response
8
+
9
+ mapping(
10
+ :token => :TOKEN,
11
+ :ack => :ACK,
12
+ :version => :VERSION,
13
+ :build => :BUILD,
14
+ :correlaction_id => :CORRELATIONID,
15
+ :requested_at => :TIMESTAMP
16
+ )
17
+
18
+ def initialize(response = nil)
19
+ @response = response
20
+ end
21
+
22
+ def params
23
+ @params ||= CGI.parse(response.body).inject({}) do |buffer, (name, value)|
24
+ buffer.merge(name.to_sym => value.first)
25
+ end
26
+ end
27
+
28
+ def errors
29
+ @errors ||= begin
30
+ index = 0
31
+ [].tap do |errors|
32
+ while params[:"L_ERRORCODE#{index}"]
33
+ errors << {
34
+ :code => params[:"L_ERRORCODE#{index}"],
35
+ :messages => [
36
+ params[:"L_SHORTMESSAGE#{index}"],
37
+ params[:"L_LONGMESSAGE#{index}"]
38
+ ]
39
+ }
40
+
41
+ index += 1
42
+ end
43
+ end
44
+ end
45
+ end
46
+
47
+ def success?
48
+ ack == "Success"
49
+ end
50
+
51
+ def valid?
52
+ errors.empty? && success?
53
+ end
54
+
55
+ private
56
+ def build_requested_at(stamp) # :nodoc:
57
+ Time.parse(stamp)
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,11 @@
1
+ module PayPal
2
+ module Recurring
3
+ module Response
4
+ class Checkout < Base
5
+ def checkout_url
6
+ "#{PayPal::Recurring.site_endpoint}?cmd=_express-checkout&token=#{token}&useraction=commit"
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,26 @@
1
+ module PayPal
2
+ module Recurring
3
+ module Response
4
+ class Details < Base
5
+ mapping(
6
+ :status => :CHECKOUTSTATUS,
7
+ :email => :EMAIL,
8
+ :email => :EMAIL,
9
+ :payer_id => :PAYERID,
10
+ :payer_status => :PAYERSTATUS,
11
+ :first_name => :FIRSTNAME,
12
+ :last_name => :LASTNAME,
13
+ :country => :COUNTRYCODE,
14
+ :currency => :CURRENCYCODE,
15
+ :amount => :AMT,
16
+ :description => :DESC,
17
+ :ipn_url => :NOTIFYURL
18
+ )
19
+
20
+ def agreed?
21
+ params[:BILLINGAGREEMENTACCEPTEDSTATUS] == "1"
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,12 @@
1
+ module PayPal
2
+ module Recurring
3
+ module Response
4
+ class ManageProfile < Base
5
+ mapping(
6
+ :profile_id => :PROFILEID,
7
+ :status => :PROFILESTATUS
8
+ )
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,24 @@
1
+ module PayPal
2
+ module Recurring
3
+ module Response
4
+ class Payment < Base
5
+ mapping(
6
+ :status => :PAYMENTINFO_0_PAYMENTSTATUS,
7
+ :amount => :PAYMENTINFO_0_AMT,
8
+ :fees => :PAYMENTINFO_0_FEEAMT,
9
+ :transaction_id => :PAYMENTINFO_0_TRANSACTIONID,
10
+ :seller_id => :PAYMENTINFO_0_SECUREMERCHANTACCOUNTID,
11
+ :reference => [:PROFILEREFERENCE, :PAYMENTREQUEST_0_CUSTOM, :PAYMENTREQUEST_0_INVNUM]
12
+ )
13
+
14
+ def completed?
15
+ params[:PAYMENTINFO_0_PAYMENTSTATUS] == "Completed"
16
+ end
17
+
18
+ def approved?
19
+ params[:PAYMENTINFO_0_ACK] == "Success"
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end