pesapal 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +13 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +119 -0
- data/Rakefile +1 -0
- data/lib/pesapal.rb +6 -0
- data/lib/pesapal/merchant.rb +114 -0
- data/lib/pesapal/merchant/post.rb +50 -0
- data/lib/pesapal/oauth.rb +178 -0
- data/lib/pesapal/version.rb +3 -0
- data/pesapal.gemspec +23 -0
- metadata +85 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: be6b9604e4788df7261b80d64ae81c03181af7b5
|
4
|
+
data.tar.gz: 08721ecf0cd162eeca31abe10a448477bd482ed1
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 533b27e99df804b0b7678b2e03ca495cb57c4ef537ffdb6d1b50c29e5fdc868b915694ec683c2f091b86c92e8dd634300bad094a3ec67f02ad414088361cefea
|
7
|
+
data.tar.gz: 5631b6072f4ce540e87cc7a00864c9ab4a7b66898c42f1b54b4d84cde00aa23882e5fe385ca7605779a57e80f8155544bc0ac2a44f3098816b6721a87c18a2e6
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 King'ori Maina
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,119 @@
|
|
1
|
+
Pesapal RubyGem
|
2
|
+
===============
|
3
|
+
|
4
|
+
Make authenticated Pesapal API calls without the fuss! Handles all the [oAuth
|
5
|
+
stuff][1] abstracting any direct interaction with the API endpoints so that you
|
6
|
+
can focus on what matters. _Building awesome_.
|
7
|
+
|
8
|
+
This gem is work in progress. At the moment, the only functionality built-in is
|
9
|
+
posting an order i.e. fetching the URL that is required to display the post-
|
10
|
+
order iframe. Everything else should be easy to do as the groundwork has already
|
11
|
+
been laid. If you are [feeling generous and want to contribute, feel free][9].
|
12
|
+
|
13
|
+
Submit [issues and requests here][6]. The gem should be [up on RubyGems.org][7].
|
14
|
+
|
15
|
+
_Ps: No 3rd party oAuth library dependencies, it handles all the oAuth flows on
|
16
|
+
it's own so it's light on your app._
|
17
|
+
|
18
|
+
|
19
|
+
Installation
|
20
|
+
------------
|
21
|
+
|
22
|
+
Add this line to your application's Gemfile:
|
23
|
+
|
24
|
+
gem 'pesapal'
|
25
|
+
|
26
|
+
And then execute:
|
27
|
+
|
28
|
+
$ bundle
|
29
|
+
|
30
|
+
Or install it yourself as:
|
31
|
+
|
32
|
+
$ gem install pesapal
|
33
|
+
|
34
|
+
|
35
|
+
Usage
|
36
|
+
-----
|
37
|
+
|
38
|
+
### Setup ###
|
39
|
+
|
40
|
+
Initialize Pesapal object and choose the mode, there are two modes;
|
41
|
+
`:development` and `:production`. They determine if you the code will interact
|
42
|
+
with Pesapal for testing or for a live deployment.
|
43
|
+
|
44
|
+
```ruby
|
45
|
+
# initiate pesapal object set to development mode
|
46
|
+
pesapal = Pesapal::Merchant.new(:development)
|
47
|
+
```
|
48
|
+
|
49
|
+
Now set the Pesapal credentials. This assumes that you've chosen the appropriate
|
50
|
+
credentials as they differ based on the mode chosen above (Pesapal provide the
|
51
|
+
keys). Replace the placeholders below with your own credentials.
|
52
|
+
|
53
|
+
```ruby
|
54
|
+
# set pesapal api credentials
|
55
|
+
pesapal.credentials = { :consumer_key => '<YOUR_CONSUMER_KEY>',
|
56
|
+
:consumer_secret => '<YOUR_CONSUMER_SECRET>'
|
57
|
+
}
|
58
|
+
```
|
59
|
+
|
60
|
+
### Post Order ###
|
61
|
+
|
62
|
+
Once you've set up the credentials, set up the order details in a hash as shown
|
63
|
+
in the example below ... all keys **MUST** be present. If there's one that you
|
64
|
+
wish to ignore just leave it with a blank string but make sure it's included
|
65
|
+
e.g. the phonenumber.
|
66
|
+
|
67
|
+
```ruby
|
68
|
+
#set order details
|
69
|
+
pesapal.order_details = { :amount => 1000,
|
70
|
+
:description => 'this is the transaction description',
|
71
|
+
:type => 'MERCHANT',
|
72
|
+
:reference => 808-707-606,
|
73
|
+
:first_name => 'Swaleh',
|
74
|
+
:last_name => 'Mdoe',
|
75
|
+
:email => 'user@example.com',
|
76
|
+
:phonenumber => '+254722222222'
|
77
|
+
}
|
78
|
+
```
|
79
|
+
|
80
|
+
Then generate the transaction url as below. In the example, the value is
|
81
|
+
assigned to the variable `order_url` which you can pass on to the templating
|
82
|
+
system of your to generate an iframe.
|
83
|
+
|
84
|
+
```ruby
|
85
|
+
# generate transaction url
|
86
|
+
order_url = pesapal.generate_order_url
|
87
|
+
```
|
88
|
+
|
89
|
+
|
90
|
+
Contributing
|
91
|
+
------------
|
92
|
+
|
93
|
+
1. [Fork it][8]
|
94
|
+
2. Create your feature branch (`git checkout -b wip-my-new-feature`)
|
95
|
+
3. Commit your changes (`git commit -am 'Add some feature with awesome'`)
|
96
|
+
4. Push to the branch (`git push origin wip-my-new-feature`)
|
97
|
+
5. Create new pull request
|
98
|
+
|
99
|
+
_Ps: Please prefix branch name with 'wip-' ... means 'work in progress'._
|
100
|
+
|
101
|
+
|
102
|
+
References
|
103
|
+
----------
|
104
|
+
|
105
|
+
* [oAuth 1.0 Spec][1]
|
106
|
+
* [Developing a RubyGem using Bundler][2]
|
107
|
+
* [Make your own gem][3]
|
108
|
+
* [Pesapal API Reference (Official)][4]
|
109
|
+
* [Pesapal PHP API Reference (Unofficial)][5]
|
110
|
+
|
111
|
+
[1]: http://oauth.net/core/1.0/
|
112
|
+
[2]: https://github.com/radar/guides/blob/master/gem-development.md
|
113
|
+
[3]: http://guides.rubygems.org/make-your-own-gem/
|
114
|
+
[4]: http://developer.pesapal.com/how-to-integrate/api-reference
|
115
|
+
[5]: https://github.com/itsmrwave/pesapal-php#pesapal-php-api-reference-unofficial
|
116
|
+
[6]: https://github.com/itsmrwave/pesapal-rubygem/issues
|
117
|
+
[7]: http://rubygems.org/gems/pesapal
|
118
|
+
[8]: https://github.com/itsmrwave/pesapal-rubygem/fork
|
119
|
+
[9]: https://github.com/itsmrwave/pesapal-rubygem#contributing
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/lib/pesapal.rb
ADDED
@@ -0,0 +1,114 @@
|
|
1
|
+
module Pesapal
|
2
|
+
|
3
|
+
class Merchant
|
4
|
+
|
5
|
+
attr_accessor :callback_url, :credentials, :order_details
|
6
|
+
attr_reader :api_domain, :api_endpoints
|
7
|
+
|
8
|
+
def api_domain
|
9
|
+
@api_domain
|
10
|
+
end
|
11
|
+
|
12
|
+
def api_endpoints
|
13
|
+
@api_endpoints
|
14
|
+
end
|
15
|
+
|
16
|
+
def callback_url
|
17
|
+
@callback_url
|
18
|
+
end
|
19
|
+
|
20
|
+
def credentials
|
21
|
+
@credentials
|
22
|
+
end
|
23
|
+
|
24
|
+
def order_details
|
25
|
+
@order_details
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def params
|
31
|
+
@params
|
32
|
+
end
|
33
|
+
|
34
|
+
def post_xml
|
35
|
+
@post_xml
|
36
|
+
end
|
37
|
+
|
38
|
+
def token_secret
|
39
|
+
@token_secret
|
40
|
+
end
|
41
|
+
|
42
|
+
public
|
43
|
+
|
44
|
+
# constructor
|
45
|
+
def initialize(mode = :development)
|
46
|
+
|
47
|
+
# convert symbol to string and downcase
|
48
|
+
mode.to_s.downcase!
|
49
|
+
|
50
|
+
# initialize
|
51
|
+
@params = nil
|
52
|
+
@post_xml = nil
|
53
|
+
@token_secret = nil
|
54
|
+
|
55
|
+
# set the credentials from the config (if initializers/pesapal.rb
|
56
|
+
# exists they should have set these values)
|
57
|
+
@credentials = nil
|
58
|
+
|
59
|
+
# set the callback url that the iframe will respond to
|
60
|
+
@callback_url = 'http://0.0.0.0:3000/pesapal/callback'
|
61
|
+
|
62
|
+
# set api endpoints depending on the mode
|
63
|
+
@api_endpoints = {}
|
64
|
+
if mode == 'development'
|
65
|
+
set_endpoints_development
|
66
|
+
elseif mode == 'production'
|
67
|
+
set_endpoints_production
|
68
|
+
else
|
69
|
+
set_endpoints_development
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# generate pesapal order url (often iframed)
|
74
|
+
def generate_order_url
|
75
|
+
|
76
|
+
# build xml with input data, the format is standard so no editing is
|
77
|
+
# required
|
78
|
+
@post_xml = Pesapal::Post::generate_post_xml @order_details
|
79
|
+
|
80
|
+
# initialize setting of @params (oauth_signature left empty) ... this gene
|
81
|
+
@params = Pesapal::Post::set_parameters(@callback_url, @credentials[:consumer_key], @post_xml)
|
82
|
+
|
83
|
+
# generate oauth signature and add signature to the request parameters
|
84
|
+
@params[:oauth_signature] = Pesapal::Oauth::generate_oauth_signature("GET", @api_endpoints[:postpesapaldirectorderv4], @params, @credentials[:consumer_secret], @token_secret)
|
85
|
+
|
86
|
+
# change params (with signature) to a query string
|
87
|
+
query_string = Pesapal::Oauth::generate_encoded_params_query_string @params
|
88
|
+
|
89
|
+
"#{@api_endpoints[:postpesapaldirectorderv4]}?#{query_string}"
|
90
|
+
end
|
91
|
+
|
92
|
+
private
|
93
|
+
|
94
|
+
# set all endpoint for use in development mode
|
95
|
+
def set_endpoints_development
|
96
|
+
@api_domain = 'http://demo.pesapal.com'
|
97
|
+
set_endpoints @api_domain
|
98
|
+
end
|
99
|
+
|
100
|
+
# set all enpoints for use in production mode
|
101
|
+
def set_endpoints_production
|
102
|
+
@api_domain = "https://www.pesapal.com"
|
103
|
+
set_endpoints @api_domain
|
104
|
+
end
|
105
|
+
|
106
|
+
# set endpoints
|
107
|
+
def set_endpoints(domain_string)
|
108
|
+
@api_endpoints[:postpesapaldirectorderv4] = "#{domain_string}/API/PostPesapalDirectOrderV4"
|
109
|
+
@api_endpoints[:querypaymentstatus] = "#{domain_string}/API/QueryPaymentStatus"
|
110
|
+
@api_endpoints[:querypaymentstatusbymerchantref] = "#{domain_string}/API/QueryPaymentStatus"
|
111
|
+
@api_endpoints[:querypaymentdetails] = "#{domain_string}/API/QueryPaymentDetails"
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module Pesapal
|
2
|
+
|
3
|
+
module Post
|
4
|
+
|
5
|
+
# build html encoded xml string for PostPesapalDirectOrderV4
|
6
|
+
def Post.generate_post_xml(details)
|
7
|
+
|
8
|
+
# build xml with input data, the format is standard so no editing is
|
9
|
+
# required
|
10
|
+
post_xml = ''
|
11
|
+
post_xml.concat '<?xml version="1.0" encoding="utf-8"?>'
|
12
|
+
post_xml.concat '<PesapalDirectOrderInfo '
|
13
|
+
post_xml.concat 'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" '
|
14
|
+
post_xml.concat 'xmlns:xsd="http://www.w3.org/2001/XMLSchema" '
|
15
|
+
post_xml.concat "Amount=\"#{details[:amount]}\" "
|
16
|
+
post_xml.concat "Description=\"#{details[:description]}\" "
|
17
|
+
post_xml.concat "Type=\"#{details[:type]}\" "
|
18
|
+
post_xml.concat "Reference=\"#{details[:reference]}\" "
|
19
|
+
post_xml.concat "FirstName=\"#{details[:first_name]}\" "
|
20
|
+
post_xml.concat "LastName=\"#{details[:last_name]}\" "
|
21
|
+
post_xml.concat "Email=\"#{details[:email]}\" "
|
22
|
+
post_xml.concat "PhoneNumber=\"#{details[:phonenumber]}\" "
|
23
|
+
post_xml.concat 'xmlns="http://www.pesapal.com" />'
|
24
|
+
|
25
|
+
encoder = HTMLEntities.new(:xhtml1)
|
26
|
+
post_xml = encoder.encode post_xml
|
27
|
+
|
28
|
+
"#{post_xml}"
|
29
|
+
end
|
30
|
+
|
31
|
+
# set parameters required by the PostPesapalDirectOrderV4
|
32
|
+
def Post.set_parameters(callback_url, consumer_key, post_xml)
|
33
|
+
|
34
|
+
# parameters required by the PostPesapalDirectOrderV4 call (excludes
|
35
|
+
# oauth_signature parameter as per the instructions here
|
36
|
+
# http://developer.pesapal.com/how-to-integrate/api-reference#PostPesapalDirectOrderV4)
|
37
|
+
|
38
|
+
timestamp = Time.now.to_i.to_s
|
39
|
+
|
40
|
+
params = { :oauth_callback => callback_url,
|
41
|
+
:oauth_consumer_key => consumer_key,
|
42
|
+
:oauth_nonce => "#{timestamp}" + Pesapal::Oauth.generate_nonce(12),
|
43
|
+
:oauth_signature_method => 'HMAC-SHA1',
|
44
|
+
:oauth_timestamp => "#{timestamp}",
|
45
|
+
:oauth_version => '1.0',
|
46
|
+
:pesapal_request_data => post_xml
|
47
|
+
}
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,178 @@
|
|
1
|
+
module Pesapal
|
2
|
+
|
3
|
+
module Oauth
|
4
|
+
|
5
|
+
# generate query string from parameters hash
|
6
|
+
def Oauth.generate_encoded_params_query_string(params = {})
|
7
|
+
|
8
|
+
# 1) percent encode every key and value that will be signed
|
9
|
+
# 2) sort the list of parameters alphabetically by encoded key
|
10
|
+
# 3) for each key/value pair
|
11
|
+
# - append the encoded key to the output string
|
12
|
+
# - append the '=' character to the output string
|
13
|
+
# - append the encoded value to the output string
|
14
|
+
# 4) if there are more key/value pairs remaining, append a '&'
|
15
|
+
# character to the output string
|
16
|
+
|
17
|
+
# the oauth spec says to sort lexigraphically, which is the default
|
18
|
+
# alphabetical sort for many libraries. in case of two parameters
|
19
|
+
# with the same encoded key, the oauth spec says to continue
|
20
|
+
# sorting based on value
|
21
|
+
|
22
|
+
queries = []
|
23
|
+
params.each do |k,v| queries.push "#{self.parameter_encode(k.to_s)}=#{self.parameter_encode(v.to_s)}" end
|
24
|
+
|
25
|
+
# parameters are sorted by name, using lexicographical byte value
|
26
|
+
# ordering
|
27
|
+
queries.sort!
|
28
|
+
|
29
|
+
queries.join('&')
|
30
|
+
end
|
31
|
+
|
32
|
+
# generate oauth nonce
|
33
|
+
def Oauth.generate_nonce(length)
|
34
|
+
|
35
|
+
# the consumer shall then generate a nonce value that is unique for
|
36
|
+
# all requests with that timestamp. a nonce is a random string,
|
37
|
+
# uniquely generated for each request. the nonce allows the service
|
38
|
+
# provider to verify that a request has never been made before and
|
39
|
+
# helps prevent replay attacks when requests are made over a non-
|
40
|
+
# secure channel (such as http).
|
41
|
+
|
42
|
+
chars = 'abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ0123456789'
|
43
|
+
nonce = ''
|
44
|
+
length.times { nonce << chars[rand(chars.size)] }
|
45
|
+
|
46
|
+
"#{nonce}"
|
47
|
+
end
|
48
|
+
|
49
|
+
# generate the oauth signature using hmac-sha1 algorithm
|
50
|
+
def Oauth.generate_oauth_signature(http_method, absolute_url, params, consumer_secret, token_secret = nil)
|
51
|
+
|
52
|
+
# the signature is calculated by passing the signature base string
|
53
|
+
# and signing key to the hmac-sha1 hashing algorithm. the output of
|
54
|
+
# the hmac signing function is a binary string. this needs to be
|
55
|
+
# base64 encoded to produce the signature string.
|
56
|
+
|
57
|
+
# for pesapal flow we don't have a token secret to we will set as
|
58
|
+
# nil and the appropriate action will be taken as per the oauth
|
59
|
+
# spec. see notes in the method that creates signing keys
|
60
|
+
|
61
|
+
# prepare the values we need
|
62
|
+
digest = OpenSSL::Digest::Digest.new('sha1')
|
63
|
+
signature_base_string = self.generate_signature_base_string(http_method, absolute_url, params)
|
64
|
+
signing_key = self.generate_signing_key(consumer_secret, token_secret)
|
65
|
+
|
66
|
+
hmac = OpenSSL::HMAC.digest(digest, signing_key, signature_base_string)
|
67
|
+
Base64.encode64(hmac).chomp
|
68
|
+
end
|
69
|
+
|
70
|
+
# generate query string from signable parameters hash
|
71
|
+
def Oauth.generate_signable_encoded_params_query_string(params = {})
|
72
|
+
|
73
|
+
# oauth_signature parameter MUST be excluded, assumes it was already
|
74
|
+
# initialized by calling set_parameters
|
75
|
+
params.delete(:oauth_signature)
|
76
|
+
|
77
|
+
self.generate_encoded_params_query_string params
|
78
|
+
end
|
79
|
+
|
80
|
+
# generate the oauth signature
|
81
|
+
def Oauth.generate_signature_base_string(http_method, absolute_url, params)
|
82
|
+
|
83
|
+
# three values collected so far must be joined to make a single
|
84
|
+
# string, from which the signature will be generated. This is
|
85
|
+
# called the signature base string by the OAuth specification
|
86
|
+
|
87
|
+
# step 1: convert the http method to uppercase
|
88
|
+
http_method = http_method.upcase
|
89
|
+
|
90
|
+
# step 2: percent encode the url
|
91
|
+
url_encoded = self.parameter_encode(self.normalized_request_uri(absolute_url))
|
92
|
+
|
93
|
+
# step 3: percent encode the parameter string
|
94
|
+
parameter_string_encoded = self.parameter_encode(self.generate_signable_encoded_params_query_string params)
|
95
|
+
|
96
|
+
# the signature base string should contain exactly 2 ampersand '&'
|
97
|
+
# characters. The percent '%' characters in the parameter string should
|
98
|
+
# be encoded as %25 in the signature base string
|
99
|
+
|
100
|
+
"#{http_method}&#{url_encoded}&#{parameter_string_encoded}"
|
101
|
+
end
|
102
|
+
|
103
|
+
# generate signing key
|
104
|
+
def Oauth.generate_signing_key(consumer_secret, token_secret = nil)
|
105
|
+
|
106
|
+
# the signing key is simply the percent encoded consumer secret,
|
107
|
+
# followed by an ampersand character '&', followed by the percent
|
108
|
+
# encoded token secret
|
109
|
+
|
110
|
+
# note that there are some flows, such as when obtaining a request
|
111
|
+
# token, where the token secret is not yet known. In this case, the
|
112
|
+
# signing key should consist of the percent encoded consumer secret
|
113
|
+
# followed by an ampersand character '&'
|
114
|
+
|
115
|
+
# "#{@credentials[:consumer_secret]}"
|
116
|
+
consumer_secret_encoded = self.parameter_encode(consumer_secret)
|
117
|
+
|
118
|
+
token_secret_encoded = ""
|
119
|
+
unless token_secret.nil?
|
120
|
+
token_secret_encoded = self.parameter_encode(token_secret)
|
121
|
+
end
|
122
|
+
|
123
|
+
"#{consumer_secret_encoded}&#{token_secret_encoded}"
|
124
|
+
end
|
125
|
+
|
126
|
+
# normalize request absolute URL
|
127
|
+
def Oauth.normalized_request_uri(absolute_url)
|
128
|
+
|
129
|
+
# the signature base string includes the request absolute url, tying
|
130
|
+
# the signature to a specific endpoint. the url used in the
|
131
|
+
# signature base string must include the scheme, authority, and
|
132
|
+
# path, and must exclude the query and fragment as defined by
|
133
|
+
# [rfc3986] section 3.
|
134
|
+
|
135
|
+
# if the absolute request url is not available to the service
|
136
|
+
# provider (it is always available to the consumer), it can be
|
137
|
+
# constructed by combining the scheme being used, the http host
|
138
|
+
# header, and the relative http request url. if the host header is
|
139
|
+
# not available, the service provider should use the host name
|
140
|
+
# communicated to the consumer in the documentation or other means.
|
141
|
+
|
142
|
+
# the service provider should document the form of url used in the
|
143
|
+
# signature base string to avoid ambiguity due to url normalization.
|
144
|
+
# unless specified, url scheme and authority must be lowercase and
|
145
|
+
# include the port number; http default port 80 and https default
|
146
|
+
# port 443 must be excluded.
|
147
|
+
|
148
|
+
u = URI.parse(absolute_url)
|
149
|
+
|
150
|
+
scheme = u.scheme.downcase
|
151
|
+
host = u.host.downcase
|
152
|
+
path = u.path
|
153
|
+
port = u.port
|
154
|
+
|
155
|
+
port = (scheme == 'http' && port != 80) || (scheme == 'https' && port != 443) ? ":#{port}" : ""
|
156
|
+
path = (path && path != '') ? path : '/'
|
157
|
+
|
158
|
+
"#{scheme}://#{host}#{port}#{path}"
|
159
|
+
end
|
160
|
+
|
161
|
+
# percentage encode value as per the oauth spec
|
162
|
+
def Oauth.parameter_encode(string)
|
163
|
+
|
164
|
+
# all parameter names and values are escaped using the [rfc3986]
|
165
|
+
# percent-encoding (%xx) mechanism. characters not in the unreserved
|
166
|
+
# character set ([rfc3986] section 2.3) must be encoded. characters
|
167
|
+
# in the unreserved character set must not be encoded. hexadecimal
|
168
|
+
# characters in encodings must be upper case. text names and values
|
169
|
+
# must be encoded as utf-8 octets before percent-encoding them per
|
170
|
+
# [rfc3629].
|
171
|
+
|
172
|
+
# reserved character regexp, per section 5.1
|
173
|
+
reserved_characters = /[^a-zA-Z0-9\-\.\_\~]/
|
174
|
+
|
175
|
+
URI::escape(string.to_s.force_encoding(Encoding::UTF_8), reserved_characters)
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
data/pesapal.gemspec
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
lib = File.expand_path("../lib", __FILE__)
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
+
require "pesapal/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "pesapal"
|
7
|
+
spec.version = Pesapal::VERSION
|
8
|
+
spec.date = "2013-09-28"
|
9
|
+
spec.authors = ["Job King'ori Maina"]
|
10
|
+
spec.email = ["j@kingori.co"]
|
11
|
+
spec.description = "Make authenticated Pesapal API calls without the fuss!"
|
12
|
+
spec.summary = "Make authenticated Pesapal API calls without the fuss! Handles all the oAuth stuff abstracting any direct interaction with the API endpoints so that you can focus on what matters. Building awesome."
|
13
|
+
spec.homepage = "http://rubygems.org/gems/pesapal"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
22
|
+
spec.add_development_dependency "rake"
|
23
|
+
end
|
metadata
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: pesapal
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Job King'ori Maina
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-09-28 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.3'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.3'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
description: Make authenticated Pesapal API calls without the fuss!
|
42
|
+
email:
|
43
|
+
- j@kingori.co
|
44
|
+
executables: []
|
45
|
+
extensions: []
|
46
|
+
extra_rdoc_files: []
|
47
|
+
files:
|
48
|
+
- .gitignore
|
49
|
+
- Gemfile
|
50
|
+
- LICENSE.txt
|
51
|
+
- README.md
|
52
|
+
- Rakefile
|
53
|
+
- lib/pesapal.rb
|
54
|
+
- lib/pesapal/merchant.rb
|
55
|
+
- lib/pesapal/merchant/post.rb
|
56
|
+
- lib/pesapal/oauth.rb
|
57
|
+
- lib/pesapal/version.rb
|
58
|
+
- pesapal.gemspec
|
59
|
+
homepage: http://rubygems.org/gems/pesapal
|
60
|
+
licenses:
|
61
|
+
- MIT
|
62
|
+
metadata: {}
|
63
|
+
post_install_message:
|
64
|
+
rdoc_options: []
|
65
|
+
require_paths:
|
66
|
+
- lib
|
67
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
68
|
+
requirements:
|
69
|
+
- - '>='
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
version: '0'
|
72
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - '>='
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0'
|
77
|
+
requirements: []
|
78
|
+
rubyforge_project:
|
79
|
+
rubygems_version: 2.1.5
|
80
|
+
signing_key:
|
81
|
+
specification_version: 4
|
82
|
+
summary: Make authenticated Pesapal API calls without the fuss! Handles all the oAuth
|
83
|
+
stuff abstracting any direct interaction with the API endpoints so that you can
|
84
|
+
focus on what matters. Building awesome.
|
85
|
+
test_files: []
|