mobile-pesa 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: db79393cf87f6b160a078adea84a309e577ad21586d30394e6878c47d0426cdf
4
+ data.tar.gz: c70af84a9fec001435b43ab0c79f26fec7c4ad94ec343c7ae506c1247c133c6d
5
+ SHA512:
6
+ metadata.gz: 976f73cff909f1b8cb493de47894bd3acf8b03d59ffb993c08f132c13bf0c223d19cddcbf2663697fdc433452ab6ffdb4cee102a28861d3c0bc9fc98afdf66e2
7
+ data.tar.gz: 2ea2bdf435f03e9f4a8117a490095edf2db7128a44f83b71c29f1014422dacda979156ede18c028e0cbf85b3b9f994e765aadb232022bae3fac1406db6c81172
@@ -0,0 +1,16 @@
1
+ name: Ruby
2
+
3
+ on: [push,pull_request]
4
+
5
+ jobs:
6
+ build:
7
+ runs-on: ubuntu-latest
8
+ steps:
9
+ - uses: actions/checkout@v2
10
+ - name: Set up Ruby
11
+ uses: ruby/setup-ruby@v1
12
+ with:
13
+ ruby-version: 3.0.2
14
+ bundler-cache: true
15
+ - name: Run the default task
16
+ run: bundle exec rake
data/.gitignore ADDED
@@ -0,0 +1,12 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ # rspec failure tracking
11
+ .rspec_status
12
+ .env
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,13 @@
1
+ AllCops:
2
+ TargetRubyVersion: 2.4
3
+
4
+ Style/StringLiterals:
5
+ Enabled: true
6
+ EnforcedStyle: double_quotes
7
+
8
+ Style/StringLiteralsInInterpolation:
9
+ Enabled: true
10
+ EnforcedStyle: double_quotes
11
+
12
+ Layout/LineLength:
13
+ Max: 120
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ ## [Unreleased]
2
+
3
+ ## [0.1.0] - 2021-12-04
4
+
5
+ - Initial release
data/Gemfile ADDED
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ # Specify your gem's dependencies in mobile-pesa.gemspec
6
+ gemspec
7
+
8
+ gem "rake", "~> 13.0"
9
+ gem "rspec", "~> 3.0"
10
+ gem "rubocop", "~> 1.7"
data/Gemfile.lock ADDED
@@ -0,0 +1,57 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ mobile-pesa (0.1.1)
5
+ colorize
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ ast (2.4.2)
11
+ colorize (0.8.1)
12
+ diff-lcs (1.4.4)
13
+ parallel (1.21.0)
14
+ parser (3.0.3.1)
15
+ ast (~> 2.4.1)
16
+ rainbow (3.0.0)
17
+ rake (13.0.6)
18
+ regexp_parser (2.2.0)
19
+ rexml (3.2.5)
20
+ rspec (3.10.0)
21
+ rspec-core (~> 3.10.0)
22
+ rspec-expectations (~> 3.10.0)
23
+ rspec-mocks (~> 3.10.0)
24
+ rspec-core (3.10.1)
25
+ rspec-support (~> 3.10.0)
26
+ rspec-expectations (3.10.1)
27
+ diff-lcs (>= 1.2.0, < 2.0)
28
+ rspec-support (~> 3.10.0)
29
+ rspec-mocks (3.10.2)
30
+ diff-lcs (>= 1.2.0, < 2.0)
31
+ rspec-support (~> 3.10.0)
32
+ rspec-support (3.10.3)
33
+ rubocop (1.23.0)
34
+ parallel (~> 1.10)
35
+ parser (>= 3.0.0.0)
36
+ rainbow (>= 2.2.2, < 4.0)
37
+ regexp_parser (>= 1.8, < 3.0)
38
+ rexml
39
+ rubocop-ast (>= 1.12.0, < 2.0)
40
+ ruby-progressbar (~> 1.7)
41
+ unicode-display_width (>= 1.4.0, < 3.0)
42
+ rubocop-ast (1.14.0)
43
+ parser (>= 3.0.1.1)
44
+ ruby-progressbar (1.11.0)
45
+ unicode-display_width (2.1.0)
46
+
47
+ PLATFORMS
48
+ x86_64-darwin-21
49
+
50
+ DEPENDENCIES
51
+ mobile-pesa!
52
+ rake (~> 13.0)
53
+ rspec (~> 3.0)
54
+ rubocop (~> 1.7)
55
+
56
+ BUNDLED WITH
57
+ 2.2.22
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2021 Sylvance
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,37 @@
1
+ # Mobile::Pesa
2
+
3
+ Mpesa gem. This gem helps you carry out operations for Mpesa the easy way.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'mobile-pesa'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle install
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install mobile-pesa
20
+
21
+ ## Usage
22
+
23
+ TODO: Write usage instructions here
24
+
25
+ ## Development
26
+
27
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
28
+
29
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
30
+
31
+ ## Contributing
32
+
33
+ Bug reports and pull requests are welcome on GitHub at https://github.com/Sylvance/mobile-pesa.
34
+
35
+ ## License
36
+
37
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ require "rubocop/rake_task"
9
+
10
+ RuboCop::RakeTask.new
11
+
12
+ task default: %i[spec rubocop]
data/bin/console ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "bundler/setup"
5
+ require "mobile/pesa"
6
+
7
+ # You can add fixtures and/or initialization code here to make experimenting
8
+ # with your gem easier. You can also use a different console, if you like.
9
+
10
+ # (If you use this, don't forget to add pry to your Gemfile!)
11
+ # require "pry"
12
+ # Pry.start
13
+
14
+ require "irb"
15
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'uri'
4
+ require 'net/http'
5
+ require 'openssl'
6
+ require 'ostruct'
7
+ require 'json'
8
+
9
+ module Mobile
10
+ module Pesa
11
+ class Authorization
12
+ def initialize; end
13
+
14
+ def self.call
15
+ url = URI("https://sandbox.safaricom.co.ke/oauth/v1/generate?grant_type=client_credentials")
16
+
17
+ http = Net::HTTP.new(url.host, url.port)
18
+ http.use_ssl = true
19
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
20
+
21
+ request = Net::HTTP::Get.new(url)
22
+ request.basic_auth(Mobile::Pesa.configuration.consumer_key, Mobile::Pesa.configuration.consumer_secret)
23
+
24
+ response = http.request(request)
25
+ parsed_body = JSON.parse(response.read_body)
26
+
27
+ if parsed_body.key?("errorCode")
28
+ error = OpenStruct.new(
29
+ error_code: parsed_body["errorCode"],
30
+ error_message: parsed_body["errorMessage"],
31
+ request_id: parsed_body["requestId"]
32
+ )
33
+ OpenStruct.new(result: nil, error: error)
34
+ else
35
+ result = OpenStruct.new(access_token: parsed_body["access_token"], expires_in: parsed_body["expires_in"])
36
+ OpenStruct.new(result: result, error: nil)
37
+ end
38
+ rescue JSON::ParserError => error
39
+ OpenStruct.new(result: nil, error: error)
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,117 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'uri'
4
+ require 'net/http'
5
+ require 'openssl'
6
+ require 'ostruct'
7
+ require 'json'
8
+ require 'base64'
9
+
10
+ module Mobile
11
+ module Pesa
12
+ class B2cPayment
13
+ attr_reader :amount, :phone_number, :short_code, :command_id, :remarks, :occasion
14
+
15
+ def self.call(amount:, phone_number:, short_code:, command_id:, remarks:, occasion:)
16
+ new(amount, phone_number, short_code, command_id, remarks, occasion).call
17
+ end
18
+
19
+ def initialize(amount, phone_number, short_code, command_id, remarks, occasion)
20
+ @amount = amount
21
+ @phone_number = phone_number
22
+ @short_code = short_code
23
+ @command_id = command_id
24
+ @remarks = remarks
25
+ @occasion = occasion
26
+ end
27
+
28
+ def call
29
+ url = URI("https://sandbox.safaricom.co.ke/mpesa/b2c/v1/paymentrequest")
30
+
31
+ http = Net::HTTP.new(url.host, url.port)
32
+ http.use_ssl = true
33
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
34
+
35
+ request = Net::HTTP::Post.new(url)
36
+ request["Content-Type"] = 'application/json'
37
+ request["Authorization"] = "Bearer #{token}"
38
+ request.body = JSON.dump(body)
39
+
40
+ response = http.request(request)
41
+ parsed_body = JSON.parse(response.read_body)
42
+
43
+ if parsed_body.key?("errorCode")
44
+ error = OpenStruct.new(
45
+ error_code: parsed_body["errorCode"],
46
+ error_message: parsed_body["errorMessage"],
47
+ request_id: parsed_body["requestId"]
48
+ )
49
+ OpenStruct.new(result: nil, error: error)
50
+ else
51
+ result = OpenStruct.new(
52
+ conversation_id: parsed_body["ConversationID"],
53
+ originator_conversation_id: parsed_body["OriginatorConversationID"],
54
+ response_code: parsed_body["ResponseCode"],
55
+ response_description: parsed_body["ResponseDescription"]
56
+ )
57
+ OpenStruct.new(result: result, error: nil)
58
+ end
59
+ rescue JSON::ParserError => error
60
+ OpenStruct.new(result: nil, error: error)
61
+ end
62
+
63
+ private
64
+
65
+ def token
66
+ Mobile::Pesa::Authorization.call.result.access_token
67
+ end
68
+
69
+ def body
70
+ {
71
+ "InitiatorName": "mobile-pesa Gem",
72
+ "SecurityCredential": security_credential,
73
+ "CommandID": command_id, # SalaryPayment, BusinessPayment, PromotionPayment
74
+ "Amount": amount,
75
+ "PartyA": short_code,
76
+ "PartyB": phone_number,
77
+ "Remarks": remarks,
78
+ "QueueTimeOutURL": Mobile::Pesa.configuration.queue_time_out_url,
79
+ "ResultURL": Mobile::Pesa.configuration.result_url,
80
+ "Occasion": occasion
81
+ }
82
+ end
83
+
84
+ def security_credential
85
+ cert = OpenSSL::X509::Certificate.new(file_data)
86
+ key = cert.public_key
87
+
88
+ Base64.strict_encode64(key.public_encrypt(password))
89
+ end
90
+
91
+ def file_data
92
+ file = File.open(cert_file_path)
93
+ data = file.read
94
+ file.close
95
+
96
+ data
97
+ end
98
+
99
+ def cert_file_path
100
+ File.join(File.dirname(__FILE__), file_path)
101
+ end
102
+
103
+ def file_path
104
+ return 'certificates/SandboxCertificate.cer' if Mobile::Pesa.configuration.enviroment == 'sandbox'
105
+ return 'certificates/ProductionCertificate.cer' if Mobile::Pesa.configuration.enviroment == 'production'
106
+ end
107
+
108
+ def password
109
+ Base64.strict_encode64("#{short_code}#{Mobile::Pesa.configuration.pass_key}#{timestamp}")
110
+ end
111
+
112
+ def timestamp
113
+ Time.now.strftime('%Y%m%d%H%M%S').to_i
114
+ end
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,38 @@
1
+ -----BEGIN CERTIFICATE-----
2
+ MIIGkzCCBXugAwIBAgIKXfBp5gAAAD+hNjANBgkqhkiG9w0BAQsFADBbMRMwEQYK
3
+ CZImiZPyLGQBGRYDbmV0MRkwFwYKCZImiZPyLGQBGRYJc2FmYXJpY29tMSkwJwYD
4
+ VQQDEyBTYWZhcmljb20gSW50ZXJuYWwgSXNzdWluZyBDQSAwMjAeFw0xNzA0MjUx
5
+ NjA3MjRaFw0xODAzMjExMzIwMTNaMIGNMQswCQYDVQQGEwJLRTEQMA4GA1UECBMH
6
+ TmFpcm9iaTEQMA4GA1UEBxMHTmFpcm9iaTEaMBgGA1UEChMRU2FmYXJpY29tIExp
7
+ bWl0ZWQxEzARBgNVBAsTClRlY2hub2xvZ3kxKTAnBgNVBAMTIGFwaWdlZS5hcGlj
8
+ YWxsZXIuc2FmYXJpY29tLmNvLmtlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
9
+ CgKCAQEAoknIb5Tm1hxOVdFsOejAs6veAai32Zv442BLuOGkFKUeCUM2s0K8XEsU
10
+ t6BP25rQGNlTCTEqfdtRrym6bt5k0fTDscf0yMCoYzaxTh1mejg8rPO6bD8MJB0c
11
+ FWRUeLEyWjMeEPsYVSJFv7T58IdAn7/RhkrpBl1dT7SmIZfNVkIlD35+Cxgab+u7
12
+ +c7dHh6mWguEEoE3NbV7Xjl60zbD/Buvmu6i9EYz+27jNVPI6pRXHvp+ajIzTSsi
13
+ eD8Ztz1eoC9mphErasAGpMbR1sba9bM6hjw4tyTWnJDz7RdQQmnsW1NfFdYdK0qD
14
+ RKUX7SG6rQkBqVhndFve4SDFRq6wvQIDAQABo4IDJDCCAyAwHQYDVR0OBBYEFG2w
15
+ ycrgEBPFzPUZVjh8KoJ3EpuyMB8GA1UdIwQYMBaAFOsy1E9+YJo6mCBjug1evuh5
16
+ TtUkMIIBOwYDVR0fBIIBMjCCAS4wggEqoIIBJqCCASKGgdZsZGFwOi8vL0NOPVNh
17
+ ZmFyaWNvbSUyMEludGVybmFsJTIwSXNzdWluZyUyMENBJTIwMDIsQ049U1ZEVDNJ
18
+ U1NDQTAxLENOPUNEUCxDTj1QdWJsaWMlMjBLZXklMjBTZXJ2aWNlcyxDTj1TZXJ2
19
+ aWNlcyxDTj1Db25maWd1cmF0aW9uLERDPXNhZmFyaWNvbSxEQz1uZXQ/Y2VydGlm
20
+ aWNhdGVSZXZvY2F0aW9uTGlzdD9iYXNlP29iamVjdENsYXNzPWNSTERpc3RyaWJ1
21
+ dGlvblBvaW50hkdodHRwOi8vY3JsLnNhZmFyaWNvbS5jby5rZS9TYWZhcmljb20l
22
+ MjBJbnRlcm5hbCUyMElzc3VpbmclMjBDQSUyMDAyLmNybDCCAQkGCCsGAQUFBwEB
23
+ BIH8MIH5MIHJBggrBgEFBQcwAoaBvGxkYXA6Ly8vQ049U2FmYXJpY29tJTIwSW50
24
+ ZXJuYWwlMjBJc3N1aW5nJTIwQ0ElMjAwMixDTj1BSUEsQ049UHVibGljJTIwS2V5
25
+ JTIwU2VydmljZXMsQ049U2VydmljZXMsQ049Q29uZmlndXJhdGlvbixEQz1zYWZh
26
+ cmljb20sREM9bmV0P2NBQ2VydGlmaWNhdGU/YmFzZT9vYmplY3RDbGFzcz1jZXJ0
27
+ aWZpY2F0aW9uQXV0aG9yaXR5MCsGCCsGAQUFBzABhh9odHRwOi8vY3JsLnNhZmFy
28
+ aWNvbS5jby5rZS9vY3NwMAsGA1UdDwQEAwIFoDA9BgkrBgEEAYI3FQcEMDAuBiYr
29
+ BgEEAYI3FQiHz4xWhMLEA4XphTaE3tENhqCICGeGwcdsg7m5awIBZAIBDDAdBgNV
30
+ HSUEFjAUBggrBgEFBQcDAgYIKwYBBQUHAwEwJwYJKwYBBAGCNxUKBBowGDAKBggr
31
+ BgEFBQcDAjAKBggrBgEFBQcDATANBgkqhkiG9w0BAQsFAAOCAQEAC/hWx7KTwSYr
32
+ x2SOyyHNLTRmCnCJmqxA/Q+IzpW1mGtw4Sb/8jdsoWrDiYLxoKGkgkvmQmB2J3zU
33
+ ngzJIM2EeU921vbjLqX9sLWStZbNC2Udk5HEecdpe1AN/ltIoE09ntglUNINyCmf
34
+ zChs2maF0Rd/y5hGnMM9bX9ub0sqrkzL3ihfmv4vkXNxYR8k246ZZ8tjQEVsKehE
35
+ dqAmj8WYkYdWIHQlkKFP9ba0RJv7aBKb8/KP+qZ5hJip0I5Ey6JJ3wlEWRWUYUKh
36
+ gYoPHrJ92ToadnFCCpOlLKWc0xVxANofy6fqreOVboPO0qTAYpoXakmgeRNLUiar
37
+ 0ah6M/q/KA==
38
+ -----END CERTIFICATE-----
@@ -0,0 +1,35 @@
1
+ -----BEGIN CERTIFICATE-----
2
+ MIIGKzCCBROgAwIBAgIQDL7NH8cxSdUpl0ihH0A1wTANBgkqhkiG9w0BAQsFADBN
3
+ MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMScwJQYDVQQDEx5E
4
+ aWdpQ2VydCBTSEEyIFNlY3VyZSBTZXJ2ZXIgQ0EwHhcNMTgwODI3MDAwMDAwWhcN
5
+ MTkwNDA0MTIwMDAwWjBuMQswCQYDVQQGEwJLRTEQMA4GA1UEBxMHTmFpcm9iaTEW
6
+ MBQGA1UEChMNU2FmYXJpY29tIFBMQzETMBEGA1UECxMKRGlnaXRhbCBJVDEgMB4G
7
+ A1UEAxMXc2FuZGJveC5zYWZhcmljb20uY28ua2UwggEiMA0GCSqGSIb3DQEBAQUA
8
+ A4IBDwAwggEKAoIBAQC78yeC/wLoZY6TJeqc4g/9eAKIpeCwEsjX09pD8ZxAGXqT
9
+ Oi7ssdIGJBPmJZNeEVyf8ocFhisCuLngJ9Z5e/AvH52PhrEFmVu2D03zSf4C+rhZ
10
+ ndEKP6G79pUAb/bemOliU9zM8xYYkpCRzPWUzk6zSDarg0ZDLw5FrtZj/VJ9YEDL
11
+ WGgAfwExEgSN3wjyUlJ2UwI3wqQXLka0VNFWoZxUH5j436gbSWRIL6NJUmrq8V8S
12
+ aTEPz3eJHj3NOToDu245c7VKdF/KExyZjRjD2p5I+Aip80TXzKlZj6DjMb3DlfXF
13
+ Hsnu0+1uJE701mvKX7BiscxKr8tCRphL63as4dqvAgMBAAGjggLkMIIC4DAfBgNV
14
+ HSMEGDAWgBQPgGEcgjFh1S8o541GOLQs4cbZ4jAdBgNVHQ4EFgQUzZmY7ZORLw9w
15
+ qRbAQN5m9lJ28qMwIgYDVR0RBBswGYIXc2FuZGJveC5zYWZhcmljb20uY28ua2Uw
16
+ DgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjBr
17
+ BgNVHR8EZDBiMC+gLaArhilodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vc3NjYS1z
18
+ aGEyLWc2LmNybDAvoC2gK4YpaHR0cDovL2NybDQuZGlnaWNlcnQuY29tL3NzY2Et
19
+ c2hhMi1nNi5jcmwwTAYDVR0gBEUwQzA3BglghkgBhv1sAQEwKjAoBggrBgEFBQcC
20
+ ARYcaHR0cHM6Ly93d3cuZGlnaWNlcnQuY29tL0NQUzAIBgZngQwBAgIwfAYIKwYB
21
+ BQUHAQEEcDBuMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20w
22
+ RgYIKwYBBQUHMAKGOmh0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2Vy
23
+ dFNIQTJTZWN1cmVTZXJ2ZXJDQS5jcnQwCQYDVR0TBAIwADCCAQUGCisGAQQB1nkC
24
+ BAIEgfYEgfMA8QB2AKS5CZC0GFgUh7sTosxncAo8NZgE+RvfuON3zQ7IDdwQAAAB
25
+ ZXs1FvEAAAQDAEcwRQIgBzVMkm7SNprjJ1GBqiXIc9rNzY+y7gt6s/O02oMkyFoC
26
+ IQDBuThGlpmUKpeZoHhK6HGwB4jDMIecmKaOcMS18R2jxwB3AId1v+dZfPiMQ5lf
27
+ vfNu/1aNR1Y2/0q1YMG06v9eoIMPAAABZXs1F8IAAAQDAEgwRgIhAIRq2XFiC+RS
28
+ uDCYq8ICJg0QafSV+e9BLpJnElEdaSjiAiEAyiiW4vxwv4cWcAXE6FAipctyUBs6
29
+ bE5QyaCnmNpoDiQwDQYJKoZIhvcNAQELBQADggEBAB0YoWve9Sxhb0PBS3Hc46Rf
30
+ a7H1jhHuwE+UyscSQsdJdk8uPAgDuKRZMvJPGEaCkNHm36NfcaXXFjPOl7LI1d1a
31
+ 9zqSP0xeZBI6cF0x96WuQGrI9/WR2tfxjmaUSp8a/aJ6n+tZA28eJZNPrIaMm+6j
32
+ gh7AkKnqcf+g8F/MvCCVdNAiVMdz6UpCscf6BRPHNZ5ifvChGh7aUKjrVLLuF4Ls
33
+ HE05qm6HNyV5eTa6wvcbc4ewguN1UDZvPWetSyfBk10Wbpor4znQ4TJ3Y9uCvsJH
34
+ 41ldblDvZZ2z4kB2UYQ7iBkPlJSxSOaFgW/GGDXq49sz/995xzhVITHxh2SdLkI=
35
+ -----END CERTIFICATE-----
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'uri'
4
+ require 'net/http'
5
+ require 'openssl'
6
+ require 'ostruct'
7
+ require 'json'
8
+
9
+ module Mobile
10
+ module Pesa
11
+ class RegisterUrls
12
+ attr_reader :short_code, :response_type, :confirmation_url, :validation_url
13
+
14
+ def self.call(short_code:, response_type:, confirmation_url:, validation_url:)
15
+ new(short_code, response_type, confirmation_url, validation_url).call
16
+ end
17
+
18
+ def initialize(short_code, response_type, confirmation_url, validation_url)
19
+ @short_code = short_code
20
+ @response_type = response_type
21
+ @confirmation_url = confirmation_url
22
+ @validation_url = validation_url
23
+ end
24
+
25
+ def call
26
+ url = URI("https://sandbox.safaricom.co.ke/mpesa/c2b/v1/registerurl")
27
+
28
+ http = Net::HTTP.new(url.host, url.port)
29
+ http.use_ssl = true
30
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
31
+
32
+ request = Net::HTTP::Post.new(url)
33
+ request["Content-Type"] = 'application/json'
34
+ request["Authorization"] = "Bearer #{token}"
35
+ request.body = JSON.dump(body)
36
+
37
+ response = http.request(request)
38
+ parsed_body = JSON.parse(response.read_body)
39
+
40
+ if parsed_body.key?("errorCode")
41
+ error = OpenStruct.new(
42
+ error_code: parsed_body["errorCode"],
43
+ error_message: parsed_body["errorMessage"],
44
+ request_id: parsed_body["requestId"]
45
+ )
46
+ OpenStruct.new(result: nil, error: error)
47
+ else
48
+ result = OpenStruct.new(
49
+ originator_converstion_id: parsed_body["OriginatorConverstionID"],
50
+ response_code: parsed_body["ResponseCode"],
51
+ response_description: parsed_body["ResponseDescription"]
52
+ )
53
+ OpenStruct.new(result: result, error: nil)
54
+ end
55
+ rescue JSON::ParserError => error
56
+ OpenStruct.new(result: nil, error: error)
57
+ end
58
+
59
+ private
60
+
61
+ def token
62
+ Mobile::Pesa::Authorization.call.result.access_token
63
+ end
64
+
65
+ def body
66
+ {
67
+ "ShortCode": short_code,
68
+ "ResponseType": response_type, # Cancelled Completed
69
+ "ConfirmationURL": confirmation_url,
70
+ "ValidationURL": validation_url
71
+ }
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,116 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'uri'
4
+ require 'net/http'
5
+ require 'openssl'
6
+ require 'ostruct'
7
+ require 'json'
8
+ require 'base64'
9
+
10
+ module Mobile
11
+ module Pesa
12
+ class Reversal
13
+ attr_reader :amount, :transaction_id, :short_code, :remarks, :occasion
14
+
15
+ def self.call(amount:, transaction_id:, short_code:, remarks:, occasion:)
16
+ new(amount, transaction_id, short_code, remarks, occasion).call
17
+ end
18
+
19
+ def initialize(amount, transaction_id, short_code, remarks, occasion)
20
+ @amount = amount
21
+ @transaction_id = transaction_id
22
+ @short_code = short_code
23
+ @remarks = remarks
24
+ @occasion = occasion
25
+ end
26
+
27
+ def call
28
+ url = URI("https://sandbox.safaricom.co.ke/mpesa/reversal/v1/request")
29
+
30
+ http = Net::HTTP.new(url.host, url.port)
31
+ http.use_ssl = true
32
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
33
+
34
+ request = Net::HTTP::Post.new(url)
35
+ request["Content-Type"] = 'application/json'
36
+ request["Authorization"] = "Bearer #{token}"
37
+ request.body = JSON.dump(body)
38
+
39
+ response = http.request(request)
40
+ parsed_body = JSON.parse(response.read_body)
41
+
42
+ if parsed_body.key?("errorCode")
43
+ error = OpenStruct.new(
44
+ error_code: parsed_body["errorCode"],
45
+ error_message: parsed_body["errorMessage"],
46
+ request_id: parsed_body["requestId"]
47
+ )
48
+ OpenStruct.new(result: nil, error: error)
49
+ else
50
+ result = OpenStruct.new(
51
+ originator_converstion_id: parsed_body["OriginatorConverstionID"],
52
+ response_code: parsed_body["ResponseCode"],
53
+ response_description: parsed_body["ResponseDescription"]
54
+ )
55
+ OpenStruct.new(result: result, error: nil)
56
+ end
57
+ rescue JSON::ParserError => error
58
+ OpenStruct.new(result: nil, error: error)
59
+ end
60
+
61
+ private
62
+
63
+ def token
64
+ Mobile::Pesa::Authorization.call.result.access_token
65
+ end
66
+
67
+ def body
68
+ {
69
+ "Initiator": "mobile-pesa Gem",
70
+ "SecurityCredential": security_credential,
71
+ "CommandID": "TransactionReversal",
72
+ "TransactionID": transaction_id,
73
+ "Amount": amount,
74
+ "ReceiverParty": short_code,
75
+ "RecieverIdentifierType": "4",
76
+ "Remarks": remarks,
77
+ "QueueTimeOutURL": Mobile::Pesa.configuration.queue_time_out_url,
78
+ "ResultURL": Mobile::Pesa.configuration.result_url,
79
+ "Occasion": occasion
80
+ }
81
+ end
82
+
83
+ def security_credential
84
+ cert = OpenSSL::X509::Certificate.new(file_data)
85
+ key = cert.public_key
86
+
87
+ Base64.strict_encode64(key.public_encrypt(password))
88
+ end
89
+
90
+ def file_data
91
+ file = File.open(cert_file_path)
92
+ data = file.read
93
+ file.close
94
+
95
+ data
96
+ end
97
+
98
+ def cert_file_path
99
+ File.join(File.dirname(__FILE__), file_path)
100
+ end
101
+
102
+ def file_path
103
+ return 'certificates/SandboxCertificate.cer' if Mobile::Pesa.configuration.enviroment == 'sandbox'
104
+ return 'certificates/ProductionCertificate.cer' if Mobile::Pesa.configuration.enviroment == 'production'
105
+ end
106
+
107
+ def password
108
+ Base64.strict_encode64("#{short_code}#{Mobile::Pesa.configuration.pass_key}#{timestamp}")
109
+ end
110
+
111
+ def timestamp
112
+ Time.now.strftime('%Y%m%d%H%M%S').to_i
113
+ end
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'uri'
4
+ require 'net/http'
5
+ require 'openssl'
6
+ require 'ostruct'
7
+ require 'json'
8
+
9
+ module Mobile
10
+ module Pesa
11
+ class SimulateC2bViaPaybillNumber
12
+ attr_reader :amount, :phone_number, :account_number, :pay_bill_number
13
+
14
+ def self.call(amount:, phone_number:, account_number:, pay_bill_number:)
15
+ new(amount, phone_number, account_number, pay_bill_number).call
16
+ end
17
+
18
+ def initialize(amount, phone_number, account_number, pay_bill_number)
19
+ @amount = amount
20
+ @phone_number = phone_number
21
+ @account_number = account_number
22
+ @pay_bill_number = pay_bill_number
23
+ end
24
+
25
+ def call
26
+ url = URI("https://sandbox.safaricom.co.ke/mpesa/c2b/v1/simulate")
27
+
28
+ http = Net::HTTP.new(url.host, url.port)
29
+ http.use_ssl = true
30
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
31
+
32
+ request = Net::HTTP::Post.new(url)
33
+ request["Content-Type"] = 'application/json'
34
+ request["Authorization"] = "Bearer #{token}"
35
+ request.body = JSON.dump(body)
36
+
37
+ response = http.request(request)
38
+ parsed_body = JSON.parse(response.read_body)
39
+
40
+ if parsed_body.key?("errorCode")
41
+ error = OpenStruct.new(
42
+ error_code: parsed_body["errorCode"],
43
+ error_message: parsed_body["errorMessage"],
44
+ request_id: parsed_body["requestId"]
45
+ )
46
+ OpenStruct.new(result: nil, error: error)
47
+ else
48
+ result = OpenStruct.new(
49
+ merchant_request_id: parsed_body["MerchantRequestID"],
50
+ checkout_request_id: parsed_body["CheckoutRequestID"],
51
+ response_code: parsed_body["ResponseCode"],
52
+ response_description: parsed_body["ResponseDescription"],
53
+ customer_message: parsed_body["CustomerMessage"]
54
+ )
55
+ OpenStruct.new(result: result, error: nil)
56
+ end
57
+ rescue JSON::ParserError => error
58
+ OpenStruct.new(result: nil, error: error)
59
+ end
60
+
61
+ private
62
+
63
+ def token
64
+ Mobile::Pesa::Authorization.call.result.access_token
65
+ end
66
+
67
+ def body
68
+ {
69
+ "ShortCode": pay_bill_number,
70
+ "CommandID": "CustomerPayBillOnline",
71
+ "Amount": amount,
72
+ "Msisdn": phone_number,
73
+ "BillRefNumber": account_number
74
+ }
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'uri'
4
+ require 'net/http'
5
+ require 'openssl'
6
+ require 'ostruct'
7
+ require 'json'
8
+
9
+ module Mobile
10
+ module Pesa
11
+ class SimulateC2bViaTillNumber
12
+ attr_reader :amount, :phone_number, :till_number
13
+
14
+ def self.call(amount:, phone_number:, till_number:)
15
+ new(amount, phone_number, till_number).call
16
+ end
17
+
18
+ def initialize(amount, phone_number, till_number)
19
+ @amount = amount
20
+ @phone_number = phone_number
21
+ @till_number = till_number
22
+ end
23
+
24
+ def call
25
+ url = URI("https://sandbox.safaricom.co.ke/mpesa/c2b/v1/simulate")
26
+
27
+ http = Net::HTTP.new(url.host, url.port)
28
+ http.use_ssl = true
29
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
30
+
31
+ request = Net::HTTP::Post.new(url)
32
+ request["Content-Type"] = 'application/json'
33
+ request["Authorization"] = "Bearer #{token}"
34
+ request.body = JSON.dump(body)
35
+
36
+ response = http.request(request)
37
+ parsed_body = JSON.parse(response.read_body)
38
+
39
+ if parsed_body.key?("errorCode")
40
+ error = OpenStruct.new(
41
+ error_code: parsed_body["errorCode"],
42
+ error_message: parsed_body["errorMessage"],
43
+ request_id: parsed_body["requestId"]
44
+ )
45
+ OpenStruct.new(result: nil, error: error)
46
+ else
47
+ result = OpenStruct.new(
48
+ merchant_request_id: parsed_body["MerchantRequestID"],
49
+ checkout_request_id: parsed_body["CheckoutRequestID"],
50
+ response_code: parsed_body["ResponseCode"],
51
+ response_description: parsed_body["ResponseDescription"],
52
+ customer_message: parsed_body["CustomerMessage"]
53
+ )
54
+ OpenStruct.new(result: result, error: nil)
55
+ end
56
+ rescue JSON::ParserError => error
57
+ OpenStruct.new(result: nil, error: error)
58
+ end
59
+
60
+ private
61
+
62
+ def token
63
+ Mobile::Pesa::Authorization.call.result.access_token
64
+ end
65
+
66
+ def body
67
+ {
68
+ "ShortCode": till_number,
69
+ "CommandID": "CustomerBuyGoodsOnline",
70
+ "Amount": amount,
71
+ "Msisdn": phone_number
72
+ }
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,85 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'uri'
4
+ require 'net/http'
5
+ require 'openssl'
6
+ require 'ostruct'
7
+ require 'json'
8
+ require 'base64'
9
+
10
+ module Mobile
11
+ module Pesa
12
+ class StkPushStatus
13
+ attr_reader :checkout_request_id, :short_code
14
+
15
+ def self.call(checkout_request_id:, short_code:)
16
+ new(checkout_request_id, short_code).call
17
+ end
18
+
19
+ def initialize(checkout_request_id, short_code)
20
+ @checkout_request_id = checkout_request_id
21
+ @short_code = short_code
22
+ end
23
+
24
+ def call
25
+ url = URI("https://sandbox.safaricom.co.ke/mpesa/stkpushquery/v1/query")
26
+
27
+ http = Net::HTTP.new(url.host, url.port)
28
+ http.use_ssl = true
29
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
30
+
31
+ request = Net::HTTP::Post.new(url)
32
+ request["Content-Type"] = 'application/json'
33
+ request["Authorization"] = "Bearer #{token}"
34
+ request.body = JSON.dump(body)
35
+
36
+ response = http.request(request)
37
+ parsed_body = JSON.parse(response.read_body)
38
+
39
+ if parsed_body.key?("errorCode")
40
+ error = OpenStruct.new(
41
+ error_code: parsed_body["errorCode"],
42
+ error_message: parsed_body["errorMessage"],
43
+ request_id: parsed_body["requestId"]
44
+ )
45
+ OpenStruct.new(result: nil, error: error)
46
+ else
47
+ result = OpenStruct.new(
48
+ merchant_request_id: parsed_body["MerchantRequestID"],
49
+ checkout_request_id: parsed_body["CheckoutRequestID"],
50
+ response_code: parsed_body["ResponseCode"],
51
+ response_description: parsed_body["ResponseDescription"],
52
+ result_desc: parsed_body["ResultDesc"],
53
+ result_code: parsed_body["ResultCode"]
54
+ )
55
+ OpenStruct.new(result: result, error: nil)
56
+ end
57
+ rescue JSON::ParserError => error
58
+ OpenStruct.new(result: nil, error: error)
59
+ end
60
+
61
+ private
62
+
63
+ def token
64
+ Mobile::Pesa::Authorization.call.result.access_token
65
+ end
66
+
67
+ def body
68
+ {
69
+ "BusinessShortCode": short_code,
70
+ "Password": password,
71
+ "Timestamp": timestamp.to_s,
72
+ "CheckoutRequestID": checkout_request_id
73
+ }
74
+ end
75
+
76
+ def password
77
+ Base64.strict_encode64("#{short_code}#{Mobile::Pesa.configuration.pass_key}#{timestamp}")
78
+ end
79
+
80
+ def timestamp
81
+ Time.now.strftime('%Y%m%d%H%M%S').to_i
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,93 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'uri'
4
+ require 'net/http'
5
+ require 'openssl'
6
+ require 'ostruct'
7
+ require 'json'
8
+ require 'base64'
9
+
10
+ module Mobile
11
+ module Pesa
12
+ class StkPushViaPaybillNumber
13
+ attr_reader :amount, :phone_number, :account_number, :pay_bill_number
14
+
15
+ def self.call(amount:, phone_number:, account_number:, pay_bill_number:)
16
+ new(amount, phone_number, account_number, pay_bill_number).call
17
+ end
18
+
19
+ def initialize(amount, phone_number, account_number, pay_bill_number)
20
+ @amount = amount
21
+ @phone_number = phone_number
22
+ @account_number = account_number
23
+ @pay_bill_number = pay_bill_number
24
+ end
25
+
26
+ def call
27
+ url = URI("https://sandbox.safaricom.co.ke/mpesa/stkpush/v1/processrequest")
28
+
29
+ http = Net::HTTP.new(url.host, url.port)
30
+ http.use_ssl = true
31
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
32
+
33
+ request = Net::HTTP::Post.new(url)
34
+ request["Content-Type"] = 'application/json'
35
+ request["Authorization"] = "Bearer #{token}"
36
+ request.body = JSON.dump(body)
37
+
38
+ response = http.request(request)
39
+ parsed_body = JSON.parse(response.read_body)
40
+
41
+ if parsed_body.key?("errorCode")
42
+ error = OpenStruct.new(
43
+ error_code: parsed_body["errorCode"],
44
+ error_message: parsed_body["errorMessage"],
45
+ request_id: parsed_body["requestId"]
46
+ )
47
+ OpenStruct.new(result: nil, error: error)
48
+ else
49
+ result = OpenStruct.new(
50
+ merchant_request_id: parsed_body["MerchantRequestID"],
51
+ checkout_request_id: parsed_body["CheckoutRequestID"],
52
+ response_code: parsed_body["ResponseCode"],
53
+ response_description: parsed_body["ResponseDescription"],
54
+ customer_message: parsed_body["CustomerMessage"]
55
+ )
56
+ OpenStruct.new(result: result, error: nil)
57
+ end
58
+ rescue JSON::ParserError => error
59
+ OpenStruct.new(result: nil, error: error)
60
+ end
61
+
62
+ private
63
+
64
+ def token
65
+ Mobile::Pesa::Authorization.call.result.access_token
66
+ end
67
+
68
+ def body
69
+ {
70
+ "BusinessShortCode": pay_bill_number,
71
+ "Password": password,
72
+ "Timestamp": timestamp.to_s,
73
+ "TransactionType": "CustomerPayBillOnline",
74
+ "Amount": amount,
75
+ "PartyA": phone_number,
76
+ "PartyB": pay_bill_number,
77
+ "PhoneNumber": phone_number,
78
+ "CallBackURL": Mobile::Pesa.configuration.callback_url,
79
+ "AccountReference": account_number,
80
+ "TransactionDesc": "Payment of X"
81
+ }
82
+ end
83
+
84
+ def password
85
+ Base64.strict_encode64("#{pay_bill_number}#{Mobile::Pesa.configuration.pass_key}#{timestamp}")
86
+ end
87
+
88
+ def timestamp
89
+ Time.now.strftime('%Y%m%d%H%M%S').to_i
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,91 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'uri'
4
+ require 'net/http'
5
+ require 'openssl'
6
+ require 'ostruct'
7
+ require 'json'
8
+ require 'base64'
9
+
10
+ module Mobile
11
+ module Pesa
12
+ class StkPushViaTillNumber
13
+ attr_reader :amount, :phone_number, :till_number
14
+
15
+ def self.call(amount:, phone_number:, till_number:)
16
+ new(amount, phone_number, till_number).call
17
+ end
18
+
19
+ def initialize(amount, phone_number, till_number)
20
+ @amount = amount
21
+ @phone_number = phone_number
22
+ @till_number = till_number
23
+ end
24
+
25
+ def call
26
+ url = URI("https://sandbox.safaricom.co.ke/mpesa/stkpush/v1/processrequest")
27
+
28
+ http = Net::HTTP.new(url.host, url.port)
29
+ http.use_ssl = true
30
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
31
+
32
+ request = Net::HTTP::Post.new(url)
33
+ request["Content-Type"] = 'application/json'
34
+ request["Authorization"] = "Bearer #{token}"
35
+ request.body = JSON.dump(body)
36
+
37
+ response = http.request(request)
38
+ parsed_body = JSON.parse(response.read_body)
39
+
40
+ if parsed_body.key?("errorCode")
41
+ error = OpenStruct.new(
42
+ error_code: parsed_body["errorCode"],
43
+ error_message: parsed_body["errorMessage"],
44
+ request_id: parsed_body["requestId"]
45
+ )
46
+ OpenStruct.new(result: nil, error: error)
47
+ else
48
+ result = OpenStruct.new(
49
+ merchant_request_id: parsed_body["MerchantRequestID"],
50
+ checkout_request_id: parsed_body["CheckoutRequestID"],
51
+ response_code: parsed_body["ResponseCode"],
52
+ response_description: parsed_body["ResponseDescription"],
53
+ customer_message: parsed_body["CustomerMessage"]
54
+ )
55
+ OpenStruct.new(result: result, error: nil)
56
+ end
57
+ rescue JSON::ParserError => error
58
+ OpenStruct.new(result: nil, error: error)
59
+ end
60
+
61
+ private
62
+
63
+ def token
64
+ Mobile::Pesa::Authorization.call.result.access_token
65
+ end
66
+
67
+ def body
68
+ {
69
+ "BusinessShortCode": till_number,
70
+ "Password": password,
71
+ "Timestamp": timestamp.to_s,
72
+ "TransactionType": "CustomerBuyGoodsOnline",
73
+ "Amount": amount,
74
+ "PartyA": phone_number,
75
+ "PartyB": till_number,
76
+ "PhoneNumber": phone_number,
77
+ "CallBackURL": Mobile::Pesa.configuration.callback_url,
78
+ "TransactionDesc": Mobile::Pesa.configuration.default_description
79
+ }
80
+ end
81
+
82
+ def password
83
+ Base64.strict_encode64("#{till_number}#{Mobile::Pesa.configuration.pass_key}#{timestamp}")
84
+ end
85
+
86
+ def timestamp
87
+ Time.now.strftime('%Y%m%d%H%M%S').to_i
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mobile
4
+ module Pesa
5
+ VERSION = "0.1.1"
6
+ end
7
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "pesa/authorization"
4
+ require_relative "pesa/b2c_payment"
5
+ require_relative "pesa/register_urls"
6
+ require_relative "pesa/reversal"
7
+ require_relative "pesa/simulate_c2b_via_paybill_number"
8
+ require_relative "pesa/simulate_c2b_via_till_number"
9
+ require_relative "pesa/stk_push_status"
10
+ require_relative "pesa/stk_push_via_paybill_number"
11
+ require_relative "pesa/stk_push_via_till_number"
12
+ require_relative "pesa/version"
13
+ require 'ostruct'
14
+
15
+ module Mobile
16
+ module Pesa
17
+ class Error < StandardError; end
18
+
19
+ def self.configuration
20
+ @configuration ||= OpenStruct.new(
21
+ consumer_key: nil,
22
+ consumer_secret: nil,
23
+ pass_key: nil,
24
+ short_code: nil,
25
+ response_type: nil,
26
+ callback_url: nil,
27
+ result_url: nil,
28
+ queue_time_out_url: nil,
29
+ default_description: nil,
30
+ enviroment: nil
31
+ )
32
+ end
33
+
34
+ def self.configure
35
+ yield(configuration)
36
+ end
37
+
38
+ def to_recursive_ostruct(hash)
39
+ result = hash.each_with_object({}) do |(key, val), memo|
40
+ memo[key] = val.is_a?(Hash) ? to_recursive_ostruct(val) : val
41
+ end
42
+
43
+ OpenStruct.new(result)
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/mobile/pesa/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "mobile-pesa"
7
+ spec.version = Mobile::Pesa::VERSION
8
+ spec.authors = ["Sylvance"]
9
+ spec.email = ["9350722+Sylvance@users.noreply.github.com"]
10
+
11
+ spec.summary = "Mpesa gem."
12
+ spec.description = "This gem helps you carry out operations for Mpesa the easy way."
13
+ spec.homepage = "https://github.com/Sylvance/mobile-pesa"
14
+ spec.license = "MIT"
15
+ spec.required_ruby_version = ">= 2.4.0"
16
+
17
+ spec.metadata["allowed_push_host"] = "https://rubygems.org/"
18
+
19
+ spec.metadata["homepage_uri"] = spec.homepage
20
+ spec.metadata["source_code_uri"] = "https://github.com/Sylvance/mobile-pesa"
21
+ spec.metadata["changelog_uri"] = "https://github.com/Sylvance/mobile-pesa/CHANGELOG.md"
22
+
23
+ # Specify which files should be added to the gem when it is released.
24
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
25
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
26
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features)/}) }
27
+ end
28
+ spec.bindir = "exe"
29
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
30
+ spec.require_paths = ["lib"]
31
+
32
+ # Uncomment to register a new dependency of your gem
33
+ spec.add_dependency "colorize"
34
+
35
+ # For more information and examples about making a new gem, checkout our
36
+ # guide at: https://bundler.io/guides/creating_gem.html
37
+ end
metadata ADDED
@@ -0,0 +1,87 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mobile-pesa
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Sylvance
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2021-12-17 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: colorize
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ description: This gem helps you carry out operations for Mpesa the easy way.
28
+ email:
29
+ - 9350722+Sylvance@users.noreply.github.com
30
+ executables: []
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - ".github/workflows/main.yml"
35
+ - ".gitignore"
36
+ - ".rspec"
37
+ - ".rubocop.yml"
38
+ - CHANGELOG.md
39
+ - Gemfile
40
+ - Gemfile.lock
41
+ - LICENSE.txt
42
+ - README.md
43
+ - Rakefile
44
+ - bin/console
45
+ - bin/setup
46
+ - lib/mobile/pesa.rb
47
+ - lib/mobile/pesa/authorization.rb
48
+ - lib/mobile/pesa/b2c_payment.rb
49
+ - lib/mobile/pesa/certificates/ProductionCertificate.cer
50
+ - lib/mobile/pesa/certificates/SandboxCertificate.cer
51
+ - lib/mobile/pesa/register_urls.rb
52
+ - lib/mobile/pesa/reversal.rb
53
+ - lib/mobile/pesa/simulate_c2b_via_paybill_number.rb
54
+ - lib/mobile/pesa/simulate_c2b_via_till_number.rb
55
+ - lib/mobile/pesa/stk_push_status.rb
56
+ - lib/mobile/pesa/stk_push_via_paybill_number.rb
57
+ - lib/mobile/pesa/stk_push_via_till_number.rb
58
+ - lib/mobile/pesa/version.rb
59
+ - mobile-pesa.gemspec
60
+ homepage: https://github.com/Sylvance/mobile-pesa
61
+ licenses:
62
+ - MIT
63
+ metadata:
64
+ allowed_push_host: https://rubygems.org/
65
+ homepage_uri: https://github.com/Sylvance/mobile-pesa
66
+ source_code_uri: https://github.com/Sylvance/mobile-pesa
67
+ changelog_uri: https://github.com/Sylvance/mobile-pesa/CHANGELOG.md
68
+ post_install_message:
69
+ rdoc_options: []
70
+ require_paths:
71
+ - lib
72
+ required_ruby_version: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ version: 2.4.0
77
+ required_rubygems_version: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ requirements: []
83
+ rubygems_version: 3.2.22
84
+ signing_key:
85
+ specification_version: 4
86
+ summary: Mpesa gem.
87
+ test_files: []