pesapal 1.2.1 → 1.2.2

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG.md CHANGED
@@ -1,12 +1,24 @@
1
1
  Changelog
2
2
  =========
3
3
 
4
+ v1.2.2
5
+ ------
6
+
7
+ * Fix `can't modify frozen String` error. Apparently we can't force_encoding on
8
+ a frozen string since that would modify it.
9
+
4
10
  v1.2.1
5
11
  ------
6
12
 
7
- * Fix severe bug caused by buggy initializer
13
+ * Fix severe bug caused by buggy (& poorly designed) initializer, which caused
14
+ problem for some [as evidenced here on SO][1]
15
+
16
+ _Ps: Run the install generator (as explained in the docs), if there are any
17
+ conflicts, overwrite the initializer only! Or you could just delete the
18
+ `config/initializer/pesapal.rb` file and run the generator. Whichever way suits
19
+ you._
8
20
 
9
- Ps: Run the install generator again, overwrite the initializer only!
21
+ [1]: http://stackoverflow.com/questions/19642460/rails-you-cannot-have-more-than-one-railsapplication-runtimeerror
10
22
 
11
23
  v1.2.0
12
24
  ------
data/README.md CHANGED
@@ -116,14 +116,14 @@ them appropriately.
116
116
 
117
117
  ```yaml
118
118
  development:
119
- callback_url: 'http://0.0.0.0:3000/pesapal/callback'
120
- consumer_key: '<YOUR_CONSUMER_KEY>'
121
- consumer_secret: '<YOUR_CONSUMER_SECRET>'
119
+ callback_url: 'http://0.0.0.0:3000/pesapal/callback'
120
+ consumer_key: '<YOUR_CONSUMER_KEY>'
121
+ consumer_secret: '<YOUR_CONSUMER_SECRET>'
122
122
 
123
123
  production:
124
- callback_url: 'http://1.2.3.4:3000/pesapal/callback'
125
- consumer_key: '<YOUR_CONSUMER_KEY>'
126
- consumer_secret: '<YOUR_CONSUMER_SECRET>'
124
+ callback_url: 'http://1.2.3.4:3000/pesapal/callback'
125
+ consumer_key: '<YOUR_CONSUMER_KEY>'
126
+ consumer_secret: '<YOUR_CONSUMER_SECRET>'
127
127
  ```
128
128
 
129
129
  _Ps: Immediately after initializing the Pesapal object, some people might find
@@ -145,7 +145,7 @@ e.g. the phonenumber.
145
145
  pesapal.order_details = { :amount => 1000,
146
146
  :description => 'this is the transaction description',
147
147
  :type => 'MERCHANT',
148
- :reference => 808-707-606,
148
+ :reference => '808-707-606',
149
149
  :first_name => 'Swaleh',
150
150
  :last_name => 'Mdoe',
151
151
  :email => 'user@example.com',
@@ -7,14 +7,14 @@ the values appropriately.
7
7
 
8
8
  ```
9
9
  development:
10
- callback_url: 'http://0.0.0.0:3000/pesapal/callback'
11
- consumer_key: '<YOUR_CONSUMER_KEY>'
12
- consumer_secret: '<YOUR_CONSUMER_SECRET>'
10
+ callback_url: 'http://0.0.0.0:3000/pesapal/callback'
11
+ consumer_key: '<YOUR_CONSUMER_KEY>'
12
+ consumer_secret: '<YOUR_CONSUMER_SECRET>'
13
13
 
14
14
  production:
15
- callback_url: 'http://1.2.3.4:3000/pesapal/callback'
16
- consumer_key: '<YOUR_CONSUMER_KEY>'
17
- consumer_secret: '<YOUR_CONSUMER_SECRET>'
15
+ callback_url: 'http://1.2.3.4:3000/pesapal/callback'
16
+ consumer_key: '<YOUR_CONSUMER_KEY>'
17
+ consumer_secret: '<YOUR_CONSUMER_SECRET>'
18
18
  ```
19
19
 
20
20
  The config file can be found at `#{Rails.root}/config/pesapal.yml`.
@@ -1,9 +1,9 @@
1
1
  development:
2
- callback_url: 'http://0.0.0.0:3000/pesapal/callback'
3
- consumer_key: '<YOUR_CONSUMER_KEY>'
4
- consumer_secret: '<YOUR_CONSUMER_SECRET>'
2
+ callback_url: 'http://0.0.0.0:3000/pesapal/callback'
3
+ consumer_key: '<YOUR_CONSUMER_KEY>'
4
+ consumer_secret: '<YOUR_CONSUMER_SECRET>'
5
5
 
6
6
  production:
7
- callback_url: 'http://1.2.3.4:3000/pesapal/callback'
8
- consumer_key: '<YOUR_CONSUMER_KEY>'
9
- consumer_secret: '<YOUR_CONSUMER_SECRET>'
7
+ callback_url: 'http://1.2.3.4:3000/pesapal/callback'
8
+ consumer_key: '<YOUR_CONSUMER_KEY>'
9
+ consumer_secret: '<YOUR_CONSUMER_SECRET>'
@@ -1,223 +1,226 @@
1
1
  module Pesapal
2
2
 
3
- class Merchant
3
+ class Merchant
4
4
 
5
- attr_accessor :config, :order_details
5
+ attr_accessor :config, :order_details
6
6
 
7
- def config
8
- @config ||= {}
9
- end
7
+ def config
8
+ @config ||= {}
9
+ end
10
10
 
11
- def order_details
12
- @order_details ||= {}
13
- end
11
+ def order_details
12
+ @order_details ||= {}
13
+ end
14
14
 
15
- private
15
+ private
16
16
 
17
- def api_domain
18
- @api_domain
19
- end
17
+ def api_domain
18
+ @api_domain
19
+ end
20
20
 
21
- def api_endpoints
22
- @api_endpoints
23
- end
21
+ def api_endpoints
22
+ @api_endpoints
23
+ end
24
24
 
25
- def mode
26
- @mode
27
- end
25
+ def mode
26
+ @mode
27
+ end
28
28
 
29
- def params
30
- @params
31
- end
29
+ def params
30
+ @params
31
+ end
32
32
 
33
- def post_xml
34
- @post_xml
35
- end
33
+ def post_xml
34
+ @post_xml
35
+ end
36
36
 
37
- def token_secret
38
- @token_secret
39
- end
37
+ def token_secret
38
+ @token_secret
39
+ end
40
40
 
41
- public
41
+ public
42
42
 
43
- # constructor
44
- def initialize(mode = :development, path_to_file = nil)
43
+ # constructor
44
+ def initialize(mode = :development, path_to_file = nil)
45
45
 
46
- # initialize
47
- @params = nil
48
- @post_xml = nil
49
- @token_secret = nil
46
+ # initialize
47
+ @params = nil
48
+ @post_xml = nil
49
+ @token_secret = nil
50
50
 
51
- set_mode mode
51
+ set_mode mode
52
52
 
53
- # set the credentials if we have specified a path from which we
54
- # will access a YAML file with the configurations
55
- unless path_to_file.nil?
56
- set_configuration_from_yaml path_to_file
57
- end
53
+ # set the credentials if we have specified a path from which we
54
+ # will access a YAML file with the configurations
55
+ unless path_to_file.nil?
56
+ set_configuration_from_yaml path_to_file
57
+ end
58
58
 
59
- end
59
+ end
60
60
 
61
- # generate pesapal order url (often iframed)
62
- def generate_order_url
61
+ # generate pesapal order url (often iframed)
62
+ def generate_order_url
63
63
 
64
- # check if the config is empty, if yes, we try load what was set by the initializer into Pesapal.config
65
- if config.empty?
66
- set_configuration Pesapal.config[@mode]
67
- end
64
+ # check if the config is empty, if yes, we try load what was set by the
65
+ # initializer into Pesapal.config
66
+ if config.empty?
67
+ set_configuration Pesapal.config[@mode]
68
+ end
68
69
 
69
- # build xml with input data, the format is standard so no editing is
70
- # required
71
- @post_xml = Pesapal::Post::generate_post_xml @order_details
70
+ # build xml with input data, the format is standard so no editing is
71
+ # required
72
+ @post_xml = Pesapal::Post::generate_post_xml @order_details
72
73
 
73
- # initialize setting of @params (oauth_signature left empty)
74
- @params = Pesapal::Post::set_parameters(@config[:callback_url], @config[:consumer_key], @post_xml)
74
+ # initialize setting of @params (oauth_signature left empty)
75
+ @params = Pesapal::Post::set_parameters(@config[:callback_url], @config[:consumer_key], @post_xml)
75
76
 
76
- # generate oauth signature and add signature to the request parameters
77
- @params[:oauth_signature] = Pesapal::Oauth::generate_oauth_signature("GET", @api_endpoints[:postpesapaldirectorderv4], @params, @config[:consumer_secret], @token_secret)
77
+ # generate oauth signature and add signature to the request parameters
78
+ @params[:oauth_signature] = Pesapal::Oauth::generate_oauth_signature("GET", @api_endpoints[:postpesapaldirectorderv4], @params, @config[:consumer_secret], @token_secret)
78
79
 
79
- # change params (with signature) to a query string
80
- query_string = Pesapal::Oauth::generate_encoded_params_query_string @params
80
+ # change params (with signature) to a query string
81
+ query_string = Pesapal::Oauth::generate_encoded_params_query_string @params
81
82
 
82
- "#{@api_endpoints[:postpesapaldirectorderv4]}?#{query_string}"
83
- end
83
+ "#{@api_endpoints[:postpesapaldirectorderv4]}?#{query_string}"
84
+ end
84
85
 
85
- # query the details of the transaction
86
- def query_payment_details(merchant_reference, transaction_tracking_id)
86
+ # query the details of the transaction
87
+ def query_payment_details(merchant_reference, transaction_tracking_id)
87
88
 
88
- # check if the config is empty, if yes, we try load what was set by the initializer into Pesapal.config
89
- if config.empty?
90
- set_configuration Pesapal.config[@mode]
91
- end
89
+ # check if the config is empty, if yes, we try load what was set by the
90
+ # initializer into Pesapal.config
91
+ if config.empty?
92
+ set_configuration Pesapal.config[@mode]
93
+ end
92
94
 
93
- # initialize setting of @params (oauth_signature left empty)
94
- @params = Pesapal::Details::set_parameters(@config[:consumer_key], merchant_reference, transaction_tracking_id)
95
+ # initialize setting of @params (oauth_signature left empty)
96
+ @params = Pesapal::Details::set_parameters(@config[:consumer_key], merchant_reference, transaction_tracking_id)
95
97
 
96
- # generate oauth signature and add signature to the request parameters
97
- @params[:oauth_signature] = Pesapal::Oauth::generate_oauth_signature("GET", @api_endpoints[:querypaymentdetails], @params, @config[:consumer_secret], @token_secret)
98
+ # generate oauth signature and add signature to the request parameters
99
+ @params[:oauth_signature] = Pesapal::Oauth::generate_oauth_signature("GET", @api_endpoints[:querypaymentdetails], @params, @config[:consumer_secret], @token_secret)
98
100
 
99
- # change params (with signature) to a query string
100
- query_string = Pesapal::Oauth::generate_encoded_params_query_string @params
101
+ # change params (with signature) to a query string
102
+ query_string = Pesapal::Oauth::generate_encoded_params_query_string @params
101
103
 
102
- # get status response
103
- response = Net::HTTP.get(URI("#{@api_endpoints[:querypaymentdetails]}?#{query_string}"))
104
- response = CGI::parse(response)
105
- response = response["pesapal_response_data"][0].split(',')
104
+ # get status response
105
+ response = Net::HTTP.get(URI("#{@api_endpoints[:querypaymentdetails]}?#{query_string}"))
106
+ response = CGI::parse(response)
107
+ response = response["pesapal_response_data"][0].split(',')
106
108
 
107
- details = { :method => response[1],
108
- :status => response[2],
109
- :merchant_reference => response[3],
110
- :transaction_tracking_id => response[0] }
111
- end
109
+ details = { :method => response[1],
110
+ :status => response[2],
111
+ :merchant_reference => response[3],
112
+ :transaction_tracking_id => response[0] }
113
+ end
112
114
 
113
- # query the status of the transaction
114
- def query_payment_status(merchant_reference, transaction_tracking_id = nil)
115
+ # query the status of the transaction
116
+ def query_payment_status(merchant_reference, transaction_tracking_id = nil)
115
117
 
116
- # check if the config is empty, if yes, we try load what was set by the initializer into Pesapal.config
117
- if config.empty?
118
- set_configuration Pesapal.config[@mode]
119
- end
118
+ # check if the config is empty, if yes, we try load what was set by the
119
+ # initializer into Pesapal.config
120
+ if config.empty?
121
+ set_configuration Pesapal.config[@mode]
122
+ end
120
123
 
121
- # initialize setting of @params (oauth_signature left empty)
122
- @params = Pesapal::Status::set_parameters(@config[:consumer_key], merchant_reference, transaction_tracking_id)
124
+ # initialize setting of @params (oauth_signature left empty)
125
+ @params = Pesapal::Status::set_parameters(@config[:consumer_key], merchant_reference, transaction_tracking_id)
123
126
 
124
- # generate oauth signature and add signature to the request parameters
125
- @params[:oauth_signature] = Pesapal::Oauth::generate_oauth_signature("GET", @api_endpoints[:querypaymentstatus], @params, @config[:consumer_secret], @token_secret)
127
+ # generate oauth signature and add signature to the request parameters
128
+ @params[:oauth_signature] = Pesapal::Oauth::generate_oauth_signature("GET", @api_endpoints[:querypaymentstatus], @params, @config[:consumer_secret], @token_secret)
126
129
 
127
- # change params (with signature) to a query string
128
- query_string = Pesapal::Oauth::generate_encoded_params_query_string @params
130
+ # change params (with signature) to a query string
131
+ query_string = Pesapal::Oauth::generate_encoded_params_query_string @params
129
132
 
130
- # get status response
131
- response = Net::HTTP.get(URI("#{@api_endpoints[:querypaymentstatus]}?#{query_string}"))
132
- response = CGI::parse(response)
133
+ # get status response
134
+ response = Net::HTTP.get(URI("#{@api_endpoints[:querypaymentstatus]}?#{query_string}"))
135
+ response = CGI::parse(response)
133
136
 
134
- # return the string result of what we want
135
- response["pesapal_response_data"][0]
136
- end
137
+ # return the string result of what we want
138
+ response["pesapal_response_data"][0]
139
+ end
137
140
 
138
- # set mode when called
139
- def set_mode(mode = :development)
141
+ # set mode when called
142
+ def set_mode(mode = :development)
140
143
 
141
- # convert symbol to string and downcase
142
- @mode = "#{mode.to_s.downcase}"
144
+ # convert symbol to string and downcase
145
+ @mode = "#{mode.to_s.downcase}"
143
146
 
144
- # set api endpoints depending on the mode
145
- set_endpoints
146
- end
147
+ # set api endpoints depending on the mode
148
+ set_endpoints
149
+ end
147
150
 
148
- # listen to ipn response
149
- def ipn_listener(notification_type, merchant_reference, transaction_tracking_id)
151
+ # listen to ipn response
152
+ def ipn_listener(notification_type, merchant_reference, transaction_tracking_id)
150
153
 
151
- status = query_payment_status(merchant_reference, transaction_tracking_id)
154
+ status = query_payment_status(merchant_reference, transaction_tracking_id)
152
155
 
153
- output = { :status => status }
156
+ output = { :status => status }
154
157
 
155
- if status == "COMPLETED"
156
- output[:response] = "pesapal_notification_type=CHANGE&pesapal_transaction_tracking_id=#{transaction_tracking_id}&pesapal_merchant_reference=#{merchant_reference}"
157
- elsif status == "FAILED"
158
- output[:response] = "pesapal_notification_type=CHANGE&pesapal_transaction_tracking_id=#{transaction_tracking_id}&pesapal_merchant_reference=#{merchant_reference}"
159
- else
160
- output[:response] = ""
161
- end
158
+ if status == "COMPLETED"
159
+ output[:response] = "pesapal_notification_type=CHANGE&pesapal_transaction_tracking_id=#{transaction_tracking_id}&pesapal_merchant_reference=#{merchant_reference}"
160
+ elsif status == "FAILED"
161
+ output[:response] = "pesapal_notification_type=CHANGE&pesapal_transaction_tracking_id=#{transaction_tracking_id}&pesapal_merchant_reference=#{merchant_reference}"
162
+ else
163
+ output[:response] = ""
164
+ end
162
165
 
163
- output
164
- end
166
+ output
167
+ end
165
168
 
166
- private
169
+ private
167
170
 
168
- # set endpoints
169
- def set_endpoints
171
+ # set endpoints
172
+ def set_endpoints
170
173
 
171
- if @mode == 'production'
172
- @api_domain = 'https://www.pesapal.com'
173
- else
174
- @api_domain = 'http://demo.pesapal.com'
175
- end
174
+ if @mode == 'production'
175
+ @api_domain = 'https://www.pesapal.com'
176
+ else
177
+ @api_domain = 'http://demo.pesapal.com'
178
+ end
176
179
 
177
- @api_endpoints = {}
178
- @api_endpoints[:postpesapaldirectorderv4] = "#{@api_domain}/API/PostPesapalDirectOrderV4"
179
- @api_endpoints[:querypaymentstatus] = "#{@api_domain}/API/QueryPaymentStatus"
180
- @api_endpoints[:querypaymentdetails] = "#{@api_domain}/API/QueryPaymentDetails"
181
- end
180
+ @api_endpoints = {}
181
+ @api_endpoints[:postpesapaldirectorderv4] = "#{@api_domain}/API/PostPesapalDirectOrderV4"
182
+ @api_endpoints[:querypaymentstatus] = "#{@api_domain}/API/QueryPaymentStatus"
183
+ @api_endpoints[:querypaymentdetails] = "#{@api_domain}/API/QueryPaymentDetails"
184
+ end
182
185
 
183
- # set credentialts through hash, uses default if nothing is input
184
- def set_configuration(consumer_details = {})
186
+ # set credentialts through hash, uses default if nothing is input
187
+ def set_configuration(consumer_details = {})
185
188
 
186
- # set the configuration
187
- @config = { :callback_url => 'http://0.0.0.0:3000/pesapal/callback',
188
- :consumer_key => '<YOUR_CONSUMER_KEY>',
189
- :consumer_secret => '<YOUR_CONSUMER_SECRET>'
190
- }
189
+ # set the configuration
190
+ @config = { :callback_url => 'http://0.0.0.0:3000/pesapal/callback',
191
+ :consumer_key => '<YOUR_CONSUMER_KEY>',
192
+ :consumer_secret => '<YOUR_CONSUMER_SECRET>'
193
+ }
191
194
 
192
- valid_config_keys = @config.keys
195
+ valid_config_keys = @config.keys
193
196
 
194
- consumer_details.each { |k,v| @config[k.to_sym] = v if valid_config_keys.include? k.to_sym }
195
- end
197
+ consumer_details.each { |k,v| @config[k.to_sym] = v if valid_config_keys.include? k.to_sym }
198
+ end
196
199
 
197
- # set configuration through yaml file
198
- def set_configuration_from_yaml(path_to_file)
200
+ # set configuration through yaml file
201
+ def set_configuration_from_yaml(path_to_file)
199
202
 
200
- if File.exist?(path_to_file)
203
+ if File.exist?(path_to_file)
201
204
 
202
- # load file, read it and parse the YAML
203
- begin
204
- loaded_config = YAML::load(IO.read(path_to_file))
205
- rescue Errno::ENOENT
206
- logger.info("YAML configuration file couldn't be found. Using defaults."); return
207
- rescue Psych::SyntaxError
208
- logger.info("YAML configuration file contains invalid syntax. Using defaults."); return
209
- end
205
+ # load file, read it and parse the YAML
206
+ begin
207
+ loaded_config = YAML::load(IO.read(path_to_file))
208
+ rescue Errno::ENOENT
209
+ logger.info("YAML configuration file couldn't be found. Using defaults."); return
210
+ rescue Psych::SyntaxError
211
+ logger.info("YAML configuration file contains invalid syntax. Using defaults."); return
212
+ end
210
213
 
211
- # pick the correct settings depending on the the mode and
212
- # set it appropriately. this file is expected to have the
213
- # settings for development and production
214
- set_configuration loaded_config[@mode]
214
+ # pick the correct settings depending on the the mode and set it
215
+ # appropriately. this file is expected to have the settings for
216
+ # development and production
217
+ set_configuration loaded_config[@mode]
215
218
 
216
- else
219
+ else
217
220
 
218
- # in this case default values will be set
219
- set_configuration
220
- end
221
- end
222
- end
221
+ # in this case default values will be set
222
+ set_configuration
223
+ end
224
+ end
225
+ end
223
226
  end
@@ -1,24 +1,24 @@
1
1
  module Pesapal
2
2
 
3
- module Details
3
+ module Details
4
4
 
5
- # set parameters required by the QueryPaymentDetails call
6
- def Details.set_parameters(consumer_key, merchant_reference, transaction_tracking_id)
5
+ # set parameters required by the QueryPaymentDetails call
6
+ def Details.set_parameters(consumer_key, merchant_reference, transaction_tracking_id)
7
7
 
8
- # parameters required by the QueryPaymentDetails call (excludes
9
- # oauth_signature parameter as per the instructions here
10
- # http://developer.pesapal.com/how-to-integrate/api-reference#QueryPaymentDetails)
8
+ # parameters required by the QueryPaymentDetails call (excludes
9
+ # oauth_signature parameter as per the instructions here
10
+ # http://developer.pesapal.com/how-to-integrate/api-reference#QueryPaymentDetails)
11
11
 
12
- timestamp = Time.now.to_i.to_s
12
+ timestamp = Time.now.to_i.to_s
13
13
 
14
- params = { :oauth_consumer_key => consumer_key,
15
- :oauth_nonce => "#{timestamp}" + Pesapal::Oauth.generate_nonce(12),
16
- :oauth_signature_method => 'HMAC-SHA1',
17
- :oauth_timestamp => "#{timestamp}",
18
- :oauth_version => '1.0',
19
- :pesapal_merchant_reference => merchant_reference,
20
- :pesapal_transaction_tracking_id => transaction_tracking_id
21
- }
22
- end
14
+ params = { :oauth_consumer_key => consumer_key,
15
+ :oauth_nonce => "#{timestamp}" + Pesapal::Oauth.generate_nonce(12),
16
+ :oauth_signature_method => 'HMAC-SHA1',
17
+ :oauth_timestamp => "#{timestamp}",
18
+ :oauth_version => '1.0',
19
+ :pesapal_merchant_reference => merchant_reference,
20
+ :pesapal_transaction_tracking_id => transaction_tracking_id
21
+ }
23
22
  end
23
+ end
24
24
  end
@@ -1,51 +1,51 @@
1
1
  module Pesapal
2
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 "Currency=\"#{details[:currency]}\" "
24
- post_xml.concat 'xmlns="http://www.pesapal.com" />'
25
-
26
- encoder = HTMLEntities.new(:xhtml1)
27
- post_xml = encoder.encode post_xml
28
-
29
- "#{post_xml}"
30
- end
31
-
32
- # set parameters required by the PostPesapalDirectOrderV4 call
33
- def Post.set_parameters(callback_url, consumer_key, post_xml)
34
-
35
- # parameters required by the PostPesapalDirectOrderV4 call (excludes
36
- # oauth_signature parameter as per the instructions here
37
- # http://developer.pesapal.com/how-to-integrate/api-reference#PostPesapalDirectOrderV4)
38
-
39
- timestamp = Time.now.to_i.to_s
40
-
41
- params = { :oauth_callback => callback_url,
42
- :oauth_consumer_key => consumer_key,
43
- :oauth_nonce => "#{timestamp}" + Pesapal::Oauth.generate_nonce(12),
44
- :oauth_signature_method => 'HMAC-SHA1',
45
- :oauth_timestamp => "#{timestamp}",
46
- :oauth_version => '1.0',
47
- :pesapal_request_data => post_xml
48
- }
49
- end
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 "Currency=\"#{details[:currency]}\" "
24
+ post_xml.concat 'xmlns="http://www.pesapal.com" />'
25
+
26
+ encoder = HTMLEntities.new(:xhtml1)
27
+ post_xml = encoder.encode post_xml
28
+
29
+ "#{post_xml}"
50
30
  end
51
- end
31
+
32
+ # set parameters required by the PostPesapalDirectOrderV4 call
33
+ def Post.set_parameters(callback_url, consumer_key, post_xml)
34
+
35
+ # parameters required by the PostPesapalDirectOrderV4 call (excludes
36
+ # oauth_signature parameter as per the instructions here
37
+ # http://developer.pesapal.com/how-to-integrate/api-reference#PostPesapalDirectOrderV4)
38
+
39
+ timestamp = Time.now.to_i.to_s
40
+
41
+ params = { :oauth_callback => callback_url,
42
+ :oauth_consumer_key => consumer_key,
43
+ :oauth_nonce => "#{timestamp}" + Pesapal::Oauth.generate_nonce(12),
44
+ :oauth_signature_method => 'HMAC-SHA1',
45
+ :oauth_timestamp => "#{timestamp}",
46
+ :oauth_version => '1.0',
47
+ :pesapal_request_data => post_xml
48
+ }
49
+ end
50
+ end
51
+ end
@@ -1,29 +1,29 @@
1
1
  module Pesapal
2
2
 
3
- module Status
3
+ module Status
4
4
 
5
- # set parameters required by the QueryPaymentStatus & QueryPaymentStatusByMerchantRef calls
6
- def Status.set_parameters(consumer_key, merchant_reference, transaction_tracking_id = nil)
5
+ # set parameters required by the QueryPaymentStatus & QueryPaymentStatusByMerchantRef calls
6
+ def Status.set_parameters(consumer_key, merchant_reference, transaction_tracking_id = nil)
7
7
 
8
- # parameters required by the QueryPaymentStatus call (excludes
9
- # oauth_signature parameter as per the instructions here
10
- # http://developer.pesapal.com/how-to-integrate/api-reference)
8
+ # parameters required by the QueryPaymentStatus call (excludes
9
+ # oauth_signature parameter as per the instructions here
10
+ # http://developer.pesapal.com/how-to-integrate/api-reference)
11
11
 
12
- timestamp = Time.now.to_i.to_s
12
+ timestamp = Time.now.to_i.to_s
13
13
 
14
- params = { :oauth_consumer_key => consumer_key,
15
- :oauth_nonce => "#{timestamp}" + Pesapal::Oauth.generate_nonce(12),
16
- :oauth_signature_method => 'HMAC-SHA1',
17
- :oauth_timestamp => "#{timestamp}",
18
- :oauth_version => '1.0',
19
- :pesapal_merchant_reference => merchant_reference
20
- }
14
+ params = { :oauth_consumer_key => consumer_key,
15
+ :oauth_nonce => "#{timestamp}" + Pesapal::Oauth.generate_nonce(12),
16
+ :oauth_signature_method => 'HMAC-SHA1',
17
+ :oauth_timestamp => "#{timestamp}",
18
+ :oauth_version => '1.0',
19
+ :pesapal_merchant_reference => merchant_reference
20
+ }
21
21
 
22
- unless transaction_tracking_id.nil? # do, if not true
23
- params[:pesapal_transaction_tracking_id] = transaction_tracking_id
24
- end
22
+ unless transaction_tracking_id.nil? # do, if not true
23
+ params[:pesapal_transaction_tracking_id] = transaction_tracking_id
24
+ end
25
25
 
26
- params
27
- end
26
+ params
28
27
  end
28
+ end
29
29
  end
data/lib/pesapal/oauth.rb CHANGED
@@ -1,178 +1,178 @@
1
1
  module Pesapal
2
2
 
3
- module Oauth
3
+ module Oauth
4
4
 
5
- # generate query string from parameters hash
6
- def Oauth.generate_encoded_params_query_string(params = {})
5
+ # generate query string from parameters hash
6
+ def Oauth.generate_encoded_params_query_string(params = {})
7
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
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 '&' character
15
+ # to the output string
16
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
17
+ # the oauth spec says to sort lexigraphically, which is the default
18
+ # alphabetical sort for many libraries. in case of two parameters with
19
+ # the same encoded key, the oauth spec says to continue sorting based on
20
+ # value
21
21
 
22
- queries = []
23
- params.each do |k,v| queries.push "#{self.parameter_encode(k.to_s)}=#{self.parameter_encode(v.to_s)}" end
22
+ queries = []
23
+ params.each do |k,v| queries.push "#{self.parameter_encode(k.to_s)}=#{self.parameter_encode(v.to_s)}" end
24
24
 
25
- # parameters are sorted by name, using lexicographical byte value
26
- # ordering
27
- queries.sort!
25
+ # parameters are sorted by name, using lexicographical byte value
26
+ # ordering
27
+ queries.sort!
28
28
 
29
- queries.join('&')
30
- end
29
+ queries.join('&')
30
+ end
31
31
 
32
- # generate oauth nonce
33
- def Oauth.generate_nonce(length)
32
+ # generate oauth nonce
33
+ def Oauth.generate_nonce(length)
34
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).
35
+ # the consumer shall then generate a nonce value that is unique for all
36
+ # requests with that timestamp. a nonce is a random string, uniquely
37
+ # generated for each request. the nonce allows the service provider to
38
+ # verify that a request has never been made before and helps prevent
39
+ # replay attacks when requests are made over a non- secure channel (such
40
+ # as http).
41
41
 
42
- chars = 'abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ0123456789'
43
- nonce = ''
44
- length.times { nonce << chars[rand(chars.size)] }
42
+ chars = 'abcdefghjkmnpqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ0123456789'
43
+ nonce = ''
44
+ length.times { nonce << chars[rand(chars.size)] }
45
45
 
46
- "#{nonce}"
47
- end
46
+ "#{nonce}"
47
+ end
48
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)
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
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.
52
+ # the signature is calculated by passing the signature base string and
53
+ # signing key to the hmac-sha1 hashing algorithm. the output of the hmac
54
+ # signing function is a binary string. this needs to be base64 encoded to
55
+ # produce the signature string.
56
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
57
+ # for pesapal flow we don't have a token secret to we will set as nil and
58
+ # the appropriate action will be taken as per the oauth spec. see notes in
59
+ # the method that creates signing keys
60
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)
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
65
 
66
- hmac = OpenSSL::HMAC.digest(digest, signing_key, signature_base_string)
67
- Base64.encode64(hmac).chomp
68
- end
66
+ hmac = OpenSSL::HMAC.digest(digest, signing_key, signature_base_string)
67
+ Base64.encode64(hmac).chomp
68
+ end
69
69
 
70
- # generate query string from signable parameters hash
71
- def Oauth.generate_signable_encoded_params_query_string(params = {})
70
+ # generate query string from signable parameters hash
71
+ def Oauth.generate_signable_encoded_params_query_string(params = {})
72
72
 
73
- # oauth_signature parameter MUST be excluded, assumes it was already
74
- # initialized by calling set_parameters
75
- params.delete(:oauth_signature)
73
+ # oauth_signature parameter MUST be excluded, assumes it was already
74
+ # initialized by calling set_parameters
75
+ params.delete(:oauth_signature)
76
76
 
77
- self.generate_encoded_params_query_string params
78
- end
77
+ self.generate_encoded_params_query_string params
78
+ end
79
79
 
80
- # generate the oauth signature
81
- def Oauth.generate_signature_base_string(http_method, absolute_url, params)
80
+ # generate the oauth signature
81
+ def Oauth.generate_signature_base_string(http_method, absolute_url, params)
82
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
83
+ # three values collected so far must be joined to make a single string,
84
+ # from which the signature will be generated. This is called the
85
+ # signature base string by the OAuth specification
86
86
 
87
- # step 1: convert the http method to uppercase
88
- http_method = http_method.upcase
87
+ # step 1: convert the http method to uppercase
88
+ http_method = http_method.upcase
89
89
 
90
- # step 2: percent encode the url
91
- url_encoded = self.parameter_encode(self.normalized_request_uri(absolute_url))
90
+ # step 2: percent encode the url
91
+ url_encoded = self.parameter_encode(self.normalized_request_uri(absolute_url))
92
92
 
93
- # step 3: percent encode the parameter string
94
- parameter_string_encoded = self.parameter_encode(self.generate_signable_encoded_params_query_string params)
93
+ # step 3: percent encode the parameter string
94
+ parameter_string_encoded = self.parameter_encode(self.generate_signable_encoded_params_query_string params)
95
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
96
+ # the signature base string should contain exactly 2 ampersand '&'
97
+ # characters. The percent '%' characters in the parameter string should be
98
+ # encoded as %25 in the signature base string
99
99
 
100
- "#{http_method}&#{url_encoded}&#{parameter_string_encoded}"
101
- end
100
+ "#{http_method}&#{url_encoded}&#{parameter_string_encoded}"
101
+ end
102
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 '&'
103
+ # generate signing key
104
+ def Oauth.generate_signing_key(consumer_secret, token_secret = nil)
114
105
 
115
- # "#{@credentials[:consumer_secret]}"
116
- consumer_secret_encoded = self.parameter_encode(consumer_secret)
106
+ # the signing key is simply the percent encoded consumer secret, followed
107
+ # by an ampersand character '&', followed by the percent encoded token
108
+ # secret
117
109
 
118
- token_secret_encoded = ""
119
- unless token_secret.nil?
120
- token_secret_encoded = self.parameter_encode(token_secret)
121
- end
110
+ # note that there are some flows, such as when obtaining a request token,
111
+ # where the token secret is not yet known. In this case, the signing key
112
+ # should consist of the percent encoded consumer secret followed by an
113
+ # ampersand character '&'
122
114
 
123
- "#{consumer_secret_encoded}&#{token_secret_encoded}"
124
- end
115
+ # "#{@credentials[:consumer_secret]}"
116
+ consumer_secret_encoded = self.parameter_encode(consumer_secret)
125
117
 
126
- # normalize request absolute URL
127
- def Oauth.normalized_request_uri(absolute_url)
118
+ token_secret_encoded = ""
119
+ unless token_secret.nil?
120
+ token_secret_encoded = self.parameter_encode(token_secret)
121
+ end
128
122
 
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.
123
+ "#{consumer_secret_encoded}&#{token_secret_encoded}"
124
+ end
125
+
126
+ # normalize request absolute URL
127
+ def Oauth.normalized_request_uri(absolute_url)
134
128
 
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.
129
+ # the signature base string includes the request absolute url, tying the
130
+ # signature to a specific endpoint. the url used in the signature base
131
+ # string must include the scheme, authority, and path, and must exclude
132
+ # the query and fragment as defined by [rfc3986] section 3.
141
133
 
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.
134
+ # if the absolute request url is not available to the service provider (it
135
+ # is always available to the consumer), it can be constructed by combining
136
+ # the scheme being used, the http host header, and the relative http
137
+ # request url. if the host header is not available, the service provider
138
+ # should use the host name communicated to the consumer in the
139
+ # documentation or other means.
147
140
 
148
- u = URI.parse(absolute_url)
141
+ # the service provider should document the form of url used in the
142
+ # signature base string to avoid ambiguity due to url normalization.
143
+ # unless specified, url scheme and authority must be lowercase and include
144
+ # the port number; http default port 80 and https default port 443 must be
145
+ # excluded.
149
146
 
150
- scheme = u.scheme.downcase
151
- host = u.host.downcase
152
- path = u.path
153
- port = u.port
147
+ u = URI.parse(absolute_url)
154
148
 
155
- port = (scheme == 'http' && port != 80) || (scheme == 'https' && port != 443) ? ":#{port}" : ""
156
- path = (path && path != '') ? path : '/'
149
+ scheme = u.scheme.downcase
150
+ host = u.host.downcase
151
+ path = u.path
152
+ port = u.port
157
153
 
158
- "#{scheme}://#{host}#{port}#{path}"
159
- end
154
+ port = (scheme == 'http' && port != 80) || (scheme == 'https' && port != 443) ? ":#{port}" : ""
155
+ path = (path && path != '') ? path : '/'
156
+
157
+ "#{scheme}://#{host}#{port}#{path}"
158
+ end
160
159
 
161
- # percentage encode value as per the oauth spec
162
- def Oauth.parameter_encode(string)
160
+ # percentage encode value as per the oauth spec
161
+ def Oauth.parameter_encode(string)
163
162
 
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].
163
+ # all parameter names and values are escaped using the [rfc3986] percent-
164
+ # encoding (%xx) mechanism. characters not in the unreserved character set
165
+ # ([rfc3986] section 2.3) must be encoded. characters in the unreserved
166
+ # character set must not be encoded. hexadecimal characters in encodings
167
+ # must be upper case. text names and values must be encoded as utf-8
168
+ # octets before percent-encoding them per [rfc3629].
171
169
 
172
- # reserved character regexp, per section 5.1
173
- reserved_characters = /[^a-zA-Z0-9\-\.\_\~]/
170
+ # reserved character regexp, per section 5.1
171
+ reserved_characters = /[^a-zA-Z0-9\-\.\_\~]/
174
172
 
175
- URI::escape(string.to_s.force_encoding(Encoding::UTF_8), reserved_characters)
176
- end
173
+ # Apparently we can't force_encoding on a frozen string since that would modify it.
174
+ # What we can do is work with a copy
175
+ URI::escape(string.dup.to_s.force_encoding(Encoding::UTF_8), reserved_characters)
177
176
  end
178
- end
177
+ end
178
+ end
@@ -1,3 +1,3 @@
1
1
  module Pesapal
2
- VERSION = "1.2.1"
2
+ VERSION = "1.2.2"
3
3
  end
data/pesapal.gemspec CHANGED
@@ -3,23 +3,23 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
3
  require "pesapal/version"
4
4
 
5
5
  Gem::Specification.new do |spec|
6
- spec.name = "pesapal"
7
- spec.version = Pesapal::VERSION
8
- spec.date = Time.new.getlocal("+03:00").strftime("%Y-%m-%d")
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://rubydoc.info/gems/pesapal/"
14
- spec.license = "MIT"
6
+ spec.name = "pesapal"
7
+ spec.version = Pesapal::VERSION
8
+ spec.date = Time.new.getlocal("+03:00").strftime("%Y-%m-%d")
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://rubydoc.info/gems/pesapal/"
14
+ spec.license = "MIT"
15
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"]
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
20
 
21
- spec.add_development_dependency "bundler", "~> 1.3"
22
- spec.add_development_dependency "rake"
21
+ spec.add_development_dependency "bundler", "~> 1.3"
22
+ spec.add_development_dependency "rake"
23
23
 
24
- spec.add_dependency "htmlentities"
25
- end
24
+ spec.add_dependency "htmlentities"
25
+ end
metadata CHANGED
@@ -1,18 +1,20 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pesapal
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.1
4
+ version: 1.2.2
5
+ prerelease:
5
6
  platform: ruby
6
7
  authors:
7
8
  - Job King'ori Maina
8
9
  autorequire:
9
10
  bindir: bin
10
11
  cert_chain: []
11
- date: 2013-11-07 00:00:00.000000000 Z
12
+ date: 2013-12-20 00:00:00.000000000 Z
12
13
  dependencies:
13
14
  - !ruby/object:Gem::Dependency
14
15
  name: bundler
15
16
  requirement: !ruby/object:Gem::Requirement
17
+ none: false
16
18
  requirements:
17
19
  - - ~>
18
20
  - !ruby/object:Gem::Version
@@ -20,6 +22,7 @@ dependencies:
20
22
  type: :development
21
23
  prerelease: false
22
24
  version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
23
26
  requirements:
24
27
  - - ~>
25
28
  - !ruby/object:Gem::Version
@@ -27,29 +30,33 @@ dependencies:
27
30
  - !ruby/object:Gem::Dependency
28
31
  name: rake
29
32
  requirement: !ruby/object:Gem::Requirement
33
+ none: false
30
34
  requirements:
31
- - - '>='
35
+ - - ! '>='
32
36
  - !ruby/object:Gem::Version
33
37
  version: '0'
34
38
  type: :development
35
39
  prerelease: false
36
40
  version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
37
42
  requirements:
38
- - - '>='
43
+ - - ! '>='
39
44
  - !ruby/object:Gem::Version
40
45
  version: '0'
41
46
  - !ruby/object:Gem::Dependency
42
47
  name: htmlentities
43
48
  requirement: !ruby/object:Gem::Requirement
49
+ none: false
44
50
  requirements:
45
- - - '>='
51
+ - - ! '>='
46
52
  - !ruby/object:Gem::Version
47
53
  version: '0'
48
54
  type: :runtime
49
55
  prerelease: false
50
56
  version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
51
58
  requirements:
52
- - - '>='
59
+ - - ! '>='
53
60
  - !ruby/object:Gem::Version
54
61
  version: '0'
55
62
  description: Make authenticated Pesapal API calls without the fuss!
@@ -81,26 +88,27 @@ files:
81
88
  homepage: http://rubydoc.info/gems/pesapal/
82
89
  licenses:
83
90
  - MIT
84
- metadata: {}
85
91
  post_install_message:
86
92
  rdoc_options: []
87
93
  require_paths:
88
94
  - lib
89
95
  required_ruby_version: !ruby/object:Gem::Requirement
96
+ none: false
90
97
  requirements:
91
- - - '>='
98
+ - - ! '>='
92
99
  - !ruby/object:Gem::Version
93
100
  version: '0'
94
101
  required_rubygems_version: !ruby/object:Gem::Requirement
102
+ none: false
95
103
  requirements:
96
- - - '>='
104
+ - - ! '>='
97
105
  - !ruby/object:Gem::Version
98
106
  version: '0'
99
107
  requirements: []
100
108
  rubyforge_project:
101
- rubygems_version: 2.1.8
109
+ rubygems_version: 1.8.23
102
110
  signing_key:
103
- specification_version: 4
111
+ specification_version: 3
104
112
  summary: Make authenticated Pesapal API calls without the fuss! Handles all the oAuth
105
113
  stuff abstracting any direct interaction with the API endpoints so that you can
106
114
  focus on what matters. Building awesome.
checksums.yaml DELETED
@@ -1,7 +0,0 @@
1
- ---
2
- SHA1:
3
- metadata.gz: 1b3d98d9279b31f66a9996b6045d19a0ae01c2c7
4
- data.tar.gz: e132dc33bd5a88e3c2980e4c5eff9f8bfe438e33
5
- SHA512:
6
- metadata.gz: 64ff6e09c9b02066fe9b78120cc2f60594eea9fc3d23c5c010b2eb9eccce2ef3d5bd0d4d4de20b360f380a171278a18794e472dec65ff69f572e721a1797d61e
7
- data.tar.gz: 510c5cadf3d39ed31c86d266c5023c9b79bd087329818401e801a2e3a1075f9bb858f7fd891f89966005b4ca3ce20a44d7571be948069dac60fd4640dc5f0fbb