sealine_insurance 0.1.1 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +151 -1
- data/lib/sealine_insurance/client.rb +16 -62
- data/lib/sealine_insurance/operations/base.rb +59 -0
- data/lib/sealine_insurance/operations/calculate.rb +49 -0
- data/lib/sealine_insurance/operations/cancel_order.rb +36 -0
- data/lib/sealine_insurance/operations/create_order.rb +82 -0
- data/lib/sealine_insurance/operations/create_payment.rb +35 -0
- data/lib/sealine_insurance/request.rb +4 -0
- data/lib/sealine_insurance/responses/base.rb +77 -0
- data/lib/sealine_insurance/responses/calculate.rb +29 -0
- data/lib/sealine_insurance/responses/order.rb +43 -0
- data/lib/sealine_insurance/responses/payment.rb +25 -0
- data/lib/sealine_insurance/version.rb +1 -1
- data/lib/sealine_insurance.rb +9 -3
- metadata +11 -5
- data/lib/sealine_insurance/base_response.rb +0 -73
- data/lib/sealine_insurance/calculate_response.rb +0 -37
- data/lib/sealine_insurance/order_response.rb +0 -39
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 899ba793d64bc085d6d163ccf4c9f33d708e3f6b
|
4
|
+
data.tar.gz: 954959b1f88ae37f6694bf3456b22fb3b80a1b4e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7d9eac9bc6fe8b5e90326227b94348251e1e3342e06cf1e80c054f34319608be484cc0f38a166bd581700f16bffb88095a57a01f97b6e1c6bcf9fbc40e9e59d6
|
7
|
+
data.tar.gz: 61a12fe07c3bd971d8877fae0529d01494c300e880d15aabf763db680ca5b61854b5173ae010456311b4581a55d1665e1975227419d3162005e54a1458b96e4a
|
data/README.md
CHANGED
@@ -18,7 +18,157 @@ Or install it yourself as:
|
|
18
18
|
|
19
19
|
## Usage
|
20
20
|
|
21
|
-
|
21
|
+
Перед началом работы нужно создать клиента, задав необходимые настройки:
|
22
|
+
|
23
|
+
```ruby
|
24
|
+
client = SealineInsurance::Client.new(
|
25
|
+
token: '0123456789abcdef', # required
|
26
|
+
host: 'sealine.ru', # optional, default: dev.sealine.ru
|
27
|
+
timeout: 10, # optional
|
28
|
+
open_timeout: 15, # optional
|
29
|
+
logger: Logger.new(...), # optional
|
30
|
+
)
|
31
|
+
```
|
32
|
+
|
33
|
+
### 1. Запрос информации
|
34
|
+
|
35
|
+
Запрос типов продуктов:
|
36
|
+
|
37
|
+
```ruby
|
38
|
+
result = client.product_types
|
39
|
+
result.success?
|
40
|
+
# => true
|
41
|
+
result.body
|
42
|
+
# => {
|
43
|
+
# "meta": {
|
44
|
+
# ...
|
45
|
+
# },
|
46
|
+
# "objects": [
|
47
|
+
# {
|
48
|
+
# "id": 6,
|
49
|
+
# "code": "transport",
|
50
|
+
# "title": "НС пассажирских перевозок",
|
51
|
+
# "details": "https://sealine.ru/api/v1/classifiers/product-type/6.json"
|
52
|
+
# }
|
53
|
+
# ]
|
54
|
+
# }
|
55
|
+
```
|
56
|
+
|
57
|
+
Аналогично можно запрашивать другую информацию:
|
58
|
+
|
59
|
+
```ruby
|
60
|
+
# списки статусов
|
61
|
+
client.calculate_status_list
|
62
|
+
client.order_status_list
|
63
|
+
client.payment_status_list
|
64
|
+
|
65
|
+
# продукты и их типы
|
66
|
+
client.product_types
|
67
|
+
client.product_type(id: 6)
|
68
|
+
client.products
|
69
|
+
client.search_products(product_type: 'transport')
|
70
|
+
|
71
|
+
# кастомные запросы информации
|
72
|
+
client.classifiers(path: 'product/vsk_trans')
|
73
|
+
client.classifiers(path: 'product/component-group')
|
74
|
+
```
|
75
|
+
|
76
|
+
### 2. Расчет стоимости страховки
|
77
|
+
|
78
|
+
Операция расчета асинхроная, в рамках одного расчета нужно выполнять несколько запросов к API. Эта логика инкапсулирована в класс операции.
|
79
|
+
|
80
|
+
Запуск расчета:
|
81
|
+
|
82
|
+
```ruby
|
83
|
+
operation = client.calculate(
|
84
|
+
product_type: 'transport',
|
85
|
+
products: %w[vsk_trans],
|
86
|
+
ticket_price: Money.new(100, 'RUB'),
|
87
|
+
)
|
88
|
+
```
|
89
|
+
|
90
|
+
Операция может быть завершена сразу, при первом запросе.
|
91
|
+
|
92
|
+
Метод `finished?` проверяет статус без дополнительного запроса к API:
|
93
|
+
|
94
|
+
```ruby
|
95
|
+
operation.finished?
|
96
|
+
# => false
|
97
|
+
```
|
98
|
+
|
99
|
+
Далее необходимо запрашивать актуальный статус из API до тех пор, пока `finished?` не вернет `true`.
|
100
|
+
|
101
|
+
**Метод `finished?` не делает дополнительных запросов к API**, за это отвечает метод `fetch_status!`:
|
102
|
+
|
103
|
+
```ruby
|
104
|
+
operation.fetch_status!
|
105
|
+
operation.finished?
|
106
|
+
# => false
|
107
|
+
|
108
|
+
sleep(2)
|
109
|
+
operation.fetch_status!
|
110
|
+
operation.finished?
|
111
|
+
# => true
|
112
|
+
```
|
113
|
+
|
114
|
+
После завершения операции можно получить ее результат:
|
115
|
+
|
116
|
+
```ruby
|
117
|
+
operation.success?
|
118
|
+
# => true
|
119
|
+
operation.result.status
|
120
|
+
# => 'DONE'
|
121
|
+
operation.result.price
|
122
|
+
# => #<Money fractional:70 currency:RUB>
|
123
|
+
```
|
124
|
+
|
125
|
+
Если с ошибкой:
|
126
|
+
|
127
|
+
```ruby
|
128
|
+
operation.success?
|
129
|
+
# => false
|
130
|
+
operation.result.error_code
|
131
|
+
# => 'unauthorized'
|
132
|
+
operation.result.error_message
|
133
|
+
# => 'Недопустимый токен.'
|
134
|
+
```
|
135
|
+
|
136
|
+
### 3. Создание страховки
|
137
|
+
|
138
|
+
```ruby
|
139
|
+
operation = client.create_order(
|
140
|
+
product_type: 'transport',
|
141
|
+
product: 'vsk_trans',
|
142
|
+
ticket_number: 12345678,
|
143
|
+
ticket_price: Money.new(100, 'RUB'),
|
144
|
+
departure_datetime: Time.new(2018, 8, 1, 10, 0, 0),
|
145
|
+
arrival_datetime: Time.new(2018, 8, 1, 18, 0, 0),
|
146
|
+
insured_first_name: 'Иван',
|
147
|
+
insured_last_name: 'Иванов',
|
148
|
+
insured_birthday: Date.new(1985, 1, 15),
|
149
|
+
insurer_first_name: 'Петр',
|
150
|
+
insurer_last_name: 'Петров',
|
151
|
+
)
|
152
|
+
```
|
153
|
+
|
154
|
+
Дальнейшие действия с `operation` аналогичны расчету стоимости.
|
155
|
+
|
156
|
+
### 4. Подтверждение страховки
|
157
|
+
|
158
|
+
```ruby
|
159
|
+
operation = client.create_payment(order_id: 7311)
|
160
|
+
```
|
161
|
+
|
162
|
+
Дальнейшие действия с `operation` аналогичны расчету стоимости.
|
163
|
+
|
164
|
+
### 5. Возврат страховки
|
165
|
+
|
166
|
+
```ruby
|
167
|
+
operation = client.cancel_order(order_id: 7311)
|
168
|
+
```
|
169
|
+
|
170
|
+
Дальнейшие действия с `operation` аналогичны расчету стоимости.
|
171
|
+
|
22
172
|
|
23
173
|
## License
|
24
174
|
|
@@ -2,8 +2,6 @@
|
|
2
2
|
|
3
3
|
module SealineInsurance
|
4
4
|
class Client
|
5
|
-
attr_reader :config
|
6
|
-
|
7
5
|
def initialize(**args)
|
8
6
|
@config = Config.new(**args)
|
9
7
|
end
|
@@ -20,10 +18,6 @@ module SealineInsurance
|
|
20
18
|
classifiers(path: 'product')
|
21
19
|
end
|
22
20
|
|
23
|
-
def component_groups
|
24
|
-
classifiers(path: 'product/component-group')
|
25
|
-
end
|
26
|
-
|
27
21
|
def calculate_status_list
|
28
22
|
classifiers(path: 'status/calculate-product')
|
29
23
|
end
|
@@ -38,7 +32,7 @@ module SealineInsurance
|
|
38
32
|
|
39
33
|
def classifiers(path:)
|
40
34
|
response = request.get("/classifiers/#{path}")
|
41
|
-
|
35
|
+
Responses::Base.new(response)
|
42
36
|
end
|
43
37
|
|
44
38
|
def search_products(product_type:, options: [])
|
@@ -46,74 +40,34 @@ module SealineInsurance
|
|
46
40
|
product_type: product_type,
|
47
41
|
options: options,
|
48
42
|
)
|
49
|
-
|
43
|
+
Responses::Base.new(response)
|
50
44
|
end
|
51
45
|
|
52
|
-
def calculate(
|
53
|
-
|
54
|
-
product_type: product_type,
|
55
|
-
products: products,
|
56
|
-
options: options,
|
57
|
-
data: {
|
58
|
-
ticket_price: ticket_price.to_i,
|
59
|
-
},
|
60
|
-
)
|
61
|
-
CalculateResponse.new(response)
|
46
|
+
def calculate(**args)
|
47
|
+
Operations::Calculate.new(config: @config, **args).tap(&:call)
|
62
48
|
end
|
63
49
|
|
64
|
-
def
|
65
|
-
|
66
|
-
CalculateResponse.new(response)
|
50
|
+
def create_order(**args)
|
51
|
+
Operations::CreateOrder.new(config: @config, **args).tap(&:call)
|
67
52
|
end
|
68
53
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
product:,
|
73
|
-
options: [],
|
74
|
-
ticket_number:,
|
75
|
-
ticket_price:,
|
76
|
-
departure_datetime:,
|
77
|
-
arrival_datetime:,
|
78
|
-
insured_first_name:,
|
79
|
-
insured_last_name:,
|
80
|
-
insured_birthday:,
|
81
|
-
insurer_first_name:,
|
82
|
-
insurer_last_name:
|
83
|
-
)
|
84
|
-
response = request.post('/order',
|
85
|
-
product_type: product_type,
|
86
|
-
product: product,
|
87
|
-
options: options,
|
88
|
-
data: {
|
89
|
-
ticket_number: ticket_number.to_i,
|
90
|
-
ticket_price: ticket_price.to_i,
|
91
|
-
departure: departure_datetime.strftime('%Y-%m-%dT%H:%M'),
|
92
|
-
arrival: arrival_datetime.strftime('%Y-%m-%dT%H:%M'),
|
93
|
-
insured: {
|
94
|
-
first_name: insured_first_name,
|
95
|
-
last_name: insured_last_name,
|
96
|
-
birthday: insured_birthday.strftime('%Y-%m-%d'),
|
97
|
-
},
|
98
|
-
insurer: {
|
99
|
-
first_name: insurer_first_name,
|
100
|
-
last_name: insurer_last_name,
|
101
|
-
},
|
102
|
-
},
|
103
|
-
)
|
104
|
-
OrderResponse.new(response)
|
54
|
+
def get_order(order_id:)
|
55
|
+
raw_response = request.get("/order/#{order_id}")
|
56
|
+
Responses::Order.new(raw_response)
|
105
57
|
end
|
106
|
-
# rubocop:enable Metrics/ParameterLists
|
107
58
|
|
108
|
-
def
|
109
|
-
|
110
|
-
|
59
|
+
def create_payment(**args)
|
60
|
+
Operations::CreatePayment.new(config: @config, **args).tap(&:call)
|
61
|
+
end
|
62
|
+
|
63
|
+
def cancel_order(**args)
|
64
|
+
Operations::CancelOrder.new(config: @config, **args).tap(&:call)
|
111
65
|
end
|
112
66
|
|
113
67
|
private
|
114
68
|
|
115
69
|
def request
|
116
|
-
@request ||= Request.new(config: config)
|
70
|
+
@request ||= Request.new(config: @config)
|
117
71
|
end
|
118
72
|
end
|
119
73
|
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Часть методов в API асинхронные. Класс предоставляет абстракцию для них.
|
4
|
+
# Работа с API выглядит примерно так:
|
5
|
+
# 1. Запрос на выполнение действия.
|
6
|
+
# 2. Запрос актуального статуса до тех пор, пока операция не будет завершена.
|
7
|
+
module SealineInsurance
|
8
|
+
module Operations
|
9
|
+
class Base
|
10
|
+
# Промежуточный ответ сервера
|
11
|
+
attr_reader :response
|
12
|
+
|
13
|
+
def initialize(config:)
|
14
|
+
@config = config
|
15
|
+
end
|
16
|
+
|
17
|
+
# Запуск операции
|
18
|
+
def call
|
19
|
+
raise NotImplementedError
|
20
|
+
end
|
21
|
+
|
22
|
+
# Получение актуального статуса выполнения и результата
|
23
|
+
def fetch_status!
|
24
|
+
raise NotImplementedError
|
25
|
+
end
|
26
|
+
|
27
|
+
# Завершена ли операция (с успехом или ошибкой)
|
28
|
+
def finished?
|
29
|
+
response.error? || finished_status_list.include?(response.status)
|
30
|
+
end
|
31
|
+
|
32
|
+
# Завершена ли операция с успехом
|
33
|
+
def success?
|
34
|
+
response.success? && success_status_list.include?(response.status)
|
35
|
+
end
|
36
|
+
|
37
|
+
# Окончательный результат операции (успешный или нет)
|
38
|
+
def result
|
39
|
+
response if finished?
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
# Массив статусов, соответствующий завершенной операции
|
45
|
+
def finished_status_list
|
46
|
+
raise NotImplementedError
|
47
|
+
end
|
48
|
+
|
49
|
+
# Массив статусов, соответствующий успешно завершенной операции
|
50
|
+
def success_status_list
|
51
|
+
raise NotImplementedError
|
52
|
+
end
|
53
|
+
|
54
|
+
def request
|
55
|
+
@request ||= Request.new(config: @config)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SealineInsurance
|
4
|
+
module Operations
|
5
|
+
class Calculate < Base
|
6
|
+
def initialize(config:, product_type:, products:, ticket_price:, options: [])
|
7
|
+
super(config: config)
|
8
|
+
@product_type = product_type
|
9
|
+
@products = products
|
10
|
+
@ticket_price = ticket_price
|
11
|
+
@options = options
|
12
|
+
end
|
13
|
+
|
14
|
+
def call
|
15
|
+
raw_response = request.post('/calculate-product',
|
16
|
+
product_type: @product_type,
|
17
|
+
products: @products,
|
18
|
+
options: @options,
|
19
|
+
data: {
|
20
|
+
ticket_price: @ticket_price.to_i,
|
21
|
+
},
|
22
|
+
)
|
23
|
+
@response = Responses::Calculate.new(raw_response)
|
24
|
+
@request_id = @response.request_id
|
25
|
+
end
|
26
|
+
|
27
|
+
def fetch_status!
|
28
|
+
return unless @request_id
|
29
|
+
|
30
|
+
raw_response = request.get("/calculate-product/#{@request_id}")
|
31
|
+
@response = Responses::Calculate.new(raw_response)
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def finished_status_list
|
37
|
+
@finished_status_list ||= [
|
38
|
+
'DONE', # Выполнено
|
39
|
+
'DONE_WITH_ERRORS', # Выполнено с ошибками
|
40
|
+
'ERROR', # Ошибка
|
41
|
+
]
|
42
|
+
end
|
43
|
+
|
44
|
+
def success_status_list
|
45
|
+
@success_status_list ||= ['DONE']
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SealineInsurance
|
4
|
+
module Operations
|
5
|
+
class CancelOrder < Base
|
6
|
+
def initialize(config:, order_id:)
|
7
|
+
super(config: config)
|
8
|
+
@order_id = order_id
|
9
|
+
end
|
10
|
+
|
11
|
+
def call
|
12
|
+
raw_response = request.delete("/order/#{@order_id}")
|
13
|
+
@response = Responses::Order.new(raw_response)
|
14
|
+
end
|
15
|
+
|
16
|
+
def fetch_status!
|
17
|
+
raw_response = request.get("/order/#{@order_id}")
|
18
|
+
@response = Responses::Order.new(raw_response)
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def finished_status_list
|
24
|
+
@finished_status_list ||= [
|
25
|
+
'REPEAT_CANCELLATION', # Требуется повтор отмены
|
26
|
+
'NOT_CANCELLED', # Не отменен
|
27
|
+
'CANCELLED', # Отменен
|
28
|
+
]
|
29
|
+
end
|
30
|
+
|
31
|
+
def success_status_list
|
32
|
+
@success_status_list ||= ['CANCELLED']
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SealineInsurance
|
4
|
+
module Operations
|
5
|
+
class CreateOrder < Base
|
6
|
+
# rubocop:disable Metrics/ParameterLists
|
7
|
+
def initialize(
|
8
|
+
config:,
|
9
|
+
product_type:,
|
10
|
+
product:,
|
11
|
+
options: [],
|
12
|
+
ticket_number:,
|
13
|
+
ticket_price:,
|
14
|
+
departure_datetime:,
|
15
|
+
arrival_datetime:,
|
16
|
+
insured_first_name:,
|
17
|
+
insured_last_name:,
|
18
|
+
insured_birthday:,
|
19
|
+
insurer_first_name:,
|
20
|
+
insurer_last_name:
|
21
|
+
)
|
22
|
+
super(config: config)
|
23
|
+
@product_type = product_type
|
24
|
+
@product = product
|
25
|
+
@options = options
|
26
|
+
@ticket_number = ticket_number
|
27
|
+
@ticket_price = ticket_price
|
28
|
+
@departure_datetime = departure_datetime
|
29
|
+
@arrival_datetime = arrival_datetime
|
30
|
+
@insured_first_name = insured_first_name
|
31
|
+
@insured_last_name = insured_last_name
|
32
|
+
@insured_birthday = insured_birthday
|
33
|
+
@insurer_first_name = insurer_first_name
|
34
|
+
@insurer_last_name = insurer_last_name
|
35
|
+
end
|
36
|
+
# rubocop:enable Metrics/ParameterLists
|
37
|
+
|
38
|
+
def call
|
39
|
+
raw_response = request.post('/order',
|
40
|
+
product_type: @product_type,
|
41
|
+
product: @product,
|
42
|
+
options: @options,
|
43
|
+
data: {
|
44
|
+
ticket_number: @ticket_number.to_i,
|
45
|
+
ticket_price: @ticket_price.to_i,
|
46
|
+
departure: @departure_datetime.strftime('%Y-%m-%dT%H:%M'),
|
47
|
+
arrival: @arrival_datetime.strftime('%Y-%m-%dT%H:%M'),
|
48
|
+
insured: {
|
49
|
+
first_name: @insured_first_name,
|
50
|
+
last_name: @insured_last_name,
|
51
|
+
birthday: @insured_birthday.strftime('%Y-%m-%d'),
|
52
|
+
},
|
53
|
+
insurer: {
|
54
|
+
first_name: @insurer_first_name,
|
55
|
+
last_name: @insurer_last_name,
|
56
|
+
},
|
57
|
+
},
|
58
|
+
)
|
59
|
+
@response = Responses::Order.new(raw_response)
|
60
|
+
@order_id = @response.order_id
|
61
|
+
end
|
62
|
+
|
63
|
+
def fetch_status!
|
64
|
+
raw_response = request.get("/order/#{@order_id}")
|
65
|
+
@response = Responses::Order.new(raw_response)
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
def finished_status_list
|
71
|
+
@finished_status_list ||= [
|
72
|
+
'ERROR', # Ошибка
|
73
|
+
'NEED_PAYMENT', # Требуется оплата
|
74
|
+
]
|
75
|
+
end
|
76
|
+
|
77
|
+
def success_status_list
|
78
|
+
@success_status_list ||= ['NEED_PAYMENT']
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SealineInsurance
|
4
|
+
module Operations
|
5
|
+
class CreatePayment < Base
|
6
|
+
def initialize(config:, order_id:)
|
7
|
+
super(config: config)
|
8
|
+
@order_id = order_id
|
9
|
+
end
|
10
|
+
|
11
|
+
def call
|
12
|
+
raw_response = request.post('/payment', order: @order_id)
|
13
|
+
@response = Responses::Payment.new(raw_response)
|
14
|
+
end
|
15
|
+
|
16
|
+
def fetch_status!
|
17
|
+
raw_response = request.get("/order/#{@order_id}")
|
18
|
+
@response = Responses::Order.new(raw_response)
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def finished_status_list
|
24
|
+
@finished_status_list ||= [
|
25
|
+
'DONE', # Выполнено
|
26
|
+
'ERROR', # Ошибка
|
27
|
+
]
|
28
|
+
end
|
29
|
+
|
30
|
+
def success_status_list
|
31
|
+
@success_status_list ||= ['DONE']
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SealineInsurance
|
4
|
+
module Responses
|
5
|
+
class Base
|
6
|
+
attr_reader :body
|
7
|
+
|
8
|
+
def initialize(http_response)
|
9
|
+
@body = parse_response_body(http_response.body)
|
10
|
+
if @body.nil?
|
11
|
+
# ошибка парсинга JSON
|
12
|
+
@success = false
|
13
|
+
@error_code = 'invalid_response'
|
14
|
+
@error_message = 'Invalid JSON'
|
15
|
+
@body = {}
|
16
|
+
elsif http_response.status == 401
|
17
|
+
# ошибка авторизации
|
18
|
+
@success = false
|
19
|
+
@error_code = 'unauthorized'
|
20
|
+
@error_message = body['detail']
|
21
|
+
elsif http_response.status == 400
|
22
|
+
# невалидные входные данные
|
23
|
+
@success = false
|
24
|
+
@error_code = 'invalid_params'
|
25
|
+
@error_message = fetch_validation_error(body)
|
26
|
+
elsif http_response.status == 409
|
27
|
+
# еще один вариант ошибки - 409 Conflict
|
28
|
+
@success = false
|
29
|
+
@error_code = 'conflict'
|
30
|
+
@error_message = body['error']
|
31
|
+
else
|
32
|
+
@success = true
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def success?
|
37
|
+
@success
|
38
|
+
end
|
39
|
+
|
40
|
+
def error?
|
41
|
+
!success?
|
42
|
+
end
|
43
|
+
|
44
|
+
def error_code
|
45
|
+
@error_code ||= body['error_code']
|
46
|
+
end
|
47
|
+
|
48
|
+
def error_message
|
49
|
+
@error_message ||= body['error_message']
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def parse_response_body(body)
|
55
|
+
JSON.parse(body)
|
56
|
+
rescue JSON::ParserError
|
57
|
+
nil
|
58
|
+
end
|
59
|
+
|
60
|
+
# Ошибки валидации возвращаются в виде хэша различной вложенности.
|
61
|
+
# Ключи хеша - поля, которые не прошли валидацию. Значение - текст ошибки.
|
62
|
+
# Метод рекурсивный, он находит первую ошибку и возвращает ее в виде строки.
|
63
|
+
# Пример возвращаемого значения:
|
64
|
+
# "data.arrival: Не может быть меньше даты trip.departure.date"
|
65
|
+
def fetch_validation_error(data, key_path = [])
|
66
|
+
if data.is_a?(Hash) && !data.empty?
|
67
|
+
key = data.keys.first
|
68
|
+
fetch_validation_error(data[key], key_path + [key])
|
69
|
+
elsif data.is_a?(Array) && !data.empty? && data.all? { |item| item.is_a?(String) }
|
70
|
+
fetch_validation_error(data.join(', '), key_path)
|
71
|
+
elsif data.is_a?(String)
|
72
|
+
"#{key_path.join('.')}: #{data}"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SealineInsurance
|
4
|
+
module Responses
|
5
|
+
class Calculate < Base
|
6
|
+
STATUSES = [
|
7
|
+
'IN_PROGRESS', # Обрабатывается
|
8
|
+
'DONE', # Выполнено
|
9
|
+
'DONE_WITH_ERRORS', # Выполнено с ошибками
|
10
|
+
'ERROR', # Ошибка
|
11
|
+
].freeze
|
12
|
+
|
13
|
+
def request_id
|
14
|
+
body['id']
|
15
|
+
end
|
16
|
+
|
17
|
+
def status
|
18
|
+
body['status']
|
19
|
+
end
|
20
|
+
|
21
|
+
def price
|
22
|
+
result = body['results']&.detect { |item| item['status'] == 'DONE' }
|
23
|
+
if result && result['price']
|
24
|
+
Money.new(result['price'], DEFAULT_CURRENCY)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SealineInsurance
|
4
|
+
module Responses
|
5
|
+
class Order < Base
|
6
|
+
STATUSES = [
|
7
|
+
'IN_PROGRESS', # Обрабатывается
|
8
|
+
'ERROR', # Ошибка
|
9
|
+
'UPDATE_IN_PROGRESS', # Ожидает обновления
|
10
|
+
'NOT_UPDATED', # Не обновлен
|
11
|
+
'NEED_PAYMENT', # Требуется оплата
|
12
|
+
'PAYMENT_IN_PROGRESS', # Производится подтверждение оплаты
|
13
|
+
'WAITING_FOR_DOCUMENTS', # Загрузка электронного документа
|
14
|
+
'DONE', # Создан
|
15
|
+
'CANCEL_IN_PROGRESS', # Ожидает отмены
|
16
|
+
'WAITING_CANCELLATION_APPROVAL', # Ожидает подтверждения отмены
|
17
|
+
'REPEAT_CANCELLATION', # Требуется повтор отмены
|
18
|
+
'NOT_CANCELLED', # Не отменен
|
19
|
+
'CANCELLED', # Отменен
|
20
|
+
].freeze
|
21
|
+
|
22
|
+
def order_id
|
23
|
+
body['id']
|
24
|
+
end
|
25
|
+
|
26
|
+
def status
|
27
|
+
body['status']
|
28
|
+
end
|
29
|
+
|
30
|
+
def price
|
31
|
+
Money.new(body['price'], DEFAULT_CURRENCY) if body['price']
|
32
|
+
end
|
33
|
+
|
34
|
+
def documents
|
35
|
+
body['documents'] || []
|
36
|
+
end
|
37
|
+
|
38
|
+
def external_numbers
|
39
|
+
body['external_numbers'] || []
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SealineInsurance
|
4
|
+
module Responses
|
5
|
+
class Payment < Base
|
6
|
+
STATUSES = [
|
7
|
+
'IN_PROGRESS', # Обрабатывается
|
8
|
+
'DONE', # Выполнено
|
9
|
+
'ERROR', # Ошибка
|
10
|
+
].freeze
|
11
|
+
|
12
|
+
def payment_id
|
13
|
+
body['id']
|
14
|
+
end
|
15
|
+
|
16
|
+
def order_id
|
17
|
+
body['order']
|
18
|
+
end
|
19
|
+
|
20
|
+
def status
|
21
|
+
body['status']
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/lib/sealine_insurance.rb
CHANGED
@@ -9,9 +9,15 @@ require 'sealine_insurance/errors'
|
|
9
9
|
require 'sealine_insurance/config'
|
10
10
|
require 'sealine_insurance/client'
|
11
11
|
require 'sealine_insurance/request'
|
12
|
-
require 'sealine_insurance/
|
13
|
-
require 'sealine_insurance/
|
14
|
-
require 'sealine_insurance/
|
12
|
+
require 'sealine_insurance/operations/base'
|
13
|
+
require 'sealine_insurance/operations/calculate'
|
14
|
+
require 'sealine_insurance/operations/create_order'
|
15
|
+
require 'sealine_insurance/operations/create_payment'
|
16
|
+
require 'sealine_insurance/operations/cancel_order'
|
17
|
+
require 'sealine_insurance/responses/base'
|
18
|
+
require 'sealine_insurance/responses/calculate'
|
19
|
+
require 'sealine_insurance/responses/order'
|
20
|
+
require 'sealine_insurance/responses/payment'
|
15
21
|
|
16
22
|
module SealineInsurance
|
17
23
|
DEFAULT_CURRENCY = 'RUB'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sealine_insurance
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Roman Khrebtov
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-
|
11
|
+
date: 2018-08-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: faraday
|
@@ -140,13 +140,19 @@ files:
|
|
140
140
|
- bin/console
|
141
141
|
- bin/setup
|
142
142
|
- lib/sealine_insurance.rb
|
143
|
-
- lib/sealine_insurance/base_response.rb
|
144
|
-
- lib/sealine_insurance/calculate_response.rb
|
145
143
|
- lib/sealine_insurance/client.rb
|
146
144
|
- lib/sealine_insurance/config.rb
|
147
145
|
- lib/sealine_insurance/errors.rb
|
148
|
-
- lib/sealine_insurance/
|
146
|
+
- lib/sealine_insurance/operations/base.rb
|
147
|
+
- lib/sealine_insurance/operations/calculate.rb
|
148
|
+
- lib/sealine_insurance/operations/cancel_order.rb
|
149
|
+
- lib/sealine_insurance/operations/create_order.rb
|
150
|
+
- lib/sealine_insurance/operations/create_payment.rb
|
149
151
|
- lib/sealine_insurance/request.rb
|
152
|
+
- lib/sealine_insurance/responses/base.rb
|
153
|
+
- lib/sealine_insurance/responses/calculate.rb
|
154
|
+
- lib/sealine_insurance/responses/order.rb
|
155
|
+
- lib/sealine_insurance/responses/payment.rb
|
150
156
|
- lib/sealine_insurance/version.rb
|
151
157
|
- sealine_insurance.gemspec
|
152
158
|
homepage: https://github.com/busfor/sealine_insurance
|
@@ -1,73 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module SealineInsurance
|
4
|
-
class BaseResponse
|
5
|
-
attr_reader :raw_body
|
6
|
-
attr_reader :error_code
|
7
|
-
attr_reader :error_message
|
8
|
-
|
9
|
-
def initialize(http_response)
|
10
|
-
@raw_body = parse_response_body(http_response.body)
|
11
|
-
if @error_code
|
12
|
-
# ошибка парсинга JSON
|
13
|
-
@success = false
|
14
|
-
elsif http_response.status == 401
|
15
|
-
# ошибка авторизации
|
16
|
-
@success = false
|
17
|
-
@error_code = 'unauthorized'
|
18
|
-
@error_message = raw_body['detail']
|
19
|
-
elsif http_response.status == 400
|
20
|
-
# невалидные входные данные
|
21
|
-
@success = false
|
22
|
-
@error_code = 'invalid_params'
|
23
|
-
@error_message = fetch_validation_error(raw_body)
|
24
|
-
else
|
25
|
-
# определение ошибки по статусу в JSON
|
26
|
-
# если !error_status? - считаем ответ успешным
|
27
|
-
@success = !error_status?
|
28
|
-
@error_code = raw_body['error_code']
|
29
|
-
@error_message = raw_body['error_message']
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
def success?
|
34
|
-
@success
|
35
|
-
end
|
36
|
-
|
37
|
-
def error?
|
38
|
-
!success?
|
39
|
-
end
|
40
|
-
|
41
|
-
private
|
42
|
-
|
43
|
-
# Дочерние классы переопределяют этот метод.
|
44
|
-
# В общем случае статуса в raw_body может не быть, поэтому по дефолту false.
|
45
|
-
def error_status?
|
46
|
-
false
|
47
|
-
end
|
48
|
-
|
49
|
-
def parse_response_body(body)
|
50
|
-
JSON.parse(body)
|
51
|
-
rescue JSON::ParserError
|
52
|
-
@error_code = 'invalid_response'
|
53
|
-
@error_message = 'Invalid JSON'
|
54
|
-
{}
|
55
|
-
end
|
56
|
-
|
57
|
-
# Ошибки валидации возвращаются в виде хэша различной вложенности.
|
58
|
-
# Ключи хеша - поля, которые не прошли валидацию. Значение - текст ошибки.
|
59
|
-
# Метод рекурсивный, он находит первую ошибку и возвращает ее в виде строки.
|
60
|
-
# Пример возвращаемого значения:
|
61
|
-
# "data.arrival: Не может быть меньше даты trip.departure.date"
|
62
|
-
def fetch_validation_error(data, key_path = [])
|
63
|
-
if data.is_a?(Hash) && !data.empty?
|
64
|
-
key = data.keys.first
|
65
|
-
fetch_validation_error(data[key], key_path + [key])
|
66
|
-
elsif data.is_a?(Array) && !data.empty? && data.all? { |item| item.is_a?(String) }
|
67
|
-
fetch_validation_error(data.join(', '), key_path)
|
68
|
-
elsif data.is_a?(String)
|
69
|
-
"#{key_path.join('.')}: #{data}"
|
70
|
-
end
|
71
|
-
end
|
72
|
-
end
|
73
|
-
end
|
@@ -1,37 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module SealineInsurance
|
4
|
-
class CalculateResponse < BaseResponse
|
5
|
-
STATUSES = [
|
6
|
-
'IN_PROGRESS', # Обрабатывается
|
7
|
-
'DONE', # Выполнено
|
8
|
-
'DONE_WITH_ERRORS', # Выполнено с ошибками
|
9
|
-
'ERROR', # Ошибка
|
10
|
-
].freeze
|
11
|
-
|
12
|
-
def request_id
|
13
|
-
raw_body['id']
|
14
|
-
end
|
15
|
-
|
16
|
-
def status
|
17
|
-
raw_body['status']
|
18
|
-
end
|
19
|
-
|
20
|
-
def calculated?
|
21
|
-
status == 'DONE'
|
22
|
-
end
|
23
|
-
|
24
|
-
def price
|
25
|
-
result = raw_body['results']&.detect { |item| item['status'] == 'DONE' }
|
26
|
-
if result && result['price']
|
27
|
-
Money.new(result['price'], DEFAULT_CURRENCY)
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
private
|
32
|
-
|
33
|
-
def error_status?
|
34
|
-
%w[DONE_WITH_ERRORS ERROR].include?(status)
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
@@ -1,39 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module SealineInsurance
|
4
|
-
class OrderResponse < BaseResponse
|
5
|
-
STATUSES = [
|
6
|
-
'IN_PROGRESS', # Обрабатывается
|
7
|
-
'ERROR', # Ошибка
|
8
|
-
'UPDATE_IN_PROGRESS', # Ожидает обновления
|
9
|
-
'NOT_UPDATED', # Не обновлен
|
10
|
-
'NEED_PAYMENT', # Требуется оплата
|
11
|
-
'PAYMENT_IN_PROGRESS', # Производится подтверждение оплаты
|
12
|
-
'WAITING_FOR_DOCUMENTS', # Загрузка электронного документа
|
13
|
-
'DONE', # Создан
|
14
|
-
'CANCEL_IN_PROGRESS', # Ожидает отмены
|
15
|
-
'WAITING_CANCELLATION_APPROVAL', # Ожидает подтверждения отмены
|
16
|
-
'REPEAT_CANCELLATION', # Требуется повтор отмены
|
17
|
-
'NOT_CANCELLED', # Не отменен
|
18
|
-
'CANCELLED', # Отменен
|
19
|
-
].freeze
|
20
|
-
|
21
|
-
def order_id
|
22
|
-
raw_body['id']
|
23
|
-
end
|
24
|
-
|
25
|
-
def status
|
26
|
-
raw_body['status']
|
27
|
-
end
|
28
|
-
|
29
|
-
def created?
|
30
|
-
status == 'NEED_PAYMENT'
|
31
|
-
end
|
32
|
-
|
33
|
-
private
|
34
|
-
|
35
|
-
def error_status?
|
36
|
-
status == 'ERROR'
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|