paypal_api 0.3.4 → 0.3.5

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.
@@ -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