pesapal 0.2.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8166e77a88a3f62b22458f737b9342b1750aa18d
4
- data.tar.gz: d95d4174f1b2b324b08c9a3a611d7a187e915d05
3
+ metadata.gz: 2b77c7352914478f5db52c64dd9e5611cd9c8652
4
+ data.tar.gz: 382a808b6a1d6464587477cd0ab30aa4c6e53b5b
5
5
  SHA512:
6
- metadata.gz: 23618f43a0a739ff6871a1f1dba80324b6cad7de1f8006c13c651708792c3d7ba2d3fdea0f608a10e95a3fef8b5d4c52c16ca10e494da704bce922dbef6159e9
7
- data.tar.gz: a833ad55d35d4c83b736ec708d4e4e4762565ca1d0d2a044059d099b4ef7479c0b54b26e33d370cf555e81140fd64a62791937fd1feda9aa61b9502a85103a15
6
+ metadata.gz: 74576337022966c7ae613394507be8f2a4d1611fee53067430b80ae7d2376a8c34acb21cf933ab6c9f8697c119892c3608b17e9fd4604c59977fea2c6dd4e646
7
+ data.tar.gz: 35fe01d3f3a0dbe097474e85710e65862f14a041ea5726df5c21ff2f1c13e94796b7ea07036acca4b800f672f034f4572481126939cdd0ea1bc8c680c6c7ea5f
data/CHANGELOG.md CHANGED
@@ -1,6 +1,20 @@
1
1
  Changelog
2
2
  =========
3
3
 
4
+ v1.0.0
5
+ ------
6
+
7
+ * Automate release date to reduce chances of forgetting to update it
8
+ * Add new functionality: Query Payment Status
9
+ * Add new functionality: Query Payment Details
10
+ * Add `set_mode()` method to enable changing mode at runtime
11
+ * `api_domain` & `api_endpoints` are now private, use `set_mode` to alter them
12
+
13
+ v0.2.1
14
+ ------
15
+
16
+ * Fix screwed up rubygem date
17
+
4
18
  v0.2.0
5
19
  ------
6
20
 
data/README.md CHANGED
@@ -7,10 +7,10 @@ Make authenticated Pesapal API calls without the fuss! Handles all the [oAuth
7
7
  stuff][1] abstracting any direct interaction with the API endpoints so that you
8
8
  can focus on what matters. _Building awesome_.
9
9
 
10
- This gem is work in progress. At the moment, the only functionality built-in is
11
- posting an order i.e. fetching the URL that is required to display the post-
12
- order iframe. Everything else should be easy to do as the groundwork has already
13
- been laid. If you are [feeling generous and want to contribute, feel free][9].
10
+ This gem, in a nutshell, allows you to easily post an order, query payment
11
+ status and fetch payment details.
12
+
13
+ If you are [feeling generous and want to contribute, feel free][9].
14
14
 
15
15
  Submit [issues and requests here][6] and [find all the releases here][12].
16
16
 
@@ -19,11 +19,6 @@ The gem should be [up on RubyGems.org][7] and it's [accompanying RubyDoc referen
19
19
  _Ps: No 3rd party oAuth library dependencies, it handles all the oAuth flows on
20
20
  it's own so your app is one dependency less._
21
21
 
22
- _Ps 2: We are still at pre-release stage ... target is version 1.0.0 for a
23
- public release (suitable for production deployment with the basic functionality
24
- in place). As a result always check the documentation carefully on upgrades to
25
- mitigate breaking changes._
26
-
27
22
 
28
23
  Installation
29
24
  ------------
@@ -49,7 +44,13 @@ Usage
49
44
  -----
50
45
 
51
46
 
52
- ### Setup ###
47
+ ### Initialization ###
48
+
49
+ There are 3 ways to initialize the Pesapal object:
50
+
51
+ 1. YAML config at `/config/pesapal.yml` (default & recommended)
52
+ 2. YAML config at custom location
53
+ 3. Config hash
53
54
 
54
55
  Initialize Pesapal object and choose the mode, there are two modes;
55
56
  `:development` and `:production`. They determine if the code will interact
@@ -60,12 +61,18 @@ with the testing or the live Pesapal API.
60
61
  pesapal = Pesapal::Merchant.new(:development)
61
62
  ```
62
63
 
63
- You can set the configuration details from a YAML file at the location of your
64
- choice upon initialization as shown in the example below for a Rails app. The
65
- second parameter is optional and has a default value of
66
- `"#{Rails.root}/config/pesapal.yml"` if left out as in the example above. In
67
- this case, please note that this YAML file is only loaded when your application
68
- starts and not everytime an object is initialized.
64
+ ####Option 1####
65
+
66
+ In the case, the configuration has already been loaded (at application start by
67
+ initializer) from a YAML file located at `"#{Rails.root}/config/pesapal.yml"` by
68
+ default. This is the recommended method.
69
+
70
+ ####Option 2####
71
+
72
+ It's also possible to set the configuration details from a YAML file at the
73
+ location of your choice upon initialization as shown in the example below. This
74
+ option overrides the default YAML config (explained above) and will be loaded
75
+ everytime during initialization (which is not a good idea for production).
69
76
 
70
77
  ```ruby
71
78
  # initiate pesapal object set to development mode and use the YAML file found at
@@ -73,6 +80,29 @@ starts and not everytime an object is initialized.
73
80
  pesapal = Pesapal::Merchant.new(:development, "<PATH_TO_YAML_FILE>")
74
81
  ```
75
82
 
83
+ ####Option 3####
84
+
85
+ If you wish not to use the YAML config method or the YAML file for some reason
86
+ does not exist, then the object is set up with some bogus credentials which
87
+ would not work anyway and therefore, the other option is that you set them
88
+ yourself. Which, you can do using a hash as shown below (please note that
89
+ Pesapal provides different keys for different modes and since this is like an
90
+ override, there's the assumption that you chose the right one).
91
+
92
+ ```ruby
93
+ # set pesapal api configuration manually (override YAML & bogus credentials)
94
+ pesapal.config = { :callback_url => 'http://0.0.0.0:3000/pesapal/callback'
95
+ :consumer_key => '<YOUR_CONSUMER_KEY>',
96
+ :consumer_secret => '<YOUR_CONSUMER_SECRET>'
97
+ }
98
+ ```
99
+
100
+ _Ps: You can change the mode using `pesapal.set_mode(:development)` (example) if
101
+ for some reason you want to override what was set in the constructor._
102
+
103
+
104
+ ###YAML Configuration###
105
+
76
106
  The YAML file should look something like this. If you ran the generator you
77
107
  should have it already in place with some default values. Feel free to change
78
108
  them appropriately.
@@ -89,27 +119,8 @@ production:
89
119
  consumer_secret: '<YOUR_CONSUMER_SECRET>'
90
120
  ```
91
121
 
92
- If the YAML file does not exist, then the object is set up with some bogus
93
- credentials which would not work anyway and therefore, the next logical step is
94
- that you set them yourself. Which, you can do using a hash as shown below
95
- (please note that Pesapal provides different keys for different modes and since
96
- this is like an override, there's the assumption that you chose the right one).
97
-
98
- ```ruby
99
- # set pesapal api configuration manually (override YAML & bogus credentials)
100
- pesapal.config = { :callback_url => 'http://0.0.0.0:3000/pesapal/callback'
101
- :consumer_key => '<YOUR_CONSUMER_KEY>',
102
- :consumer_secret => '<YOUR_CONSUMER_SECRET>'
103
- }
104
- ```
105
-
106
- _Ps: Make sure this hash has the appropriate values before running any methods
107
- that interact with the API as the methods pick from these values. This means
108
- that you can also override them at runtime for a truly dynamic/complex app that
109
- might have different values for different scenarios._
110
-
111
122
 
112
- ### Post Order ###
123
+ ### Posting An Order ###
113
124
 
114
125
  Once you've finalized the configuration, set up the order details in a hash as
115
126
  shown in the example below ... all keys **MUST** be present. If there's one that
@@ -149,6 +160,50 @@ url. Refer to [official Pesapal Step-By-Step integration guide][18] for more
149
160
  details._
150
161
 
151
162
 
163
+ ### Querying Payment Status ###
164
+
165
+ Use this to query the status of the transaction. When a transaction is posted to
166
+ Pesapal, it may be in a PENDING, COMPLETED, FAILED or INVALID state. If the
167
+ transaction is PENDING, the payment may complete or fail at a later stage.
168
+
169
+ Both the unique merchant reference generated by your system (compulsory) and the
170
+ pesapal transaction tracking id (optional) are input parameters to this method
171
+ but if you don't ensure that the merchant reference is unique for each order on
172
+ your system, you may get INVALID as the response. Because of this, it is
173
+ recommended that you provide both the merchant reference and transaction
174
+ tracking id as parameters to guarantee uniqueness.
175
+
176
+ ```ruby
177
+ # option 1: using merchant reference only
178
+ payment_status = pesapal.query_payment_status("<MERCHANT_REFERENCE>")
179
+
180
+ # option 2: using merchant reference and transaction id (recommended)
181
+ payment_status = pesapal.query_payment_status("<MERCHANT_REFERENCE>","<TRANSACTION_ID>")
182
+ ```
183
+
184
+
185
+ ### Querying Payment Details ###
186
+
187
+ Same as querying payment status above, but the return value contains more
188
+ information (and is a hash as opposed to a string).
189
+
190
+ ```ruby
191
+ # pass in merchant reference and transaction id
192
+ payment_details = pesapal.query_payment_details("<MERCHANT_REFERENCE>","<TRANSACTION_ID>")
193
+ ```
194
+
195
+ The result is a hash that looks something like this ...
196
+
197
+ ```
198
+ {
199
+ :method=>"MPESA",
200
+ :status=>"COMPLETED",
201
+ :merchant_reference=>"<MERCHANT_REFERENCE>",
202
+ :transaction_tracking_id=>"<TRANSACTION_ID>"
203
+ }
204
+ ```
205
+
206
+
152
207
  Contributing
153
208
  ------------
154
209
 
@@ -0,0 +1,24 @@
1
+ module Pesapal
2
+
3
+ module Details
4
+
5
+ # set parameters required by the QueryPaymentDetails call
6
+ def Details.set_parameters(consumer_key, merchant_reference, transaction_tracking_id)
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)
11
+
12
+ timestamp = Time.now.to_i.to_s
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
23
+ end
24
+ end
@@ -28,7 +28,7 @@ module Pesapal
28
28
  "#{post_xml}"
29
29
  end
30
30
 
31
- # set parameters required by the PostPesapalDirectOrderV4
31
+ # set parameters required by the PostPesapalDirectOrderV4 call
32
32
  def Post.set_parameters(callback_url, consumer_key, post_xml)
33
33
 
34
34
  # parameters required by the PostPesapalDirectOrderV4 call (excludes
@@ -0,0 +1,29 @@
1
+ module Pesapal
2
+
3
+ module Status
4
+
5
+ # set parameters required by the QueryPaymentStatus & QueryPaymentStatusByMerchantRef calls
6
+ def Status.set_parameters(consumer_key, merchant_reference, transaction_tracking_id = nil)
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)
11
+
12
+ timestamp = Time.now.to_i.to_s
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
+ }
21
+
22
+ unless transaction_tracking_id.nil? # do, if not true
23
+ params[:pesapal_transaction_tracking_id] = transaction_tracking_id
24
+ end
25
+
26
+ params
27
+ end
28
+ end
29
+ end
@@ -3,15 +3,6 @@ module Pesapal
3
3
  class Merchant
4
4
 
5
5
  attr_accessor :config, :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
6
 
16
7
  def config
17
8
  @config
@@ -23,6 +14,14 @@ module Pesapal
23
14
 
24
15
  private
25
16
 
17
+ def api_domain
18
+ @api_domain
19
+ end
20
+
21
+ def api_endpoints
22
+ @api_endpoints
23
+ end
24
+
26
25
  def mode
27
26
  @mode
28
27
  end
@@ -49,8 +48,7 @@ module Pesapal
49
48
  @post_xml = nil
50
49
  @token_secret = nil
51
50
 
52
- # convert symbol to string and downcase
53
- @mode = "#{mode.to_s.downcase}"
51
+ set_mode mode
54
52
 
55
53
  # set the credentials if we have not set a custom path for the YAML config file
56
54
  if path_to_file.nil?
@@ -61,8 +59,6 @@ module Pesapal
61
59
  set_configuration_from_yaml path_to_file
62
60
  end
63
61
 
64
- # set api endpoints depending on the mode
65
- set_endpoints
66
62
  end
67
63
 
68
64
  # generate pesapal order url (often iframed)
@@ -72,7 +68,7 @@ module Pesapal
72
68
  # required
73
69
  @post_xml = Pesapal::Post::generate_post_xml @order_details
74
70
 
75
- # initialize setting of @params (oauth_signature left empty) ... this gene
71
+ # initialize setting of @params (oauth_signature left empty)
76
72
  @params = Pesapal::Post::set_parameters(@config[:callback_url], @config[:consumer_key], @post_xml)
77
73
 
78
74
  # generate oauth signature and add signature to the request parameters
@@ -84,6 +80,59 @@ module Pesapal
84
80
  "#{@api_endpoints[:postpesapaldirectorderv4]}?#{query_string}"
85
81
  end
86
82
 
83
+ # query the details of the transaction
84
+ def query_payment_details(merchant_reference, transaction_tracking_id)
85
+
86
+ # initialize setting of @params (oauth_signature left empty)
87
+ @params = Pesapal::Details::set_parameters(@config[:consumer_key], merchant_reference, transaction_tracking_id)
88
+
89
+ # generate oauth signature and add signature to the request parameters
90
+ @params[:oauth_signature] = Pesapal::Oauth::generate_oauth_signature("GET", @api_endpoints[:querypaymentdetails], @params, @config[:consumer_secret], @token_secret)
91
+
92
+ # change params (with signature) to a query string
93
+ query_string = Pesapal::Oauth::generate_encoded_params_query_string @params
94
+
95
+ # get status response
96
+ response = Net::HTTP.get(URI("#{@api_endpoints[:querypaymentdetails]}?#{query_string}"))
97
+ response = CGI::parse(response)
98
+ response = response["pesapal_response_data"][0].split(',')
99
+
100
+ details = { :method => response[1],
101
+ :status => response[2],
102
+ :merchant_reference => response[3],
103
+ :transaction_tracking_id => response[0] }
104
+ end
105
+
106
+ # query the status of the transaction
107
+ def query_payment_status(merchant_reference, transaction_tracking_id = nil)
108
+
109
+ # initialize setting of @params (oauth_signature left empty)
110
+ @params = Pesapal::Status::set_parameters(@config[:consumer_key], merchant_reference, transaction_tracking_id)
111
+
112
+ # generate oauth signature and add signature to the request parameters
113
+ @params[:oauth_signature] = Pesapal::Oauth::generate_oauth_signature("GET", @api_endpoints[:querypaymentstatus], @params, @config[:consumer_secret], @token_secret)
114
+
115
+ # change params (with signature) to a query string
116
+ query_string = Pesapal::Oauth::generate_encoded_params_query_string @params
117
+
118
+ # get status response
119
+ response = Net::HTTP.get(URI("#{@api_endpoints[:querypaymentstatus]}?#{query_string}"))
120
+ response = CGI::parse(response)
121
+
122
+ # return the string result of what we want
123
+ response["pesapal_response_data"][0]
124
+ end
125
+
126
+ # set mode when called
127
+ def set_mode(mode = :development)
128
+
129
+ # convert symbol to string and downcase
130
+ @mode = "#{mode.to_s.downcase}"
131
+
132
+ # set api endpoints depending on the mode
133
+ set_endpoints
134
+ end
135
+
87
136
  private
88
137
 
89
138
  # set endpoints
@@ -98,7 +147,6 @@ module Pesapal
98
147
  @api_endpoints = {}
99
148
  @api_endpoints[:postpesapaldirectorderv4] = "#{@api_domain}/API/PostPesapalDirectOrderV4"
100
149
  @api_endpoints[:querypaymentstatus] = "#{@api_domain}/API/QueryPaymentStatus"
101
- @api_endpoints[:querypaymentstatusbymerchantref] = "#{@api_domain}/API/QueryPaymentStatus"
102
150
  @api_endpoints[:querypaymentdetails] = "#{@api_domain}/API/QueryPaymentDetails"
103
151
  end
104
152
 
@@ -1,3 +1,3 @@
1
1
  module Pesapal
2
- VERSION = "0.2.1"
2
+ VERSION = "1.0.0"
3
3
  end
data/lib/pesapal.rb CHANGED
@@ -1,7 +1,9 @@
1
1
  require "rails"
2
2
  require "htmlentities"
3
3
  require "pesapal/merchant"
4
+ require "pesapal/merchant/details"
4
5
  require "pesapal/merchant/post"
6
+ require "pesapal/merchant/status"
5
7
  require "pesapal/oauth"
6
8
  require "pesapal/version"
7
9
 
data/pesapal.gemspec CHANGED
@@ -5,7 +5,7 @@ require "pesapal/version"
5
5
  Gem::Specification.new do |spec|
6
6
  spec.name = "pesapal"
7
7
  spec.version = Pesapal::VERSION
8
- spec.date = "2013-10-13"
8
+ spec.date = Time.new.getlocal("+03:00").strftime("%Y-%m-%d")
9
9
  spec.authors = ["Job King'ori Maina"]
10
10
  spec.email = ["j@kingori.co"]
11
11
  spec.description = "Make authenticated Pesapal API calls without the fuss!"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pesapal
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Job King'ori Maina
@@ -71,7 +71,9 @@ files:
71
71
  - lib/generators/templates/pesapal.yml
72
72
  - lib/pesapal.rb
73
73
  - lib/pesapal/merchant.rb
74
+ - lib/pesapal/merchant/details.rb
74
75
  - lib/pesapal/merchant/post.rb
76
+ - lib/pesapal/merchant/status.rb
75
77
  - lib/pesapal/oauth.rb
76
78
  - lib/pesapal/version.rb
77
79
  - pesapal.gemspec