paypal_api 0.3.4 → 0.3.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -7,33 +7,125 @@ the gems that do exist do not cover the entire api.
7
7
 
8
8
  # Usage
9
9
 
10
- ## Interfacing with the gem:
10
+ i want interaction with the gem to be flat and straight-forward, with clear mapping between the api docs and the gem. right now
11
+ the only place where i break this is for "list type" fields, where it makes sense to treat it more like a ruby array.
12
+ all keys are ruby style (snake_case), and should automatically get converted to the proper formatting for you.
13
+
14
+ if you don't add all the required fields to the request, it will raise `Paypal::InvalidRequest` exceptions. if you try to set the wrong
15
+ type to a field, it will raise `Paypal::InvalidParameter` exceptions.
16
+
17
+ ### Payments Pro Example
18
+
19
+ the most useful methods, imo, are do_direct_payment, do_reference_
20
+
11
21
  ```ruby
12
22
  require "paypal_api"
13
23
 
14
- request = Paypal::PaymentsPro.do_direct_payment # returns instance of Paypal::DoDirectPaymentRequest
24
+ # create request
25
+ request = Paypal::PaymentsPro.do_reference_transaction # returns instance of Paypal::DoDirectPaymentRequest
15
26
 
16
- # Set required fields
17
- request.first_name = "mark"
18
- request.last_name = "winton"
27
+ # set required fields
28
+ request.reference_id = "other_paypal_transaction_id"
29
+ request.payment_action = :authorization
19
30
  request.amt = 10.00
20
31
 
21
- # Add a list type field
22
- request.item.push {
23
- :email => "bro@dudeman.com",
24
- :amt => 23.0
32
+ # make request
33
+ response = request.make
34
+
35
+ # usable information
36
+ response.success? # true if successful
37
+ response[:correlation_id] # correlation id string returned by paypal
38
+ transaction_id = response[:transaction_id] # transaction id string, not return on all calls
39
+
40
+ # in this example, since the first request was an authorization, you can then capture it
41
+ request2 = Paypal::PaymentsPro.do_capture # returns instance of Paypal::DoCaptureRequest
42
+
43
+ request2.authorization_id = transaction_id
44
+ request2.amt = 10.00
45
+
46
+ response2 = request2.make
47
+
48
+ response2.success?
49
+ ```
50
+
51
+ ### Adaptive Payments Example
52
+
53
+ even though the api's are very different, they should be abstracted the same way
54
+
55
+ ```ruby
56
+ require "paypal_api"
57
+
58
+ request = Paypal::AdaptivePayments.pay # returns instance of Paypal::PayRequest
59
+
60
+ # set required fields
61
+ request.cancel_url = "http://www.test.com/cancel"
62
+ request.return_url = "http://www.test.com/return"
63
+ request.ip_address = "192.168.1.1"
64
+
65
+ # add a list type field
66
+ request.receiver.push {
67
+ :email => "bro@dudeman.com",
68
+ :amount => 23.0
25
69
  }
26
70
 
71
+ # make request
27
72
  response = request.make
28
73
 
74
+ # usable information
29
75
  response.success? # true if successful
76
+ response[:pay_key] # usually what you use this api method for
30
77
 
31
- response[:correlation_id] # correlation id string returned by paypal
32
- response[:transaction_id] # transaction id string, not return on all calls
78
+ # in adaptive payments flows, sometimes you need to redirect to paypal
79
+ response.redirect_url
80
+ # used if you want the payment flow without the client leaving your site (see below)
81
+ response.embedded_url
82
+
83
+ # errors
84
+ response.error_message # populated by paypal response error when request fails
85
+ response.error_code # populated by paypal response error
86
+ response.error_field # some api calls let you know which field caused the issue
87
+ ```
88
+
89
+ for adaptive payments and preapprovals, sometimes you want to use the paypal javascript libraries
90
+ and their associated "embedded" payment flows. `response.embedded_url` gives you what you need for this.
91
+ note that you also need to include one of their js libraries.
92
+
93
+ ```javascript
94
+ // include one of their libraries asynchronously:
95
+ // minibrowser library: https://www.paypalobjects.com/js/external/dg.js
96
+ // embedded library: https://www.paypalobjects.com/js/external/apdg.js
97
+ window.paypalAsyncInit = function () {
98
+ window.paypalController = new PAYPAL.apps.DGFlow({expType: "mini"});
99
+ };
100
+
101
+ (function(d){
102
+ var js, id = 'paypal-sdk'; if (d.getElementById(id)) {return;}
103
+ js = d.createElement('script'); js.id = id; js.async = true;
104
+ js.src = "https://www.paypalobjects.com/js/external/dg.js";
105
+ d.getElementsByTagName('head')[0].appendChild(js);
106
+ js.onload = window.paypalAsyncInit;
107
+ }(document));
108
+
109
+
110
+ // later, after receiving embedded_url from the server:
111
+ window.paypalController.startFlow(embeddUrl);
33
112
  ```
34
113
 
114
+ ### More
115
+
116
+ the actual api method definitions should be more or less readable as is (if not, you can call me a jerk, i sorry). look in
117
+ `lib/paypal_api/apis/` for reference, until i document each method.
118
+
35
119
  ## Configure
36
120
 
121
+ it's hard to navigate paypals terribly organized everything, here are some links to help you find what you need:
122
+
123
+ * paypal api credentials for production: [https://www.paypal.com/us/cgi-bin/webscr?cmd=_profile-api-signature](https://www.paypal.com/us/cgi-bin/webscr?cmd=_profile-api-signature)
124
+ * sandbox credentials: [https://developer.paypal.com/cgi-bin/devscr?cmd=_certs-session&login_access=0](https://developer.paypal.com/cgi-bin/devscr?cmd=_certs-session&login_access=0)
125
+ * where to tell paypal what your ipn callback url is: [https://www.paypal.com/cgi-bin/customerprofileweb?cmd=_profile-ipn-notify](https://www.paypal.com/cgi-bin/customerprofileweb?cmd=_profile-ipn-notify)
126
+
127
+ ### Simple Configuration
128
+
37
129
  ```ruby
38
130
  Paypal::Request.version = "84.0"
39
131
  Paypal::Request.environment = "development" # or "production"
@@ -42,11 +134,7 @@ Paypal::Request.pwd = "some_password_they_gave_you"
42
134
  Paypal::Request.signature = "some_signature"
43
135
  ```
44
136
 
45
- paypal api credentials for production can be found here: [https://www.paypal.com/us/cgi-bin/webscr?cmd=_profile-api-signature](https://www.paypal.com/us/cgi-bin/webscr?cmd=_profile-api-signature)
46
-
47
- sandbox credentials can be found here: [https://developer.paypal.com/cgi-bin/devscr?cmd=_certs-session&login_access=0](https://developer.paypal.com/cgi-bin/devscr?cmd=_certs-session&login_access=0)
48
-
49
- ## Rails
137
+ ### Configure For Rails
50
138
 
51
139
  if you'd like to have multi environment configuration in rails, place a file at `config/paypal.yml` and the gem will read from it accordingly
52
140
 
@@ -68,7 +156,7 @@ production:
68
156
 
69
157
  ## Ipn Messages
70
158
 
71
- there is an ipn message model generator: `rails generate paypal:ipn_message`, it will create
159
+ there is an ipn message model generator: `$ rails generate paypal:ipn_message`, it will create
72
160
  a migration and the IpnMessage model.
73
161
 
74
162
  you must edit the route and add a handler like so:
@@ -98,8 +186,10 @@ end
98
186
 
99
187
  ## Testing
100
188
 
101
- i'm an rspec kinda guy. note that i have test for actual requests against the paypal
102
- sandbox server, but they are off by default. remove the line about `:slow_paypal` in
189
+ you can run the tests: `$ bundle exec rspec spec`
190
+
191
+ i'm an rspec kinda guy, hopefully you are ok with that. note that i have tests for actual requests against the paypal
192
+ sandbox server, but they are turned off by default. remove the line about `:slow_paypal` in
103
193
  `spec/spec_helper.rb` to turn these back on.
104
194
 
105
195
  also note that, for the Adaptive Payments api, an application id is required, for testing,
@@ -115,14 +205,40 @@ repository. in order to get to that stage, i will need to add support for adapti
115
205
 
116
206
  # How To Contribute
117
207
 
118
- right now the most help i could use is in writing the specs for the various api calls from the Payments Pro api. i will be working on
119
- separating out the different access methods shortly (there's a huge difference between how to call the Payments Pro api vs the Adaptive Payments api).
208
+ right now the most help i could use is in writing the signatures for the various api calls from the Payments Pro and
209
+ Adaptive Payments apis (or any others really).
120
210
 
121
- as this is my first gem, i could also use help with some of the niceties with rails. ideally there will be a generator for migrating your db
122
- to store ipn messages, and a generated class with callbacks to handle the various cases. this will probably take a lot of effort since there
123
- are many intricacies in the meanings of the different ipn's.
211
+ i've tried to make it easy to contribute, signatures are created pretty easily:
124
212
 
125
- for contributing to the api method request specs, look at `lib/paypal_api/apis/payments_pro.rb`
213
+ ```ruby
214
+ # lib/paypal_api/apis/payments_pro.rb
215
+
216
+ module Paypal
217
+ class PaymentsPro < Paypal::Api
218
+
219
+ set_request_signature :do_capture, {
220
+ :method => "DoCapture",
221
+ :authorization_id => String,
222
+ :amt => Float,
223
+ :currency_code => Default.new("USD", /^[a-z]{3}$/i),
224
+ :complete_type => Default.new("Complete", Enum.new("Complete", "NotComplete")),
225
+ :inv_num => Optional.new(String),
226
+ :note => Optional.new(String),
227
+ :soft_descriptor => Optional.new(lambda {|val|
228
+ if val.match(/^([a-z0-9]|\.|-|\*| )*$/i) && val.length <= 22
229
+ return true
230
+ else
231
+ return false
232
+ end
233
+ }),
234
+
235
+ :store_id => Optional.new(String),
236
+ :terminal_id => Optional.new(String)
237
+ }
238
+
239
+ end
240
+ end
241
+ ```
126
242
 
127
243
  this is my first gem, so i'll be excited for any contributions :'(
128
244
 
@@ -192,8 +308,6 @@ note that you need to request that paypal enable mass pay for your account befor
192
308
 
193
309
  * mass_pay - &#10003;
194
310
 
195
- ## Instant Pay Notifications
196
-
197
311
  ## Express Checkout
198
312
 
199
313
  ## Adaptive Payments
@@ -5,6 +5,7 @@
5
5
  module Paypal
6
6
 
7
7
  class AdaptivePaymentsResponse < Response
8
+
8
9
  def initialize(stringio)
9
10
  @raw_response = stringio.class == StringIO ? stringio.read : stringio
10
11
  @parsed_response = CGI.parse(@raw_response)
@@ -30,6 +31,17 @@ module Paypal
30
31
  end
31
32
  end
32
33
 
34
+ def redirect_url
35
+ "#{self.base_url}/webscr?cmd=_ap-#{command}&#{command_key}=#{command_value}" if webscr?
36
+ end
37
+
38
+ # requires https://www.paypalobjects.com/js/external/dg.js (lightbox)
39
+ # or https://www.paypalobjects.com/js/external/apdg.js (minibrowser)
40
+ # to be added to the page
41
+ def embedded_url
42
+ "#{self.base_url}/webapps/adaptivepayment/flow/pay?payKey=#{self[:pay_key]}#{"&preapprovalkey=#{self[:preapproval_key]}" if command == :preapproval}" if webscr?
43
+ end
44
+
33
45
  protected
34
46
 
35
47
  def symbol_to_key(symbol)
@@ -40,6 +52,28 @@ module Paypal
40
52
  return Paypal::Api.symbol_to_lower_camel(symbol)
41
53
  end
42
54
  end
55
+
56
+ def base_url
57
+ Paypal::Request.environment == "production" ? "https://www.paypal.com" : "https://www.sandbox.paypal.com"
58
+ end
59
+
60
+ private
61
+
62
+ def command_key
63
+ return command == :preapproval ? "preapprovalkey" : "payKey"
64
+ end
65
+
66
+ def command_value
67
+ return command == :preapproval ? self[:preapproval_key] : self[:pay_key]
68
+ end
69
+
70
+ def command
71
+ return self[:preapproval_key] ? :preapproval : :payment
72
+ end
73
+
74
+ def webscr?
75
+ return true if self[:pay_key] || self[:preapproval_key]
76
+ end
43
77
  end
44
78
 
45
79
  class AdaptivePaymentsRequest < Request
@@ -134,6 +134,7 @@ module Paypal
134
134
  @allowed_values = values[0]
135
135
  else
136
136
  @allowed_values = values
137
+ @normalized_values = @allowed_values.inject({}){|acc, v| acc[normalize(v)] = v; acc}
137
138
  end
138
139
  end
139
140
 
@@ -145,8 +146,9 @@ module Paypal
145
146
  raise Paypal::InvalidParameter, "'#{val}' must be a key in #{@allowed_values.keys}"
146
147
  end
147
148
  else
148
- if @allowed_values.include?(normalize(val))
149
- return normalize(val)
149
+ normed = normalize(val)
150
+ if @normalized_values.include?(normed)
151
+ return @normalized_values[normed]
150
152
  else
151
153
  raise Paypal::InvalidParameter, "'#{val}' must be one of #{@allowed_values}"
152
154
  end
@@ -154,9 +156,7 @@ module Paypal
154
156
  end
155
157
 
156
158
  def normalize(val)
157
- return val if val.class == String
158
- return Paypal::Api.symbol_to_camel(val) if val.class == Symbol
159
- return nil
159
+ return val.to_s.downcase.gsub(/[^a-z0-9]/,"")
160
160
  end
161
161
  end
162
162
 
@@ -1,3 +1,3 @@
1
1
  module Paypal
2
- VERSION = "0.3.4"
2
+ VERSION = "0.3.5"
3
3
  end
@@ -19,6 +19,14 @@ describe Paypal::Api::Parameter do
19
19
  }.to raise_exception(Paypal::InvalidParameter)
20
20
  end
21
21
 
22
+ it "should allow lookup on normalized matching" do
23
+ param = @api::Enum.new("This", "FirstTest", "SaweetDude")
24
+
25
+ param.parse(:this).should eq("This")
26
+ param.parse(:first_test).should eq("FirstTest")
27
+ param.parse(:saweet_dude).should eq("SaweetDude")
28
+ end
29
+
22
30
  describe "hash enumerations" do
23
31
  before(:each) do
24
32
  @param = @api::Enum.new({:thing => "OK", :that => "TEO"})
@@ -0,0 +1,35 @@
1
+ require "spec_helper"
2
+
3
+ describe Paypal::AdaptivePaymentsResponse do
4
+ describe "for pay requests" do
5
+ before(:each) do
6
+ @response = Paypal::AdaptivePaymentsResponse.new("responseEnvelope.timestamp=2012-02-29T13%3A40%3A17.074-08%3A00&responseEnvelope.ack=Success&responseEnvelope.correlationId=722689b427b7c&responseEnvelope.build=2486531&payKey=AP-1W763567RM6393227&paymentExecStatus=CREATED")
7
+ end
8
+
9
+ describe "embedded url" do
10
+ it "should create a proper embedded url" do
11
+ @response.embedded_url.should include("/webapps/adaptivepayment/flow/pay?payKey=")
12
+ end
13
+ end
14
+
15
+ describe "redirect url" do
16
+ it "should create a proper redirect url" do
17
+ @response.redirect_url.should include("/webscr?cmd=_ap-")
18
+ end
19
+ end
20
+
21
+ it "should know about production vs sandbox" do
22
+ Paypal::Request.environment = "production"
23
+ @response.redirect_url.should_not include("sandbox")
24
+ Paypal::Request.environment = "something else"
25
+ @response.redirect_url.should include("sandbox")
26
+ end
27
+ end
28
+
29
+ describe "for payapproval requests" do
30
+ it "should create a proper redirect url" do
31
+
32
+ end
33
+ end
34
+
35
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: paypal_api
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.4
4
+ version: 0.3.5
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-03-01 00:00:00.000000000Z
12
+ date: 2012-03-02 00:00:00.000000000Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rails
16
- requirement: &70244225979560 !ruby/object:Gem::Requirement
16
+ requirement: &70244405693760 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: '3.1'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *70244225979560
24
+ version_requirements: *70244405693760
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: ruby-debug19
27
- requirement: &70244225978140 !ruby/object:Gem::Requirement
27
+ requirement: &70244405693180 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: '0'
33
33
  type: :development
34
34
  prerelease: false
35
- version_requirements: *70244225978140
35
+ version_requirements: *70244405693180
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: rspec-rails
38
- requirement: &70244225976180 !ruby/object:Gem::Requirement
38
+ requirement: &70244405692480 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ~>
@@ -43,10 +43,10 @@ dependencies:
43
43
  version: '2.6'
44
44
  type: :development
45
45
  prerelease: false
46
- version_requirements: *70244225976180
46
+ version_requirements: *70244405692480
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: generator_spec
49
- requirement: &70244225974780 !ruby/object:Gem::Requirement
49
+ requirement: &70244405691920 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - =
@@ -54,7 +54,7 @@ dependencies:
54
54
  version: 0.8.5
55
55
  type: :development
56
56
  prerelease: false
57
- version_requirements: *70244225974780
57
+ version_requirements: *70244405691920
58
58
  description: alpha - currently covers part of payments pro and all of mass pay
59
59
  email:
60
60
  - matt.handler@gmail.com
@@ -92,6 +92,7 @@ files:
92
92
  - spec/support/parameter_spec.rb
93
93
  - spec/support/request_spec.rb
94
94
  - spec/support/response_spec.rb
95
+ - spec/unit/adaptive_payments_response_spec.rb
95
96
  - spec/unit/adaptive_payments_spec.rb
96
97
  - spec/unit/api_spec.rb
97
98
  - spec/unit/mass_pay_spec.rb
@@ -126,6 +127,7 @@ test_files:
126
127
  - spec/support/parameter_spec.rb
127
128
  - spec/support/request_spec.rb
128
129
  - spec/support/response_spec.rb
130
+ - spec/unit/adaptive_payments_response_spec.rb
129
131
  - spec/unit/adaptive_payments_spec.rb
130
132
  - spec/unit/api_spec.rb
131
133
  - spec/unit/mass_pay_spec.rb