conekta 6.0.3 → 6.0.4

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.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +7 -5
  3. data/VERSION +1 -1
  4. data/config-ruby.json +1 -1
  5. data/docs/ChargeRequest.md +1 -1
  6. data/docs/ChargeRequestPaymentMethod.md +39 -18
  7. data/docs/ChargeResponse.md +6 -6
  8. data/docs/ChargesApi.md +1 -1
  9. data/docs/ChargesDataResponse.md +6 -6
  10. data/docs/Checkout.md +2 -0
  11. data/docs/CreateCustomerPaymentMethodsRequest.md +4 -4
  12. data/docs/CustomerPaymentMethodsRequest.md +4 -4
  13. data/docs/OrderRefundRequest.md +2 -2
  14. data/docs/OrderRequest.md +1 -1
  15. data/docs/PaymentMethodBankTransfer.md +3 -1
  16. data/docs/PaymentMethodCard.md +12 -10
  17. data/docs/PaymentMethodCardRequest.md +12 -2
  18. data/docs/PaymentMethodCash.md +3 -1
  19. data/docs/PaymentMethodGeneralRequest.md +32 -0
  20. data/docs/PaymentMethodTokenRequest.md +20 -0
  21. data/docs/PaymentMethodsApi.md +1 -1
  22. data/docs/SubscriptionResponse.md +2 -0
  23. data/lib/conekta/models/charge_request.rb +1 -0
  24. data/lib/conekta/models/charge_request_payment_method.rb +74 -239
  25. data/lib/conekta/models/charge_response.rb +4 -0
  26. data/lib/conekta/models/charges_data_response.rb +4 -0
  27. data/lib/conekta/models/checkout.rb +11 -1
  28. data/lib/conekta/models/create_customer_payment_methods_request.rb +2 -2
  29. data/lib/conekta/models/customer_payment_methods_request.rb +2 -2
  30. data/lib/conekta/models/order_refund_request.rb +2 -0
  31. data/lib/conekta/models/order_request.rb +1 -1
  32. data/lib/conekta/models/payment_method_bank_transfer.rb +14 -5
  33. data/lib/conekta/models/payment_method_card.rb +22 -4
  34. data/lib/conekta/models/payment_method_card_request.rb +174 -12
  35. data/lib/conekta/models/payment_method_cash.rb +13 -4
  36. data/lib/conekta/models/payment_method_general_request.rb +291 -0
  37. data/lib/conekta/models/payment_method_token_request.rb +246 -0
  38. data/lib/conekta/models/subscription_response.rb +11 -1
  39. data/lib/conekta/version.rb +1 -1
  40. data/lib/conekta.rb +2 -0
  41. data/spec/api/charges_api_spec.rb +18 -10
  42. data/spec/api/customers_api_spec.rb +10 -4
  43. data/spec/api/orders_api_spec.rb +6 -6
  44. metadata +27 -23
@@ -18,14 +18,34 @@ module Conekta
18
18
  # Type of payment method
19
19
  attr_accessor :type
20
20
 
21
- # Token id that will be used to create a \"card\" type payment method. See the (subscriptions)[https://developers.conekta.com/v2.1.0/reference/createsubscription] tutorial for more information on how to tokenize cards.
22
- attr_accessor :token_id
21
+ # Card security code
22
+ attr_accessor :cvc
23
+
24
+ # Card expiration month
25
+ attr_accessor :exp_month
26
+
27
+ # Card expiration year
28
+ attr_accessor :exp_year
29
+
30
+ # Cardholder name
31
+ attr_accessor :name
32
+
33
+ # Card number
34
+ attr_accessor :number
35
+
36
+ # Optional field used to capture the customer's IP address for fraud prevention and security monitoring purposes
37
+ attr_accessor :customer_ip_address
23
38
 
24
39
  # Attribute mapping from ruby-style variable name to JSON key.
25
40
  def self.attribute_map
26
41
  {
27
42
  :'type' => :'type',
28
- :'token_id' => :'token_id'
43
+ :'cvc' => :'cvc',
44
+ :'exp_month' => :'exp_month',
45
+ :'exp_year' => :'exp_year',
46
+ :'name' => :'name',
47
+ :'number' => :'number',
48
+ :'customer_ip_address' => :'customer_ip_address'
29
49
  }
30
50
  end
31
51
 
@@ -38,7 +58,12 @@ module Conekta
38
58
  def self.openapi_types
39
59
  {
40
60
  :'type' => :'String',
41
- :'token_id' => :'String'
61
+ :'cvc' => :'String',
62
+ :'exp_month' => :'String',
63
+ :'exp_year' => :'String',
64
+ :'name' => :'String',
65
+ :'number' => :'String',
66
+ :'customer_ip_address' => :'String'
42
67
  }
43
68
  end
44
69
 
@@ -76,10 +101,38 @@ module Conekta
76
101
  self.type = nil
77
102
  end
78
103
 
79
- if attributes.key?(:'token_id')
80
- self.token_id = attributes[:'token_id']
104
+ if attributes.key?(:'cvc')
105
+ self.cvc = attributes[:'cvc']
106
+ else
107
+ self.cvc = nil
108
+ end
109
+
110
+ if attributes.key?(:'exp_month')
111
+ self.exp_month = attributes[:'exp_month']
112
+ else
113
+ self.exp_month = nil
114
+ end
115
+
116
+ if attributes.key?(:'exp_year')
117
+ self.exp_year = attributes[:'exp_year']
118
+ else
119
+ self.exp_year = nil
120
+ end
121
+
122
+ if attributes.key?(:'name')
123
+ self.name = attributes[:'name']
124
+ else
125
+ self.name = nil
126
+ end
127
+
128
+ if attributes.key?(:'number')
129
+ self.number = attributes[:'number']
81
130
  else
82
- self.token_id = nil
131
+ self.number = nil
132
+ end
133
+
134
+ if attributes.key?(:'customer_ip_address')
135
+ self.customer_ip_address = attributes[:'customer_ip_address']
83
136
  end
84
137
  end
85
138
 
@@ -92,8 +145,48 @@ module Conekta
92
145
  invalid_properties.push('invalid value for "type", type cannot be nil.')
93
146
  end
94
147
 
95
- if @token_id.nil?
96
- invalid_properties.push('invalid value for "token_id", token_id cannot be nil.')
148
+ if @cvc.nil?
149
+ invalid_properties.push('invalid value for "cvc", cvc cannot be nil.')
150
+ end
151
+
152
+ if @cvc.to_s.length > 4
153
+ invalid_properties.push('invalid value for "cvc", the character length must be smaller than or equal to 4.')
154
+ end
155
+
156
+ if @cvc.to_s.length < 3
157
+ invalid_properties.push('invalid value for "cvc", the character length must be great than or equal to 3.')
158
+ end
159
+
160
+ if @exp_month.nil?
161
+ invalid_properties.push('invalid value for "exp_month", exp_month cannot be nil.')
162
+ end
163
+
164
+ if @exp_month.to_s.length > 2
165
+ invalid_properties.push('invalid value for "exp_month", the character length must be smaller than or equal to 2.')
166
+ end
167
+
168
+ if @exp_month.to_s.length < 2
169
+ invalid_properties.push('invalid value for "exp_month", the character length must be great than or equal to 2.')
170
+ end
171
+
172
+ if @exp_year.nil?
173
+ invalid_properties.push('invalid value for "exp_year", exp_year cannot be nil.')
174
+ end
175
+
176
+ if @exp_year.to_s.length > 4
177
+ invalid_properties.push('invalid value for "exp_year", the character length must be smaller than or equal to 4.')
178
+ end
179
+
180
+ if @exp_year.to_s.length < 4
181
+ invalid_properties.push('invalid value for "exp_year", the character length must be great than or equal to 4.')
182
+ end
183
+
184
+ if @name.nil?
185
+ invalid_properties.push('invalid value for "name", name cannot be nil.')
186
+ end
187
+
188
+ if @number.nil?
189
+ invalid_properties.push('invalid value for "number", number cannot be nil.')
97
190
  end
98
191
 
99
192
  invalid_properties
@@ -104,17 +197,86 @@ module Conekta
104
197
  def valid?
105
198
  warn '[DEPRECATED] the `valid?` method is obsolete'
106
199
  return false if @type.nil?
107
- return false if @token_id.nil?
200
+ return false if @cvc.nil?
201
+ return false if @cvc.to_s.length > 4
202
+ return false if @cvc.to_s.length < 3
203
+ return false if @exp_month.nil?
204
+ return false if @exp_month.to_s.length > 2
205
+ return false if @exp_month.to_s.length < 2
206
+ return false if @exp_year.nil?
207
+ return false if @exp_year.to_s.length > 4
208
+ return false if @exp_year.to_s.length < 4
209
+ return false if @name.nil?
210
+ return false if @number.nil?
108
211
  true
109
212
  end
110
213
 
214
+ # Custom attribute writer method with validation
215
+ # @param [Object] cvc Value to be assigned
216
+ def cvc=(cvc)
217
+ if cvc.nil?
218
+ fail ArgumentError, 'cvc cannot be nil'
219
+ end
220
+
221
+ if cvc.to_s.length > 4
222
+ fail ArgumentError, 'invalid value for "cvc", the character length must be smaller than or equal to 4.'
223
+ end
224
+
225
+ if cvc.to_s.length < 3
226
+ fail ArgumentError, 'invalid value for "cvc", the character length must be great than or equal to 3.'
227
+ end
228
+
229
+ @cvc = cvc
230
+ end
231
+
232
+ # Custom attribute writer method with validation
233
+ # @param [Object] exp_month Value to be assigned
234
+ def exp_month=(exp_month)
235
+ if exp_month.nil?
236
+ fail ArgumentError, 'exp_month cannot be nil'
237
+ end
238
+
239
+ if exp_month.to_s.length > 2
240
+ fail ArgumentError, 'invalid value for "exp_month", the character length must be smaller than or equal to 2.'
241
+ end
242
+
243
+ if exp_month.to_s.length < 2
244
+ fail ArgumentError, 'invalid value for "exp_month", the character length must be great than or equal to 2.'
245
+ end
246
+
247
+ @exp_month = exp_month
248
+ end
249
+
250
+ # Custom attribute writer method with validation
251
+ # @param [Object] exp_year Value to be assigned
252
+ def exp_year=(exp_year)
253
+ if exp_year.nil?
254
+ fail ArgumentError, 'exp_year cannot be nil'
255
+ end
256
+
257
+ if exp_year.to_s.length > 4
258
+ fail ArgumentError, 'invalid value for "exp_year", the character length must be smaller than or equal to 4.'
259
+ end
260
+
261
+ if exp_year.to_s.length < 4
262
+ fail ArgumentError, 'invalid value for "exp_year", the character length must be great than or equal to 4.'
263
+ end
264
+
265
+ @exp_year = exp_year
266
+ end
267
+
111
268
  # Checks equality by comparing each attribute.
112
269
  # @param [Object] Object to be compared
113
270
  def ==(o)
114
271
  return true if self.equal?(o)
115
272
  self.class == o.class &&
116
273
  type == o.type &&
117
- token_id == o.token_id
274
+ cvc == o.cvc &&
275
+ exp_month == o.exp_month &&
276
+ exp_year == o.exp_year &&
277
+ name == o.name &&
278
+ number == o.number &&
279
+ customer_ip_address == o.customer_ip_address
118
280
  end
119
281
 
120
282
  # @see the `==` method
@@ -126,7 +288,7 @@ module Conekta
126
288
  # Calculates hash code according to all attributes.
127
289
  # @return [Integer] Hash code
128
290
  def hash
129
- [type, token_id].hash
291
+ [type, cvc, exp_month, exp_year, name, number, customer_ip_address].hash
130
292
  end
131
293
 
132
294
  # Builds the object from hash
@@ -35,6 +35,8 @@ module Conekta
35
35
 
36
36
  attr_accessor :store_name
37
37
 
38
+ attr_accessor :customer_ip_address
39
+
38
40
  # Attribute mapping from ruby-style variable name to JSON key.
39
41
  def self.attribute_map
40
42
  {
@@ -47,7 +49,8 @@ module Conekta
47
49
  :'expires_at' => :'expires_at',
48
50
  :'service_name' => :'service_name',
49
51
  :'store' => :'store',
50
- :'store_name' => :'store_name'
52
+ :'store_name' => :'store_name',
53
+ :'customer_ip_address' => :'customer_ip_address'
51
54
  }
52
55
  end
53
56
 
@@ -68,7 +71,8 @@ module Conekta
68
71
  :'expires_at' => :'Integer',
69
72
  :'service_name' => :'String',
70
73
  :'store' => :'String',
71
- :'store_name' => :'String'
74
+ :'store_name' => :'String',
75
+ :'customer_ip_address' => :'String'
72
76
  }
73
77
  end
74
78
 
@@ -144,6 +148,10 @@ module Conekta
144
148
  if attributes.key?(:'store_name')
145
149
  self.store_name = attributes[:'store_name']
146
150
  end
151
+
152
+ if attributes.key?(:'customer_ip_address')
153
+ self.customer_ip_address = attributes[:'customer_ip_address']
154
+ end
147
155
  end
148
156
 
149
157
  # Show invalid properties with the reasons. Usually used together with valid?
@@ -180,7 +188,8 @@ module Conekta
180
188
  expires_at == o.expires_at &&
181
189
  service_name == o.service_name &&
182
190
  store == o.store &&
183
- store_name == o.store_name
191
+ store_name == o.store_name &&
192
+ customer_ip_address == o.customer_ip_address
184
193
  end
185
194
 
186
195
  # @see the `==` method
@@ -192,7 +201,7 @@ module Conekta
192
201
  # Calculates hash code according to all attributes.
193
202
  # @return [Integer] Hash code
194
203
  def hash
195
- [type, object, auth_code, cashier_id, reference, barcode_url, expires_at, service_name, store, store_name].hash
204
+ [type, object, auth_code, cashier_id, reference, barcode_url, expires_at, service_name, store, store_name, customer_ip_address].hash
196
205
  end
197
206
 
198
207
  # Builds the object from hash
@@ -0,0 +1,291 @@
1
+ =begin
2
+ #Conekta API
3
+
4
+ #Conekta sdk
5
+
6
+ The version of the OpenAPI document: 2.1.0
7
+ Contact: engineering@conekta.com
8
+ Generated by: https://openapi-generator.tech
9
+ Generator version: 7.5.0
10
+
11
+ =end
12
+
13
+ require 'date'
14
+ require 'time'
15
+
16
+ module Conekta
17
+ # Payment method used in the charge. Go to the [payment methods](https://developers.conekta.com/reference/m%C3%A9todos-de-pago) section for more details
18
+ class PaymentMethodGeneralRequest
19
+ # Method expiration date as unix timestamp
20
+ attr_accessor :expires_at
21
+
22
+ # How many months without interest to apply, it can be 3, 6, 9, 12 or 18
23
+ attr_accessor :monthly_installments
24
+
25
+ # Type of payment method
26
+ attr_accessor :type
27
+
28
+ attr_accessor :token_id
29
+
30
+ attr_accessor :payment_source_id
31
+
32
+ # Optional, It is a value that allows identifying the security code of the card. Only for PCI merchants
33
+ attr_accessor :cvc
34
+
35
+ # Optional id sent to indicate the bank contract for recurrent card charges.
36
+ attr_accessor :contract_id
37
+
38
+ # Optional field used to capture the customer's IP address for fraud prevention and security monitoring purposes
39
+ attr_accessor :customer_ip_address
40
+
41
+ # Attribute mapping from ruby-style variable name to JSON key.
42
+ def self.attribute_map
43
+ {
44
+ :'expires_at' => :'expires_at',
45
+ :'monthly_installments' => :'monthly_installments',
46
+ :'type' => :'type',
47
+ :'token_id' => :'token_id',
48
+ :'payment_source_id' => :'payment_source_id',
49
+ :'cvc' => :'cvc',
50
+ :'contract_id' => :'contract_id',
51
+ :'customer_ip_address' => :'customer_ip_address'
52
+ }
53
+ end
54
+
55
+ # Returns all the JSON keys this model knows about
56
+ def self.acceptable_attributes
57
+ attribute_map.values
58
+ end
59
+
60
+ # Attribute type mapping.
61
+ def self.openapi_types
62
+ {
63
+ :'expires_at' => :'Integer',
64
+ :'monthly_installments' => :'Integer',
65
+ :'type' => :'String',
66
+ :'token_id' => :'String',
67
+ :'payment_source_id' => :'String',
68
+ :'cvc' => :'String',
69
+ :'contract_id' => :'String',
70
+ :'customer_ip_address' => :'String'
71
+ }
72
+ end
73
+
74
+ # List of attributes with nullable: true
75
+ def self.openapi_nullable
76
+ Set.new([
77
+ ])
78
+ end
79
+
80
+ # Initializes the object
81
+ # @param [Hash] attributes Model attributes in the form of hash
82
+ def initialize(attributes = {})
83
+ if (!attributes.is_a?(Hash))
84
+ fail ArgumentError, "The input argument (attributes) must be a hash in `Conekta::PaymentMethodGeneralRequest` initialize method"
85
+ end
86
+
87
+ # check to see if the attribute exists and convert string to symbol for hash key
88
+ attributes = attributes.each_with_object({}) { |(k, v), h|
89
+ if (!self.class.attribute_map.key?(k.to_sym))
90
+ fail ArgumentError, "`#{k}` is not a valid attribute in `Conekta::PaymentMethodGeneralRequest`. Please check the name to make sure it's valid. List of attributes: " + self.class.attribute_map.keys.inspect
91
+ end
92
+ h[k.to_sym] = v
93
+ }
94
+
95
+ if attributes.key?(:'expires_at')
96
+ self.expires_at = attributes[:'expires_at']
97
+ end
98
+
99
+ if attributes.key?(:'monthly_installments')
100
+ self.monthly_installments = attributes[:'monthly_installments']
101
+ end
102
+
103
+ if attributes.key?(:'type')
104
+ self.type = attributes[:'type']
105
+ else
106
+ self.type = nil
107
+ end
108
+
109
+ if attributes.key?(:'token_id')
110
+ self.token_id = attributes[:'token_id']
111
+ end
112
+
113
+ if attributes.key?(:'payment_source_id')
114
+ self.payment_source_id = attributes[:'payment_source_id']
115
+ end
116
+
117
+ if attributes.key?(:'cvc')
118
+ self.cvc = attributes[:'cvc']
119
+ end
120
+
121
+ if attributes.key?(:'contract_id')
122
+ self.contract_id = attributes[:'contract_id']
123
+ end
124
+
125
+ if attributes.key?(:'customer_ip_address')
126
+ self.customer_ip_address = attributes[:'customer_ip_address']
127
+ end
128
+ end
129
+
130
+ # Show invalid properties with the reasons. Usually used together with valid?
131
+ # @return Array for valid properties with the reasons
132
+ def list_invalid_properties
133
+ warn '[DEPRECATED] the `list_invalid_properties` method is obsolete'
134
+ invalid_properties = Array.new
135
+ if @type.nil?
136
+ invalid_properties.push('invalid value for "type", type cannot be nil.')
137
+ end
138
+
139
+ invalid_properties
140
+ end
141
+
142
+ # Check to see if the all the properties in the model are valid
143
+ # @return true if the model is valid
144
+ def valid?
145
+ warn '[DEPRECATED] the `valid?` method is obsolete'
146
+ return false if @type.nil?
147
+ true
148
+ end
149
+
150
+ # Checks equality by comparing each attribute.
151
+ # @param [Object] Object to be compared
152
+ def ==(o)
153
+ return true if self.equal?(o)
154
+ self.class == o.class &&
155
+ expires_at == o.expires_at &&
156
+ monthly_installments == o.monthly_installments &&
157
+ type == o.type &&
158
+ token_id == o.token_id &&
159
+ payment_source_id == o.payment_source_id &&
160
+ cvc == o.cvc &&
161
+ contract_id == o.contract_id &&
162
+ customer_ip_address == o.customer_ip_address
163
+ end
164
+
165
+ # @see the `==` method
166
+ # @param [Object] Object to be compared
167
+ def eql?(o)
168
+ self == o
169
+ end
170
+
171
+ # Calculates hash code according to all attributes.
172
+ # @return [Integer] Hash code
173
+ def hash
174
+ [expires_at, monthly_installments, type, token_id, payment_source_id, cvc, contract_id, customer_ip_address].hash
175
+ end
176
+
177
+ # Builds the object from hash
178
+ # @param [Hash] attributes Model attributes in the form of hash
179
+ # @return [Object] Returns the model itself
180
+ def self.build_from_hash(attributes)
181
+ return nil unless attributes.is_a?(Hash)
182
+ attributes = attributes.transform_keys(&:to_sym)
183
+ transformed_hash = {}
184
+ openapi_types.each_pair do |key, type|
185
+ if attributes.key?(attribute_map[key]) && attributes[attribute_map[key]].nil?
186
+ transformed_hash["#{key}"] = nil
187
+ elsif type =~ /\AArray<(.*)>/i
188
+ # check to ensure the input is an array given that the attribute
189
+ # is documented as an array but the input is not
190
+ if attributes[attribute_map[key]].is_a?(Array)
191
+ transformed_hash["#{key}"] = attributes[attribute_map[key]].map { |v| _deserialize($1, v) }
192
+ end
193
+ elsif !attributes[attribute_map[key]].nil?
194
+ transformed_hash["#{key}"] = _deserialize(type, attributes[attribute_map[key]])
195
+ end
196
+ end
197
+ new(transformed_hash)
198
+ end
199
+
200
+ # Deserializes the data based on type
201
+ # @param string type Data type
202
+ # @param string value Value to be deserialized
203
+ # @return [Object] Deserialized data
204
+ def self._deserialize(type, value)
205
+ case type.to_sym
206
+ when :Time
207
+ Time.parse(value)
208
+ when :Date
209
+ Date.parse(value)
210
+ when :String
211
+ value.to_s
212
+ when :Integer
213
+ value.to_i
214
+ when :Float
215
+ value.to_f
216
+ when :Boolean
217
+ if value.to_s =~ /\A(true|t|yes|y|1)\z/i
218
+ true
219
+ else
220
+ false
221
+ end
222
+ when :Object
223
+ # generic object (usually a Hash), return directly
224
+ value
225
+ when /\AArray<(?<inner_type>.+)>\z/
226
+ inner_type = Regexp.last_match[:inner_type]
227
+ value.map { |v| _deserialize(inner_type, v) }
228
+ when /\AHash<(?<k_type>.+?), (?<v_type>.+)>\z/
229
+ k_type = Regexp.last_match[:k_type]
230
+ v_type = Regexp.last_match[:v_type]
231
+ {}.tap do |hash|
232
+ value.each do |k, v|
233
+ hash[_deserialize(k_type, k)] = _deserialize(v_type, v)
234
+ end
235
+ end
236
+ else # model
237
+ # models (e.g. Pet) or oneOf
238
+ klass = Conekta.const_get(type)
239
+ klass.respond_to?(:openapi_any_of) || klass.respond_to?(:openapi_one_of) ? klass.build(value) : klass.build_from_hash(value)
240
+ end
241
+ end
242
+
243
+ # Returns the string representation of the object
244
+ # @return [String] String presentation of the object
245
+ def to_s
246
+ to_hash.to_s
247
+ end
248
+
249
+ # to_body is an alias to to_hash (backward compatibility)
250
+ # @return [Hash] Returns the object in the form of hash
251
+ def to_body
252
+ to_hash
253
+ end
254
+
255
+ # Returns the object in the form of hash
256
+ # @return [Hash] Returns the object in the form of hash
257
+ def to_hash
258
+ hash = {}
259
+ self.class.attribute_map.each_pair do |attr, param|
260
+ value = self.send(attr)
261
+ if value.nil?
262
+ is_nullable = self.class.openapi_nullable.include?(attr)
263
+ next if !is_nullable || (is_nullable && !instance_variable_defined?(:"@#{attr}"))
264
+ end
265
+
266
+ hash[param] = _to_hash(value)
267
+ end
268
+ hash
269
+ end
270
+
271
+ # Outputs non-array value in the form of hash
272
+ # For object, use to_hash. Otherwise, just return the value
273
+ # @param [Object] value Any valid value
274
+ # @return [Hash] Returns the value in the form of hash
275
+ def _to_hash(value)
276
+ if value.is_a?(Array)
277
+ value.compact.map { |v| _to_hash(v) }
278
+ elsif value.is_a?(Hash)
279
+ {}.tap do |hash|
280
+ value.each { |k, v| hash[k] = _to_hash(v) }
281
+ end
282
+ elsif value.respond_to? :to_hash
283
+ value.to_hash
284
+ else
285
+ value
286
+ end
287
+ end
288
+
289
+ end
290
+
291
+ end