bayarcash 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.
@@ -0,0 +1,177 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bayarcash
4
+ # Callback verification, one verifier per callback type.
5
+ #
6
+ # Each verifier reconstructs the exact payload the gateway signs, computes the
7
+ # HMAC-SHA256 checksum and compares it against the supplied `checksum` using a
8
+ # constant-time comparison (see {Bayarcash::SecurityUtils.secure_compare}).
9
+ module CallbackVerifications
10
+ # Verify a direct-debit bank-approval callback.
11
+ #
12
+ # @param callback_data [Hash]
13
+ # @param secret_key [String]
14
+ # @return [Boolean]
15
+ def verify_direct_debit_bank_approval_callback_data(callback_data, secret_key)
16
+ payload = {
17
+ "record_type" => cb(callback_data, "record_type"),
18
+ "approval_date" => cb(callback_data, "approval_date"),
19
+ "approval_status" => cb(callback_data, "approval_status"),
20
+ "mandate_id" => cb(callback_data, "mandate_id"),
21
+ "mandate_reference_number" => cb(callback_data, "mandate_reference_number"),
22
+ "order_number" => cb(callback_data, "order_number"),
23
+ "payer_bank_code_hashed" => cb(callback_data, "payer_bank_code_hashed"),
24
+ "payer_bank_code" => cb(callback_data, "payer_bank_code"),
25
+ "payer_bank_account_no" => cb(callback_data, "payer_bank_account_no"),
26
+ "application_type" => cb(callback_data, "application_type")
27
+ }
28
+
29
+ verify_callback(callback_data, payload, secret_key)
30
+ end
31
+
32
+ # Verify a direct-debit authorization callback.
33
+ #
34
+ # NOTE: this payload MUST include `application_type` (the gateway signs it);
35
+ # omitting it causes every genuine authorization callback to fail verification.
36
+ #
37
+ # @param callback_data [Hash]
38
+ # @param secret_key [String]
39
+ # @return [Boolean]
40
+ def verify_direct_debit_authorization_callback_data(callback_data, secret_key)
41
+ payload = {
42
+ "record_type" => cb(callback_data, "record_type"),
43
+ "transaction_id" => cb(callback_data, "transaction_id"),
44
+ "mandate_id" => cb(callback_data, "mandate_id"),
45
+ "application_type" => cb(callback_data, "application_type"),
46
+ "exchange_reference_number" => cb(callback_data, "exchange_reference_number"),
47
+ "exchange_transaction_id" => cb(callback_data, "exchange_transaction_id"),
48
+ "order_number" => cb(callback_data, "order_number"),
49
+ "currency" => cb(callback_data, "currency"),
50
+ "amount" => cb(callback_data, "amount"),
51
+ "payer_name" => cb(callback_data, "payer_name"),
52
+ "payer_email" => cb(callback_data, "payer_email"),
53
+ "payer_bank_name" => cb(callback_data, "payer_bank_name"),
54
+ "status" => cb(callback_data, "status"),
55
+ "status_description" => cb(callback_data, "status_description"),
56
+ "datetime" => cb(callback_data, "datetime")
57
+ }
58
+
59
+ verify_callback(callback_data, payload, secret_key)
60
+ end
61
+
62
+ # Verify a direct-debit transaction callback.
63
+ #
64
+ # @param callback_data [Hash]
65
+ # @param secret_key [String]
66
+ # @return [Boolean]
67
+ def verify_direct_debit_transaction_callback_data(callback_data, secret_key)
68
+ payload = {
69
+ "record_type" => cb(callback_data, "record_type"),
70
+ "batch_number" => cb(callback_data, "batch_number"),
71
+ "mandate_id" => cb(callback_data, "mandate_id"),
72
+ "mandate_reference_number" => cb(callback_data, "mandate_reference_number"),
73
+ "transaction_id" => cb(callback_data, "transaction_id"),
74
+ "datetime" => cb(callback_data, "datetime"),
75
+ "reference_number" => cb(callback_data, "reference_number"),
76
+ "amount" => cb(callback_data, "amount"),
77
+ "status" => cb(callback_data, "status"),
78
+ "status_description" => cb(callback_data, "status_description"),
79
+ "cycle" => cb(callback_data, "cycle")
80
+ }
81
+
82
+ verify_callback(callback_data, payload, secret_key)
83
+ end
84
+
85
+ # Verify a transaction callback (sent to your callback_url).
86
+ #
87
+ # @param callback_data [Hash]
88
+ # @param secret_key [String]
89
+ # @return [Boolean]
90
+ def verify_transaction_callback_data(callback_data, secret_key)
91
+ payload = {
92
+ "record_type" => cb(callback_data, "record_type"),
93
+ "transaction_id" => cb(callback_data, "transaction_id"),
94
+ "exchange_reference_number" => cb(callback_data, "exchange_reference_number"),
95
+ "exchange_transaction_id" => cb(callback_data, "exchange_transaction_id"),
96
+ "order_number" => cb(callback_data, "order_number"),
97
+ "currency" => cb(callback_data, "currency"),
98
+ "amount" => cb(callback_data, "amount"),
99
+ "payer_name" => cb(callback_data, "payer_name"),
100
+ "payer_email" => cb(callback_data, "payer_email"),
101
+ "payer_bank_name" => cb(callback_data, "payer_bank_name"),
102
+ "status" => cb(callback_data, "status"),
103
+ "status_description" => cb(callback_data, "status_description"),
104
+ "datetime" => cb(callback_data, "datetime")
105
+ }
106
+
107
+ verify_callback(callback_data, payload, secret_key)
108
+ end
109
+
110
+ # Verify a return-url callback (payer redirect).
111
+ #
112
+ # @param callback_data [Hash]
113
+ # @param secret_key [String]
114
+ # @return [Boolean]
115
+ def verify_return_url_callback_data(callback_data, secret_key)
116
+ payload = {
117
+ "transaction_id" => cb(callback_data, "transaction_id"),
118
+ "exchange_reference_number" => cb(callback_data, "exchange_reference_number"),
119
+ "exchange_transaction_id" => cb(callback_data, "exchange_transaction_id"),
120
+ "order_number" => cb(callback_data, "order_number"),
121
+ "currency" => cb(callback_data, "currency"),
122
+ "amount" => cb(callback_data, "amount"),
123
+ "payer_bank_name" => cb(callback_data, "payer_bank_name"),
124
+ "status" => cb(callback_data, "status"),
125
+ "status_description" => cb(callback_data, "status_description")
126
+ }
127
+
128
+ verify_callback(callback_data, payload, secret_key)
129
+ end
130
+
131
+ # Verify a pre-transaction callback (sent before the transaction record).
132
+ #
133
+ # @param callback_data [Hash]
134
+ # @param secret_key [String]
135
+ # @return [Boolean]
136
+ def verify_pre_transaction_callback_data(callback_data, secret_key)
137
+ payload = {
138
+ "record_type" => cb(callback_data, "record_type"),
139
+ "exchange_reference_number" => cb(callback_data, "exchange_reference_number"),
140
+ "order_number" => cb(callback_data, "order_number")
141
+ }
142
+
143
+ verify_callback(callback_data, payload, secret_key)
144
+ end
145
+
146
+ private
147
+
148
+ # Reconstruct the checksum from the payload and compare it, constant-time,
149
+ # against the checksum supplied in the callback data.
150
+ def verify_callback(callback_data, payload, secret_key)
151
+ provided_checksum = cb(callback_data, "checksum") || ""
152
+
153
+ payload_string = payload
154
+ .sort_by { |key, _| key.to_s }
155
+ .map { |_, value| Bayarcash::Util.php_string(value) }
156
+ .join("|")
157
+
158
+ computed = OpenSSL::HMAC.hexdigest(
159
+ "SHA256",
160
+ Bayarcash::Util.php_string(secret_key),
161
+ payload_string
162
+ )
163
+
164
+ Bayarcash::SecurityUtils.secure_compare(computed, provided_checksum.to_s)
165
+ end
166
+
167
+ # Read a callback field that may use string or symbol keys; nil when absent.
168
+ def cb(callback_data, key)
169
+ return callback_data[key] if callback_data.key?(key)
170
+
171
+ symbol = key.to_sym
172
+ return callback_data[symbol] if callback_data.key?(symbol)
173
+
174
+ nil
175
+ end
176
+ end
177
+ end
@@ -0,0 +1,110 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bayarcash
4
+ # Checksum generation (HMAC-SHA256), byte-compatible with the gateway.
5
+ #
6
+ # The recipe is always the same: sort the payload hash by KEY ascending, join the
7
+ # VALUES with "|", then HMAC-SHA256 with the merchant secret key and return the
8
+ # lowercase hex digest.
9
+ module ChecksumGenerator
10
+ # Generic checksum primitive: ksort by key, implode values with "|", HMAC-SHA256.
11
+ #
12
+ # @param secret_key [String]
13
+ # @param payload [Hash]
14
+ # @return [String] lowercase hex HMAC-SHA256 digest
15
+ def create_checksum_value(secret_key, payload)
16
+ hmac_from_payload(secret_key, payload)
17
+ end
18
+
19
+ # Checksum for a payment intent request.
20
+ #
21
+ # Signs `payment_channel` (comma-joined when an array/multiple, empty when none),
22
+ # `order_number`, `amount`, `payer_name` and `payer_email`.
23
+ #
24
+ # @param secret_key [String]
25
+ # @param data [Hash]
26
+ # @return [String]
27
+ def create_payment_intent_checksum_value(secret_key, data)
28
+ payment_channel = fetch_field(data, "payment_channel", [])
29
+ payment_channel = [payment_channel] unless payment_channel.is_a?(Array)
30
+ payment_channel = payment_channel.join(",")
31
+
32
+ payload = {
33
+ "payment_channel" => payment_channel,
34
+ "order_number" => fetch_field(data, "order_number"),
35
+ "amount" => fetch_field(data, "amount"),
36
+ "payer_name" => fetch_field(data, "payer_name"),
37
+ "payer_email" => fetch_field(data, "payer_email")
38
+ }
39
+
40
+ hmac_from_payload(secret_key, payload)
41
+ end
42
+
43
+ # Old typo version, kept for backward compatibility.
44
+ #
45
+ # @see #create_payment_intent_checksum_value
46
+ def create_payment_inten_checksum_value(secret_key, data)
47
+ create_payment_intent_checksum_value(secret_key, data)
48
+ end
49
+
50
+ # Checksum for an FPX Direct Debit enrolment request.
51
+ #
52
+ # @param secret_key [String]
53
+ # @param data [Hash]
54
+ # @return [String]
55
+ def create_fpx_direct_debit_enrolment_checksum_value(secret_key, data)
56
+ payload = {
57
+ "order_number" => fetch_field(data, "order_number"),
58
+ "amount" => fetch_field(data, "amount"),
59
+ "payer_name" => fetch_field(data, "payer_name"),
60
+ "payer_email" => fetch_field(data, "payer_email"),
61
+ "payer_telephone_number" => fetch_field(data, "payer_telephone_number"),
62
+ "payer_id_type" => fetch_field(data, "payer_id_type"),
63
+ "payer_id" => fetch_field(data, "payer_id"),
64
+ "application_reason" => fetch_field(data, "application_reason"),
65
+ "frequency_mode" => fetch_field(data, "frequency_mode")
66
+ }
67
+
68
+ hmac_from_payload(secret_key, payload)
69
+ end
70
+
71
+ # Checksum for an FPX Direct Debit maintenance request.
72
+ #
73
+ # @param secret_key [String]
74
+ # @param data [Hash]
75
+ # @return [String]
76
+ def create_fpx_direct_debit_maintenance_checksum_value(secret_key, data)
77
+ payload = {
78
+ "amount" => fetch_field(data, "amount"),
79
+ "payer_email" => fetch_field(data, "payer_email"),
80
+ "payer_telephone_number" => fetch_field(data, "payer_telephone_number"),
81
+ "application_reason" => fetch_field(data, "application_reason"),
82
+ "frequency_mode" => fetch_field(data, "frequency_mode")
83
+ }
84
+
85
+ hmac_from_payload(secret_key, payload)
86
+ end
87
+
88
+ private
89
+
90
+ # Sort the payload by key, join stringified values with "|", HMAC-SHA256.
91
+ def hmac_from_payload(secret_key, payload)
92
+ payload_string = payload
93
+ .sort_by { |key, _| key.to_s }
94
+ .map { |_, value| Bayarcash::Util.php_string(value) }
95
+ .join("|")
96
+
97
+ OpenSSL::HMAC.hexdigest("SHA256", Bayarcash::Util.php_string(secret_key), payload_string)
98
+ end
99
+
100
+ # Read a field from a hash that may use string or symbol keys.
101
+ def fetch_field(data, key, default = nil)
102
+ return data[key] if data.key?(key)
103
+
104
+ symbol = key.to_sym
105
+ return data[symbol] if data.key?(symbol)
106
+
107
+ default
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,293 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bayarcash
4
+ # The Bayarcash API client.
5
+ #
6
+ # Mirrors the public surface of the official PHP SDK while remaining idiomatic
7
+ # Ruby and framework-agnostic.
8
+ #
9
+ # @example
10
+ # client = Bayarcash::Client.new("YOUR_API_TOKEN")
11
+ # client.use_sandbox.set_api_version("v3")
12
+ # intent = client.create_payment_intent(data)
13
+ # redirect_to intent.url
14
+ class Client
15
+ include MakesHttpRequests
16
+ include ChecksumGenerator
17
+ include CallbackVerifications
18
+ include FpxDirectDebitPaymentIntent
19
+ include ManualBankTransfer
20
+
21
+ # Payment channels
22
+ FPX = 1
23
+ MANUAL_TRANSFER = 2
24
+ FPX_DIRECT_DEBIT = 3
25
+ FPX_LINE_OF_CREDIT = 4
26
+ DUITNOW_DOBW = 5
27
+ DUITNOW_QR = 6
28
+ SPAYLATER = 7
29
+ BOOST_PAYFLEX = 8
30
+ QRISOB = 9
31
+ QRISWALLET = 10
32
+ NETS = 11
33
+ CREDIT_CARD = 12
34
+ ALIPAY = 13
35
+ WECHATPAY = 14
36
+ PROMPTPAY = 15
37
+ TOUCH_N_GO = 16
38
+ BOOST_WALLET = 17
39
+ GRABPAY = 18
40
+ GRABPL = 19
41
+ SHOPEE_PAY = 21
42
+
43
+ # Allowed filter keys for {#get_all_transactions}.
44
+ ALLOWED_TRANSACTION_FILTERS = %w[
45
+ order_number status payment_channel exchange_reference_number payer_email
46
+ ].freeze
47
+
48
+ # @return [String] the API token
49
+ attr_reader :token
50
+ # @return [Integer] request timeout in seconds
51
+ attr_reader :timeout
52
+ # @return [Boolean] whether the sandbox environment is in use
53
+ attr_reader :sandbox
54
+ # @return [String] the API version in use ("v2" or "v3")
55
+ attr_reader :api_version
56
+
57
+ # @param token [String] the Bayarcash API token
58
+ # @param sandbox [Boolean] use the sandbox environment
59
+ # @param api_version [String] "v2" (default) or "v3"
60
+ # @param timeout [Integer] request timeout in seconds
61
+ def initialize(token, sandbox: false, api_version: "v2", timeout: 30)
62
+ @token = token
63
+ @sandbox = sandbox
64
+ @api_version = api_version
65
+ @timeout = timeout
66
+ build_connection
67
+ end
68
+
69
+ # Set the API token and rebuild the HTTP client.
70
+ #
71
+ # @param token [String]
72
+ # @param connection [Faraday::Connection, nil] inject a custom connection (mainly for tests)
73
+ # @return [self]
74
+ def set_token(token, connection = nil)
75
+ @token = token
76
+ connection ? (@connection = connection) : build_connection
77
+ self
78
+ end
79
+
80
+ # Switch to the sandbox environment and rebuild the HTTP client.
81
+ #
82
+ # @param connection [Faraday::Connection, nil]
83
+ # @return [self]
84
+ def use_sandbox(connection = nil)
85
+ @sandbox = true
86
+ connection ? (@connection = connection) : build_connection
87
+ self
88
+ end
89
+
90
+ # @return [Boolean]
91
+ def sandbox?
92
+ @sandbox
93
+ end
94
+
95
+ # Set the request timeout and rebuild the HTTP client.
96
+ #
97
+ # @param timeout [Integer] seconds
98
+ # @return [self]
99
+ def set_timeout(timeout)
100
+ @timeout = timeout
101
+ build_connection
102
+ self
103
+ end
104
+
105
+ # @return [Integer]
106
+ def get_timeout
107
+ @timeout
108
+ end
109
+
110
+ # Set the API version ("v2" or "v3") and rebuild the HTTP client.
111
+ #
112
+ # @param version [String]
113
+ # @return [self]
114
+ def set_api_version(version)
115
+ @api_version = version
116
+ build_connection
117
+ self
118
+ end
119
+
120
+ # @return [String]
121
+ def get_api_version
122
+ @api_version
123
+ end
124
+
125
+ # Get the list of FPX banks.
126
+ #
127
+ # @return [Array<Bayarcash::Resources::FpxBankResource>]
128
+ def fpx_banks_list
129
+ transform_collection(get("banks"), Resources::FpxBankResource)
130
+ end
131
+
132
+ # Get the list of portals.
133
+ #
134
+ # @return [Array<Bayarcash::Resources::PortalResource>]
135
+ def get_portals
136
+ response = get("portals")
137
+ collection = response.is_a?(Hash) ? (response["data"] || response) : response
138
+ transform_collection(collection, Resources::PortalResource)
139
+ end
140
+
141
+ # Get the payment channels available for a portal by portal key.
142
+ #
143
+ # @param portal_key [String]
144
+ # @return [Array]
145
+ def get_channels(portal_key)
146
+ get_portals.each do |portal|
147
+ return portal.payment_channels if portal.portal_key == portal_key
148
+ end
149
+ []
150
+ end
151
+
152
+ # Create a new payment intent.
153
+ #
154
+ # @param data [Hash]
155
+ # @return [Bayarcash::Resources::PaymentIntentResource]
156
+ def create_payment_intent(data)
157
+ Resources::PaymentIntentResource.new(post("payment-intents", data), self)
158
+ end
159
+
160
+ # Get transaction details (v2 and v3).
161
+ #
162
+ # @param id [String]
163
+ # @return [Bayarcash::Resources::TransactionResource]
164
+ def get_transaction(id)
165
+ Resources::TransactionResource.new(get("transactions/#{id}"), self)
166
+ end
167
+
168
+ # Get a payment intent by id (v3 only).
169
+ #
170
+ # @param payment_intent_id [String]
171
+ # @return [Bayarcash::Resources::PaymentIntentResource]
172
+ # @raise [Bayarcash::Error] when the API version is not v3
173
+ def get_payment_intent(payment_intent_id)
174
+ ensure_v3!("getPaymentIntent")
175
+ Resources::PaymentIntentResource.new(get("payment-intents/#{payment_intent_id}"), self)
176
+ end
177
+
178
+ # Cancel a payment intent (v3 only).
179
+ #
180
+ # @param payment_intent_id [String]
181
+ # @return [Bayarcash::Resources::PaymentIntentResource]
182
+ # @raise [Bayarcash::Error] when the API version is not v3
183
+ def cancel_payment_intent(payment_intent_id)
184
+ ensure_v3!("cancelPaymentIntent")
185
+ Resources::PaymentIntentResource.new(delete("payment-intents/#{payment_intent_id}"), self)
186
+ end
187
+
188
+ # Get all transactions with optional filters (v3 only).
189
+ #
190
+ # @param parameters [Hash] any of :order_number, :status, :payment_channel,
191
+ # :exchange_reference_number, :payer_email
192
+ # @return [Hash] { data: Array<TransactionResource>, meta: Hash }
193
+ # @raise [Bayarcash::Error] when the API version is not v3
194
+ def get_all_transactions(parameters = {})
195
+ ensure_v3!("getAllTransactions")
196
+
197
+ query_params = parameters.select { |key, _| ALLOWED_TRANSACTION_FILTERS.include?(key.to_s) }
198
+ query_string = Bayarcash::Util.build_query(query_params)
199
+ endpoint = "transactions" + (query_string.empty? ? "" : "?#{query_string}")
200
+
201
+ response = get(endpoint)
202
+
203
+ {
204
+ data: transform_collection(response.is_a?(Hash) ? (response["data"] || []) : [], Resources::TransactionResource),
205
+ meta: response.is_a?(Hash) ? (response["meta"] || {}) : {}
206
+ }
207
+ end
208
+
209
+ # Get transactions by order number (v3 only).
210
+ #
211
+ # @param order_number [String]
212
+ # @return [Array<Bayarcash::Resources::TransactionResource>]
213
+ # @raise [Bayarcash::Error] when the API version is not v3
214
+ def get_transaction_by_order_number(order_number)
215
+ ensure_v3!("getTransactionByOrderNumber")
216
+ response = get("transactions?order_number=#{order_number}")
217
+ transform_collection(response.is_a?(Hash) ? (response["data"] || []) : [], Resources::TransactionResource)
218
+ end
219
+
220
+ # Get transactions by payer email (v3 only).
221
+ #
222
+ # @param email [String]
223
+ # @return [Array<Bayarcash::Resources::TransactionResource>]
224
+ # @raise [Bayarcash::Error] when the API version is not v3
225
+ def get_transactions_by_payer_email(email)
226
+ ensure_v3!("getTransactionsByPayerEmail")
227
+ response = get("transactions?payer_email=#{Bayarcash::Util.url_encode(email)}")
228
+ transform_collection(response.is_a?(Hash) ? (response["data"] || []) : [], Resources::TransactionResource)
229
+ end
230
+
231
+ # Get transactions by status (v3 only).
232
+ #
233
+ # @param status [String]
234
+ # @return [Array<Bayarcash::Resources::TransactionResource>]
235
+ # @raise [Bayarcash::Error] when the API version is not v3
236
+ def get_transactions_by_status(status)
237
+ ensure_v3!("getTransactionsByStatus")
238
+ response = get("transactions?status=#{status}")
239
+ transform_collection(response.is_a?(Hash) ? (response["data"] || []) : [], Resources::TransactionResource)
240
+ end
241
+
242
+ # Get transactions by payment channel (v3 only).
243
+ #
244
+ # @param channel [Integer]
245
+ # @return [Array<Bayarcash::Resources::TransactionResource>]
246
+ # @raise [Bayarcash::Error] when the API version is not v3
247
+ def get_transactions_by_payment_channel(channel)
248
+ ensure_v3!("getTransactionsByPaymentChannel")
249
+ response = get("transactions?payment_channel=#{channel}")
250
+ transform_collection(response.is_a?(Hash) ? (response["data"] || []) : [], Resources::TransactionResource)
251
+ end
252
+
253
+ # Get a single transaction by exchange reference number (v3 only).
254
+ #
255
+ # @param reference_number [String]
256
+ # @return [Bayarcash::Resources::TransactionResource, nil]
257
+ # @raise [Bayarcash::Error] when the API version is not v3
258
+ def get_transaction_by_reference_number(reference_number)
259
+ ensure_v3!("getTransactionByReferenceNumber")
260
+ response = get("transactions?exchange_reference_number=#{Bayarcash::Util.url_encode(reference_number)}")
261
+ data = response.is_a?(Hash) ? (response["data"] || []) : []
262
+ return nil if data.nil? || data.empty?
263
+
264
+ transform_collection(data, Resources::TransactionResource).first
265
+ end
266
+
267
+ # Return the base URI for the current API version and environment.
268
+ #
269
+ # @return [String]
270
+ def base_uri
271
+ if @api_version == "v3"
272
+ @sandbox ? "https://api.console.bayarcash-sandbox.com/v3/" : "https://api.console.bayar.cash/v3/"
273
+ else
274
+ @sandbox ? "https://console.bayarcash-sandbox.com/api/v2/" : "https://console.bayar.cash/api/v2/"
275
+ end
276
+ end
277
+
278
+ private
279
+
280
+ # Transform a raw collection into an array of resources.
281
+ def transform_collection(collection, klass, extra = {})
282
+ (collection || []).map do |data|
283
+ klass.new((data || {}).merge(extra), self)
284
+ end
285
+ end
286
+
287
+ def ensure_v3!(method_name)
288
+ return if @api_version == "v3"
289
+
290
+ raise Bayarcash::Error, "The #{method_name} method is only available for API version v3."
291
+ end
292
+ end
293
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bayarcash
4
+ module DuitNow
5
+ # DuitNow Online Banking / Wallet (DOBW) account types and status codes.
6
+ class Dobw
7
+ CASA = "01"
8
+ CREDIT_CARD = "02"
9
+ EWALLET = "03"
10
+
11
+ # Status code
12
+ STATUS_NEW = 0
13
+ STATUS_PENDING = 1
14
+ STATUS_FAILED = 2
15
+ STATUS_SUCCESS = 3
16
+ STATUS_CANCELLED = 4
17
+
18
+ STATUS_LABELS = {
19
+ STATUS_NEW => "New",
20
+ STATUS_PENDING => "Pending",
21
+ STATUS_CANCELLED => "Cancelled",
22
+ STATUS_SUCCESS => "Successful",
23
+ STATUS_FAILED => "Failed"
24
+ }.freeze
25
+
26
+ # @param status_code [Integer]
27
+ # @return [String] label for the status, or "UNKNOWN STATUS"
28
+ def self.get_status_text(status_code)
29
+ STATUS_LABELS.fetch(status_code.to_i, "UNKNOWN STATUS")
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bayarcash
4
+ # Base class for every error raised by the SDK.
5
+ class Error < StandardError; end
6
+
7
+ # Raised on HTTP 422 responses. Carries the validation error payload from the API.
8
+ class ValidationError < Error
9
+ # @return [Hash, Array] the raw errors payload returned by the API
10
+ attr_reader :errors
11
+
12
+ def initialize(errors = {})
13
+ @errors = errors || {}
14
+ super("The given data failed to pass validation.")
15
+ end
16
+ end
17
+
18
+ # Raised on HTTP 404 responses.
19
+ class NotFoundError < Error
20
+ def initialize(message = "The resource you are looking for could not be found.")
21
+ super
22
+ end
23
+ end
24
+
25
+ # Raised on HTTP 400 responses. The message holds the reason returned by the API.
26
+ class FailedActionError < Error; end
27
+
28
+ # Raised on HTTP 429 responses. Exposes the rate-limit reset timestamp when available.
29
+ class RateLimitExceededError < Error
30
+ # @return [Integer, nil] unix timestamp at which the rate limit resets
31
+ attr_reader :rate_limit_resets_at
32
+
33
+ def initialize(rate_limit_reset = nil)
34
+ @rate_limit_resets_at = rate_limit_reset
35
+ super("Too Many Requests.")
36
+ end
37
+ end
38
+
39
+ # Raised by the optional {Bayarcash::MakesHttpRequests#retry_until} helper after a timeout.
40
+ class TimeoutError < Error
41
+ # @return [Array] the output collected before the timeout
42
+ attr_reader :output
43
+
44
+ def initialize(output = [])
45
+ @output = output
46
+ super("Script timed out while waiting for the process to complete.")
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bayarcash
4
+ # FPX transaction status codes and their human-readable labels.
5
+ class Fpx
6
+ STATUS_NEW = 0
7
+ STATUS_PENDING = 1
8
+ STATUS_FAILED = 2
9
+ STATUS_SUCCESS = 3
10
+ STATUS_CANCELLED = 4
11
+
12
+ STATUS_LABELS = {
13
+ STATUS_NEW => "New",
14
+ STATUS_PENDING => "Pending",
15
+ STATUS_CANCELLED => "Cancelled",
16
+ STATUS_SUCCESS => "Successful",
17
+ STATUS_FAILED => "Failed"
18
+ }.freeze
19
+
20
+ # @param status_code [Integer]
21
+ # @return [String] label for the status, or "UNKNOWN STATUS"
22
+ def self.get_status_text(status_code)
23
+ STATUS_LABELS.fetch(status_code.to_i, "UNKNOWN STATUS")
24
+ end
25
+ end
26
+ end