fawry 0.1.0 → 1.2.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.
- checksums.yaml +4 -4
- data/.circleci/config.yml +57 -0
- data/.github/workflows/ruby.yml +35 -0
- data/.rubocop.yml +5 -2
- data/Gemfile.lock +48 -24
- data/README-ar.md +186 -0
- data/README.md +121 -13
- data/Rakefile +18 -1
- data/fawry.gemspec +8 -5
- data/lib/fawry.rb +150 -8
- data/lib/fawry/connection.rb +41 -4
- data/lib/fawry/contracts/charge_request_contract.rb +14 -2
- data/lib/fawry/contracts/create_card_token_request_contract.rb +32 -0
- data/lib/fawry/contracts/delete_token_request_contract.rb +28 -0
- data/lib/fawry/contracts/list_tokens_request_contract.rb +27 -0
- data/lib/fawry/contracts/payment_status_request_contract.rb +27 -0
- data/lib/fawry/contracts/refund_request_contract.rb +14 -2
- data/lib/fawry/errors.rb +3 -1
- data/lib/fawry/fawry_callback.rb +56 -0
- data/lib/fawry/fawry_request.rb +20 -0
- data/lib/fawry/fawry_response.rb +3 -7
- data/lib/fawry/requests/charge_request.rb +18 -6
- data/lib/fawry/requests/create_card_token_request.rb +57 -0
- data/lib/fawry/requests/delete_token_request.rb +62 -0
- data/lib/fawry/requests/list_tokens_request.rb +57 -0
- data/lib/fawry/requests/payment_status_request.rb +57 -0
- data/lib/fawry/requests/refund_request.rb +14 -6
- data/lib/fawry/utils.rb +29 -0
- data/lib/fawry/version.rb +1 -1
- metadata +54 -26
- data/.travis.yml +0 -7
data/Rakefile
CHANGED
@@ -2,7 +2,24 @@
|
|
2
2
|
|
3
3
|
require 'bundler/gem_tasks'
|
4
4
|
require 'rspec/core/rake_task'
|
5
|
+
require 'rubocop/rake_task'
|
5
6
|
|
6
7
|
RSpec::Core::RakeTask.new(:spec)
|
8
|
+
RuboCop::RakeTask.new(:rubocop)
|
7
9
|
|
8
|
-
task default: :
|
10
|
+
task default: :rspec_rubocop
|
11
|
+
|
12
|
+
task :rspec_rubocop do
|
13
|
+
Rake::Task['rubocop'].invoke
|
14
|
+
Rake::Task['spec'].invoke
|
15
|
+
end
|
16
|
+
|
17
|
+
desc 'Start a console session with Fawry gem loaded'
|
18
|
+
task :console do
|
19
|
+
require 'irb'
|
20
|
+
require 'irb/completion'
|
21
|
+
require 'fawry'
|
22
|
+
|
23
|
+
ARGV.clear
|
24
|
+
IRB.start
|
25
|
+
end
|
data/fawry.gemspec
CHANGED
@@ -7,13 +7,14 @@ require 'fawry/version'
|
|
7
7
|
Gem::Specification.new do |spec|
|
8
8
|
spec.name = 'fawry'
|
9
9
|
spec.version = Fawry::VERSION
|
10
|
-
spec.authors = ['Amr Bakry']
|
11
|
-
spec.email = ['
|
10
|
+
spec.authors = ['Amr El Bakry']
|
11
|
+
spec.email = ['amrr@hey.com']
|
12
12
|
|
13
13
|
spec.summary = "A library to interface with Fawry's payment gateway API (charge, refund, payment status,
|
14
14
|
service callback)."
|
15
15
|
spec.homepage = 'https://github.com/amrrbakry/fawry'
|
16
16
|
spec.license = 'MIT'
|
17
|
+
spec.required_ruby_version = '>= 2.6'
|
17
18
|
|
18
19
|
spec.metadata['homepage_uri'] = 'https://github.com/amrrbakry/fawry'
|
19
20
|
spec.metadata['source_code_uri'] = 'https://github.com/amrrbakry/fawry'
|
@@ -31,8 +32,10 @@ Gem::Specification.new do |spec|
|
|
31
32
|
spec.add_dependency 'faraday', '~> 0.17.0'
|
32
33
|
|
33
34
|
spec.add_development_dependency 'bundler', '~> 2.0'
|
34
|
-
spec.add_development_dependency 'byebug', '~> 11.0'
|
35
|
-
spec.add_development_dependency 'rake', '~>
|
35
|
+
spec.add_development_dependency 'byebug', '~> 11.0'
|
36
|
+
spec.add_development_dependency 'rake', '~> 13.0'
|
36
37
|
spec.add_development_dependency 'rspec', '~> 3.0'
|
37
|
-
spec.add_development_dependency '
|
38
|
+
spec.add_development_dependency 'rspec_junit_formatter'
|
39
|
+
spec.add_development_dependency 'rubocop', '~> 1.11'
|
40
|
+
spec.add_development_dependency 'webmock', '~> 3.12'
|
38
41
|
end
|
data/lib/fawry.rb
CHANGED
@@ -3,12 +3,22 @@
|
|
3
3
|
require 'fawry/version'
|
4
4
|
require 'fawry/connection'
|
5
5
|
require 'fawry/errors'
|
6
|
+
require 'fawry/utils'
|
6
7
|
require 'fawry/fawry_request'
|
7
8
|
require 'fawry/fawry_response'
|
9
|
+
require 'fawry/fawry_callback'
|
8
10
|
require 'fawry/requests/charge_request'
|
9
11
|
require 'fawry/requests/refund_request'
|
12
|
+
require 'fawry/requests/payment_status_request'
|
13
|
+
require 'fawry/requests/create_card_token_request'
|
14
|
+
require 'fawry/requests/list_tokens_request'
|
15
|
+
require 'fawry/requests/delete_token_request'
|
10
16
|
require 'fawry/contracts/charge_request_contract'
|
11
17
|
require 'fawry/contracts/refund_request_contract'
|
18
|
+
require 'fawry/contracts/payment_status_request_contract'
|
19
|
+
require 'fawry/contracts/create_card_token_request_contract'
|
20
|
+
require 'fawry/contracts/list_tokens_request_contract'
|
21
|
+
require 'fawry/contracts/delete_token_request_contract'
|
12
22
|
|
13
23
|
module Fawry
|
14
24
|
class << self
|
@@ -17,19 +27,19 @@ module Fawry
|
|
17
27
|
# the request signature
|
18
28
|
#
|
19
29
|
# @param params [Hash] list of params to send to fawry
|
20
|
-
# required(:merchant_code).value(:string)
|
21
30
|
# required(:merchant_ref_num).value(:string)
|
22
31
|
# required(:customer_profile_id).value(:string)
|
23
32
|
# required(:amount).value(:decimal)
|
24
33
|
# required(:description).value(:string)
|
25
34
|
# required(:customer_mobile).value(:string)
|
26
|
-
# required(:fawry_secure_key).value(:string)
|
27
35
|
# required(:charge_items).array(:hash) do
|
28
36
|
# required(:item_id).value(:string)
|
29
37
|
# required(:description).value(:string)
|
30
38
|
# required(:price).value(:decimal)
|
31
39
|
# required(:quantity).value(:integer)
|
32
40
|
# end
|
41
|
+
# optional(:merchant_code).value(:string)
|
42
|
+
# optional(:fawry_secure_key).value(:string)
|
33
43
|
# optional(:currency_code).value(:string)
|
34
44
|
# optional(:card_token).value(:string)
|
35
45
|
# optional(:customer_email).value(:string)
|
@@ -43,7 +53,7 @@ module Fawry
|
|
43
53
|
# false by default
|
44
54
|
# Example: `Fawry.charge(params, sandbox: true)`
|
45
55
|
#
|
46
|
-
# @raise [Fawry::
|
56
|
+
# @raise [Fawry::InvalidFawryRequestError] raised when one
|
47
57
|
# or more of the params are invalid. the message
|
48
58
|
# specifices which params and why are they invalid
|
49
59
|
#
|
@@ -51,7 +61,7 @@ module Fawry
|
|
51
61
|
# has Fawry API response keys as instance methods
|
52
62
|
# plus some convenience methods e.g. success?
|
53
63
|
def charge(params, opts = {})
|
54
|
-
FawryRequest.new('charge', params, opts).
|
64
|
+
FawryRequest.new('charge', params, opts).fire_charge_request
|
55
65
|
end
|
56
66
|
|
57
67
|
# Sends a refund request to Fawry API
|
@@ -59,10 +69,10 @@ module Fawry
|
|
59
69
|
# the request signature
|
60
70
|
#
|
61
71
|
# @param params [Hash] list of params to send to fawry
|
62
|
-
# required(:merchant_code).value(:string)
|
63
72
|
# required(:reference_number).value(:string)
|
64
73
|
# required(:refund_amount).value(:decimal)
|
65
|
-
#
|
74
|
+
# optional(:merchant_code).value(:string)
|
75
|
+
# optional(:fawry_secure_key).value(:string)
|
66
76
|
# optional(:reason).value(:string)
|
67
77
|
#
|
68
78
|
# @param opts [Hash] list of options to
|
@@ -71,7 +81,7 @@ module Fawry
|
|
71
81
|
# send the request to fawry sandbox env or not
|
72
82
|
# false by default
|
73
83
|
#
|
74
|
-
# @raise [Fawry::
|
84
|
+
# @raise [Fawry::InvalidFawryRequestError] raised when one
|
75
85
|
# or more of the params are invalid. the message
|
76
86
|
# specifices which params and why are they invalid
|
77
87
|
#
|
@@ -79,7 +89,139 @@ module Fawry
|
|
79
89
|
# has Fawry API response keys as instance methods
|
80
90
|
# plus some convenience methods e.g. success?
|
81
91
|
def refund(params, opts = {})
|
82
|
-
FawryRequest.new('refund', params, opts).
|
92
|
+
FawryRequest.new('refund', params, opts).fire_refund_request
|
93
|
+
end
|
94
|
+
|
95
|
+
# Sends a payment status request to Fawry API
|
96
|
+
# performs param validation and builds
|
97
|
+
# the request signature
|
98
|
+
#
|
99
|
+
# @param params [Hash] list of params to send to fawry
|
100
|
+
# required(:merchant_ref_number).value(:string)
|
101
|
+
# optional(:merchant_code).value(:string)
|
102
|
+
# optional(:fawry_secure_key).value(:string)
|
103
|
+
#
|
104
|
+
# @param opts [Hash] list of options to
|
105
|
+
# configure the request
|
106
|
+
# @option opts :sandbox [Boolean] whether to
|
107
|
+
# send the request to fawry sandbox env or not
|
108
|
+
# false by default
|
109
|
+
#
|
110
|
+
# @raise [Fawry::InvalidFawryRequestError] raised when one
|
111
|
+
# or more of the params are invalid. the message
|
112
|
+
# specifices which params and why are they invalid
|
113
|
+
#
|
114
|
+
# @return [Fawry::FawryResponse] an object that
|
115
|
+
# has Fawry API response keys as instance methods
|
116
|
+
# plus some convenience methods e.g. success?
|
117
|
+
def payment_status(params, opts = {})
|
118
|
+
FawryRequest.new('payment_status', params, opts).fire_payment_status_request
|
119
|
+
end
|
120
|
+
|
121
|
+
# Sends a card token request to Fawry API
|
122
|
+
# performs param validation and builds
|
123
|
+
# the request signature
|
124
|
+
#
|
125
|
+
# @param params [Hash] list of params to send to fawry
|
126
|
+
# required(:customer_profile_id).value(:string)
|
127
|
+
# required(:customer_mobile).value(:string)
|
128
|
+
# required(:merchant_code).value(:string)
|
129
|
+
# required(:customer_email).value(:string)
|
130
|
+
# required(:card_number).value(:string)
|
131
|
+
# required(:expiry_year).value(:string)
|
132
|
+
# required(:expiry_month).value(:string)
|
133
|
+
# required(:cvv).value(:string)
|
134
|
+
#
|
135
|
+
# @param opts [Hash] list of options to
|
136
|
+
# configure the request
|
137
|
+
# @option opts :sandbox [Boolean] whether to
|
138
|
+
# send the request to fawry sandbox env or not
|
139
|
+
# false by default
|
140
|
+
#
|
141
|
+
# @raise [Fawry::InvalidFawryRequestError] raised when one
|
142
|
+
# or more of the params are invalid. the message
|
143
|
+
# specifices which params and why are they invalid
|
144
|
+
#
|
145
|
+
# @return [Fawry::FawryResponse] an object that
|
146
|
+
# has Fawry API response keys as instance methods
|
147
|
+
# plus some convenience methods e.g. success?
|
148
|
+
|
149
|
+
def create_card_token(params, opts = {})
|
150
|
+
FawryRequest.new('create_card_token', params, opts).fire_create_card_token_request
|
151
|
+
end
|
152
|
+
|
153
|
+
# Sends a list tokens request to Fawry API
|
154
|
+
# performs param validation and builds
|
155
|
+
# the request signature
|
156
|
+
#
|
157
|
+
# @param params [Hash] list of params to send to fawry
|
158
|
+
# required(:merchant_code).value(:string)
|
159
|
+
# required(:customer_profile_id).value(:string)
|
160
|
+
# optional(:fawry_secure_key).value(:string)
|
161
|
+
#
|
162
|
+
# @param opts [Hash] list of options to
|
163
|
+
# configure the request
|
164
|
+
# @option opts :sandbox [Boolean] whether to
|
165
|
+
# send the request to fawry sandbox env or not
|
166
|
+
# false by default
|
167
|
+
#
|
168
|
+
# @raise [Fawry::InvalidFawryRequestError] raised when one
|
169
|
+
# or more of the params are invalid. the message
|
170
|
+
# specifices which params and why are they invalid
|
171
|
+
#
|
172
|
+
# @return [Fawry::FawryResponse] an object that
|
173
|
+
# has Fawry API response keys as instance methods
|
174
|
+
# plus some convenience methods e.g. success?
|
175
|
+
|
176
|
+
def list_tokens(params, opts = {})
|
177
|
+
FawryRequest.new('list_tokens', params, opts).fire_list_tokens_request
|
178
|
+
end
|
179
|
+
|
180
|
+
# Sends delete token request to Fawry API
|
181
|
+
# performs param validation and builds
|
182
|
+
# the request signature
|
183
|
+
#
|
184
|
+
# @param params [Hash] list of params to send to fawry
|
185
|
+
# required(:customer_profile_id).value(:string)
|
186
|
+
# optional(:merchant_code).value(:string)
|
187
|
+
# optional(:fawry_secure_key).value(:string)
|
188
|
+
#
|
189
|
+
# @param opts [Hash] list of options to
|
190
|
+
# configure the request
|
191
|
+
# @option opts :sandbox [Boolean] whether to
|
192
|
+
# send the request to fawry sandbox env or not
|
193
|
+
# false by default
|
194
|
+
#
|
195
|
+
# @raise [Fawry::InvalidFawryRequestError] raised when one
|
196
|
+
# or more of the params are invalid. the message
|
197
|
+
# specifices which params and why are they invalid
|
198
|
+
#
|
199
|
+
# @return [Fawry::FawryResponse] an object that
|
200
|
+
# has Fawry API response keys as instance methods
|
201
|
+
# plus some convenience methods e.g. success?
|
202
|
+
|
203
|
+
def delete_token(params, opts = {})
|
204
|
+
FawryRequest.new('delete_token', params, opts).fire_delete_token_request
|
205
|
+
end
|
206
|
+
|
207
|
+
# Parses Fawry callback v2 into
|
208
|
+
# FawryCallback object with callback
|
209
|
+
# params as instance methods
|
210
|
+
#
|
211
|
+
# @param params [Hash] list of params sent
|
212
|
+
# from fawry server callback
|
213
|
+
#
|
214
|
+
# @param opts [Hash] list of options to
|
215
|
+
# configure the request. currently no
|
216
|
+
# options available
|
217
|
+
#
|
218
|
+
# @raise [Fawry::InvalidSignatureError] raised when
|
219
|
+
# request signature cannot be verified
|
220
|
+
#
|
221
|
+
# @return [Fawry::FawryCallback] an object that
|
222
|
+
# has Fawry server callback params' keys as instance methods
|
223
|
+
def parse_callback(params, opts = {})
|
224
|
+
FawryCallback.new(params, opts).parse
|
83
225
|
end
|
84
226
|
end
|
85
227
|
end
|
data/lib/fawry/connection.rb
CHANGED
@@ -5,9 +5,9 @@ require 'json'
|
|
5
5
|
|
6
6
|
module Fawry
|
7
7
|
class Connection
|
8
|
-
FAWRY_BASE_URL = 'https://www.atfawry.com/ECommerceWeb/Fawry/
|
8
|
+
FAWRY_BASE_URL = 'https://www.atfawry.com/ECommerceWeb/Fawry/'
|
9
9
|
|
10
|
-
FAWRY_SANDBOX_BASE_URL = 'https://atfawry.fawrystaging.com//ECommerceWeb/Fawry/
|
10
|
+
FAWRY_SANDBOX_BASE_URL = 'https://atfawry.fawrystaging.com//ECommerceWeb/Fawry/'
|
11
11
|
|
12
12
|
class << self
|
13
13
|
def post(path, params, body, options)
|
@@ -19,16 +19,53 @@ module Fawry
|
|
19
19
|
end
|
20
20
|
end
|
21
21
|
|
22
|
+
def get(path, params, body, options)
|
23
|
+
conn = options[:sandbox] ? sandbox_connection : connection
|
24
|
+
|
25
|
+
conn.get(path) do |request|
|
26
|
+
request.params = params
|
27
|
+
request.body = body.to_json
|
28
|
+
# Fawry doesn't understand encoded params
|
29
|
+
request.options = request.options.merge(params_encoder: ParamsSpecialEncoder)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def delete(path, params, body, options)
|
34
|
+
conn = options[:sandbox] ? sandbox_connection : connection
|
35
|
+
|
36
|
+
conn.delete(path) do |request|
|
37
|
+
request.params = params
|
38
|
+
request.body = body.to_json
|
39
|
+
# Fawry doesn't understand encoded params
|
40
|
+
request.options = request.options.merge(params_encoder: ParamsSpecialEncoder)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
22
44
|
private
|
23
45
|
|
24
46
|
def connection
|
25
47
|
@connection ||= Faraday.new(url: FAWRY_BASE_URL, headers: { 'Content-Type': 'application/json',
|
26
|
-
|
48
|
+
Accept: 'application/json' })
|
27
49
|
end
|
28
50
|
|
29
51
|
def sandbox_connection
|
30
52
|
@sandbox_connection ||= Faraday.new(url: FAWRY_SANDBOX_BASE_URL, headers: { 'Content-Type': 'application/json',
|
31
|
-
|
53
|
+
Accept: 'application/json' })
|
54
|
+
end
|
55
|
+
|
56
|
+
# Fawry does not understand encoded params
|
57
|
+
# so we use this encoder to convert the params
|
58
|
+
# hash to a string of query params without encoding
|
59
|
+
# { a: 1, b: 2 } => a=1&b=2
|
60
|
+
class ParamsSpecialEncoder
|
61
|
+
def self.encode(hash)
|
62
|
+
hash.each_with_object([]) { |(k, v), arr| arr << "#{k}=#{v}" }.join('&')
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.decode(string)
|
66
|
+
arr = string.split('&')
|
67
|
+
arr.map { |str| str.split('=') }.to_h
|
68
|
+
end
|
32
69
|
end
|
33
70
|
end
|
34
71
|
end
|
@@ -6,19 +6,19 @@ module Fawry
|
|
6
6
|
module Contracts
|
7
7
|
class ChargeRequestContract < Dry::Validation::Contract
|
8
8
|
params do
|
9
|
-
required(:merchant_code).value(:string)
|
10
9
|
required(:merchant_ref_num).value(:string)
|
11
10
|
required(:customer_profile_id).value(:string)
|
12
11
|
required(:amount).value(:decimal)
|
13
12
|
required(:description).value(:string)
|
14
13
|
required(:customer_mobile).value(:string)
|
15
|
-
required(:fawry_secure_key).value(:string)
|
16
14
|
required(:charge_items).array(:hash) do
|
17
15
|
required(:item_id).value(:string)
|
18
16
|
required(:description).value(:string)
|
19
17
|
required(:price).value(:decimal)
|
20
18
|
required(:quantity).value(:integer)
|
21
19
|
end
|
20
|
+
optional(:merchant_code).value(:string)
|
21
|
+
optional(:fawry_secure_key).value(:string)
|
22
22
|
optional(:currency_code).value(:string)
|
23
23
|
optional(:card_token).value(:string)
|
24
24
|
optional(:customer_email).value(:string)
|
@@ -37,6 +37,18 @@ module Fawry
|
|
37
37
|
key.failure('card_token is required when payment_method is CARD')
|
38
38
|
end
|
39
39
|
end
|
40
|
+
|
41
|
+
rule(:fawry_secure_key) do
|
42
|
+
if ENV['FAWRY_SECURE_KEY'].nil? && value.nil?
|
43
|
+
key(:fawry_secure_key).failure('fawry secure key is required as a param or an env var')
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
rule(:merchant_code) do
|
48
|
+
if ENV['FAWRY_MERCHANT_CODE'].nil? && value.nil?
|
49
|
+
key(:merchant_code).failure('fawry merchant code is required as a param or an env var')
|
50
|
+
end
|
51
|
+
end
|
40
52
|
end
|
41
53
|
end
|
42
54
|
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'dry-validation'
|
4
|
+
|
5
|
+
module Fawry
|
6
|
+
module Contracts
|
7
|
+
class CreateCardTokenRequestContract < Dry::Validation::Contract
|
8
|
+
params do
|
9
|
+
required(:customer_profile_id).value(:string)
|
10
|
+
required(:customer_mobile).value(:string)
|
11
|
+
required(:merchant_code).value(:string)
|
12
|
+
required(:customer_email).value(:string)
|
13
|
+
required(:card_number).value(:string)
|
14
|
+
required(:expiry_year).value(:string)
|
15
|
+
required(:expiry_month).value(:string)
|
16
|
+
required(:cvv).value(:string)
|
17
|
+
end
|
18
|
+
|
19
|
+
rule(:customer_email) do
|
20
|
+
unless /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i.match?(value)
|
21
|
+
key? && key.failure('has invalid format')
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
rule(:merchant_code) do
|
26
|
+
if ENV['FAWRY_MERCHANT_CODE'].nil? && value.nil?
|
27
|
+
key(:merchant_code).failure('fawry merchant code is required as a param or an env var')
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'dry-validation'
|
4
|
+
|
5
|
+
module Fawry
|
6
|
+
module Contracts
|
7
|
+
class DeleteTokenRequestContract < Dry::Validation::Contract
|
8
|
+
params do
|
9
|
+
required(:merchant_code).value(:string)
|
10
|
+
required(:customer_profile_id).value(:string)
|
11
|
+
required(:fawry_secure_key).value(:string)
|
12
|
+
required(:card_token).value(:string)
|
13
|
+
end
|
14
|
+
|
15
|
+
rule(:fawry_secure_key) do
|
16
|
+
if ENV['FAWRY_SECURE_KEY'].nil? && value.nil?
|
17
|
+
key(:fawry_secure_key).failure('fawry secure key is required as a param or an env var')
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
rule(:merchant_code) do
|
22
|
+
if ENV['FAWRY_MERCHANT_CODE'].nil? && value.nil?
|
23
|
+
key(:merchant_code).failure('fawry merchant code is required as a param or an env var')
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|