rma-payment-gateway 1.0.1 → 1.0.3
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/lib/rma/payment/gateway/account_inquiry.rb +4 -11
- data/lib/rma/payment/gateway/authorization.rb +4 -11
- data/lib/rma/payment/gateway/client.rb +50 -20
- data/lib/rma/payment/gateway/debit_request.rb +4 -11
- data/lib/rma/payment/gateway/utils.rb +2 -2
- data/lib/rma/payment/gateway/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7e2093f132f0620f9d9455eae15c1d2b4b2a7eb31fc4cfab608fe0244683c50b
|
|
4
|
+
data.tar.gz: e62e9cef18f17683d4eb208f56dc09ca8d3991fa7ce7471f73dc93672d6854b0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: '0699e0a7456ca06ebac7a52e187b58aab1cab83ebf0cf835c10d78aa6d42891fba070094ade6ddc3d46b91d3780dd4a50783ae77fa8b83e250250a90bd3fb956'
|
|
7
|
+
data.tar.gz: '088a8cc8091ddbbde76bf65ea34ed473e6f51a4f4a64aedbb7d0b26801a70f20a21e5cc74c24c2091d4aad87bf297e4469a0cf231838435ef3c57d20463e6f42'
|
|
@@ -33,7 +33,7 @@ module Rma
|
|
|
33
33
|
|
|
34
34
|
validate_account_inquiry_response!(response)
|
|
35
35
|
|
|
36
|
-
response
|
|
36
|
+
response
|
|
37
37
|
rescue StandardError => e
|
|
38
38
|
raise AuthenticationError, "Failed to fetch account inquiry: #{e.message}"
|
|
39
39
|
end
|
|
@@ -41,27 +41,20 @@ module Rma
|
|
|
41
41
|
private
|
|
42
42
|
|
|
43
43
|
def account_inquiry_request_body
|
|
44
|
-
|
|
44
|
+
{
|
|
45
45
|
bfs_bfsTxnId: transaction_id,
|
|
46
46
|
bfs_remitterBankId: bank_id,
|
|
47
47
|
bfs_remitterAccNo: account_no,
|
|
48
48
|
bfs_benfId: client.config.beneficiary_id,
|
|
49
49
|
bfs_msgType: "AE"
|
|
50
50
|
}
|
|
51
|
-
|
|
52
|
-
# Convert to URL-encoded format
|
|
53
|
-
URI.encode_www_form(params)
|
|
54
51
|
end
|
|
55
52
|
|
|
56
53
|
def validate_account_inquiry_response!(response)
|
|
57
|
-
unless response.is_a?(Hash) && response["
|
|
58
|
-
error_detail = response["
|
|
54
|
+
unless response.is_a?(Hash) && response["bfs_responseCode"] == "00"
|
|
55
|
+
error_detail = response["bfs_responseDesc"] || "Unknown error"
|
|
59
56
|
raise AuthenticationError, "Account inquiry failed: #{error_detail}"
|
|
60
57
|
end
|
|
61
|
-
|
|
62
|
-
return if response["result"]
|
|
63
|
-
|
|
64
|
-
raise AuthenticationError, "No response data in response"
|
|
65
58
|
end
|
|
66
59
|
end
|
|
67
60
|
end
|
|
@@ -32,7 +32,7 @@ module Rma
|
|
|
32
32
|
|
|
33
33
|
validate_authorization_response!(response)
|
|
34
34
|
|
|
35
|
-
response
|
|
35
|
+
response
|
|
36
36
|
rescue StandardError => e
|
|
37
37
|
raise AuthenticationError, "Failed to authorize: #{e.message}"
|
|
38
38
|
end
|
|
@@ -40,7 +40,7 @@ module Rma
|
|
|
40
40
|
private
|
|
41
41
|
|
|
42
42
|
def authorization_request_body
|
|
43
|
-
|
|
43
|
+
{
|
|
44
44
|
bfs_benfTxnTime: Utils.generate_timestamp,
|
|
45
45
|
bfs_orderNo: order_no,
|
|
46
46
|
bfs_benfBankCode: "01",
|
|
@@ -52,9 +52,6 @@ module Rma
|
|
|
52
52
|
bfs_msgType: "AR",
|
|
53
53
|
bfs_version: "5.0"
|
|
54
54
|
}
|
|
55
|
-
|
|
56
|
-
# Convert to URL-encoded format
|
|
57
|
-
URI.encode_www_form(params)
|
|
58
55
|
end
|
|
59
56
|
|
|
60
57
|
def validate_authorization_request!
|
|
@@ -65,14 +62,10 @@ module Rma
|
|
|
65
62
|
end
|
|
66
63
|
|
|
67
64
|
def validate_authorization_response!(response)
|
|
68
|
-
unless response.is_a?(Hash) && response["
|
|
69
|
-
error_detail = response["
|
|
65
|
+
unless response.is_a?(Hash) && response["bfs_responseCode"] == "00"
|
|
66
|
+
error_detail = response["bfs_responseDesc"] || "Unknown error"
|
|
70
67
|
raise AuthenticationError, "Authorization failed: #{error_detail}"
|
|
71
68
|
end
|
|
72
|
-
|
|
73
|
-
return if response["result"]
|
|
74
|
-
|
|
75
|
-
raise AuthenticationError, "No response data in response"
|
|
76
69
|
end
|
|
77
70
|
end
|
|
78
71
|
end
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require "faraday"
|
|
4
4
|
require "json"
|
|
5
|
+
require "openssl"
|
|
5
6
|
|
|
6
7
|
module Rma
|
|
7
8
|
module Payment
|
|
@@ -17,13 +18,17 @@ module Rma
|
|
|
17
18
|
#
|
|
18
19
|
# @param config [Rma::Payment::Gateway::Configuration] Configuration instance
|
|
19
20
|
class Client
|
|
20
|
-
attr_reader :config, :access_token
|
|
21
|
+
attr_reader :config, :access_token
|
|
21
22
|
|
|
22
23
|
def initialize(config = nil)
|
|
23
24
|
@config = config || Rma::Payment::Gateway.configuration
|
|
24
25
|
validate_configuration!
|
|
25
26
|
end
|
|
26
27
|
|
|
28
|
+
def private_key
|
|
29
|
+
@private_key ||= load_private_key
|
|
30
|
+
end
|
|
31
|
+
|
|
27
32
|
# Authorization methods
|
|
28
33
|
def authorization
|
|
29
34
|
@authorization ||= Authorization.new(self)
|
|
@@ -41,7 +46,8 @@ module Rma
|
|
|
41
46
|
|
|
42
47
|
# HTTP request methods
|
|
43
48
|
def post(body: {}, headers: {})
|
|
44
|
-
|
|
49
|
+
signed = body.empty? ? body : body.merge(bfs_checkSum: compute_checksum(body))
|
|
50
|
+
request(:post, body: signed, headers: headers)
|
|
45
51
|
end
|
|
46
52
|
|
|
47
53
|
def get(headers: {})
|
|
@@ -50,6 +56,23 @@ module Rma
|
|
|
50
56
|
|
|
51
57
|
private
|
|
52
58
|
|
|
59
|
+
def compute_checksum(params)
|
|
60
|
+
sorted_values = params.sort_by { |k, _| k }.to_h.values.join("|")
|
|
61
|
+
signature = private_key.sign(OpenSSL::Digest.new("SHA1"), sorted_values)
|
|
62
|
+
signature.unpack1("H*")&.upcase
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def load_private_key
|
|
66
|
+
key_content = if !config.rsa_key_path.to_s.strip.empty?
|
|
67
|
+
File.read(config.rsa_key_path)
|
|
68
|
+
else
|
|
69
|
+
config.rsa_key
|
|
70
|
+
end
|
|
71
|
+
OpenSSL::PKey::RSA.new(key_content)
|
|
72
|
+
rescue OpenSSL::PKey::RSAError => e
|
|
73
|
+
raise ConfigurationError, "Invalid RSA key: #{e.message}"
|
|
74
|
+
end
|
|
75
|
+
|
|
53
76
|
def validate_configuration!
|
|
54
77
|
raise ConfigurationError, "Configuration is required" if config.nil?
|
|
55
78
|
return if config.valid?
|
|
@@ -72,7 +95,7 @@ module Rma
|
|
|
72
95
|
def connection
|
|
73
96
|
@connection ||= Faraday.new(url: config.base_url) do |conn|
|
|
74
97
|
conn.request :url_encoded
|
|
75
|
-
conn.response :json
|
|
98
|
+
conn.response :json
|
|
76
99
|
conn.adapter Faraday.default_adapter
|
|
77
100
|
conn.options.timeout = config.timeout
|
|
78
101
|
conn.options.open_timeout = config.open_timeout
|
|
@@ -87,7 +110,7 @@ module Rma
|
|
|
87
110
|
def handle_response(response)
|
|
88
111
|
case response.status
|
|
89
112
|
when 200..299
|
|
90
|
-
response.body
|
|
113
|
+
parse_body(response.body)
|
|
91
114
|
when 400..499
|
|
92
115
|
handle_client_error(response)
|
|
93
116
|
when 500..599
|
|
@@ -98,25 +121,32 @@ module Rma
|
|
|
98
121
|
end
|
|
99
122
|
|
|
100
123
|
def handle_client_error(response)
|
|
101
|
-
body = response.body || {}
|
|
102
|
-
error_message = body["
|
|
103
|
-
|
|
104
|
-
raise InvalidParameterError.new(
|
|
105
|
-
error_message,
|
|
106
|
-
response_code: body["result"]["bfs_responseCode"],
|
|
107
|
-
response_detail: body["result"]["bfs_responseDesc"]
|
|
108
|
-
)
|
|
124
|
+
body = parse_body(response.body) || {}
|
|
125
|
+
error_message = body["bfs_responseDesc"] || "Client error"
|
|
126
|
+
raise InvalidParameterError, error_message
|
|
109
127
|
end
|
|
110
128
|
|
|
111
129
|
def handle_server_error(response)
|
|
112
|
-
body = response.body || {}
|
|
113
|
-
error_message = body["
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
130
|
+
body = parse_body(response.body) || {}
|
|
131
|
+
error_message = body["bfs_responseDesc"] || "Server error"
|
|
132
|
+
raise APIError, error_message
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def parse_body(body)
|
|
136
|
+
return body if body.is_a?(Hash)
|
|
137
|
+
return {} if body.nil? || body.empty?
|
|
138
|
+
|
|
139
|
+
begin
|
|
140
|
+
return JSON.parse(body)
|
|
141
|
+
rescue JSON::ParserError
|
|
142
|
+
# Not JSON, fall through to URL-encoded parsing
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
parse_url_encoded(body)
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def parse_url_encoded(query_string)
|
|
149
|
+
Rack::Utils.parse_nested_query(query_string)
|
|
120
150
|
end
|
|
121
151
|
end
|
|
122
152
|
end
|
|
@@ -30,7 +30,7 @@ module Rma
|
|
|
30
30
|
|
|
31
31
|
validate_debit_request_response!(response)
|
|
32
32
|
|
|
33
|
-
response
|
|
33
|
+
response
|
|
34
34
|
rescue StandardError => e
|
|
35
35
|
raise AuthenticationError, "Failed to fetch debit request: #{e.message}"
|
|
36
36
|
end
|
|
@@ -38,26 +38,19 @@ module Rma
|
|
|
38
38
|
private
|
|
39
39
|
|
|
40
40
|
def debit_request_body
|
|
41
|
-
|
|
41
|
+
{
|
|
42
42
|
bfs_bfsTxnId: transaction_id,
|
|
43
43
|
bfs_remitterOtp: otp,
|
|
44
44
|
bfs_benfId: client.config.beneficiary_id,
|
|
45
45
|
bfs_msgType: "DR"
|
|
46
46
|
}
|
|
47
|
-
|
|
48
|
-
# Convert to URL-encoded format
|
|
49
|
-
URI.encode_www_form(params)
|
|
50
47
|
end
|
|
51
48
|
|
|
52
49
|
def validate_debit_request_response!(response)
|
|
53
|
-
unless response.is_a?(Hash) && response["
|
|
54
|
-
error_detail = response["
|
|
50
|
+
unless response.is_a?(Hash) && response["bfs_responseCode"] == "00"
|
|
51
|
+
error_detail = response["bfs_responseDesc"] || "Unknown error"
|
|
55
52
|
raise AuthenticationError, "Debit request failed: #{error_detail}"
|
|
56
53
|
end
|
|
57
|
-
|
|
58
|
-
return if response["result"]
|
|
59
|
-
|
|
60
|
-
raise AuthenticationError, "No response data in response"
|
|
61
54
|
end
|
|
62
55
|
end
|
|
63
56
|
end
|
|
@@ -21,7 +21,7 @@ module Rma
|
|
|
21
21
|
return false if account_number.nil? || account_number.empty?
|
|
22
22
|
|
|
23
23
|
# Account numbers should be numeric and between 8-15 digits
|
|
24
|
-
account_number.to_s.match?(/^\d{8,
|
|
24
|
+
account_number.to_s.match?(/^\d{8,40}$/)
|
|
25
25
|
end
|
|
26
26
|
|
|
27
27
|
# Validate phone number format (Bhutan)
|
|
@@ -49,7 +49,7 @@ module Rma
|
|
|
49
49
|
def self.valid_amount?(amount)
|
|
50
50
|
return false if amount.nil?
|
|
51
51
|
|
|
52
|
-
amount.is_a?(Numeric) && amount
|
|
52
|
+
amount.is_a?(Numeric) && amount > 0
|
|
53
53
|
end
|
|
54
54
|
|
|
55
55
|
# Format amount to 2 decimal places
|