qpay-sdk 1.0.0
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 +7 -0
- data/LICENSE +21 -0
- data/lib/qpay/client.rb +205 -0
- data/lib/qpay/config.rb +45 -0
- data/lib/qpay/endpoints/auth.rb +72 -0
- data/lib/qpay/endpoints/ebarimt.rb +129 -0
- data/lib/qpay/endpoints/invoices.rb +70 -0
- data/lib/qpay/endpoints/payments.rb +172 -0
- data/lib/qpay/errors.rb +83 -0
- data/lib/qpay/models.rb +226 -0
- data/lib/qpay/version.rb +5 -0
- data/lib/qpay.rb +11 -0
- data/qpay.gemspec +29 -0
- metadata +113 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: bc257f4bb9cb15d977730172bacef5f3a8541626115dab42d5e4aacb211f60cd
|
|
4
|
+
data.tar.gz: 4f41766daa7f8b795db58f60134484eddfa4a554c081172d93ea8ef106fcc3a6
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 9796ce71359a8402226a70b0193426289e0d53e57b3d932119f6d090c9857151563f3fbe1a28885b3979fca79d9d87fce04e4a9650a1b2f20123bfb04a6e32e4
|
|
7
|
+
data.tar.gz: 399a6092fe9bfa1bd9f1cfba6da767cd8d6348b0d784ae147874591727d330943fbd819347a9335678865bb60cf2de4a80b4ee04bf9a90a61790bea5d43b4cc5
|
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 QPay Ruby SDK Authors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/lib/qpay/client.rb
ADDED
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "faraday"
|
|
4
|
+
require "json"
|
|
5
|
+
|
|
6
|
+
module QPay
|
|
7
|
+
class Client
|
|
8
|
+
include Endpoints::Auth
|
|
9
|
+
include Endpoints::Invoices
|
|
10
|
+
include Endpoints::Payments
|
|
11
|
+
include Endpoints::Ebarimt
|
|
12
|
+
|
|
13
|
+
TOKEN_BUFFER_SECONDS = 30
|
|
14
|
+
|
|
15
|
+
# Creates a new QPay client.
|
|
16
|
+
#
|
|
17
|
+
# @param config [QPay::Config] the configuration object
|
|
18
|
+
# @param faraday [Faraday::Connection, nil] optional custom Faraday connection
|
|
19
|
+
def initialize(config:, faraday: nil)
|
|
20
|
+
@config = config
|
|
21
|
+
@mutex = Mutex.new
|
|
22
|
+
@access_token = nil
|
|
23
|
+
@refresh_token = nil
|
|
24
|
+
@expires_at = 0
|
|
25
|
+
@refresh_expires_at = 0
|
|
26
|
+
|
|
27
|
+
@http = faraday || Faraday.new do |f|
|
|
28
|
+
f.options.timeout = 30
|
|
29
|
+
f.options.open_timeout = 10
|
|
30
|
+
f.adapter Faraday.default_adapter
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
private
|
|
35
|
+
|
|
36
|
+
def ensure_token
|
|
37
|
+
now = Time.now.to_i
|
|
38
|
+
|
|
39
|
+
can_refresh = false
|
|
40
|
+
refresh_tok = nil
|
|
41
|
+
|
|
42
|
+
@mutex.synchronize do
|
|
43
|
+
# Access token still valid
|
|
44
|
+
if @access_token && now < @expires_at - TOKEN_BUFFER_SECONDS
|
|
45
|
+
return
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
can_refresh = @refresh_token && !@refresh_token.empty? && now < @refresh_expires_at - TOKEN_BUFFER_SECONDS
|
|
49
|
+
refresh_tok = @refresh_token
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Try refresh first
|
|
53
|
+
if can_refresh
|
|
54
|
+
begin
|
|
55
|
+
token = do_refresh_token_http(refresh_tok)
|
|
56
|
+
store_token(token)
|
|
57
|
+
return
|
|
58
|
+
rescue QPay::Error
|
|
59
|
+
# Refresh failed, fall through to get new token
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Both expired or no tokens, get new token
|
|
64
|
+
token = get_token_request
|
|
65
|
+
store_token(token)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def store_token(token)
|
|
69
|
+
@mutex.synchronize do
|
|
70
|
+
@access_token = token.access_token
|
|
71
|
+
@refresh_token = token.refresh_token
|
|
72
|
+
@expires_at = token.expires_in
|
|
73
|
+
@refresh_expires_at = token.refresh_expires_in
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def do_request(method, path, body: nil)
|
|
78
|
+
ensure_token
|
|
79
|
+
|
|
80
|
+
url = "#{@config.base_url}#{path}"
|
|
81
|
+
access_tok = @mutex.synchronize { @access_token }
|
|
82
|
+
|
|
83
|
+
response = @http.run_request(method.downcase.to_sym, url, body, {}) do |req|
|
|
84
|
+
req.headers["Content-Type"] = "application/json"
|
|
85
|
+
req.headers["Authorization"] = "Bearer #{access_tok}"
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
handle_response(response)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def do_basic_auth_request(method, path)
|
|
92
|
+
url = "#{@config.base_url}#{path}"
|
|
93
|
+
|
|
94
|
+
response = @http.run_request(method.downcase.to_sym, url, nil, {}) do |req|
|
|
95
|
+
req.headers["Authorization"] = "Basic #{basic_auth_credentials}"
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
body = response.body
|
|
99
|
+
|
|
100
|
+
unless response.success?
|
|
101
|
+
parsed = parse_json(body)
|
|
102
|
+
code = parsed["error"]
|
|
103
|
+
message = parsed["message"] || body
|
|
104
|
+
raise QPay::Error.new(
|
|
105
|
+
status_code: response.status,
|
|
106
|
+
code: code.nil? || code.empty? ? status_text(response.status) : code,
|
|
107
|
+
message: message.nil? || message.empty? ? body : message,
|
|
108
|
+
raw_body: body
|
|
109
|
+
)
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
parsed = parse_json(body)
|
|
113
|
+
build_token_response(parsed)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def handle_response(response)
|
|
117
|
+
body = response.body
|
|
118
|
+
|
|
119
|
+
unless response.success?
|
|
120
|
+
parsed = parse_json(body)
|
|
121
|
+
code = parsed["error"]
|
|
122
|
+
message = parsed["message"]
|
|
123
|
+
raise QPay::Error.new(
|
|
124
|
+
status_code: response.status,
|
|
125
|
+
code: code.nil? || code.empty? ? status_text(response.status) : code,
|
|
126
|
+
message: message.nil? || message.empty? ? body : message,
|
|
127
|
+
raw_body: body
|
|
128
|
+
)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
return nil if body.nil? || body.empty?
|
|
132
|
+
|
|
133
|
+
parse_json(body)
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def serialize(obj)
|
|
137
|
+
hash = obj.to_h.compact.transform_keys { |k| camelize_key(k) }
|
|
138
|
+
deep_serialize(hash).to_json
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def deep_serialize(obj)
|
|
142
|
+
case obj
|
|
143
|
+
when Hash
|
|
144
|
+
obj.each_with_object({}) do |(k, v), result|
|
|
145
|
+
result[k] = deep_serialize(v)
|
|
146
|
+
end
|
|
147
|
+
when Array
|
|
148
|
+
obj.map { |item| deep_serialize(item) }
|
|
149
|
+
when Struct
|
|
150
|
+
deep_serialize(obj.to_h.compact.transform_keys { |k| camelize_key(k) })
|
|
151
|
+
else
|
|
152
|
+
obj
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
# Converts Ruby snake_case keys to the JSON keys expected by QPay API.
|
|
157
|
+
# Most keys are simple snake_case, but some need special handling.
|
|
158
|
+
KEY_MAP = {
|
|
159
|
+
"qpay_short_url" => "qPay_shortUrl",
|
|
160
|
+
"obj_id" => "object_id",
|
|
161
|
+
}.freeze
|
|
162
|
+
|
|
163
|
+
def camelize_key(key)
|
|
164
|
+
str = key.to_s
|
|
165
|
+
KEY_MAP.fetch(str, str)
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def parse_json(body)
|
|
169
|
+
return {} if body.nil? || body.empty?
|
|
170
|
+
|
|
171
|
+
JSON.parse(body)
|
|
172
|
+
rescue JSON::ParserError
|
|
173
|
+
{}
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
def basic_auth_credentials
|
|
177
|
+
require "base64"
|
|
178
|
+
Base64.strict_encode64("#{@config.username}:#{@config.password}")
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def status_text(code)
|
|
182
|
+
Rack::Utils::HTTP_STATUS_CODES[code] || "Unknown Status"
|
|
183
|
+
rescue NameError
|
|
184
|
+
# Rack not available, use basic mapping
|
|
185
|
+
HTTP_STATUS_TEXTS.fetch(code, "Unknown Status")
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
HTTP_STATUS_TEXTS = {
|
|
189
|
+
200 => "OK",
|
|
190
|
+
201 => "Created",
|
|
191
|
+
204 => "No Content",
|
|
192
|
+
400 => "Bad Request",
|
|
193
|
+
401 => "Unauthorized",
|
|
194
|
+
403 => "Forbidden",
|
|
195
|
+
404 => "Not Found",
|
|
196
|
+
405 => "Method Not Allowed",
|
|
197
|
+
409 => "Conflict",
|
|
198
|
+
422 => "Unprocessable Entity",
|
|
199
|
+
429 => "Too Many Requests",
|
|
200
|
+
500 => "Internal Server Error",
|
|
201
|
+
502 => "Bad Gateway",
|
|
202
|
+
503 => "Service Unavailable",
|
|
203
|
+
}.freeze
|
|
204
|
+
end
|
|
205
|
+
end
|
data/lib/qpay/config.rb
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module QPay
|
|
4
|
+
class Config
|
|
5
|
+
attr_accessor :base_url, :username, :password, :invoice_code, :callback_url
|
|
6
|
+
|
|
7
|
+
def initialize(base_url:, username:, password:, invoice_code:, callback_url:)
|
|
8
|
+
@base_url = base_url
|
|
9
|
+
@username = username
|
|
10
|
+
@password = password
|
|
11
|
+
@invoice_code = invoice_code
|
|
12
|
+
@callback_url = callback_url
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Loads configuration from environment variables.
|
|
16
|
+
# Raises ArgumentError if any required variable is missing.
|
|
17
|
+
def self.from_env
|
|
18
|
+
mapping = {
|
|
19
|
+
"QPAY_BASE_URL" => :base_url,
|
|
20
|
+
"QPAY_USERNAME" => :username,
|
|
21
|
+
"QPAY_PASSWORD" => :password,
|
|
22
|
+
"QPAY_INVOICE_CODE" => :invoice_code,
|
|
23
|
+
"QPAY_CALLBACK_URL" => :callback_url,
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
values = {}
|
|
27
|
+
missing = []
|
|
28
|
+
|
|
29
|
+
mapping.each do |env_var, key|
|
|
30
|
+
val = ENV[env_var]
|
|
31
|
+
if val.nil? || val.empty?
|
|
32
|
+
missing << env_var
|
|
33
|
+
else
|
|
34
|
+
values[key] = val
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
unless missing.empty?
|
|
39
|
+
raise ArgumentError, "required environment variable(s) not set: #{missing.join(", ")}"
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
new(**values)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module QPay
|
|
4
|
+
module Endpoints
|
|
5
|
+
module Auth
|
|
6
|
+
# Authenticates with QPay using Basic Auth and returns a new token pair.
|
|
7
|
+
# Also stores the token for subsequent requests.
|
|
8
|
+
#
|
|
9
|
+
# @return [QPay::TokenResponse]
|
|
10
|
+
def get_token
|
|
11
|
+
token = get_token_request
|
|
12
|
+
store_token(token)
|
|
13
|
+
token
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Uses the current refresh token to obtain a new access token.
|
|
17
|
+
#
|
|
18
|
+
# @return [QPay::TokenResponse]
|
|
19
|
+
def refresh_token
|
|
20
|
+
refresh_tok = @mutex.synchronize { @refresh_token }
|
|
21
|
+
token = do_refresh_token_http(refresh_tok)
|
|
22
|
+
store_token(token)
|
|
23
|
+
token
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
def get_token_request
|
|
29
|
+
do_basic_auth_request("POST", "/v2/auth/token")
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def do_refresh_token_http(refresh_tok)
|
|
33
|
+
url = "#{@config.base_url}/v2/auth/refresh"
|
|
34
|
+
response = @http.post(url) do |req|
|
|
35
|
+
req.headers["Authorization"] = "Bearer #{refresh_tok}"
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
handle_token_response(response)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def handle_token_response(response)
|
|
42
|
+
body = response.body
|
|
43
|
+
|
|
44
|
+
unless response.success?
|
|
45
|
+
parsed = parse_json(body)
|
|
46
|
+
raise QPay::Error.new(
|
|
47
|
+
status_code: response.status,
|
|
48
|
+
code: parsed["error"],
|
|
49
|
+
message: parsed["message"] || body,
|
|
50
|
+
raw_body: body
|
|
51
|
+
)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
parsed = parse_json(body)
|
|
55
|
+
build_token_response(parsed)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def build_token_response(data)
|
|
59
|
+
QPay::TokenResponse.new(
|
|
60
|
+
token_type: data["token_type"],
|
|
61
|
+
refresh_expires_in: data["refresh_expires_in"],
|
|
62
|
+
refresh_token: data["refresh_token"],
|
|
63
|
+
access_token: data["access_token"],
|
|
64
|
+
expires_in: data["expires_in"],
|
|
65
|
+
scope: data["scope"],
|
|
66
|
+
not_before_policy: data["not-before-policy"],
|
|
67
|
+
session_state: data["session_state"]
|
|
68
|
+
)
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module QPay
|
|
4
|
+
module Endpoints
|
|
5
|
+
module Ebarimt
|
|
6
|
+
# Creates an ebarimt (electronic tax receipt) for a payment.
|
|
7
|
+
# POST /v2/ebarimt_v3/create
|
|
8
|
+
#
|
|
9
|
+
# @param request [QPay::CreateEbarimtRequest]
|
|
10
|
+
# @return [QPay::EbarimtResponse]
|
|
11
|
+
def create_ebarimt(request)
|
|
12
|
+
data = do_request("POST", "/v2/ebarimt_v3/create", body: serialize(request))
|
|
13
|
+
build_ebarimt_response(data)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Cancels an ebarimt by payment ID.
|
|
17
|
+
# DELETE /v2/ebarimt_v3/{id}
|
|
18
|
+
#
|
|
19
|
+
# @param payment_id [String]
|
|
20
|
+
# @return [QPay::EbarimtResponse]
|
|
21
|
+
def cancel_ebarimt(payment_id)
|
|
22
|
+
data = do_request("DELETE", "/v2/ebarimt_v3/#{payment_id}")
|
|
23
|
+
build_ebarimt_response(data)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
def build_ebarimt_response(data)
|
|
29
|
+
return nil if data.nil?
|
|
30
|
+
|
|
31
|
+
items = (data["barimt_items"] || []).map do |item|
|
|
32
|
+
QPay::EbarimtItem.new(
|
|
33
|
+
id: item["id"],
|
|
34
|
+
barimt_id: item["barimt_id"],
|
|
35
|
+
merchant_product_code: item["merchant_product_code"],
|
|
36
|
+
tax_product_code: item["tax_product_code"],
|
|
37
|
+
bar_code: item["bar_code"],
|
|
38
|
+
name: item["name"],
|
|
39
|
+
unit_price: item["unit_price"],
|
|
40
|
+
quantity: item["quantity"],
|
|
41
|
+
amount: item["amount"],
|
|
42
|
+
city_tax_amount: item["city_tax_amount"],
|
|
43
|
+
vat_amount: item["vat_amount"],
|
|
44
|
+
note: item["note"],
|
|
45
|
+
created_by: item["created_by"],
|
|
46
|
+
created_date: item["created_date"],
|
|
47
|
+
updated_by: item["updated_by"],
|
|
48
|
+
updated_date: item["updated_date"],
|
|
49
|
+
status: item["status"]
|
|
50
|
+
)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
histories = (data["barimt_histories"] || []).map do |h|
|
|
54
|
+
QPay::EbarimtHistory.new(
|
|
55
|
+
id: h["id"],
|
|
56
|
+
barimt_id: h["barimt_id"],
|
|
57
|
+
ebarimt_receiver_type: h["ebarimt_receiver_type"],
|
|
58
|
+
ebarimt_receiver: h["ebarimt_receiver"],
|
|
59
|
+
ebarimt_register_no: h["ebarimt_register_no"],
|
|
60
|
+
ebarimt_bill_id: h["ebarimt_bill_id"],
|
|
61
|
+
ebarimt_date: h["ebarimt_date"],
|
|
62
|
+
ebarimt_mac_address: h["ebarimt_mac_address"],
|
|
63
|
+
ebarimt_internal_code: h["ebarimt_internal_code"],
|
|
64
|
+
ebarimt_bill_type: h["ebarimt_bill_type"],
|
|
65
|
+
ebarimt_qr_data: h["ebarimt_qr_data"],
|
|
66
|
+
ebarimt_lottery: h["ebarimt_lottery"],
|
|
67
|
+
ebarimt_lottery_msg: h["ebarimt_lottery_msg"],
|
|
68
|
+
ebarimt_error_code: h["ebarimt_error_code"],
|
|
69
|
+
ebarimt_error_msg: h["ebarimt_error_msg"],
|
|
70
|
+
ebarimt_response_code: h["ebarimt_response_code"],
|
|
71
|
+
ebarimt_response_msg: h["ebarimt_response_msg"],
|
|
72
|
+
note: h["note"],
|
|
73
|
+
barimt_status: h["barimt_status"],
|
|
74
|
+
barimt_status_date: h["barimt_status_date"],
|
|
75
|
+
ebarimt_sent_email: h["ebarimt_sent_email"],
|
|
76
|
+
ebarimt_receiver_phone: h["ebarimt_receiver_phone"],
|
|
77
|
+
tax_type: h["tax_type"],
|
|
78
|
+
created_by: h["created_by"],
|
|
79
|
+
created_date: h["created_date"],
|
|
80
|
+
updated_by: h["updated_by"],
|
|
81
|
+
updated_date: h["updated_date"],
|
|
82
|
+
status: h["status"]
|
|
83
|
+
)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
QPay::EbarimtResponse.new(
|
|
87
|
+
id: data["id"],
|
|
88
|
+
ebarimt_by: data["ebarimt_by"],
|
|
89
|
+
g_wallet_id: data["g_wallet_id"],
|
|
90
|
+
g_wallet_customer_id: data["g_wallet_customer_id"],
|
|
91
|
+
ebarimt_receiver_type: data["ebarimt_receiver_type"],
|
|
92
|
+
ebarimt_receiver: data["ebarimt_receiver"],
|
|
93
|
+
ebarimt_district_code: data["ebarimt_district_code"],
|
|
94
|
+
ebarimt_bill_type: data["ebarimt_bill_type"],
|
|
95
|
+
g_merchant_id: data["g_merchant_id"],
|
|
96
|
+
merchant_branch_code: data["merchant_branch_code"],
|
|
97
|
+
merchant_terminal_code: data["merchant_terminal_code"],
|
|
98
|
+
merchant_staff_code: data["merchant_staff_code"],
|
|
99
|
+
merchant_register_no: data["merchant_register_no"],
|
|
100
|
+
g_payment_id: data["g_payment_id"],
|
|
101
|
+
paid_by: data["paid_by"],
|
|
102
|
+
object_type: data["object_type"],
|
|
103
|
+
obj_id: data["object_id"],
|
|
104
|
+
amount: data["amount"],
|
|
105
|
+
vat_amount: data["vat_amount"],
|
|
106
|
+
city_tax_amount: data["city_tax_amount"],
|
|
107
|
+
ebarimt_qr_data: data["ebarimt_qr_data"],
|
|
108
|
+
ebarimt_lottery: data["ebarimt_lottery"],
|
|
109
|
+
note: data["note"],
|
|
110
|
+
barimt_status: data["barimt_status"],
|
|
111
|
+
barimt_status_date: data["barimt_status_date"],
|
|
112
|
+
ebarimt_sent_email: data["ebarimt_sent_email"],
|
|
113
|
+
ebarimt_receiver_phone: data["ebarimt_receiver_phone"],
|
|
114
|
+
tax_type: data["tax_type"],
|
|
115
|
+
merchant_tin: data["merchant_tin"],
|
|
116
|
+
ebarimt_receipt_id: data["ebarimt_receipt_id"],
|
|
117
|
+
created_by: data["created_by"],
|
|
118
|
+
created_date: data["created_date"],
|
|
119
|
+
updated_by: data["updated_by"],
|
|
120
|
+
updated_date: data["updated_date"],
|
|
121
|
+
status: data["status"],
|
|
122
|
+
barimt_items: items,
|
|
123
|
+
barimt_transactions: data["barimt_transactions"] || [],
|
|
124
|
+
barimt_histories: histories
|
|
125
|
+
)
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
end
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module QPay
|
|
4
|
+
module Endpoints
|
|
5
|
+
module Invoices
|
|
6
|
+
# Creates a detailed invoice with full options.
|
|
7
|
+
# POST /v2/invoice
|
|
8
|
+
#
|
|
9
|
+
# @param request [QPay::CreateInvoiceRequest]
|
|
10
|
+
# @return [QPay::InvoiceResponse]
|
|
11
|
+
def create_invoice(request)
|
|
12
|
+
data = do_request("POST", "/v2/invoice", body: serialize(request))
|
|
13
|
+
build_invoice_response(data)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Creates a simple invoice with minimal fields.
|
|
17
|
+
# POST /v2/invoice
|
|
18
|
+
#
|
|
19
|
+
# @param request [QPay::CreateSimpleInvoiceRequest]
|
|
20
|
+
# @return [QPay::InvoiceResponse]
|
|
21
|
+
def create_simple_invoice(request)
|
|
22
|
+
data = do_request("POST", "/v2/invoice", body: serialize(request))
|
|
23
|
+
build_invoice_response(data)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Creates an invoice with ebarimt (tax) information.
|
|
27
|
+
# POST /v2/invoice
|
|
28
|
+
#
|
|
29
|
+
# @param request [QPay::CreateEbarimtInvoiceRequest]
|
|
30
|
+
# @return [QPay::InvoiceResponse]
|
|
31
|
+
def create_ebarimt_invoice(request)
|
|
32
|
+
data = do_request("POST", "/v2/invoice", body: serialize(request))
|
|
33
|
+
build_invoice_response(data)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Cancels an existing invoice by ID.
|
|
37
|
+
# DELETE /v2/invoice/{id}
|
|
38
|
+
#
|
|
39
|
+
# @param invoice_id [String]
|
|
40
|
+
# @return [void]
|
|
41
|
+
def cancel_invoice(invoice_id)
|
|
42
|
+
do_request("DELETE", "/v2/invoice/#{invoice_id}")
|
|
43
|
+
nil
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
private
|
|
47
|
+
|
|
48
|
+
def build_invoice_response(data)
|
|
49
|
+
return nil if data.nil?
|
|
50
|
+
|
|
51
|
+
urls = (data["urls"] || []).map do |u|
|
|
52
|
+
QPay::Deeplink.new(
|
|
53
|
+
name: u["name"],
|
|
54
|
+
description: u["description"],
|
|
55
|
+
logo: u["logo"],
|
|
56
|
+
link: u["link"]
|
|
57
|
+
)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
QPay::InvoiceResponse.new(
|
|
61
|
+
invoice_id: data["invoice_id"],
|
|
62
|
+
qr_text: data["qr_text"],
|
|
63
|
+
qr_image: data["qr_image"],
|
|
64
|
+
qpay_short_url: data["qPay_shortUrl"],
|
|
65
|
+
urls: urls
|
|
66
|
+
)
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module QPay
|
|
4
|
+
module Endpoints
|
|
5
|
+
module Payments
|
|
6
|
+
# Retrieves payment details by payment ID.
|
|
7
|
+
# GET /v2/payment/{id}
|
|
8
|
+
#
|
|
9
|
+
# @param payment_id [String]
|
|
10
|
+
# @return [QPay::PaymentDetail]
|
|
11
|
+
def get_payment(payment_id)
|
|
12
|
+
data = do_request("GET", "/v2/payment/#{payment_id}")
|
|
13
|
+
build_payment_detail(data)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Checks if a payment has been made for an invoice.
|
|
17
|
+
# POST /v2/payment/check
|
|
18
|
+
#
|
|
19
|
+
# @param request [QPay::PaymentCheckRequest]
|
|
20
|
+
# @return [QPay::PaymentCheckResponse]
|
|
21
|
+
def check_payment(request)
|
|
22
|
+
data = do_request("POST", "/v2/payment/check", body: serialize(request))
|
|
23
|
+
build_payment_check_response(data)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Returns a list of payments matching the given criteria.
|
|
27
|
+
# POST /v2/payment/list
|
|
28
|
+
#
|
|
29
|
+
# @param request [QPay::PaymentListRequest]
|
|
30
|
+
# @return [QPay::PaymentListResponse]
|
|
31
|
+
def list_payments(request)
|
|
32
|
+
data = do_request("POST", "/v2/payment/list", body: serialize(request))
|
|
33
|
+
build_payment_list_response(data)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Cancels a payment (card transactions only).
|
|
37
|
+
# DELETE /v2/payment/cancel/{id}
|
|
38
|
+
#
|
|
39
|
+
# @param payment_id [String]
|
|
40
|
+
# @param request [QPay::PaymentCancelRequest, nil]
|
|
41
|
+
# @return [void]
|
|
42
|
+
def cancel_payment(payment_id, request: nil)
|
|
43
|
+
do_request("DELETE", "/v2/payment/cancel/#{payment_id}", body: request ? serialize(request) : nil)
|
|
44
|
+
nil
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Refunds a payment (card transactions only).
|
|
48
|
+
# DELETE /v2/payment/refund/{id}
|
|
49
|
+
#
|
|
50
|
+
# @param payment_id [String]
|
|
51
|
+
# @param request [QPay::PaymentRefundRequest, nil]
|
|
52
|
+
# @return [void]
|
|
53
|
+
def refund_payment(payment_id, request: nil)
|
|
54
|
+
do_request("DELETE", "/v2/payment/refund/#{payment_id}", body: request ? serialize(request) : nil)
|
|
55
|
+
nil
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
private
|
|
59
|
+
|
|
60
|
+
def build_card_transactions(items)
|
|
61
|
+
(items || []).map do |ct|
|
|
62
|
+
QPay::CardTransaction.new(
|
|
63
|
+
card_merchant_code: ct["card_merchant_code"],
|
|
64
|
+
card_terminal_code: ct["card_terminal_code"],
|
|
65
|
+
card_number: ct["card_number"],
|
|
66
|
+
card_type: ct["card_type"],
|
|
67
|
+
is_cross_border: ct["is_cross_border"],
|
|
68
|
+
amount: ct["amount"],
|
|
69
|
+
transaction_amount: ct["transaction_amount"],
|
|
70
|
+
currency: ct["currency"],
|
|
71
|
+
transaction_currency: ct["transaction_currency"],
|
|
72
|
+
date: ct["date"],
|
|
73
|
+
transaction_date: ct["transaction_date"],
|
|
74
|
+
status: ct["status"],
|
|
75
|
+
transaction_status: ct["transaction_status"],
|
|
76
|
+
settlement_status: ct["settlement_status"],
|
|
77
|
+
settlement_status_date: ct["settlement_status_date"]
|
|
78
|
+
)
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def build_p2p_transactions(items)
|
|
83
|
+
(items || []).map do |pt|
|
|
84
|
+
QPay::P2PTransaction.new(
|
|
85
|
+
transaction_bank_code: pt["transaction_bank_code"],
|
|
86
|
+
account_bank_code: pt["account_bank_code"],
|
|
87
|
+
account_bank_name: pt["account_bank_name"],
|
|
88
|
+
account_number: pt["account_number"],
|
|
89
|
+
status: pt["status"],
|
|
90
|
+
amount: pt["amount"],
|
|
91
|
+
currency: pt["currency"],
|
|
92
|
+
settlement_status: pt["settlement_status"]
|
|
93
|
+
)
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def build_payment_detail(data)
|
|
98
|
+
return nil if data.nil?
|
|
99
|
+
|
|
100
|
+
QPay::PaymentDetail.new(
|
|
101
|
+
payment_id: data["payment_id"],
|
|
102
|
+
payment_status: data["payment_status"],
|
|
103
|
+
payment_fee: data["payment_fee"],
|
|
104
|
+
payment_amount: data["payment_amount"],
|
|
105
|
+
payment_currency: data["payment_currency"],
|
|
106
|
+
payment_date: data["payment_date"],
|
|
107
|
+
payment_wallet: data["payment_wallet"],
|
|
108
|
+
transaction_type: data["transaction_type"],
|
|
109
|
+
object_type: data["object_type"],
|
|
110
|
+
obj_id: data["object_id"],
|
|
111
|
+
next_payment_date: data["next_payment_date"],
|
|
112
|
+
next_payment_datetime: data["next_payment_datetime"],
|
|
113
|
+
card_transactions: build_card_transactions(data["card_transactions"]),
|
|
114
|
+
p2p_transactions: build_p2p_transactions(data["p2p_transactions"])
|
|
115
|
+
)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def build_payment_check_response(data)
|
|
119
|
+
return nil if data.nil?
|
|
120
|
+
|
|
121
|
+
rows = (data["rows"] || []).map do |row|
|
|
122
|
+
QPay::PaymentCheckRow.new(
|
|
123
|
+
payment_id: row["payment_id"],
|
|
124
|
+
payment_status: row["payment_status"],
|
|
125
|
+
payment_amount: row["payment_amount"],
|
|
126
|
+
trx_fee: row["trx_fee"],
|
|
127
|
+
payment_currency: row["payment_currency"],
|
|
128
|
+
payment_wallet: row["payment_wallet"],
|
|
129
|
+
payment_type: row["payment_type"],
|
|
130
|
+
next_payment_date: row["next_payment_date"],
|
|
131
|
+
next_payment_datetime: row["next_payment_datetime"],
|
|
132
|
+
card_transactions: build_card_transactions(row["card_transactions"]),
|
|
133
|
+
p2p_transactions: build_p2p_transactions(row["p2p_transactions"])
|
|
134
|
+
)
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
QPay::PaymentCheckResponse.new(
|
|
138
|
+
count: data["count"],
|
|
139
|
+
paid_amount: data["paid_amount"],
|
|
140
|
+
rows: rows
|
|
141
|
+
)
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def build_payment_list_response(data)
|
|
145
|
+
return nil if data.nil?
|
|
146
|
+
|
|
147
|
+
rows = (data["rows"] || []).map do |row|
|
|
148
|
+
QPay::PaymentListItem.new(
|
|
149
|
+
payment_id: row["payment_id"],
|
|
150
|
+
payment_date: row["payment_date"],
|
|
151
|
+
payment_status: row["payment_status"],
|
|
152
|
+
payment_fee: row["payment_fee"],
|
|
153
|
+
payment_amount: row["payment_amount"],
|
|
154
|
+
payment_currency: row["payment_currency"],
|
|
155
|
+
payment_wallet: row["payment_wallet"],
|
|
156
|
+
payment_name: row["payment_name"],
|
|
157
|
+
payment_description: row["payment_description"],
|
|
158
|
+
qr_code: row["qr_code"],
|
|
159
|
+
paid_by: row["paid_by"],
|
|
160
|
+
object_type: row["object_type"],
|
|
161
|
+
obj_id: row["object_id"]
|
|
162
|
+
)
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
QPay::PaymentListResponse.new(
|
|
166
|
+
count: data["count"],
|
|
167
|
+
rows: rows
|
|
168
|
+
)
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
end
|
data/lib/qpay/errors.rb
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module QPay
|
|
4
|
+
class Error < StandardError
|
|
5
|
+
attr_reader :status_code, :code, :raw_body
|
|
6
|
+
|
|
7
|
+
def initialize(status_code:, code: nil, message: nil, raw_body: nil)
|
|
8
|
+
@status_code = status_code
|
|
9
|
+
@code = code
|
|
10
|
+
@raw_body = raw_body
|
|
11
|
+
super(build_message(message))
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
private
|
|
15
|
+
|
|
16
|
+
def build_message(msg)
|
|
17
|
+
"qpay: #{@code} - #{msg} (status #{@status_code})"
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Returns [QPay::Error, true] if the error is a QPay::Error, [nil, false] otherwise.
|
|
22
|
+
def self.qpay_error?(err)
|
|
23
|
+
if err.is_a?(QPay::Error)
|
|
24
|
+
[err, true]
|
|
25
|
+
else
|
|
26
|
+
[nil, false]
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Error code constants
|
|
31
|
+
ACCOUNT_BANK_DUPLICATED = "ACCOUNT_BANK_DUPLICATED"
|
|
32
|
+
ACCOUNT_SELECTION_INVALID = "ACCOUNT_SELECTION_INVALID"
|
|
33
|
+
AUTHENTICATION_FAILED = "AUTHENTICATION_FAILED"
|
|
34
|
+
BANK_ACCOUNT_NOTFOUND = "BANK_ACCOUNT_NOTFOUND"
|
|
35
|
+
BANK_MCC_ALREADY_ADDED = "BANK_MCC_ALREADY_ADDED"
|
|
36
|
+
BANK_MCC_NOT_FOUND = "BANK_MCC_NOT_FOUND"
|
|
37
|
+
CARD_TERMINAL_NOTFOUND = "CARD_TERMINAL_NOTFOUND"
|
|
38
|
+
CLIENT_NOTFOUND = "CLIENT_NOTFOUND"
|
|
39
|
+
CLIENT_USERNAME_DUPLICATED = "CLIENT_USERNAME_DUPLICATED"
|
|
40
|
+
CUSTOMER_DUPLICATE = "CUSTOMER_DUPLICATE"
|
|
41
|
+
CUSTOMER_NOTFOUND = "CUSTOMER_NOTFOUND"
|
|
42
|
+
CUSTOMER_REGISTER_INVALID = "CUSTOMER_REGISTER_INVALID"
|
|
43
|
+
EBARIMT_CANCEL_NOTSUPPERDED = "EBARIMT_CANCEL_NOTSUPPERDED"
|
|
44
|
+
EBARIMT_NOT_REGISTERED = "EBARIMT_NOT_REGISTERED"
|
|
45
|
+
EBARIMT_QR_CODE_INVALID = "EBARIMT_QR_CODE_INVALID"
|
|
46
|
+
INFORM_NOTFOUND = "INFORM_NOTFOUND"
|
|
47
|
+
INPUT_CODE_REGISTERED = "INPUT_CODE_REGISTERED"
|
|
48
|
+
INPUT_NOTFOUND = "INPUT_NOTFOUND"
|
|
49
|
+
INVALID_AMOUNT = "INVALID_AMOUNT"
|
|
50
|
+
INVALID_OBJECT_TYPE = "INVALID_OBJECT_TYPE"
|
|
51
|
+
INVOICE_ALREADY_CANCELED = "INVOICE_ALREADY_CANCELED"
|
|
52
|
+
INVOICE_CODE_INVALID = "INVOICE_CODE_INVALID"
|
|
53
|
+
INVOICE_CODE_REGISTERED = "INVOICE_CODE_REGISTERED"
|
|
54
|
+
INVOICE_LINE_REQUIRED = "INVOICE_LINE_REQUIRED"
|
|
55
|
+
INVOICE_NOTFOUND = "INVOICE_NOTFOUND"
|
|
56
|
+
INVOICE_PAID = "INVOICE_PAID"
|
|
57
|
+
INVOICE_RECEIVER_DATA_ADDRESS_REQUIRED = "INVOICE_RECEIVER_DATA_ADDRESS_REQUIRED"
|
|
58
|
+
INVOICE_RECEIVER_DATA_EMAIL_REQUIRED = "INVOICE_RECEIVER_DATA_EMAIL_REQUIRED"
|
|
59
|
+
INVOICE_RECEIVER_DATA_PHONE_REQUIRED = "INVOICE_RECEIVER_DATA_PHONE_REQUIRED"
|
|
60
|
+
INVOICE_RECEIVER_DATA_REQUIRED = "INVOICE_RECEIVER_DATA_REQUIRED"
|
|
61
|
+
MAX_AMOUNT_ERR = "MAX_AMOUNT_ERR"
|
|
62
|
+
MCC_NOTFOUND = "MCC_NOTFOUND"
|
|
63
|
+
MERCHANT_ALREADY_REGISTERED = "MERCHANT_ALREADY_REGISTERED"
|
|
64
|
+
MERCHANT_INACTIVE = "MERCHANT_INACTIVE"
|
|
65
|
+
MERCHANT_NOTFOUND = "MERCHANT_NOTFOUND"
|
|
66
|
+
MIN_AMOUNT_ERR = "MIN_AMOUNT_ERR"
|
|
67
|
+
NO_CREDENDIALS = "NO_CREDENDIALS"
|
|
68
|
+
OBJECT_DATA_ERROR = "OBJECT_DATA_ERROR"
|
|
69
|
+
P2P_TERMINAL_NOTFOUND = "P2P_TERMINAL_NOTFOUND"
|
|
70
|
+
PAYMENT_ALREADY_CANCELED = "PAYMENT_ALREADY_CANCELED"
|
|
71
|
+
PAYMENT_NOT_PAID = "PAYMENT_NOT_PAID"
|
|
72
|
+
PAYMENT_NOTFOUND = "PAYMENT_NOTFOUND"
|
|
73
|
+
PERMISSION_DENIED = "PERMISSION_DENIED"
|
|
74
|
+
QRACCOUNT_INACTIVE = "QRACCOUNT_INACTIVE"
|
|
75
|
+
QRACCOUNT_NOTFOUND = "QRACCOUNT_NOTFOUND"
|
|
76
|
+
QRCODE_NOTFOUND = "QRCODE_NOTFOUND"
|
|
77
|
+
QRCODE_USED = "QRCODE_USED"
|
|
78
|
+
SENDER_BRANCH_DATA_REQUIRED = "SENDER_BRANCH_DATA_REQUIRED"
|
|
79
|
+
TAX_LINE_REQUIRED = "TAX_LINE_REQUIRED"
|
|
80
|
+
TAX_PRODUCT_CODE_REQUIRED = "TAX_PRODUCT_CODE_REQUIRED"
|
|
81
|
+
TRANSACTION_NOT_APPROVED = "TRANSACTION_NOT_APPROVED"
|
|
82
|
+
TRANSACTION_REQUIRED = "TRANSACTION_REQUIRED"
|
|
83
|
+
end
|
data/lib/qpay/models.rb
ADDED
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module QPay
|
|
4
|
+
# --- Auth ---
|
|
5
|
+
|
|
6
|
+
TokenResponse = Struct.new(
|
|
7
|
+
:token_type, :refresh_expires_in, :refresh_token,
|
|
8
|
+
:access_token, :expires_in, :scope,
|
|
9
|
+
:not_before_policy, :session_state,
|
|
10
|
+
keyword_init: true
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
# --- Common nested types ---
|
|
14
|
+
|
|
15
|
+
Address = Struct.new(
|
|
16
|
+
:city, :district, :street, :building,
|
|
17
|
+
:address, :zipcode, :longitude, :latitude,
|
|
18
|
+
keyword_init: true
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
SenderBranchData = Struct.new(
|
|
22
|
+
:register, :name, :email, :phone, :address,
|
|
23
|
+
keyword_init: true
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
SenderStaffData = Struct.new(
|
|
27
|
+
:name, :email, :phone,
|
|
28
|
+
keyword_init: true
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
InvoiceReceiverData = Struct.new(
|
|
32
|
+
:register, :name, :email, :phone, :address,
|
|
33
|
+
keyword_init: true
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
Account = Struct.new(
|
|
37
|
+
:account_bank_code, :account_number, :iban_number,
|
|
38
|
+
:account_name, :account_currency, :is_default,
|
|
39
|
+
keyword_init: true
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
Transaction = Struct.new(
|
|
43
|
+
:description, :amount, :accounts,
|
|
44
|
+
keyword_init: true
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
InvoiceLine = Struct.new(
|
|
48
|
+
:tax_product_code, :line_description, :line_quantity,
|
|
49
|
+
:line_unit_price, :note, :discounts, :surcharges, :taxes,
|
|
50
|
+
keyword_init: true
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
EbarimtInvoiceLine = Struct.new(
|
|
54
|
+
:tax_product_code, :line_description, :barcode,
|
|
55
|
+
:line_quantity, :line_unit_price, :note,
|
|
56
|
+
:classification_code, :taxes,
|
|
57
|
+
keyword_init: true
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
TaxEntry = Struct.new(
|
|
61
|
+
:tax_code, :discount_code, :surcharge_code,
|
|
62
|
+
:description, :amount, :note,
|
|
63
|
+
keyword_init: true
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
Deeplink = Struct.new(
|
|
67
|
+
:name, :description, :logo, :link,
|
|
68
|
+
keyword_init: true
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
# --- Invoice ---
|
|
72
|
+
|
|
73
|
+
CreateInvoiceRequest = Struct.new(
|
|
74
|
+
:invoice_code, :sender_invoice_no, :sender_branch_code,
|
|
75
|
+
:sender_branch_data, :sender_staff_data, :sender_staff_code,
|
|
76
|
+
:invoice_receiver_code, :invoice_receiver_data, :invoice_description,
|
|
77
|
+
:enable_expiry, :allow_partial, :minimum_amount,
|
|
78
|
+
:allow_exceed, :maximum_amount, :amount,
|
|
79
|
+
:callback_url, :sender_terminal_code, :sender_terminal_data,
|
|
80
|
+
:allow_subscribe, :subscription_interval, :subscription_webhook,
|
|
81
|
+
:note, :transactions, :lines,
|
|
82
|
+
keyword_init: true
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
CreateSimpleInvoiceRequest = Struct.new(
|
|
86
|
+
:invoice_code, :sender_invoice_no, :invoice_receiver_code,
|
|
87
|
+
:invoice_description, :sender_branch_code, :amount, :callback_url,
|
|
88
|
+
keyword_init: true
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
CreateEbarimtInvoiceRequest = Struct.new(
|
|
92
|
+
:invoice_code, :sender_invoice_no, :sender_branch_code,
|
|
93
|
+
:sender_staff_data, :sender_staff_code, :invoice_receiver_code,
|
|
94
|
+
:invoice_receiver_data, :invoice_description, :tax_type,
|
|
95
|
+
:district_code, :callback_url, :lines,
|
|
96
|
+
keyword_init: true
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
InvoiceResponse = Struct.new(
|
|
100
|
+
:invoice_id, :qr_text, :qr_image, :qpay_short_url, :urls,
|
|
101
|
+
keyword_init: true
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
# --- Payment ---
|
|
105
|
+
|
|
106
|
+
Offset = Struct.new(
|
|
107
|
+
:page_number, :page_limit,
|
|
108
|
+
keyword_init: true
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
PaymentCheckRequest = Struct.new(
|
|
112
|
+
:object_type, :obj_id, :offset,
|
|
113
|
+
keyword_init: true
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
PaymentCheckResponse = Struct.new(
|
|
117
|
+
:count, :paid_amount, :rows,
|
|
118
|
+
keyword_init: true
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
PaymentCheckRow = Struct.new(
|
|
122
|
+
:payment_id, :payment_status, :payment_amount,
|
|
123
|
+
:trx_fee, :payment_currency, :payment_wallet,
|
|
124
|
+
:payment_type, :next_payment_date, :next_payment_datetime,
|
|
125
|
+
:card_transactions, :p2p_transactions,
|
|
126
|
+
keyword_init: true
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
PaymentDetail = Struct.new(
|
|
130
|
+
:payment_id, :payment_status, :payment_fee,
|
|
131
|
+
:payment_amount, :payment_currency, :payment_date,
|
|
132
|
+
:payment_wallet, :transaction_type, :object_type,
|
|
133
|
+
:obj_id, :next_payment_date, :next_payment_datetime,
|
|
134
|
+
:card_transactions, :p2p_transactions,
|
|
135
|
+
keyword_init: true
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
CardTransaction = Struct.new(
|
|
139
|
+
:card_merchant_code, :card_terminal_code, :card_number,
|
|
140
|
+
:card_type, :is_cross_border, :amount,
|
|
141
|
+
:transaction_amount, :currency, :transaction_currency,
|
|
142
|
+
:date, :transaction_date, :status,
|
|
143
|
+
:transaction_status, :settlement_status, :settlement_status_date,
|
|
144
|
+
keyword_init: true
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
P2PTransaction = Struct.new(
|
|
148
|
+
:transaction_bank_code, :account_bank_code, :account_bank_name,
|
|
149
|
+
:account_number, :status, :amount,
|
|
150
|
+
:currency, :settlement_status,
|
|
151
|
+
keyword_init: true
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
PaymentListRequest = Struct.new(
|
|
155
|
+
:object_type, :obj_id, :start_date, :end_date, :offset,
|
|
156
|
+
keyword_init: true
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
PaymentListResponse = Struct.new(
|
|
160
|
+
:count, :rows,
|
|
161
|
+
keyword_init: true
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
PaymentListItem = Struct.new(
|
|
165
|
+
:payment_id, :payment_date, :payment_status,
|
|
166
|
+
:payment_fee, :payment_amount, :payment_currency,
|
|
167
|
+
:payment_wallet, :payment_name, :payment_description,
|
|
168
|
+
:qr_code, :paid_by, :object_type, :obj_id,
|
|
169
|
+
keyword_init: true
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
PaymentCancelRequest = Struct.new(
|
|
173
|
+
:callback_url, :note,
|
|
174
|
+
keyword_init: true
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
PaymentRefundRequest = Struct.new(
|
|
178
|
+
:callback_url, :note,
|
|
179
|
+
keyword_init: true
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
# --- Ebarimt ---
|
|
183
|
+
|
|
184
|
+
CreateEbarimtRequest = Struct.new(
|
|
185
|
+
:payment_id, :ebarimt_receiver_type, :ebarimt_receiver,
|
|
186
|
+
:district_code, :classification_code,
|
|
187
|
+
keyword_init: true
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
EbarimtResponse = Struct.new(
|
|
191
|
+
:id, :ebarimt_by, :g_wallet_id, :g_wallet_customer_id,
|
|
192
|
+
:ebarimt_receiver_type, :ebarimt_receiver, :ebarimt_district_code,
|
|
193
|
+
:ebarimt_bill_type, :g_merchant_id, :merchant_branch_code,
|
|
194
|
+
:merchant_terminal_code, :merchant_staff_code, :merchant_register_no,
|
|
195
|
+
:g_payment_id, :paid_by, :object_type, :obj_id,
|
|
196
|
+
:amount, :vat_amount, :city_tax_amount,
|
|
197
|
+
:ebarimt_qr_data, :ebarimt_lottery, :note,
|
|
198
|
+
:barimt_status, :barimt_status_date, :ebarimt_sent_email,
|
|
199
|
+
:ebarimt_receiver_phone, :tax_type, :merchant_tin,
|
|
200
|
+
:ebarimt_receipt_id, :created_by, :created_date,
|
|
201
|
+
:updated_by, :updated_date, :status,
|
|
202
|
+
:barimt_items, :barimt_transactions, :barimt_histories,
|
|
203
|
+
keyword_init: true
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
EbarimtItem = Struct.new(
|
|
207
|
+
:id, :barimt_id, :merchant_product_code, :tax_product_code,
|
|
208
|
+
:bar_code, :name, :unit_price, :quantity,
|
|
209
|
+
:amount, :city_tax_amount, :vat_amount, :note,
|
|
210
|
+
:created_by, :created_date, :updated_by, :updated_date, :status,
|
|
211
|
+
keyword_init: true
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
EbarimtHistory = Struct.new(
|
|
215
|
+
:id, :barimt_id, :ebarimt_receiver_type, :ebarimt_receiver,
|
|
216
|
+
:ebarimt_register_no, :ebarimt_bill_id, :ebarimt_date,
|
|
217
|
+
:ebarimt_mac_address, :ebarimt_internal_code, :ebarimt_bill_type,
|
|
218
|
+
:ebarimt_qr_data, :ebarimt_lottery, :ebarimt_lottery_msg,
|
|
219
|
+
:ebarimt_error_code, :ebarimt_error_msg,
|
|
220
|
+
:ebarimt_response_code, :ebarimt_response_msg,
|
|
221
|
+
:note, :barimt_status, :barimt_status_date,
|
|
222
|
+
:ebarimt_sent_email, :ebarimt_receiver_phone, :tax_type,
|
|
223
|
+
:created_by, :created_date, :updated_by, :updated_date, :status,
|
|
224
|
+
keyword_init: true
|
|
225
|
+
)
|
|
226
|
+
end
|
data/lib/qpay/version.rb
ADDED
data/lib/qpay.rb
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "qpay/version"
|
|
4
|
+
require_relative "qpay/errors"
|
|
5
|
+
require_relative "qpay/config"
|
|
6
|
+
require_relative "qpay/models"
|
|
7
|
+
require_relative "qpay/endpoints/auth"
|
|
8
|
+
require_relative "qpay/endpoints/invoices"
|
|
9
|
+
require_relative "qpay/endpoints/payments"
|
|
10
|
+
require_relative "qpay/endpoints/ebarimt"
|
|
11
|
+
require_relative "qpay/client"
|
data/qpay.gemspec
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "lib/qpay/version"
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |spec|
|
|
6
|
+
spec.name = "qpay-sdk"
|
|
7
|
+
spec.version = QPay::VERSION
|
|
8
|
+
spec.authors = ["Usukhbayar"]
|
|
9
|
+
spec.email = ["coding.usukh@gmail.com"]
|
|
10
|
+
spec.license = "MIT"
|
|
11
|
+
|
|
12
|
+
spec.summary = "QPay V2 API Ruby SDK"
|
|
13
|
+
spec.description = "Ruby SDK for QPay V2 payment gateway API with auto token management."
|
|
14
|
+
spec.homepage = "https://github.com/qpay-sdk/qpay-ruby"
|
|
15
|
+
|
|
16
|
+
spec.required_ruby_version = ">= 3.0.0"
|
|
17
|
+
|
|
18
|
+
spec.files = Dir["lib/**/*.rb", "LICENSE", "qpay.gemspec"]
|
|
19
|
+
spec.require_paths = ["lib"]
|
|
20
|
+
|
|
21
|
+
spec.add_dependency "faraday", "~> 2.0"
|
|
22
|
+
|
|
23
|
+
spec.add_development_dependency "rake", "~> 13.0"
|
|
24
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
|
25
|
+
spec.add_development_dependency "webmock", "~> 3.0"
|
|
26
|
+
|
|
27
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
|
28
|
+
spec.metadata["source_code_uri"] = spec.homepage
|
|
29
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: qpay-sdk
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Usukhbayar
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2026-02-26 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: faraday
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '2.0'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - "~>"
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '2.0'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: rake
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - "~>"
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '13.0'
|
|
34
|
+
type: :development
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - "~>"
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '13.0'
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: rspec
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - "~>"
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '3.0'
|
|
48
|
+
type: :development
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - "~>"
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '3.0'
|
|
55
|
+
- !ruby/object:Gem::Dependency
|
|
56
|
+
name: webmock
|
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - "~>"
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '3.0'
|
|
62
|
+
type: :development
|
|
63
|
+
prerelease: false
|
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - "~>"
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: '3.0'
|
|
69
|
+
description: Ruby SDK for QPay V2 payment gateway API with auto token management.
|
|
70
|
+
email:
|
|
71
|
+
- coding.usukh@gmail.com
|
|
72
|
+
executables: []
|
|
73
|
+
extensions: []
|
|
74
|
+
extra_rdoc_files: []
|
|
75
|
+
files:
|
|
76
|
+
- LICENSE
|
|
77
|
+
- lib/qpay.rb
|
|
78
|
+
- lib/qpay/client.rb
|
|
79
|
+
- lib/qpay/config.rb
|
|
80
|
+
- lib/qpay/endpoints/auth.rb
|
|
81
|
+
- lib/qpay/endpoints/ebarimt.rb
|
|
82
|
+
- lib/qpay/endpoints/invoices.rb
|
|
83
|
+
- lib/qpay/endpoints/payments.rb
|
|
84
|
+
- lib/qpay/errors.rb
|
|
85
|
+
- lib/qpay/models.rb
|
|
86
|
+
- lib/qpay/version.rb
|
|
87
|
+
- qpay.gemspec
|
|
88
|
+
homepage: https://github.com/qpay-sdk/qpay-ruby
|
|
89
|
+
licenses:
|
|
90
|
+
- MIT
|
|
91
|
+
metadata:
|
|
92
|
+
homepage_uri: https://github.com/qpay-sdk/qpay-ruby
|
|
93
|
+
source_code_uri: https://github.com/qpay-sdk/qpay-ruby
|
|
94
|
+
post_install_message:
|
|
95
|
+
rdoc_options: []
|
|
96
|
+
require_paths:
|
|
97
|
+
- lib
|
|
98
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
99
|
+
requirements:
|
|
100
|
+
- - ">="
|
|
101
|
+
- !ruby/object:Gem::Version
|
|
102
|
+
version: 3.0.0
|
|
103
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
104
|
+
requirements:
|
|
105
|
+
- - ">="
|
|
106
|
+
- !ruby/object:Gem::Version
|
|
107
|
+
version: '0'
|
|
108
|
+
requirements: []
|
|
109
|
+
rubygems_version: 3.0.3.1
|
|
110
|
+
signing_key:
|
|
111
|
+
specification_version: 4
|
|
112
|
+
summary: QPay V2 API Ruby SDK
|
|
113
|
+
test_files: []
|