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.
@@ -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
- required(:fawry_secure_key).value(:string)
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
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Fawry
4
- class InvalidFawryRequest < StandardError; end
4
+ class InvalidFawryRequestError < StandardError; end
5
+
6
+ class InvalidSignatureError < StandardError; end
5
7
  end
@@ -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
@@ -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
@@ -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.keys.each do |key|
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 fire
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: request_params[:merchant_code],
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 InvalidFawryRequest, contract.errors.to_h if contract.failure?
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("#{request_params[:merchant_code]}#{request_params[:merchant_ref_num]}"\
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]}#{request_params[:fawry_secure_key]}")
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