beyonic 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: a9cb750b8209850df7f7aa7b95776d92af061fda
4
+ data.tar.gz: 6323d13294482469a53bccb3616b46a4b94b2be1
5
+ SHA512:
6
+ metadata.gz: d6591580528bcaf8318a6b1e4dccb473a1f0754d5314d5cf2befc3811bb93155306fba56e1e7d336244c9ce0e0e9379cb028fa39396aaea8afa911ed9b091440
7
+ data.tar.gz: 01437d12906e27237ef5908c86655d82993d31866153f7d3644861cfaafdd8f3f55ffad923fc2732c9f695c17b12de8d7371ff6edeee910f1347997c48284201
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in beyonic.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Oleg German
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,63 @@
1
+ # Beyonic
2
+
3
+ Ruby API wrapper for http://beyonic.com
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'beyonic_api', git: 'git@bitbucket.org:ogerman/beyonic_api.git'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ ## Usage
18
+ To start using API you must setup your api key
19
+
20
+ ```ruby
21
+ Beyonic.api_key = "my-authorization-token"
22
+ ```
23
+
24
+ Now you can create payment
25
+
26
+ ```ruby
27
+ Beyonic::Payment.create(
28
+ phonenumber: "+256773712831",
29
+ amount: "100.2",
30
+ currency: "UGX",
31
+ description: "Per diem payment",
32
+ payment_type: "money",
33
+ callback_url: "https://my.website/payments/callback",
34
+ metadata: "{'id': '1234', 'name': 'Lucy'}"
35
+ )
36
+ ```
37
+
38
+ After successeful creation you will get object Payment with id.
39
+
40
+ Also you can list all your payments or get one by id
41
+
42
+ ```ruby
43
+ Beyonic::Payment.list
44
+ Beyonic::Payment.get(123)
45
+ ```
46
+
47
+ Apart from everything else, you can manage Webohooks to define URLs on your server that notifications should be sent to.
48
+ ```ruby
49
+ #Create
50
+ Beyonic::Webhook.create(
51
+ event: "payment.status.changed",
52
+ target: "https://my.callback.url/"
53
+ )
54
+
55
+ #Update by id
56
+ Beyonic::Webhook.update(2,
57
+ target: "https://mynew.callback.url/")
58
+
59
+ #Delete by id
60
+ Beyonic::Webhook.delete(2)
61
+ ```
62
+
63
+ You can find more detailed API description here http://support.beyonic.com/category/api/
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
@@ -0,0 +1,30 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'beyonic/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "beyonic"
8
+ spec.version = Beyonic::VERSION
9
+ spec.authors = ['Oleg German', 'Luke Kyohere']
10
+ spec.email = ['oleg.german@gmail.com', 'luke@beyonic.com']
11
+ spec.summary = %q{Ruby library for the beyonic.com api}
12
+ spec.description = %q{Beyonic.com makes enterprise payments to mobile easy. Details: http://beyonic.com}
13
+ spec.homepage = "http://support.beyonic.com/api/"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_runtime_dependency "rest-client"
22
+ spec.add_runtime_dependency "oj"
23
+
24
+ spec.add_development_dependency "bundler", "~> 1.7"
25
+ spec.add_development_dependency "rake", "~> 10.0"
26
+ spec.add_development_dependency "rspec"
27
+ spec.add_development_dependency "rspec-collection_matchers"
28
+ spec.add_development_dependency "webmock"
29
+ spec.add_development_dependency "simplecov"
30
+ end
@@ -0,0 +1,30 @@
1
+ module Beyonic
2
+
3
+ require "rest-client"
4
+ require "oj"
5
+
6
+ #Fixme! remove me after getting new cert
7
+ OpenSSL::SSL::VERIFY_PEER = OpenSSL::SSL::VERIFY_NONE
8
+
9
+ def self.api_key=(key)
10
+ @api_key = key
11
+ end
12
+
13
+ def self.api_key
14
+ @api_key
15
+ end
16
+
17
+ def self.api_version
18
+ "v1"
19
+ end
20
+
21
+ def self.endpoint_base
22
+ "https://staging.beyonic.com/api/"
23
+ end
24
+
25
+ end
26
+
27
+ require "beyonic/version"
28
+ require "beyonic/abstract_api"
29
+ require "beyonic/payment"
30
+ require "beyonic/webhook"
@@ -0,0 +1,88 @@
1
+ module Beyonic::AbstractApi
2
+ class ApiError < StandardError
3
+ end
4
+
5
+ module ClassMethods
6
+
7
+ def set_endpoint_resource(resource)
8
+ @endpoint_url = Beyonic.endpoint_base + resource
9
+ end
10
+
11
+ def create(payload = {})
12
+ resp = RestClient.post(@endpoint_url, payload, headers)
13
+ self.new(Oj.load(resp))
14
+ rescue RestClient::BadRequest => e
15
+ raise ApiError.new(Oj.load(e.response.body))
16
+ end
17
+
18
+ def list
19
+ resp = RestClient.get(@endpoint_url, headers)
20
+ Oj.load(resp).map { |obj_attrs| self.new(obj_attrs)}
21
+ end
22
+
23
+ def get(id)
24
+ resp = RestClient.get("#{@endpoint_url}/#{id}", headers)
25
+ self.new(Oj.load(resp))
26
+ end
27
+
28
+ def update(id, payload)
29
+ resp = RestClient.patch("#{@endpoint_url}/#{id}", payload, headers)
30
+ self.new(Oj.load(resp))
31
+ rescue RestClient::BadRequest => e
32
+ raise ApiError.new(Oj.load(e.response.body))
33
+ end
34
+
35
+ def delete(id)
36
+ resp = RestClient.delete("#{@endpoint_url}/#{id}", headers)
37
+ return true if resp == ""
38
+ end
39
+
40
+ private
41
+
42
+ def headers
43
+ headers_hash = {}
44
+ headers_hash.merge!({"Authorization" => "Token #{Beyonic.api_key}"}) if Beyonic.api_key
45
+ headers_hash.merge!({"Beyonic-Version" => Beyonic.api_version})
46
+ headers_hash
47
+ end
48
+
49
+ end
50
+
51
+ module InstanceMethods
52
+
53
+ def save
54
+ if respond_to?(:id) && !id.nil?
55
+ self.class.update(id, to_h)
56
+ else
57
+ self.class.create(to_h)
58
+ end
59
+ end
60
+
61
+
62
+ def []=(name, value)
63
+ if name.to_sym == :id
64
+ self.id=(value)
65
+ else
66
+ super(name,value)
67
+ end
68
+ end
69
+
70
+ end
71
+
72
+ module Initializer
73
+ def initialize(*args)
74
+ super(*args)
75
+ #We should define it after Object initialization
76
+ define_singleton_method(:id=) do |val|
77
+ raise "Can't change id of existing #{self.class}"
78
+ end
79
+ end
80
+ end
81
+
82
+ def self.included(receiver)
83
+ receiver.extend ClassMethods
84
+ receiver.send :include, InstanceMethods
85
+ receiver.send :prepend, Initializer
86
+ end
87
+
88
+ end
@@ -0,0 +1,6 @@
1
+ require 'ostruct'
2
+ class Beyonic::Payment < OpenStruct
3
+
4
+ include Beyonic::AbstractApi
5
+ set_endpoint_resource "payments"
6
+ end
@@ -0,0 +1,3 @@
1
+ module Beyonic
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,5 @@
1
+ require 'ostruct'
2
+ class Beyonic::Webhook < OpenStruct
3
+ include Beyonic::AbstractApi
4
+ set_endpoint_resource "webhooks"
5
+ end
@@ -0,0 +1,223 @@
1
+ require 'spec_helper'
2
+
3
+ describe Beyonic::Payment do
4
+ describe ".crate" do
5
+ let(:payload) {
6
+ {
7
+ phonenumber: "+256773712831",
8
+ amount: "100.2",
9
+ currency: "UGX",
10
+ description: "Per diem payment",
11
+ payment_type: "money",
12
+ callback_url: "https://my.website/payments/callback",
13
+ metadata: "{'id': '1234', 'name': 'Lucy'}"
14
+ }
15
+ }
16
+
17
+ subject {
18
+ Beyonic.api_key = "my-authorization-token"
19
+ Beyonic::Payment.create(payload)
20
+ }
21
+
22
+ context 'Success response' do
23
+ before {
24
+ stub_request(:post, "https://staging.beyonic.com/api/payments").to_return(
25
+ body: File.new('spec/examples/payments/create_response.json'))
26
+ }
27
+
28
+ it {
29
+ is_expected.to have_requested(:post, "https://staging.beyonic.com/api/payments").with(
30
+ headers: {"Authorization" => "Token my-authorization-token", "Beyonic-Version" => "v1"}
31
+ )
32
+ }
33
+ it { is_expected.to be_an(Beyonic::Payment) }
34
+
35
+ it { is_expected.to have_attributes(id: 3607, state: 'new') }
36
+ end
37
+
38
+ context 'Bad request' do
39
+ before {
40
+ stub_request(:post, "https://staging.beyonic.com/api/payments").to_return(
41
+ body: File.new('spec/examples/payments/create_invalid_response.json'),
42
+ status: 400
43
+ )
44
+ }
45
+
46
+ subject {
47
+ -> {
48
+ Beyonic.api_key = "my-authorization-token"
49
+ Beyonic::Payment.create(payload)
50
+ }
51
+ }
52
+ it {
53
+ is_expected.to raise_error(Beyonic::AbstractApi::ApiError)
54
+ }
55
+ end
56
+
57
+ context 'Unauthorized' do
58
+ before {
59
+ stub_request(:post, "https://staging.beyonic.com/api/payments").to_return(
60
+ body: File.new('spec/examples/invalid_token_error.json'),
61
+ status: 401
62
+ )
63
+ }
64
+
65
+ subject {
66
+ -> {
67
+ Beyonic.api_key = "my-authorization-token"
68
+ Beyonic::Payment.create(payload)
69
+ }
70
+ }
71
+ it {
72
+ is_expected.to raise_error
73
+ }
74
+ end
75
+
76
+ end
77
+
78
+ describe ".list" do
79
+ subject {
80
+ Beyonic.api_key = "my-authorization-token"
81
+ Beyonic::Payment.list
82
+ }
83
+
84
+ context 'Success response' do
85
+ before {
86
+ stub_request(:get, "https://staging.beyonic.com/api/payments").to_return(
87
+ body: File.new('spec/examples/payments/list_response.json'))
88
+ }
89
+
90
+ it {
91
+ is_expected.to have_requested(:get, "https://staging.beyonic.com/api/payments").with(
92
+ headers: {"Authorization" => "Token my-authorization-token", "Beyonic-Version" => "v1"}
93
+ )
94
+ }
95
+ it { is_expected.to be_an(Array) }
96
+
97
+ it { is_expected.to all(be_an(Beyonic::Payment)) }
98
+ end
99
+
100
+ context 'Unauthorized' do
101
+ before {
102
+ stub_request(:get, "https://staging.beyonic.com/api/payments").to_return(
103
+ body: File.new('spec/examples/invalid_token_error.json'),
104
+ status: 401
105
+ )
106
+ }
107
+
108
+ subject {
109
+ -> {
110
+ Beyonic.api_key = "my-authorization-token"
111
+ Beyonic::Payment.list
112
+ }
113
+ }
114
+ it {
115
+ is_expected.to raise_error
116
+ }
117
+ end
118
+ end
119
+
120
+ describe ".get" do
121
+ subject {
122
+ Beyonic.api_key = "my-authorization-token"
123
+ Beyonic::Payment.get(23)
124
+ }
125
+
126
+ context 'Success response' do
127
+ before {
128
+ stub_request(:get, "https://staging.beyonic.com/api/payments/23").to_return(
129
+ body: File.new('spec/examples/payments/get_response.json'))
130
+ }
131
+
132
+ it {
133
+ is_expected.to have_requested(:get, "https://staging.beyonic.com/api/payments/23").with(
134
+ headers: {"Authorization" => "Token my-authorization-token", "Beyonic-Version" => "v1"}
135
+ )
136
+ }
137
+ it { is_expected.to be_an(Beyonic::Payment) }
138
+
139
+ it { is_expected.to have_attributes(id: 23, state: 'processed_with_errors') }
140
+ end
141
+
142
+ context 'Unauthorized' do
143
+ before {
144
+ stub_request(:get, "https://staging.beyonic.com/api/payments/23").to_return(
145
+ body: File.new('spec/examples/invalid_token_error.json'),
146
+ status: 401
147
+ )
148
+ }
149
+
150
+ subject {
151
+ -> {
152
+ Beyonic.api_key = "my-authorization-token"
153
+ Beyonic::Payment.get(23)
154
+ }
155
+ }
156
+ it {
157
+ is_expected.to raise_error
158
+ }
159
+ end
160
+
161
+ context 'Unauthorized' do
162
+ before {
163
+ stub_request(:get, "https://staging.beyonic.com/api/payments/666").to_return(
164
+ body: File.new('spec/examples/no_permissions_error.json'),
165
+ status: 403
166
+ )
167
+ }
168
+
169
+ subject {
170
+ -> {
171
+ Beyonic.api_key = "my-authorization-token"
172
+ Beyonic::Payment.get(666)
173
+ }
174
+ }
175
+ it {
176
+ is_expected.to raise_error
177
+ }
178
+ end
179
+ end
180
+
181
+ describe "#save" do
182
+ context 'new object' do
183
+ subject { Beyonic::Payment }
184
+ let(:payload) {
185
+ {
186
+ phonenumber: "+256773712831",
187
+ amount: "100.2",
188
+ currency: "UGX",
189
+ description: "Per diem payment",
190
+ payment_type: "money",
191
+ callback_url: "https://my.website/payments/callback",
192
+ metadata: "{'id': '1234', 'name': 'Lucy'}"
193
+ }
194
+ }
195
+
196
+ before {
197
+ allow(subject).to receive(:create)
198
+ subject.new(payload).save
199
+ }
200
+
201
+ it {
202
+ is_expected.to have_received(:create).with(payload)
203
+ }
204
+ end
205
+
206
+ context 'loaded object' do
207
+ subject { Beyonic::Payment }
208
+ before {
209
+ stub_request(:get, "https://staging.beyonic.com/api/payments/23").to_return(
210
+ body: File.new('spec/examples/payments/get_response.json'))
211
+ allow(subject).to receive(:update)
212
+
213
+ temp = subject.get(23)
214
+ temp.description = "foo"
215
+ temp.save
216
+ }
217
+
218
+ it {
219
+ is_expected.to have_received(:update).with(23, hash_including(description: "foo"))
220
+ }
221
+ end
222
+ end
223
+ end