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
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'dry-validation'
|
4
|
+
|
5
|
+
module Fawry
|
6
|
+
module Contracts
|
7
|
+
class ListTokensRequestContract < Dry::Validation::Contract
|
8
|
+
params do
|
9
|
+
required(:merchant_code).value(:string)
|
10
|
+
required(:customer_profile_id).value(:string)
|
11
|
+
optional(:fawry_secure_key).value(:string)
|
12
|
+
end
|
13
|
+
|
14
|
+
rule(:fawry_secure_key) do
|
15
|
+
if ENV['FAWRY_SECURE_KEY'].nil? && value.nil?
|
16
|
+
key(:fawry_secure_key).failure('fawry secure key is required as a param or an env var')
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
rule(:merchant_code) do
|
21
|
+
if ENV['FAWRY_MERCHANT_CODE'].nil? && value.nil?
|
22
|
+
key(:merchant_code).failure('fawry merchant code is required as a param or an env var')
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'dry-validation'
|
4
|
+
|
5
|
+
module Fawry
|
6
|
+
module Contracts
|
7
|
+
class PaymentStatusRequestContract < Dry::Validation::Contract
|
8
|
+
params do
|
9
|
+
required(:merchant_ref_number).value(:string)
|
10
|
+
optional(:merchant_code).value(:string)
|
11
|
+
optional(:fawry_secure_key).value(:string)
|
12
|
+
end
|
13
|
+
|
14
|
+
rule(:fawry_secure_key) do
|
15
|
+
if ENV['FAWRY_SECURE_KEY'].nil? && value.nil?
|
16
|
+
key(:fawry_secure_key).failure('fawry secure key is required as a param or an env var')
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
rule(:merchant_code) do
|
21
|
+
if ENV['FAWRY_MERCHANT_CODE'].nil? && value.nil?
|
22
|
+
key(:merchant_code).failure('fawry merchant code is required as a param or an env var')
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -6,12 +6,24 @@ module Fawry
|
|
6
6
|
module Contracts
|
7
7
|
class RefundRequestContract < Dry::Validation::Contract
|
8
8
|
params do
|
9
|
-
required(:merchant_code).value(:string)
|
10
9
|
required(:reference_number).value(:string)
|
11
10
|
required(:refund_amount).value(:decimal)
|
12
|
-
|
11
|
+
optional(:merchant_code).value(:string)
|
12
|
+
optional(:fawry_secure_key).value(:string)
|
13
13
|
optional(:reason).value(:string)
|
14
14
|
end
|
15
|
+
|
16
|
+
rule(:fawry_secure_key) do
|
17
|
+
if ENV['FAWRY_SECURE_KEY'].nil? && value.nil?
|
18
|
+
key(:fawry_secure_key).failure('fawry secure key is required as a param or an env var')
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
rule(:merchant_code) do
|
23
|
+
if ENV['FAWRY_MERCHANT_CODE'].nil? && value.nil?
|
24
|
+
key(:merchant_code).failure('fawry merchant code is required as a param or an env var')
|
25
|
+
end
|
26
|
+
end
|
15
27
|
end
|
16
28
|
end
|
17
29
|
end
|
data/lib/fawry/errors.rb
CHANGED
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Fawry
|
4
|
+
class FawryCallback
|
5
|
+
include Utils
|
6
|
+
|
7
|
+
attr_reader :callback_params, :fawry_secure_key, :options
|
8
|
+
|
9
|
+
def initialize(callback_params, opts)
|
10
|
+
@callback_params = callback_params
|
11
|
+
@fawry_secure_key = ENV.fetch('FAWRY_SECURE_KEY')
|
12
|
+
@options = opts
|
13
|
+
end
|
14
|
+
|
15
|
+
def parse
|
16
|
+
verify_callback_signature!
|
17
|
+
build_callback
|
18
|
+
|
19
|
+
self
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
# Adds keys from fawry API response as methods
|
25
|
+
# on FawryCallback instance that return the value
|
26
|
+
# of each key
|
27
|
+
#
|
28
|
+
# type => type
|
29
|
+
# referenceNumber => reference_number
|
30
|
+
# merchantRefNumber => merchant_ref_number
|
31
|
+
# expirationTime => expiration_time
|
32
|
+
# statusCode => status_code
|
33
|
+
# statusDescription => status_description
|
34
|
+
#
|
35
|
+
# fawry_callback = FawryCallback.new(callback_params, fawry_secure_key)
|
36
|
+
# fawry_callback.order_status => PAID
|
37
|
+
# fawry_callback.fawry_ref_number => 1234567
|
38
|
+
def build_callback
|
39
|
+
enrich_object(callback_params)
|
40
|
+
end
|
41
|
+
|
42
|
+
def verify_callback_signature!
|
43
|
+
raise InvalidSignatureError, 'Invalid Signature' unless signature == callback_params[:messageSignature]
|
44
|
+
end
|
45
|
+
|
46
|
+
# rubocop:disable Metrics/AbcSize
|
47
|
+
def signature
|
48
|
+
Digest::SHA256.hexdigest("#{callback_params[:fawryRefNumber]}#{callback_params[:merchantRefNum]}"\
|
49
|
+
"#{format('%<paymentAmount>.2f', paymentAmount: callback_params[:paymentAmount])}"\
|
50
|
+
"#{format('%<orderAmount>.2f', orderAmount: callback_params[:orderAmount])}"\
|
51
|
+
"#{callback_params[:orderStatus]}#{callback_params[:paymentMethod]}"\
|
52
|
+
"#{callback_params[:paymentRefrenceNumber]}#{fawry_secure_key}")
|
53
|
+
end
|
54
|
+
# rubocop:enable Metrics/AbcSize
|
55
|
+
end
|
56
|
+
end
|
data/lib/fawry/fawry_request.rb
CHANGED
@@ -16,6 +16,8 @@ module Fawry
|
|
16
16
|
|
17
17
|
private
|
18
18
|
|
19
|
+
# rubocop:disable Metrics/MethodLength
|
20
|
+
# rubocop:disable Metrics/AbcSize
|
19
21
|
def build_request
|
20
22
|
case action
|
21
23
|
when 'charge'
|
@@ -26,7 +28,25 @@ module Fawry
|
|
26
28
|
self.class.include Requests::RefundRequest
|
27
29
|
validate_refund_params!
|
28
30
|
@request = build_refund_request
|
31
|
+
when 'payment_status'
|
32
|
+
self.class.include Requests::PaymentStatusRequest
|
33
|
+
validate_payment_status_params!
|
34
|
+
@request = build_payment_status_request
|
35
|
+
when 'create_card_token'
|
36
|
+
self.class.include Requests::CreateCardTokenRequest
|
37
|
+
validate_card_token_params!
|
38
|
+
@request = build_create_card_token_request
|
39
|
+
when 'list_tokens'
|
40
|
+
self.class.include Requests::ListTokensRequest
|
41
|
+
validate_list_tokens_params!
|
42
|
+
@request = build_list_tokens_request
|
43
|
+
when 'delete_token'
|
44
|
+
self.class.include Requests::DeleteTokenRequest
|
45
|
+
validate_delete_token_params!
|
46
|
+
@request = build_delete_token_request
|
29
47
|
end
|
48
|
+
# rubocop:enable Metrics/AbcSize
|
49
|
+
# rubocop:enable Metrics/MethodLength
|
30
50
|
end
|
31
51
|
end
|
32
52
|
end
|
data/lib/fawry/fawry_response.rb
CHANGED
@@ -2,6 +2,8 @@
|
|
2
2
|
|
3
3
|
module Fawry
|
4
4
|
class FawryResponse
|
5
|
+
include Utils
|
6
|
+
|
5
7
|
attr_reader :fawry_api_response
|
6
8
|
|
7
9
|
def initialize(fawry_api_response)
|
@@ -39,13 +41,7 @@ module Fawry
|
|
39
41
|
# fawry_res.status_code => 200
|
40
42
|
# fawry_res.reference_number => 1234567
|
41
43
|
def build_response
|
42
|
-
fawry_api_response
|
43
|
-
method_name = key.split(/(?=[A-Z])/).map(&:downcase).join('_') # statusCode => status_code
|
44
|
-
instance_variable_set("@#{method_name}", fawry_api_response[key])
|
45
|
-
method_body = proc { instance_variable_get("@#{method_name}") }
|
46
|
-
|
47
|
-
self.class.public_send(:define_method, method_name, method_body)
|
48
|
-
end
|
44
|
+
enrich_object(fawry_api_response)
|
49
45
|
end
|
50
46
|
end
|
51
47
|
end
|
@@ -7,7 +7,7 @@ module Fawry
|
|
7
7
|
module ChargeRequest
|
8
8
|
DEFAULTS = { payment_method: 'PAYATFAWRY', currency_code: 'EGP' }.freeze
|
9
9
|
|
10
|
-
def
|
10
|
+
def fire_charge_request
|
11
11
|
fawry_api_response = Connection.post(request[:path], request[:params], request[:body], request[:options])
|
12
12
|
response_body = JSON.parse(fawry_api_response.body)
|
13
13
|
|
@@ -18,7 +18,7 @@ module Fawry
|
|
18
18
|
|
19
19
|
def build_charge_request
|
20
20
|
{
|
21
|
-
path: 'charge',
|
21
|
+
path: 'payments/charge',
|
22
22
|
params: {},
|
23
23
|
body: charge_request_transformed_params,
|
24
24
|
options: options
|
@@ -29,9 +29,11 @@ module Fawry
|
|
29
29
|
@request_params ||= DEFAULTS.merge(params)
|
30
30
|
end
|
31
31
|
|
32
|
+
# rubocop:disable Metrics/AbcSize
|
33
|
+
# rubocop:disable Metrics/MethodLength
|
32
34
|
def charge_request_transformed_params
|
33
35
|
{
|
34
|
-
merchantCode:
|
36
|
+
merchantCode: fawry_merchant_code,
|
35
37
|
merchantRefNum: request_params[:merchant_ref_num],
|
36
38
|
customerProfileId: request_params[:customer_profile_id],
|
37
39
|
cardToken: request_params[:card_token],
|
@@ -46,10 +48,20 @@ module Fawry
|
|
46
48
|
signature: charge_request_signature
|
47
49
|
}.compact
|
48
50
|
end
|
51
|
+
# rubocop:enable Metrics/MethodLength
|
52
|
+
# rubocop:enable Metrics/AbcSize
|
53
|
+
|
54
|
+
def fawry_merchant_code
|
55
|
+
ENV.fetch('FAWRY_MERCHANT_CODE') { request_params[:merchant_code] }
|
56
|
+
end
|
57
|
+
|
58
|
+
def fawry_secure_key
|
59
|
+
ENV.fetch('FAWRY_SECURE_KEY') { request_params[:fawry_secure_key] }
|
60
|
+
end
|
49
61
|
|
50
62
|
def validate_charge_params!
|
51
63
|
contract = Contracts::ChargeRequestContract.new.call(request_params)
|
52
|
-
raise
|
64
|
+
raise InvalidFawryRequestError, contract.errors.to_h if contract.failure?
|
53
65
|
end
|
54
66
|
|
55
67
|
def charge_items
|
@@ -57,10 +69,10 @@ module Fawry
|
|
57
69
|
end
|
58
70
|
|
59
71
|
def charge_request_signature
|
60
|
-
Digest::SHA256.hexdigest("#{
|
72
|
+
Digest::SHA256.hexdigest("#{fawry_merchant_code}#{request_params[:merchant_ref_num]}"\
|
61
73
|
"#{request_params[:customer_profile_id]}#{request_params[:payment_method]}"\
|
62
74
|
"#{format('%<amount>.2f', amount: request_params[:amount])}"\
|
63
|
-
"#{request_params[:card_token]}#{
|
75
|
+
"#{request_params[:card_token]}#{fawry_secure_key}")
|
64
76
|
end
|
65
77
|
end
|
66
78
|
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'digest'
|
4
|
+
|
5
|
+
module Fawry
|
6
|
+
module Requests
|
7
|
+
module CreateCardTokenRequest
|
8
|
+
def fire_create_card_token_request
|
9
|
+
fawry_api_response = Connection.post(request[:path], request[:params], request[:body], request[:options])
|
10
|
+
response_body = JSON.parse(fawry_api_response.body)
|
11
|
+
|
12
|
+
FawryResponse.new(response_body)
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def build_create_card_token_request
|
18
|
+
{
|
19
|
+
path: 'cards/cardToken',
|
20
|
+
params: {},
|
21
|
+
body: create_card_token_request_transformed_params,
|
22
|
+
options: options
|
23
|
+
}
|
24
|
+
end
|
25
|
+
|
26
|
+
def create_card_token_request
|
27
|
+
@create_card_token_request ||= params
|
28
|
+
end
|
29
|
+
|
30
|
+
def create_card_token_request_transformed_params
|
31
|
+
{
|
32
|
+
merchantCode: fawry_merchant_code,
|
33
|
+
customerProfileId: create_card_token_request[:customer_profile_id],
|
34
|
+
customerMobile: create_card_token_request[:customer_mobile],
|
35
|
+
customerEmail: create_card_token_request[:customer_email],
|
36
|
+
cardNumber: create_card_token_request[:card_number],
|
37
|
+
expiryYear: create_card_token_request[:expiry_year],
|
38
|
+
expiryMonth: create_card_token_request[:expiry_month],
|
39
|
+
cvv: create_card_token_request[:cvv]
|
40
|
+
}.compact
|
41
|
+
end
|
42
|
+
|
43
|
+
def fawry_merchant_code
|
44
|
+
ENV.fetch('FAWRY_MERCHANT_CODE') { create_card_token_request[:merchant_code] }
|
45
|
+
end
|
46
|
+
|
47
|
+
def fawry_secure_key
|
48
|
+
ENV.fetch('FAWRY_SECURE_KEY') { create_card_token_request[:fawry_secure_key] }
|
49
|
+
end
|
50
|
+
|
51
|
+
def validate_card_token_params!
|
52
|
+
contract = Contracts::CreateCardTokenRequestContract.new.call(create_card_token_request)
|
53
|
+
raise InvalidFawryRequestError, contract.errors.to_h if contract.failure?
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'digest'
|
4
|
+
|
5
|
+
module Fawry
|
6
|
+
module Requests
|
7
|
+
module DeleteTokenRequest
|
8
|
+
def fire_delete_token_request
|
9
|
+
fawry_api_response = Connection.delete(request[:path], request[:params], request[:body], request[:options])
|
10
|
+
response_body = JSON.parse(fawry_api_response.body)
|
11
|
+
|
12
|
+
FawryResponse.new(response_body)
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def build_delete_token_request
|
18
|
+
{
|
19
|
+
path: 'cards/cardToken',
|
20
|
+
params: {},
|
21
|
+
body: delete_token_request_transformed_params,
|
22
|
+
options: options
|
23
|
+
}
|
24
|
+
end
|
25
|
+
|
26
|
+
def request_params
|
27
|
+
@request_params = params
|
28
|
+
end
|
29
|
+
|
30
|
+
def delete_token_request_transformed_params
|
31
|
+
{
|
32
|
+
merchantCode: fawry_merchant_code,
|
33
|
+
customerProfileId: request_params[:customer_profile_id],
|
34
|
+
signature: delete_token_request_signature,
|
35
|
+
cardToken: request_params[:card_token]
|
36
|
+
}.compact
|
37
|
+
end
|
38
|
+
|
39
|
+
def fawry_merchant_code
|
40
|
+
ENV.fetch('FAWRY_MERCHANT_CODE') { request_params[:merchant_code] }
|
41
|
+
end
|
42
|
+
|
43
|
+
def fawry_secure_key
|
44
|
+
ENV.fetch('FAWRY_SECURE_KEY') { request_params[:fawry_secure_key] }
|
45
|
+
end
|
46
|
+
|
47
|
+
def card_token
|
48
|
+
request_params[:card_token]
|
49
|
+
end
|
50
|
+
|
51
|
+
def validate_delete_token_params!
|
52
|
+
contract = Contracts::ListTokensRequestContract.new.call(request_params)
|
53
|
+
raise InvalidFawryRequestError, contract.errors.to_h if contract.failure?
|
54
|
+
end
|
55
|
+
|
56
|
+
def delete_token_request_signature
|
57
|
+
Digest::SHA256.hexdigest("#{fawry_merchant_code}#{request_params[:customer_profile_id]}"\
|
58
|
+
"#{card_token}#{fawry_secure_key}")
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'digest'
|
4
|
+
|
5
|
+
module Fawry
|
6
|
+
module Requests
|
7
|
+
module ListTokensRequest
|
8
|
+
def fire_list_tokens_request
|
9
|
+
fawry_api_response = Connection.get(request[:path], request[:params], request[:body], request[:options])
|
10
|
+
response_body = JSON.parse(fawry_api_response.body)
|
11
|
+
|
12
|
+
FawryResponse.new(response_body)
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def build_list_tokens_request
|
18
|
+
{
|
19
|
+
path: 'cards/cardToken',
|
20
|
+
params: list_tokens_request_transformed_params,
|
21
|
+
body: {},
|
22
|
+
options: options
|
23
|
+
}
|
24
|
+
end
|
25
|
+
|
26
|
+
def request_params
|
27
|
+
@request_params = params
|
28
|
+
end
|
29
|
+
|
30
|
+
def list_tokens_request_transformed_params
|
31
|
+
{
|
32
|
+
merchantCode: fawry_merchant_code,
|
33
|
+
customerProfileId: request_params[:customer_profile_id],
|
34
|
+
signature: list_tokens_request_signature
|
35
|
+
}.compact
|
36
|
+
end
|
37
|
+
|
38
|
+
def fawry_merchant_code
|
39
|
+
ENV.fetch('FAWRY_MERCHANT_CODE') { request_params[:merchant_code] }
|
40
|
+
end
|
41
|
+
|
42
|
+
def fawry_secure_key
|
43
|
+
ENV.fetch('FAWRY_SECURE_KEY') { request_params[:fawry_secure_key] }
|
44
|
+
end
|
45
|
+
|
46
|
+
def validate_list_tokens_params!
|
47
|
+
contract = Contracts::ListTokensRequestContract.new.call(request_params)
|
48
|
+
raise InvalidFawryRequestError, contract.errors.to_h if contract.failure?
|
49
|
+
end
|
50
|
+
|
51
|
+
def list_tokens_request_signature
|
52
|
+
Digest::SHA256.hexdigest("#{fawry_merchant_code}#{request_params[:customer_profile_id]}"\
|
53
|
+
"#{fawry_secure_key}")
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|