sadad 0.1.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/README.md +209 -0
- data/lib/generators/sadad/install_generator.rb +15 -0
- data/lib/generators/sadad/templates/sadad.rb +7 -0
- data/lib/sadad/api/api_base.rb +54 -0
- data/lib/sadad/api/bill.rb +37 -0
- data/lib/sadad/api/multiple_bills/create.rb +25 -0
- data/lib/sadad/api/multiple_bills/deactivate.rb +28 -0
- data/lib/sadad/api/multiple_bills.rb +11 -0
- data/lib/sadad/api/request.rb +152 -0
- data/lib/sadad/api/single_bill/create.rb +37 -0
- data/lib/sadad/api/single_bill/inquire.rb +23 -0
- data/lib/sadad/api/single_bill.rb +29 -0
- data/lib/sadad/configuration.rb +5 -0
- data/lib/sadad/errors.rb +71 -0
- data/lib/sadad/json_schemas/beneficiary.rb +20 -0
- data/lib/sadad/json_schemas/deactivate_bill.rb +32 -0
- data/lib/sadad/json_schemas/deactivate_invoice.rb +19 -0
- data/lib/sadad/json_schemas/inquire_bill.rb +24 -0
- data/lib/sadad/json_schemas/invoice.rb +26 -0
- data/lib/sadad/json_schemas/multiple_bills.rb +28 -0
- data/lib/sadad/json_schemas/payment_range.rb +18 -0
- data/lib/sadad/json_schemas/single_bill.rb +32 -0
- data/lib/sadad/json_schemas/types/decimal.rb +17 -0
- data/lib/sadad/json_schemas/types/enum.rb +15 -0
- data/lib/sadad/json_schemas/types/identifier.rb +21 -0
- data/lib/sadad/json_schemas/types/string.rb +21 -0
- data/lib/sadad/json_schemas/types/timestamp.rb +19 -0
- data/lib/sadad/json_schemas/types/uuid.rb +19 -0
- data/lib/sadad/json_schemas/validator.rb +63 -0
- data/lib/sadad/version.rb +3 -0
- data/lib/sadad.rb +75 -0
- metadata +127 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 54fb49202126ef2f736a0b0453132aacd8ec0c38ffdce64eb77b66e3cff580d7
|
4
|
+
data.tar.gz: 4bceae03cebf3de09de544649eb603c5e20eda8371dd28004a09768fa90a104c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: c19a0cba2a92d66c03741d4183167c18659d56c7f07a1ebd1852485fc2561b024fab8273cde210f4740d8a24903bc9bf15da1c75d1b7d9853f95a140d1a7776b
|
7
|
+
data.tar.gz: db66ce797a97366442ce423da19b6ac28cc16a69a503f23b4d6bbd573fd80b249af51ab3ea1a9830841fa0d348f392e257a2d1dae6e7c766dd7ef5e35ca32789
|
data/README.md
ADDED
@@ -0,0 +1,209 @@
|
|
1
|
+
# Sadad Payments Gem
|
2
|
+
|
3
|
+
Ruby gem for integrating with Sadad payment gateway. This gem provides complete functionality for creating and managing bills through Sadad's API.
|
4
|
+
|
5
|
+
## Table of Contents
|
6
|
+
1. [Installation](#installation)
|
7
|
+
2. [Configuration](#configuration)
|
8
|
+
3. [API Usage](#api-usage)
|
9
|
+
1. [Single Bill Operations](#single-bill-operations)
|
10
|
+
1. [Create Bill](#create-bill)
|
11
|
+
2. [Update Bill](#update-bill)
|
12
|
+
3. [Inquiry Bill](#inquiry-bill)
|
13
|
+
4. [Expire Bill](#expire-bill)
|
14
|
+
2. [Multiple Bills Operations](#multiple-bills-operations)
|
15
|
+
1. [Create Bills](#create-bills)
|
16
|
+
2. [Deactivate Bill](#deactivate-bill)
|
17
|
+
4. [Error Handling](#error-handling)
|
18
|
+
5. [Contributing](#contributing)
|
19
|
+
6. [License](#license)
|
20
|
+
|
21
|
+
|
22
|
+
## Installation
|
23
|
+
|
24
|
+
Install the gem and add to the application's Gemfile by executing:
|
25
|
+
|
26
|
+
$ bundle add sadad
|
27
|
+
|
28
|
+
If bundler is not being used to manage dependencies, install the gem by executing:
|
29
|
+
|
30
|
+
$ gem install sadad
|
31
|
+
|
32
|
+
|
33
|
+
Next, you need to run the generator:
|
34
|
+
|
35
|
+
$ rails generate sadad:install
|
36
|
+
|
37
|
+
**Note:** This will create a `config/initializers/sadad.rb` file with the default configuration.
|
38
|
+
|
39
|
+
|
40
|
+
## Configuration
|
41
|
+
|
42
|
+
Configure the gem with your configuration
|
43
|
+
|
44
|
+
```ruby
|
45
|
+
Sadad.configure do |config|
|
46
|
+
config.merchant_id = "merchant_id"
|
47
|
+
config.program_id = "program_id"
|
48
|
+
config.client_certificate_pem_file = "client_certificate_pem_file"
|
49
|
+
end
|
50
|
+
```
|
51
|
+
|
52
|
+
For `merchant_id`, `program_id`, and `client_certificate_pem_file`, you can set its value in 3 ways
|
53
|
+
- Global across whole application (through initializer and configuration)
|
54
|
+
```ruby
|
55
|
+
Sadad.configure do |config|
|
56
|
+
config.merchant_id = "merchant_id"
|
57
|
+
config.program_id = "program_id"
|
58
|
+
config.client_certificate_pem_file = "client_certificate_pem_file"
|
59
|
+
end
|
60
|
+
```
|
61
|
+
|
62
|
+
- Per method or block (through the application variables). All consecutive requests will use that token until it is set to a different value
|
63
|
+
```ruby
|
64
|
+
Sadad.merchant_id = "merchant_id"
|
65
|
+
Sadad.program_id = "program_id"
|
66
|
+
Sadad.client_certificate_pem_file = "client_certificate_pem_file"
|
67
|
+
```
|
68
|
+
|
69
|
+
- Per request (through opts parameter)
|
70
|
+
```ruby
|
71
|
+
Sadad::Bills.create(opts: { merchant_id: "merchant_id", program_id: "program_id", client_certificate_pem_file: "client_certificate_pem_file" })
|
72
|
+
```
|
73
|
+
|
74
|
+
|
75
|
+
## API Usage
|
76
|
+
|
77
|
+
Any API call will return an object with following methods:
|
78
|
+
|
79
|
+
```ruby
|
80
|
+
result = Sadad.doSomething
|
81
|
+
result.success?
|
82
|
+
result.failure?
|
83
|
+
result.payload
|
84
|
+
result.error
|
85
|
+
```
|
86
|
+
|
87
|
+
### Single Bill Operations
|
88
|
+
|
89
|
+
#### Create Bill
|
90
|
+
|
91
|
+
```ruby
|
92
|
+
Sadad::SingleBill.create(
|
93
|
+
user_national_id: "1133331101",
|
94
|
+
user_first_name: "Khaled",
|
95
|
+
user_last_name: "Ahmed",
|
96
|
+
invoice_id: "1998654321",
|
97
|
+
bill_type: "OneTime",
|
98
|
+
display_info: "Details of the invoice issued",
|
99
|
+
amount_due: 275.25,
|
100
|
+
invoice_created_date: "2025-02-23T10:46:37",
|
101
|
+
expiry_date: "2025-03-23T10:46:37"
|
102
|
+
opts: {}
|
103
|
+
)
|
104
|
+
```
|
105
|
+
|
106
|
+
#### Update Bill
|
107
|
+
|
108
|
+
```ruby
|
109
|
+
Sadad::SingleBill.update(
|
110
|
+
user_national_id: "1133331101",
|
111
|
+
user_first_name: "Khaled",
|
112
|
+
user_last_name: "Ahmed",
|
113
|
+
invoice_id: "1998654321",
|
114
|
+
bill_type: "OneTime",
|
115
|
+
display_info: "Details of the invoice issued",
|
116
|
+
amount_due: 275.25,
|
117
|
+
invoice_created_date: "2025-02-23T10:46:37",
|
118
|
+
expiry_date: "2025-03-23T10:46:37",
|
119
|
+
opts: {}
|
120
|
+
)
|
121
|
+
```
|
122
|
+
|
123
|
+
#### Expire Bill
|
124
|
+
|
125
|
+
```ruby
|
126
|
+
Sadad::SingleBill.expire(
|
127
|
+
user_national_id: "1133331101",
|
128
|
+
user_first_name: "Khaled",
|
129
|
+
user_last_name: "Ahmed",
|
130
|
+
invoice_id: "1998654321",
|
131
|
+
bill_type: "OneTime",
|
132
|
+
display_info: "Details of the invoice issued",
|
133
|
+
amount_due: 275.25,
|
134
|
+
invoice_created_date: "2025-02-23T10:46:37",
|
135
|
+
expiry_date: "2025-03-23T10:46:37",
|
136
|
+
opts: {}
|
137
|
+
)
|
138
|
+
```
|
139
|
+
|
140
|
+
#### Inquiry Bill
|
141
|
+
|
142
|
+
```ruby
|
143
|
+
Sadad::SingleBill.inquiry(
|
144
|
+
sadad_number: "1998654321",
|
145
|
+
opts: {}
|
146
|
+
)
|
147
|
+
```
|
148
|
+
|
149
|
+
|
150
|
+
### Multiple Bills Operations
|
151
|
+
|
152
|
+
#### Create Bills
|
153
|
+
|
154
|
+
```ruby
|
155
|
+
invoices = [
|
156
|
+
{
|
157
|
+
user_national_id: "1133331101",
|
158
|
+
user_first_name: "Khaled",
|
159
|
+
user_last_name: "Ahmed",
|
160
|
+
invoice_id: "1998054111",
|
161
|
+
invoice_status: "BillNew",
|
162
|
+
bill_type: "OneTime",
|
163
|
+
display_info: "Details of the invoice issued",
|
164
|
+
amount_due: 275.25,
|
165
|
+
invoice_created_date: "2025-02-23T10:46:37",
|
166
|
+
expiry_date: "2025-03-23T10:46:37"
|
167
|
+
},
|
168
|
+
{
|
169
|
+
user_national_id: "1983331102",
|
170
|
+
user_first_name: "Ahmed",
|
171
|
+
user_last_name: "Ali",
|
172
|
+
invoice_id: "1998254321",
|
173
|
+
invoice_status: "BillNew",
|
174
|
+
bill_type: "Recurring",
|
175
|
+
display_info: "Monthly subscription payment",
|
176
|
+
amount_due: 5000.00,
|
177
|
+
invoice_created_date: "2025-02-23T10:46:37",
|
178
|
+
expiry_date: "2025-03-23T10:46:37",
|
179
|
+
minimum_partial_amount: 500
|
180
|
+
}
|
181
|
+
]
|
182
|
+
|
183
|
+
Sadad::MultipleBills.create(invoices, opts: {})
|
184
|
+
```
|
185
|
+
|
186
|
+
#### Deactivate Bill
|
187
|
+
|
188
|
+
```ruby
|
189
|
+
Sadad::MultipleBills.deactivate(sadad_numbers: ["1998654321", "1998654322"], opts: {})
|
190
|
+
```
|
191
|
+
|
192
|
+
## Errors:
|
193
|
+
Errors could be one of the following:
|
194
|
+
|
195
|
+
```ruby
|
196
|
+
Sadad::ForbiddenError
|
197
|
+
Sadad::APIConnectionError
|
198
|
+
Sadad::APIError
|
199
|
+
Sadad::InvalidRequestError (With `param` attribute)
|
200
|
+
```
|
201
|
+
|
202
|
+
## Contributing
|
203
|
+
|
204
|
+
Bug reports and pull requests are welcome.
|
205
|
+
|
206
|
+
## License
|
207
|
+
|
208
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
209
|
+
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require "rails/generators"
|
2
|
+
|
3
|
+
module Sadad
|
4
|
+
module Generators
|
5
|
+
class InstallGenerator < Rails::Generators::Base
|
6
|
+
source_root File.expand_path("templates", __dir__)
|
7
|
+
|
8
|
+
desc "Creates a Sadad initializers file."
|
9
|
+
|
10
|
+
def copy_initializer
|
11
|
+
copy_file "sadad.rb", "config/initializers/sadad.rb"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module Sadad
|
2
|
+
class ApiBase
|
3
|
+
include Sadad::Request
|
4
|
+
include Sadad::JsonSchemas::Validator
|
5
|
+
|
6
|
+
Response = Struct.new(:success?, :payload, :error) do
|
7
|
+
def failure?
|
8
|
+
!success?
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize(**params)
|
13
|
+
@params = params
|
14
|
+
@opts = filtered_opts(params[:opts])
|
15
|
+
end
|
16
|
+
|
17
|
+
def call
|
18
|
+
validate_params!
|
19
|
+
@response = call_api
|
20
|
+
handle_response_error
|
21
|
+
handle_sadad_response
|
22
|
+
rescue StandardError => e
|
23
|
+
failure(e)
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def filtered_opts(opts)
|
29
|
+
opts&.reject { |_k, v| v&.empty? } || {}
|
30
|
+
end
|
31
|
+
|
32
|
+
def call_api
|
33
|
+
::Faraday
|
34
|
+
.new(Sadad.base_uri, ssl: ssl_client_certificate)
|
35
|
+
.post(uri_path, api_params, headers)
|
36
|
+
end
|
37
|
+
|
38
|
+
def uri_path
|
39
|
+
raise NotImplementedError, "Subclasses must implement uri_path"
|
40
|
+
end
|
41
|
+
|
42
|
+
def request_body
|
43
|
+
raise NotImplementedError, "Subclasses must implement request_body"
|
44
|
+
end
|
45
|
+
|
46
|
+
def success(payload = nil)
|
47
|
+
Response.new(true, payload)
|
48
|
+
end
|
49
|
+
|
50
|
+
def failure(exception)
|
51
|
+
Response.new(false, nil, exception)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Sadad
|
2
|
+
module Api
|
3
|
+
class Bill
|
4
|
+
def self.request_body(params)
|
5
|
+
sanity_checks!(params)
|
6
|
+
|
7
|
+
{
|
8
|
+
Beneficiary: {
|
9
|
+
Id: params[:user_national_id],
|
10
|
+
FirstName: params[:user_first_name],
|
11
|
+
LastName: params[:user_last_name]
|
12
|
+
},
|
13
|
+
InvoiceId: params[:invoice_id],
|
14
|
+
InvoiceStatus: params[:invoice_status],
|
15
|
+
BillType: params[:bill_type],
|
16
|
+
DisplayInfo: params[:display_info] || "Details of invoice number: #{params[:invoice_id]}",
|
17
|
+
AmountDue: params[:amount_due],
|
18
|
+
CreateDate: format_time(params[:invoice_created_date]),
|
19
|
+
ExpiryDate: format_time(params[:expiry_date]),
|
20
|
+
PaymentRange: payment_range(params)
|
21
|
+
}.compact
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.format_time(time_string)
|
25
|
+
Time.parse(time_string).strftime("%Y-%m-%dT%H:%M:%S") if time_string
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.payment_range(params)
|
29
|
+
{ MinPartialAmount: params[:minimum_partial_amount] } if params[:minimum_partial_amount]
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.sanity_checks!(params)
|
33
|
+
raise Sadad::InvalidRequestError.new("params cannot be empty", params) if params.empty?
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Sadad
|
2
|
+
module MultipleBills
|
3
|
+
class Create < ApiBase
|
4
|
+
include Sadad::JsonSchemas::MultipleBills
|
5
|
+
|
6
|
+
def self.call(invoices:, opts: {})
|
7
|
+
new(invoices: invoices, opts: opts).call
|
8
|
+
end
|
9
|
+
|
10
|
+
def uri_path
|
11
|
+
"#{BILL_API_PREFIX}/ProcessMultiBills"
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def request_body
|
17
|
+
{
|
18
|
+
Invoices: @params[:invoices].map do |invoice|
|
19
|
+
Sadad::Api::Bill.request_body(invoice)
|
20
|
+
end
|
21
|
+
}
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Sadad
|
2
|
+
module MultipleBills
|
3
|
+
class Deactivate < ApiBase
|
4
|
+
include Sadad::JsonSchemas::DeactivateBill
|
5
|
+
|
6
|
+
def self.call(sadad_numbers:, opts: {})
|
7
|
+
new(sadad_numbers: sadad_numbers, opts: opts).call
|
8
|
+
end
|
9
|
+
|
10
|
+
def uri_path
|
11
|
+
"#{BILL_API_PREFIX}/DeactivateBill"
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def request_body
|
17
|
+
{
|
18
|
+
Invoices: @params[:sadad_numbers].map do |sadad_number|
|
19
|
+
{
|
20
|
+
SADADNumber: sadad_number,
|
21
|
+
InvoiceStatus: BILL_DEACTIVATED
|
22
|
+
}
|
23
|
+
end
|
24
|
+
}
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module Sadad
|
2
|
+
module MultipleBills
|
3
|
+
def self.create(invoices:, opts: {})
|
4
|
+
Sadad::MultipleBills::Create.call(invoices: invoices, opts: opts)
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.deactivate(sadad_numbers:, opts: {})
|
8
|
+
Sadad::MultipleBills::Deactivate.call(sadad_numbers: sadad_numbers, opts: opts)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,152 @@
|
|
1
|
+
module Sadad
|
2
|
+
module Request
|
3
|
+
def headers
|
4
|
+
@headers = {
|
5
|
+
"Content-Type": "application/json"
|
6
|
+
}
|
7
|
+
end
|
8
|
+
|
9
|
+
def ssl_client_certificate
|
10
|
+
@ssl_client_certificate = {
|
11
|
+
client_cert: OpenSSL::X509::Certificate.new(File.read(client_certificate_pem_file)),
|
12
|
+
client_key: OpenSSL::PKey::RSA.new(File.read(client_certificate_pem_file))
|
13
|
+
}
|
14
|
+
rescue StandardError
|
15
|
+
{}
|
16
|
+
end
|
17
|
+
|
18
|
+
def api_params
|
19
|
+
merchant_api_params.merge(request_body).to_json
|
20
|
+
end
|
21
|
+
|
22
|
+
def merchant_api_params
|
23
|
+
{
|
24
|
+
UUID: SecureRandom.uuid,
|
25
|
+
Timestamp: Time.now.strftime("%Y-%m-%dT%H:%M:%S"), ## formats to "2018-08-18T17:46:37"
|
26
|
+
MerchantId: merchant_id,
|
27
|
+
ProgramId: program_id
|
28
|
+
}
|
29
|
+
end
|
30
|
+
|
31
|
+
def program_id
|
32
|
+
@opts[:program_id] || Sadad.program_id || Sadad.configuration.program_id
|
33
|
+
end
|
34
|
+
|
35
|
+
def merchant_id
|
36
|
+
@opts[:merchant_id] || Sadad.merchant_id || Sadad.configuration.merchant_id
|
37
|
+
end
|
38
|
+
|
39
|
+
def client_certificate_pem_file
|
40
|
+
@opts[:client_certificate_pem_file] ||
|
41
|
+
Sadad.client_certificate_pem_file ||
|
42
|
+
Sadad.configuration.client_certificate_pem_file
|
43
|
+
end
|
44
|
+
|
45
|
+
def parsed_response
|
46
|
+
@parsed_response = begin
|
47
|
+
JSON.parse(@response.body)
|
48
|
+
rescue StandardError
|
49
|
+
{}
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def error_message
|
54
|
+
parsed_response.is_a?(Array) ? array_error_message : single_error_message
|
55
|
+
end
|
56
|
+
|
57
|
+
def error_code
|
58
|
+
if parsed_response.is_a?(Array)
|
59
|
+
error_item = parsed_response.select { |item| item["Status"] && item["Status"]["Code"] != 0 }
|
60
|
+
error_item&.map { |item| item["Status"]["Code"] }
|
61
|
+
else
|
62
|
+
parsed_response.dig("errors", 0, "error_code") || parsed_response.dig("Status", "Code")
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def handle_response_error
|
67
|
+
case response_status
|
68
|
+
when 400
|
69
|
+
raise_invalid_request_error
|
70
|
+
when 403
|
71
|
+
raise_forbidden_error
|
72
|
+
when 404
|
73
|
+
raise_not_found_error
|
74
|
+
when (400..599)
|
75
|
+
raise_api_error
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def handle_sadad_response
|
80
|
+
internal_error? ? failure(build_internal_error) : success(parsed_response)
|
81
|
+
end
|
82
|
+
|
83
|
+
private
|
84
|
+
|
85
|
+
def response_status
|
86
|
+
@response_status = @response.status
|
87
|
+
end
|
88
|
+
|
89
|
+
def response_body
|
90
|
+
@response_body = @response.body
|
91
|
+
end
|
92
|
+
|
93
|
+
def array_error_message
|
94
|
+
error_items = parsed_response.select { |item| item["Status"] && item["Status"]["Code"] != 0 }
|
95
|
+
error_items&.map { |item| item["Status"]["Description"] }&.join(", ")
|
96
|
+
end
|
97
|
+
|
98
|
+
def single_error_message
|
99
|
+
parsed_response["message"] || parsed_response.dig("Status", "Description")
|
100
|
+
end
|
101
|
+
|
102
|
+
def internal_error?
|
103
|
+
return false unless parsed_response.is_a?(Hash)
|
104
|
+
|
105
|
+
status = parsed_response["Status"] || {}
|
106
|
+
status["Code"].to_i != 0 || status["Severity"]&.downcase == "error"
|
107
|
+
end
|
108
|
+
|
109
|
+
def build_internal_error
|
110
|
+
status = parsed_response["Status"] || {}
|
111
|
+
APIError.new(
|
112
|
+
status["Description"],
|
113
|
+
http_status: response_status,
|
114
|
+
http_body: response_body,
|
115
|
+
error_code: status["Code"]
|
116
|
+
)
|
117
|
+
end
|
118
|
+
|
119
|
+
def raise_invalid_request_error
|
120
|
+
raise InvalidRequestError.new(
|
121
|
+
error_message,
|
122
|
+
error_code,
|
123
|
+
http_status: response_status,
|
124
|
+
http_body: response_body
|
125
|
+
)
|
126
|
+
end
|
127
|
+
|
128
|
+
def raise_forbidden_error
|
129
|
+
raise ForbiddenError.new(
|
130
|
+
"Invalid Credentials",
|
131
|
+
http_status: response_status,
|
132
|
+
http_body: response_body
|
133
|
+
)
|
134
|
+
end
|
135
|
+
|
136
|
+
def raise_not_found_error
|
137
|
+
raise APIError.new(
|
138
|
+
"Resource not found",
|
139
|
+
http_status: response_status,
|
140
|
+
http_body: response_body
|
141
|
+
)
|
142
|
+
end
|
143
|
+
|
144
|
+
def raise_api_error
|
145
|
+
raise APIError.new(
|
146
|
+
error_message,
|
147
|
+
http_status: response_status,
|
148
|
+
http_body: response_body
|
149
|
+
)
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Sadad
|
2
|
+
module SingleBill
|
3
|
+
class Create < ApiBase
|
4
|
+
include Sadad::JsonSchemas::SingleBill
|
5
|
+
|
6
|
+
def self.call(user_national_id:, user_first_name:, user_last_name:,
|
7
|
+
invoice_id:, invoice_status:, amount_due:, bill_type:,
|
8
|
+
invoice_created_date:, expiry_date:, display_info: nil,
|
9
|
+
minimum_partial_amount: nil, opts: {})
|
10
|
+
new(
|
11
|
+
user_national_id: user_national_id,
|
12
|
+
user_first_name: user_first_name,
|
13
|
+
user_last_name: user_last_name,
|
14
|
+
invoice_id: invoice_id,
|
15
|
+
invoice_status: invoice_status,
|
16
|
+
display_info: display_info,
|
17
|
+
amount_due: amount_due,
|
18
|
+
bill_type: bill_type,
|
19
|
+
invoice_created_date: invoice_created_date,
|
20
|
+
expiry_date: expiry_date,
|
21
|
+
minimum_partial_amount: minimum_partial_amount,
|
22
|
+
opts: opts
|
23
|
+
).call
|
24
|
+
end
|
25
|
+
|
26
|
+
def uri_path
|
27
|
+
"#{BILL_API_PREFIX}/CreateSingleBill"
|
28
|
+
end
|
29
|
+
|
30
|
+
def request_body
|
31
|
+
{
|
32
|
+
Invoice: Sadad::Api::Bill.request_body(@params)
|
33
|
+
}
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Sadad
|
2
|
+
module SingleBill
|
3
|
+
class Inquire < ApiBase
|
4
|
+
include Sadad::JsonSchemas::InquireBill
|
5
|
+
|
6
|
+
def self.call(sadad_number:, opts: {})
|
7
|
+
new(sadad_number: sadad_number, opts: opts).call
|
8
|
+
end
|
9
|
+
|
10
|
+
def uri_path
|
11
|
+
"#{BILL_API_PREFIX}/InquireBill"
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def request_body
|
17
|
+
{
|
18
|
+
SADADNumber: @params[:sadad_number]
|
19
|
+
}
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Sadad
|
2
|
+
module SingleBill
|
3
|
+
class << self
|
4
|
+
def create(params)
|
5
|
+
create_with_status(params, BILL_NEW)
|
6
|
+
end
|
7
|
+
|
8
|
+
def update(params)
|
9
|
+
create_with_status(params, BILL_UPDATED)
|
10
|
+
end
|
11
|
+
|
12
|
+
def expire(params)
|
13
|
+
create_with_status(params, BILL_EXPIRED)
|
14
|
+
end
|
15
|
+
|
16
|
+
def inquire(sadad_number:, opts: {})
|
17
|
+
Sadad::SingleBill::Inquire.call(sadad_number: sadad_number, opts: opts)
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def create_with_status(params, invoice_status)
|
23
|
+
Sadad::SingleBill::Create.call(
|
24
|
+
**params, invoice_status: invoice_status
|
25
|
+
)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/sadad/errors.rb
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
module Sadad
|
2
|
+
# SadadError is the base error from which all other more specific Sadad
|
3
|
+
# errors derive.
|
4
|
+
class SadadError < StandardError
|
5
|
+
attr_reader :message, :error, :http_body, :http_headers, :http_status, :json_body
|
6
|
+
|
7
|
+
# Initializes a SadadError.
|
8
|
+
def initialize(message = nil, http_status: nil, http_body: nil)
|
9
|
+
@message = message || http_body
|
10
|
+
@http_status = http_status
|
11
|
+
@http_body = http_body
|
12
|
+
begin
|
13
|
+
@json_body = JSON.parse(http_body)
|
14
|
+
rescue StandardError
|
15
|
+
@json_body = nil
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_s
|
20
|
+
status_string = @http_status.nil? ? "" : "(Status #{@http_status}) "
|
21
|
+
"#{status_string}#{@message}"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# ForbiddenError is raised when invalid merchant credentials are used to connect
|
26
|
+
# to Sadad's servers. Invalid merchant credentials includes:
|
27
|
+
# - Invalid Merchant ID
|
28
|
+
# - Invalid Program ID
|
29
|
+
# - Invalid Client Certificate file
|
30
|
+
class ForbiddenError < SadadError
|
31
|
+
end
|
32
|
+
|
33
|
+
# APIConnectionError is raised in the event that the SDK can't connect to
|
34
|
+
# Sadad's servers. That can be for a variety of different reasons from a
|
35
|
+
# downed network to a bad TLS certificate.
|
36
|
+
class APIConnectionError < SadadError
|
37
|
+
end
|
38
|
+
|
39
|
+
# APIError is raised when Sadad's API returns an error response. These errors
|
40
|
+
# can be caused by various issues like invalid parameters, authentication failures,
|
41
|
+
# rate limiting, or internal server errors. The error includes:
|
42
|
+
# - message: Human readable error description
|
43
|
+
# - http_status: HTTP status code (e.g. 400, 401, 429, 500)
|
44
|
+
# - http_body: Raw response body from the API
|
45
|
+
# - error_code: Sadad-specific error code for more granular error handling
|
46
|
+
class APIError < SadadError
|
47
|
+
attr_reader :error_code
|
48
|
+
|
49
|
+
def initialize(message = nil, http_status: nil, http_body: nil, error_code: nil)
|
50
|
+
super(message, http_status: http_status, http_body: http_body)
|
51
|
+
@error_code = error_code
|
52
|
+
end
|
53
|
+
|
54
|
+
def to_s
|
55
|
+
error_code_string = @error_code ? "(Error Code #{@error_code}) " : ""
|
56
|
+
status_string = @http_status ? "(Status #{@http_status}) " : ""
|
57
|
+
"#{status_string}#{error_code_string}#{@message}"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# InvalidRequestError is raised when a request is initiated with invalid
|
62
|
+
# parameters.
|
63
|
+
class InvalidRequestError < SadadError
|
64
|
+
attr_accessor :param
|
65
|
+
|
66
|
+
def initialize(message, param, http_status: nil, http_body: nil)
|
67
|
+
super(message, http_status: http_status, http_body: http_body)
|
68
|
+
@param = param
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Sadad
|
2
|
+
module JsonSchemas
|
3
|
+
module Beneficiary
|
4
|
+
include Validator
|
5
|
+
|
6
|
+
def self.schema
|
7
|
+
{
|
8
|
+
"$schema": "http://json-schema.org/draft-06/schema",
|
9
|
+
type: "object",
|
10
|
+
properties: {
|
11
|
+
Id: Types::Identifier.schema(min_length: 10, max_length: 10),
|
12
|
+
FirstName: Types::String.schema,
|
13
|
+
LastName: Types::String.schema
|
14
|
+
},
|
15
|
+
required: %w[Id FirstName LastName]
|
16
|
+
}
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Sadad
|
2
|
+
module JsonSchemas
|
3
|
+
module DeactivateBill
|
4
|
+
include Validator
|
5
|
+
|
6
|
+
private
|
7
|
+
|
8
|
+
def schema
|
9
|
+
{
|
10
|
+
"$schema": "http://json-schema.org/draft-06/schema",
|
11
|
+
type: "object",
|
12
|
+
properties: {
|
13
|
+
UUID: Types::Uuid.schema,
|
14
|
+
Timestamp: Types::Timestamp.schema,
|
15
|
+
MerchantId: Types::Identifier.schema,
|
16
|
+
ProgramId: Types::Identifier.schema,
|
17
|
+
Invoices: {
|
18
|
+
type: "array",
|
19
|
+
items: DeactivateInvoice.schema,
|
20
|
+
minItems: 1
|
21
|
+
}
|
22
|
+
},
|
23
|
+
required: required_fields
|
24
|
+
}
|
25
|
+
end
|
26
|
+
|
27
|
+
def required_fields
|
28
|
+
%w[UUID Timestamp MerchantId ProgramId Invoices]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Sadad
|
2
|
+
module JsonSchemas
|
3
|
+
module DeactivateInvoice
|
4
|
+
include Validator
|
5
|
+
|
6
|
+
def self.schema
|
7
|
+
{
|
8
|
+
"$schema": "http://json-schema.org/draft-06/schema",
|
9
|
+
type: "object",
|
10
|
+
properties: {
|
11
|
+
SADADNumber: Types::Identifier.schema,
|
12
|
+
InvoiceStatus: Types::Enum.schema(values: [BILL_DEACTIVATED])
|
13
|
+
},
|
14
|
+
required: %w[SADADNumber InvoiceStatus]
|
15
|
+
}
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Sadad
|
2
|
+
module JsonSchemas
|
3
|
+
module InquireBill
|
4
|
+
include Validator
|
5
|
+
|
6
|
+
private
|
7
|
+
|
8
|
+
def schema
|
9
|
+
{
|
10
|
+
"$schema": "http://json-schema.org/draft-06/schema",
|
11
|
+
type: "object",
|
12
|
+
properties: {
|
13
|
+
UUID: Types::Uuid.schema,
|
14
|
+
Timestamp: Types::Timestamp.schema,
|
15
|
+
MerchantId: Types::Identifier.schema,
|
16
|
+
ProgramId: Types::Identifier.schema,
|
17
|
+
SADADNumber: Types::Identifier.schema
|
18
|
+
},
|
19
|
+
required: %w[UUID Timestamp MerchantId ProgramId SADADNumber]
|
20
|
+
}
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Sadad
|
2
|
+
module JsonSchemas
|
3
|
+
module Invoice
|
4
|
+
include Validator
|
5
|
+
|
6
|
+
def self.schema
|
7
|
+
{
|
8
|
+
"$schema": "http://json-schema.org/draft-06/schema",
|
9
|
+
type: "object",
|
10
|
+
properties: {
|
11
|
+
Beneficiary: Beneficiary.schema,
|
12
|
+
InvoiceId: Types::Identifier.schema,
|
13
|
+
InvoiceStatus: Types::Enum.schema(values: INVOICE_STATUSES),
|
14
|
+
BillType: Types::Enum.schema(values: BILL_TYPES),
|
15
|
+
DisplayInfo: Types::String.schema(allows_null: true),
|
16
|
+
AmountDue: Types::Decimal.schema,
|
17
|
+
CreateDate: Types::Timestamp.schema,
|
18
|
+
ExpiryDate: Types::Timestamp.schema(allows_null: true),
|
19
|
+
PaymentRange: PaymentRange.schema(allows_null: true)
|
20
|
+
},
|
21
|
+
required: %w[Beneficiary InvoiceId InvoiceStatus BillType AmountDue CreateDate]
|
22
|
+
}
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Sadad
|
2
|
+
module JsonSchemas
|
3
|
+
module MultipleBills
|
4
|
+
include Validator
|
5
|
+
|
6
|
+
private
|
7
|
+
|
8
|
+
def schema
|
9
|
+
{
|
10
|
+
"$schema": "http://json-schema.org/draft-06/schema",
|
11
|
+
type: "object",
|
12
|
+
properties: {
|
13
|
+
UUID: Types::Uuid.schema,
|
14
|
+
Timestamp: Types::Timestamp.schema,
|
15
|
+
MerchantId: Types::Identifier.schema,
|
16
|
+
ProgramId: Types::Identifier.schema,
|
17
|
+
Invoices: {
|
18
|
+
type: "array",
|
19
|
+
items: Invoice.schema,
|
20
|
+
minItems: 1
|
21
|
+
}
|
22
|
+
},
|
23
|
+
required: %w[UUID Timestamp MerchantId ProgramId Invoices]
|
24
|
+
}
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Sadad
|
2
|
+
module JsonSchemas
|
3
|
+
module PaymentRange
|
4
|
+
include Validator
|
5
|
+
|
6
|
+
def self.schema(allows_null: false)
|
7
|
+
{
|
8
|
+
"$schema": "http://json-schema.org/draft-06/schema",
|
9
|
+
type: ["object", (allows_null ? "null" : nil)].compact,
|
10
|
+
properties: {
|
11
|
+
MinPartialAmount: Types::Decimal.schema(allows_null: allows_null)
|
12
|
+
},
|
13
|
+
required: ["MinPartialAmount"]
|
14
|
+
}
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Sadad
|
2
|
+
module JsonSchemas
|
3
|
+
module SingleBill
|
4
|
+
include Validator
|
5
|
+
|
6
|
+
private
|
7
|
+
|
8
|
+
def schema
|
9
|
+
{
|
10
|
+
"$schema": "http://json-schema.org/draft-06/schema",
|
11
|
+
type: "object",
|
12
|
+
properties: bill_properties,
|
13
|
+
required: required_fields
|
14
|
+
}
|
15
|
+
end
|
16
|
+
|
17
|
+
def bill_properties
|
18
|
+
{
|
19
|
+
UUID: Types::Uuid.schema,
|
20
|
+
Timestamp: Types::Timestamp.schema,
|
21
|
+
MerchantId: Types::Identifier.schema,
|
22
|
+
ProgramId: Types::Identifier.schema,
|
23
|
+
Invoice: Invoice.schema
|
24
|
+
}
|
25
|
+
end
|
26
|
+
|
27
|
+
def required_fields
|
28
|
+
%w[UUID Timestamp MerchantId ProgramId Invoice]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Sadad
|
2
|
+
module JsonSchemas
|
3
|
+
module Types
|
4
|
+
module Decimal
|
5
|
+
def self.schema(allows_null: false, min: 0, max: nil)
|
6
|
+
{
|
7
|
+
"$schema": "http://json-schema.org/draft-06/schema",
|
8
|
+
type: ["number", (allows_null ? "null" : nil)].compact,
|
9
|
+
minimum: min,
|
10
|
+
maximum: max,
|
11
|
+
multipleOf: 0.01
|
12
|
+
}.compact
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Sadad
|
2
|
+
module JsonSchemas
|
3
|
+
module Types
|
4
|
+
module Enum
|
5
|
+
def self.schema(values:, default: nil, allows_null: false)
|
6
|
+
{
|
7
|
+
"$schema": "http://json-schema.org/draft-06/schema",
|
8
|
+
enum: allows_null ? [*values, nil] : values,
|
9
|
+
default: default
|
10
|
+
}.compact
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Sadad
|
2
|
+
module JsonSchemas
|
3
|
+
module Types
|
4
|
+
module Identifier
|
5
|
+
def self.schema(min_length: 1, max_length: nil)
|
6
|
+
{
|
7
|
+
"$schema": "http://json-schema.org/draft-06/schema",
|
8
|
+
type: "string",
|
9
|
+
pattern: "^\\d+$",
|
10
|
+
minLength: min_length,
|
11
|
+
maxLength: max_length
|
12
|
+
}.compact
|
13
|
+
end
|
14
|
+
|
15
|
+
def schema
|
16
|
+
Sadad::JsonSchemas::Types::Identifier.schema
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Sadad
|
2
|
+
module JsonSchemas
|
3
|
+
module Types
|
4
|
+
module String
|
5
|
+
def self.schema(allows_null: false, max_length: nil, default: nil)
|
6
|
+
{
|
7
|
+
"$schema": "http://json-schema.org/draft-06/schema",
|
8
|
+
type: ["string", (allows_null ? "null" : nil)].compact,
|
9
|
+
minLength: allows_null ? 0 : 1,
|
10
|
+
maxLength: max_length,
|
11
|
+
default: default
|
12
|
+
}.compact
|
13
|
+
end
|
14
|
+
|
15
|
+
def schema
|
16
|
+
Sadad::JsonSchemas::Types::String.schema
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Sadad
|
2
|
+
module JsonSchemas
|
3
|
+
module Types
|
4
|
+
module Timestamp
|
5
|
+
def self.schema(allows_null: false)
|
6
|
+
{
|
7
|
+
"$schema": "http://json-schema.org/draft-06/schema",
|
8
|
+
type: ["string", (allows_null ? "null" : nil)].compact,
|
9
|
+
pattern: "^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}$"
|
10
|
+
}
|
11
|
+
end
|
12
|
+
|
13
|
+
def schema
|
14
|
+
Sadad::JsonSchemas::Types::Timestamp.schema
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Sadad
|
2
|
+
module JsonSchemas
|
3
|
+
module Types
|
4
|
+
module Uuid
|
5
|
+
def self.schema(allows_null: false)
|
6
|
+
{
|
7
|
+
"$schema": "http://json-schema.org/draft-06/schema",
|
8
|
+
type: ["string", (allows_null ? "null" : nil)].compact,
|
9
|
+
pattern: "^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$"
|
10
|
+
}
|
11
|
+
end
|
12
|
+
|
13
|
+
def schema
|
14
|
+
Sadad::JsonSchemas::Types::Uuid.schema
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module Sadad
|
2
|
+
module JsonSchemas
|
3
|
+
module Validator
|
4
|
+
ERROR_MESSAGES = {
|
5
|
+
Pattern: "%<property>s has invalid format",
|
6
|
+
Type: "%<property>s must be a %<type>s",
|
7
|
+
Required: "%<property>s is required",
|
8
|
+
Minimum: "%<property>s is too small",
|
9
|
+
Maximum: "%<property>s is too large",
|
10
|
+
Format: "%<property>s has invalid format",
|
11
|
+
Enum: "%<property>s must be one of: %<allowed_values>s"
|
12
|
+
}.freeze
|
13
|
+
|
14
|
+
def validate_params!
|
15
|
+
JSON::Validator.validate!(schema, api_params)
|
16
|
+
rescue JSON::Schema::ValidationError => e
|
17
|
+
handle_validation_error(e)
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def handle_validation_error(error)
|
23
|
+
error_key = normalize_error_key(error)
|
24
|
+
property = extract_property_name(error)
|
25
|
+
error_params = extract_error_params(error)
|
26
|
+
|
27
|
+
raise InvalidRequestError.new(
|
28
|
+
format_error_message(error_key, property, error_params),
|
29
|
+
error.message
|
30
|
+
)
|
31
|
+
end
|
32
|
+
|
33
|
+
def normalize_error_key(error)
|
34
|
+
error.failed_attribute.to_s
|
35
|
+
.split("::")
|
36
|
+
.last
|
37
|
+
.delete_suffix("Attribute")
|
38
|
+
end
|
39
|
+
|
40
|
+
def extract_property_name(error)
|
41
|
+
path = error.message[/'#\/([^']+)'/i, 1]
|
42
|
+
path&.humanize || "Field"
|
43
|
+
end
|
44
|
+
|
45
|
+
def extract_error_params(error)
|
46
|
+
schema_data = error.schema.instance_variable_get(:@schema)
|
47
|
+
{
|
48
|
+
type: schema_data["type"],
|
49
|
+
pattern: schema_data["pattern"],
|
50
|
+
allowed_values: Array(schema_data["enum"])&.join(", ")
|
51
|
+
}.compact
|
52
|
+
end
|
53
|
+
|
54
|
+
def format_error_message(error_key, property, error_params)
|
55
|
+
format(
|
56
|
+
ERROR_MESSAGES.fetch(error_key.to_sym, "%<property>s is invalid"),
|
57
|
+
property: property,
|
58
|
+
**error_params
|
59
|
+
)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
data/lib/sadad.rb
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
require "logger"
|
2
|
+
require "active_support"
|
3
|
+
require "active_support/core_ext/module"
|
4
|
+
require "active_support/core_ext/numeric/time"
|
5
|
+
require "faraday"
|
6
|
+
require "json-schema"
|
7
|
+
require "securerandom"
|
8
|
+
require "openssl"
|
9
|
+
require "json"
|
10
|
+
|
11
|
+
require_relative "sadad/version"
|
12
|
+
require_relative "sadad/errors"
|
13
|
+
|
14
|
+
# JSON Schemas
|
15
|
+
require "sadad/json_schemas/validator"
|
16
|
+
require "sadad/json_schemas/types/identifier"
|
17
|
+
require "sadad/json_schemas/types/uuid"
|
18
|
+
require "sadad/json_schemas/types/timestamp"
|
19
|
+
require "sadad/json_schemas/types/string"
|
20
|
+
require "sadad/json_schemas/types/decimal"
|
21
|
+
require "sadad/json_schemas/types/enum"
|
22
|
+
require "sadad/json_schemas/beneficiary"
|
23
|
+
require "sadad/json_schemas/invoice"
|
24
|
+
require "sadad/json_schemas/single_bill"
|
25
|
+
require "sadad/json_schemas/payment_range"
|
26
|
+
require "sadad/json_schemas/multiple_bills"
|
27
|
+
require "sadad/json_schemas/deactivate_bill"
|
28
|
+
require "sadad/json_schemas/deactivate_invoice"
|
29
|
+
require "sadad/json_schemas/inquire_bill"
|
30
|
+
|
31
|
+
# API
|
32
|
+
require "sadad/api/request"
|
33
|
+
require "sadad/configuration"
|
34
|
+
require "sadad/api/api_base"
|
35
|
+
require "sadad/api/bill"
|
36
|
+
require "sadad/api/single_bill/create"
|
37
|
+
require "sadad/api/single_bill/inquire"
|
38
|
+
require "sadad/api/single_bill"
|
39
|
+
require "sadad/api/multiple_bills/create"
|
40
|
+
require "sadad/api/multiple_bills/deactivate"
|
41
|
+
require "sadad/api/multiple_bills"
|
42
|
+
|
43
|
+
module Sadad
|
44
|
+
SANDBOX_URI = "https://rosomtest.brightware.com.sa/RosomAPI/".freeze
|
45
|
+
PRODUCTION_URI = "".freeze ## TODO: add production URI
|
46
|
+
|
47
|
+
BILL_API_PREFIX = "api/Bill".freeze
|
48
|
+
|
49
|
+
INVOICE_STATUSES = %w[BillNew BillUpdated BillExpired].freeze
|
50
|
+
BILL_TYPES = %w[OneTime Recurring].freeze
|
51
|
+
BILL_DEACTIVATED = "BillDeactivated".freeze
|
52
|
+
BILL_NEW = "BillNew".freeze
|
53
|
+
BILL_UPDATED = "BillUpdated".freeze
|
54
|
+
BILL_EXPIRED = "BillExpired".freeze
|
55
|
+
|
56
|
+
class << self
|
57
|
+
attr_accessor :merchant_id, :program_id, :client_certificate_pem_file
|
58
|
+
|
59
|
+
def base_uri
|
60
|
+
if defined?(Rails) && Rails.respond_to?(:env) && Rails.env.production?
|
61
|
+
PRODUCTION_URI
|
62
|
+
else
|
63
|
+
SANDBOX_URI
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def configure
|
68
|
+
yield configuration
|
69
|
+
end
|
70
|
+
|
71
|
+
def configuration
|
72
|
+
@configuration ||= Configuration.new
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
metadata
ADDED
@@ -0,0 +1,127 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: sadad
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Shimaa Marzouk Ali
|
8
|
+
- Elaraby Elaidy
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2025-04-22 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: activesupport
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - ">="
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: '5.2'
|
21
|
+
- - "<"
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: '7.0'
|
24
|
+
type: :runtime
|
25
|
+
prerelease: false
|
26
|
+
version_requirements: !ruby/object:Gem::Requirement
|
27
|
+
requirements:
|
28
|
+
- - ">="
|
29
|
+
- !ruby/object:Gem::Version
|
30
|
+
version: '5.2'
|
31
|
+
- - "<"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '7.0'
|
34
|
+
- !ruby/object:Gem::Dependency
|
35
|
+
name: faraday
|
36
|
+
requirement: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 1.10.3
|
41
|
+
type: :runtime
|
42
|
+
prerelease: false
|
43
|
+
version_requirements: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 1.10.3
|
48
|
+
- !ruby/object:Gem::Dependency
|
49
|
+
name: json-schema
|
50
|
+
requirement: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 4.3.1
|
55
|
+
type: :runtime
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 4.3.1
|
62
|
+
description: Integration with Sadad API! All required resources are included to make
|
63
|
+
your payment with Sadad
|
64
|
+
email:
|
65
|
+
- eng.shimaa1985@gmail.com
|
66
|
+
- elarabyelaidy@gmail.com
|
67
|
+
executables: []
|
68
|
+
extensions: []
|
69
|
+
extra_rdoc_files: []
|
70
|
+
files:
|
71
|
+
- README.md
|
72
|
+
- lib/generators/sadad/install_generator.rb
|
73
|
+
- lib/generators/sadad/templates/sadad.rb
|
74
|
+
- lib/sadad.rb
|
75
|
+
- lib/sadad/api/api_base.rb
|
76
|
+
- lib/sadad/api/bill.rb
|
77
|
+
- lib/sadad/api/multiple_bills.rb
|
78
|
+
- lib/sadad/api/multiple_bills/create.rb
|
79
|
+
- lib/sadad/api/multiple_bills/deactivate.rb
|
80
|
+
- lib/sadad/api/request.rb
|
81
|
+
- lib/sadad/api/single_bill.rb
|
82
|
+
- lib/sadad/api/single_bill/create.rb
|
83
|
+
- lib/sadad/api/single_bill/inquire.rb
|
84
|
+
- lib/sadad/configuration.rb
|
85
|
+
- lib/sadad/errors.rb
|
86
|
+
- lib/sadad/json_schemas/beneficiary.rb
|
87
|
+
- lib/sadad/json_schemas/deactivate_bill.rb
|
88
|
+
- lib/sadad/json_schemas/deactivate_invoice.rb
|
89
|
+
- lib/sadad/json_schemas/inquire_bill.rb
|
90
|
+
- lib/sadad/json_schemas/invoice.rb
|
91
|
+
- lib/sadad/json_schemas/multiple_bills.rb
|
92
|
+
- lib/sadad/json_schemas/payment_range.rb
|
93
|
+
- lib/sadad/json_schemas/single_bill.rb
|
94
|
+
- lib/sadad/json_schemas/types/decimal.rb
|
95
|
+
- lib/sadad/json_schemas/types/enum.rb
|
96
|
+
- lib/sadad/json_schemas/types/identifier.rb
|
97
|
+
- lib/sadad/json_schemas/types/string.rb
|
98
|
+
- lib/sadad/json_schemas/types/timestamp.rb
|
99
|
+
- lib/sadad/json_schemas/types/uuid.rb
|
100
|
+
- lib/sadad/json_schemas/validator.rb
|
101
|
+
- lib/sadad/version.rb
|
102
|
+
homepage: https://gitlab.com/autocloud/sadad_rails
|
103
|
+
licenses:
|
104
|
+
- MIT
|
105
|
+
metadata:
|
106
|
+
rubygems_mfa_required: 'true'
|
107
|
+
allowed_push_host: https://rubygems.org
|
108
|
+
post_install_message:
|
109
|
+
rdoc_options: []
|
110
|
+
require_paths:
|
111
|
+
- lib
|
112
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
113
|
+
requirements:
|
114
|
+
- - ">="
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
version: 2.6.1
|
117
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
118
|
+
requirements:
|
119
|
+
- - ">="
|
120
|
+
- !ruby/object:Gem::Version
|
121
|
+
version: '0'
|
122
|
+
requirements: []
|
123
|
+
rubygems_version: 3.2.3
|
124
|
+
signing_key:
|
125
|
+
specification_version: 4
|
126
|
+
summary: Integration with Sadad API
|
127
|
+
test_files: []
|