mastercard_merchant_checkout 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE.txt +24 -0
- data/README.md +73 -0
- data/lib/mastercard_merchant_checkout.rb +32 -0
- data/lib/mastercard_merchant_checkout/api/payment_data_api.rb +40 -0
- data/lib/mastercard_merchant_checkout/api/postback_api.rb +39 -0
- data/lib/mastercard_merchant_checkout/models/address.rb +255 -0
- data/lib/mastercard_merchant_checkout/models/authentication_options.rb +255 -0
- data/lib/mastercard_merchant_checkout/models/card.rb +234 -0
- data/lib/mastercard_merchant_checkout/models/payment_data.rb +215 -0
- data/lib/mastercard_merchant_checkout/models/personal_info.rb +189 -0
- data/lib/mastercard_merchant_checkout/models/postback.rb +222 -0
- data/lib/mastercard_merchant_checkout/tracker/sdk_api_tracker.rb +57 -0
- data/lib/mastercard_merchant_checkout/version.rb +3 -0
- metadata +174 -0
@@ -0,0 +1,255 @@
|
|
1
|
+
require 'date'
|
2
|
+
require 'roxml'
|
3
|
+
|
4
|
+
|
5
|
+
module MastercardMerchantCheckout
|
6
|
+
# This class contains methods to set different authentication options required during DSRP.
|
7
|
+
class AuthenticationOptions
|
8
|
+
include ROXML
|
9
|
+
|
10
|
+
xml_name "AuthenticationOptions"
|
11
|
+
|
12
|
+
# @!attribute authenticate_method
|
13
|
+
# @return [String] the method used to authenticate the cardholder at checkout. Valid values are MERCHANT ONLY, 3DS and No Authentication.
|
14
|
+
xml_accessor :authenticate_method, :from =>"authenticateMethod"
|
15
|
+
|
16
|
+
# @!attribute card_enrollment_method
|
17
|
+
# @return [String] the method by which the card was added to the wallet. Valid values are: Manual Direct Provisioned 3DS Manual NFC Tap.
|
18
|
+
xml_accessor :card_enrollment_method, :from =>"cardEnrollmentMethod"
|
19
|
+
|
20
|
+
# @!attribute c_avv
|
21
|
+
# @return [String] the (CAVV) Cardholder Authentication Verification Value generated by card issuer upon successful authentication of the cardholder. This must be passed in the authorization message.
|
22
|
+
xml_accessor :c_avv, :from =>"cAvv"
|
23
|
+
|
24
|
+
# @!attribute eci_flag
|
25
|
+
# @return [String] the Electronic commerce indicator (ECI) flag. Possible values are as follows: MasterCard: 00:No Authentication 01:Attempts (Card Issuer Liability) 02:Authenticated by ACS (Card Issuer Liability) 03:Maestro (MARP) 05:Risk Based Authentication (Issuer, not in use) 06:Risk Based Authentication (Merchant, not in use) Visa: 05:Authenticated (Card Issuer Liability) 06:Attempts (Card Issuer Liability) 07:No 3DS Authentication (Merchant Liability)
|
26
|
+
xml_accessor :eci_flag, :from =>"eciFlag"
|
27
|
+
|
28
|
+
# @!attribute master_card_assigned_id
|
29
|
+
# @return [String] the value assigned by MasterCard and represents programs associated directly with Maestro cards. This field should be supplied in the authorization request by the merchant.
|
30
|
+
xml_accessor :master_card_assigned_id, :from =>"masterCardAssignedID"
|
31
|
+
|
32
|
+
# @!attribute pa_res_status
|
33
|
+
# @return [String] the message formatted, digitally signed, and sent from the ACS (issuer) to the MPI providing the results of the issuer's MasterCard SecureCode/Verified by Visa cardholder authentication. Possible values are: Y-The card was successfully authenticated via 3-D Secure A-signifies that either (a) the transaction was successfully authenticated via a 3-D Secure attempts transaction or (b)the cardholder was prompted to activate 3-D Secure during shopping but declined (Visa). U-Authentication results were unavailable.
|
34
|
+
xml_accessor :pa_res_status, :from =>"paResStatus"
|
35
|
+
|
36
|
+
# @!attribute sc_enrollment_status
|
37
|
+
# @return [String] the MasterCard SecureCode Enrollment Status. Indicates if the issuer of the card supports payer authentication for this card. Possible values are as follows: Y-The card is eligible for 3-D Secure authentication. N-The card is not eligible for 3-D Secure authentication. U-Lookup of the card's 3-D Secure eligibility status was either unavailable, or the card is inapplicable (for example, prepaid cards).
|
38
|
+
xml_accessor :sc_enrollment_status, :from =>"scEnrollmentStatus"
|
39
|
+
|
40
|
+
# @!attribute signature_verification
|
41
|
+
# @return [String] the signature verification. Possible values are as follows: Y-Indicates that the signature of the PaRes has been validated successfully and the message contents can be trusted. N-Indicates that for a variety of reasons (tampering, certificate expiration, and so on) the PaRes could not be validated, and the result should not be trusted.
|
42
|
+
xml_accessor :signature_verification, :from =>"signatureVerification"
|
43
|
+
|
44
|
+
# @!attribute xid
|
45
|
+
# @return [String] the transaction identifier resulting from authentication processing.
|
46
|
+
xml_accessor :xid, :from =>"xid"
|
47
|
+
|
48
|
+
|
49
|
+
# Attribute mapping from ruby-style variable name to JSON key.
|
50
|
+
def self.attribute_map
|
51
|
+
{
|
52
|
+
:authenticate_method => :authenticateMethod ,
|
53
|
+
:card_enrollment_method => :cardEnrollmentMethod ,
|
54
|
+
:c_avv => :cAvv ,
|
55
|
+
:eci_flag => :eciFlag ,
|
56
|
+
:master_card_assigned_id => :masterCardAssignedID ,
|
57
|
+
:pa_res_status => :paResStatus ,
|
58
|
+
:sc_enrollment_status => :scEnrollmentStatus ,
|
59
|
+
:signature_verification => :signatureVerification ,
|
60
|
+
:xid => :xid
|
61
|
+
|
62
|
+
}
|
63
|
+
end
|
64
|
+
|
65
|
+
def initialize(attributes = {})
|
66
|
+
return unless attributes.is_a?(Hash)
|
67
|
+
|
68
|
+
# convert string to symbol for hash key
|
69
|
+
attributes = attributes.inject({}){|memo,(k,v)| memo[k.to_sym] = v; memo}
|
70
|
+
|
71
|
+
|
72
|
+
if attributes.has_key?(:authenticate_method)
|
73
|
+
self.authenticate_method = attributes[:authenticate_method]
|
74
|
+
end
|
75
|
+
|
76
|
+
if attributes.has_key?(:card_enrollment_method)
|
77
|
+
self.card_enrollment_method = attributes[:card_enrollment_method]
|
78
|
+
end
|
79
|
+
|
80
|
+
if attributes.has_key?(:c_avv)
|
81
|
+
self.c_avv = attributes[:c_avv]
|
82
|
+
end
|
83
|
+
|
84
|
+
if attributes.has_key?(:eci_flag)
|
85
|
+
self.eci_flag = attributes[:eci_flag]
|
86
|
+
end
|
87
|
+
|
88
|
+
if attributes.has_key?(:master_card_assigned_id)
|
89
|
+
self.master_card_assigned_id = attributes[:master_card_assigned_id]
|
90
|
+
end
|
91
|
+
|
92
|
+
if attributes.has_key?(:pa_res_status)
|
93
|
+
self.pa_res_status = attributes[:pa_res_status]
|
94
|
+
end
|
95
|
+
|
96
|
+
if attributes.has_key?(:sc_enrollment_status)
|
97
|
+
self.sc_enrollment_status = attributes[:sc_enrollment_status]
|
98
|
+
end
|
99
|
+
|
100
|
+
if attributes.has_key?(:signature_verification)
|
101
|
+
self.signature_verification = attributes[:signature_verification]
|
102
|
+
end
|
103
|
+
|
104
|
+
if attributes.has_key?(:xid)
|
105
|
+
self.xid = attributes[:xid]
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
109
|
+
|
110
|
+
|
111
|
+
|
112
|
+
|
113
|
+
# Check equality by comparing each attribute.
|
114
|
+
def ==(o)
|
115
|
+
return true if self.equal?(o)
|
116
|
+
self.class == o.class &&
|
117
|
+
authenticate_method == o.authenticate_method &&
|
118
|
+
card_enrollment_method == o.card_enrollment_method &&
|
119
|
+
c_avv == o.c_avv &&
|
120
|
+
eci_flag == o.eci_flag &&
|
121
|
+
master_card_assigned_id == o.master_card_assigned_id &&
|
122
|
+
pa_res_status == o.pa_res_status &&
|
123
|
+
sc_enrollment_status == o.sc_enrollment_status &&
|
124
|
+
signature_verification == o.signature_verification &&
|
125
|
+
xid == o.xid
|
126
|
+
end
|
127
|
+
|
128
|
+
# @see the `==` method
|
129
|
+
def eql?(o)
|
130
|
+
self == o
|
131
|
+
end
|
132
|
+
|
133
|
+
# Calculate hash code according to all attributes.
|
134
|
+
def hash
|
135
|
+
[authenticate_method, card_enrollment_method, c_avv, eci_flag, master_card_assigned_id, pa_res_status, sc_enrollment_status, signature_verification, xid].hash
|
136
|
+
end
|
137
|
+
|
138
|
+
# build the object from hash
|
139
|
+
def build_from_hash(attributes)
|
140
|
+
return nil unless attributes.is_a?(Hash)
|
141
|
+
self.class.datatype_map.each_pair do |key, type|
|
142
|
+
if type =~ /^Array<(.*)>/i
|
143
|
+
if attributes[self.class.attribute_map[key]].is_a?(Array)
|
144
|
+
self.send("#{key}=", attributes[self.class.attribute_map[key]].map{ |v| _deserialize($1, v) } )
|
145
|
+
else
|
146
|
+
#TODO show warning in debug mode
|
147
|
+
end
|
148
|
+
elsif !attributes[self.class.attribute_map[key]].nil?
|
149
|
+
self.send("#{key}=", _deserialize(type, attributes[self.class.attribute_map[key]]))
|
150
|
+
else
|
151
|
+
# data not found in attributes(hash), not an issue as the data can be optional
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
self
|
156
|
+
end
|
157
|
+
|
158
|
+
def _deserialize(type, value)
|
159
|
+
case type.to_sym
|
160
|
+
when :DateTime
|
161
|
+
DateTime.parse(value)
|
162
|
+
when :Date
|
163
|
+
Date.parse(value)
|
164
|
+
when :String
|
165
|
+
value.to_s
|
166
|
+
when :Integer
|
167
|
+
value.to_i
|
168
|
+
when :Float
|
169
|
+
value.to_f
|
170
|
+
when :BOOLEAN
|
171
|
+
if value =~ /^(true|t|yes|y|1)$/i
|
172
|
+
true
|
173
|
+
else
|
174
|
+
false
|
175
|
+
end
|
176
|
+
when /\AArray<(?<inner_type>.+)>\z/
|
177
|
+
inner_type = Regexp.last_match[:inner_type]
|
178
|
+
value.map { |v| _deserialize(inner_type, v) }
|
179
|
+
when /\AHash<(?<k_type>.+), (?<v_type>.+)>\z/
|
180
|
+
k_type = Regexp.last_match[:k_type]
|
181
|
+
v_type = Regexp.last_match[:v_type]
|
182
|
+
{}.tap do |hash|
|
183
|
+
value.each do |k, v|
|
184
|
+
hash[_deserialize(k_type, k)] = _deserialize(v_type, v)
|
185
|
+
end
|
186
|
+
end
|
187
|
+
else # model
|
188
|
+
_model = MastercardMerchantCheckout.const_get(type).new
|
189
|
+
_model.build_from_hash(value)
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
def to_s
|
194
|
+
to_hash.to_s
|
195
|
+
end
|
196
|
+
|
197
|
+
# to_body is an alias to to_body (backward compatibility))
|
198
|
+
def to_body
|
199
|
+
to_hash
|
200
|
+
end
|
201
|
+
|
202
|
+
# return the object in the form of hash
|
203
|
+
def to_hash(include_root = false)
|
204
|
+
attributes_hash = {}
|
205
|
+
hash = {}
|
206
|
+
self.class.attribute_map.each_pair do |attr, param|
|
207
|
+
value = self.send(attr)
|
208
|
+
next if value.nil?
|
209
|
+
hash[param] = _to_hash(value)
|
210
|
+
end
|
211
|
+
attributes_hash = include_root ? { "AuthenticationOptions" => hash } : hash
|
212
|
+
return attributes_hash
|
213
|
+
end
|
214
|
+
|
215
|
+
# Method to output non-array value in the form of hash
|
216
|
+
# For object, use to_hash. Otherwise, just return the value
|
217
|
+
def _to_hash(value)
|
218
|
+
if value.is_a?(Array)
|
219
|
+
value.compact.map{ |v| _to_hash(v) }
|
220
|
+
elsif value.is_a?(Hash)
|
221
|
+
{}.tap do |hash|
|
222
|
+
value.each { |k, v| hash[k] = _to_hash(v) }
|
223
|
+
end
|
224
|
+
elsif value.respond_to? :to_hash
|
225
|
+
value.to_hash
|
226
|
+
else
|
227
|
+
value
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
|
232
|
+
private
|
233
|
+
def after_parse
|
234
|
+
self.send(:remove_instance_variable, :@roxml_references) if defined? self.roxml_references
|
235
|
+
end
|
236
|
+
|
237
|
+
# Attribute datatype mapping.
|
238
|
+
def self.datatype_map
|
239
|
+
{
|
240
|
+
:authenticate_method => 'String',
|
241
|
+
:card_enrollment_method => 'String',
|
242
|
+
:c_avv => 'String',
|
243
|
+
:eci_flag => 'String',
|
244
|
+
:master_card_assigned_id => 'String',
|
245
|
+
:pa_res_status => 'String',
|
246
|
+
:sc_enrollment_status => 'String',
|
247
|
+
:signature_verification => 'String',
|
248
|
+
:xid => 'String'
|
249
|
+
|
250
|
+
}
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
|
255
|
+
end
|
@@ -0,0 +1,234 @@
|
|
1
|
+
require 'date'
|
2
|
+
require 'roxml'
|
3
|
+
require_relative '../../mastercard_merchant_checkout/models/address'
|
4
|
+
|
5
|
+
|
6
|
+
module MastercardMerchantCheckout
|
7
|
+
# This class contains card details for available cards.
|
8
|
+
class Card
|
9
|
+
include ROXML
|
10
|
+
|
11
|
+
xml_name "Card"
|
12
|
+
|
13
|
+
# @!attribute brand_id
|
14
|
+
# @return [String] the card's brand id; for example, master for MasterCard.
|
15
|
+
xml_accessor :brand_id, :from =>"brandId"
|
16
|
+
|
17
|
+
# @!attribute brand_name
|
18
|
+
# @return [String] the card's brand name; for example, MasterCard.
|
19
|
+
xml_accessor :brand_name, :from =>"brandName"
|
20
|
+
|
21
|
+
# @!attribute account_number
|
22
|
+
# @return [String] the PAN.
|
23
|
+
xml_accessor :account_number, :from =>"accountNumber"
|
24
|
+
|
25
|
+
# @!attribute card_holder_name
|
26
|
+
# @return [String] the cardholder's name.
|
27
|
+
xml_accessor :card_holder_name, :from =>"cardHolderName"
|
28
|
+
|
29
|
+
# @!attribute expiry_month
|
30
|
+
# @return [Integer] the expiration month displayed on the payment card.
|
31
|
+
xml_accessor :expiry_month, :from =>"expiryMonth"
|
32
|
+
|
33
|
+
# @!attribute expiry_year
|
34
|
+
# @return [Integer] the expiration year displayed on the payment card.
|
35
|
+
xml_accessor :expiry_year, :from =>"expiryYear"
|
36
|
+
|
37
|
+
# @!attribute billing_address
|
38
|
+
# @return [Address] the card's billing details.
|
39
|
+
xml_accessor :billing_address, :from =>"billingAddress",:as => Address
|
40
|
+
|
41
|
+
|
42
|
+
# Attribute mapping from ruby-style variable name to JSON key.
|
43
|
+
def self.attribute_map
|
44
|
+
{
|
45
|
+
:brand_id => :brandId ,
|
46
|
+
:brand_name => :brandName ,
|
47
|
+
:account_number => :accountNumber ,
|
48
|
+
:card_holder_name => :cardHolderName ,
|
49
|
+
:expiry_month => :expiryMonth ,
|
50
|
+
:expiry_year => :expiryYear ,
|
51
|
+
:billing_address => :billingAddress
|
52
|
+
|
53
|
+
}
|
54
|
+
end
|
55
|
+
|
56
|
+
def initialize(attributes = {})
|
57
|
+
return unless attributes.is_a?(Hash)
|
58
|
+
|
59
|
+
# convert string to symbol for hash key
|
60
|
+
attributes = attributes.inject({}){|memo,(k,v)| memo[k.to_sym] = v; memo}
|
61
|
+
|
62
|
+
|
63
|
+
if attributes.has_key?(:brand_id)
|
64
|
+
self.brand_id = attributes[:brand_id]
|
65
|
+
end
|
66
|
+
|
67
|
+
if attributes.has_key?(:brand_name)
|
68
|
+
self.brand_name = attributes[:brand_name]
|
69
|
+
end
|
70
|
+
|
71
|
+
if attributes.has_key?(:account_number)
|
72
|
+
self.account_number = attributes[:account_number]
|
73
|
+
end
|
74
|
+
|
75
|
+
if attributes.has_key?(:card_holder_name)
|
76
|
+
self.card_holder_name = attributes[:card_holder_name]
|
77
|
+
end
|
78
|
+
|
79
|
+
if attributes.has_key?(:expiry_month)
|
80
|
+
self.expiry_month = attributes[:expiry_month]
|
81
|
+
end
|
82
|
+
|
83
|
+
if attributes.has_key?(:expiry_year)
|
84
|
+
self.expiry_year = attributes[:expiry_year]
|
85
|
+
end
|
86
|
+
|
87
|
+
if attributes.has_key?(:billing_address)
|
88
|
+
self.billing_address = attributes[:billing_address]
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
|
93
|
+
|
94
|
+
|
95
|
+
|
96
|
+
# Check equality by comparing each attribute.
|
97
|
+
def ==(o)
|
98
|
+
return true if self.equal?(o)
|
99
|
+
self.class == o.class &&
|
100
|
+
brand_id == o.brand_id &&
|
101
|
+
brand_name == o.brand_name &&
|
102
|
+
account_number == o.account_number &&
|
103
|
+
card_holder_name == o.card_holder_name &&
|
104
|
+
expiry_month == o.expiry_month &&
|
105
|
+
expiry_year == o.expiry_year &&
|
106
|
+
billing_address == o.billing_address
|
107
|
+
end
|
108
|
+
|
109
|
+
# @see the `==` method
|
110
|
+
def eql?(o)
|
111
|
+
self == o
|
112
|
+
end
|
113
|
+
|
114
|
+
# Calculate hash code according to all attributes.
|
115
|
+
def hash
|
116
|
+
[brand_id, brand_name, account_number, card_holder_name, expiry_month, expiry_year, billing_address].hash
|
117
|
+
end
|
118
|
+
|
119
|
+
# build the object from hash
|
120
|
+
def build_from_hash(attributes)
|
121
|
+
return nil unless attributes.is_a?(Hash)
|
122
|
+
self.class.datatype_map.each_pair do |key, type|
|
123
|
+
if type =~ /^Array<(.*)>/i
|
124
|
+
if attributes[self.class.attribute_map[key]].is_a?(Array)
|
125
|
+
self.send("#{key}=", attributes[self.class.attribute_map[key]].map{ |v| _deserialize($1, v) } )
|
126
|
+
else
|
127
|
+
#TODO show warning in debug mode
|
128
|
+
end
|
129
|
+
elsif !attributes[self.class.attribute_map[key]].nil?
|
130
|
+
self.send("#{key}=", _deserialize(type, attributes[self.class.attribute_map[key]]))
|
131
|
+
else
|
132
|
+
# data not found in attributes(hash), not an issue as the data can be optional
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
self
|
137
|
+
end
|
138
|
+
|
139
|
+
def _deserialize(type, value)
|
140
|
+
case type.to_sym
|
141
|
+
when :DateTime
|
142
|
+
DateTime.parse(value)
|
143
|
+
when :Date
|
144
|
+
Date.parse(value)
|
145
|
+
when :String
|
146
|
+
value.to_s
|
147
|
+
when :Integer
|
148
|
+
value.to_i
|
149
|
+
when :Float
|
150
|
+
value.to_f
|
151
|
+
when :BOOLEAN
|
152
|
+
if value =~ /^(true|t|yes|y|1)$/i
|
153
|
+
true
|
154
|
+
else
|
155
|
+
false
|
156
|
+
end
|
157
|
+
when /\AArray<(?<inner_type>.+)>\z/
|
158
|
+
inner_type = Regexp.last_match[:inner_type]
|
159
|
+
value.map { |v| _deserialize(inner_type, v) }
|
160
|
+
when /\AHash<(?<k_type>.+), (?<v_type>.+)>\z/
|
161
|
+
k_type = Regexp.last_match[:k_type]
|
162
|
+
v_type = Regexp.last_match[:v_type]
|
163
|
+
{}.tap do |hash|
|
164
|
+
value.each do |k, v|
|
165
|
+
hash[_deserialize(k_type, k)] = _deserialize(v_type, v)
|
166
|
+
end
|
167
|
+
end
|
168
|
+
else # model
|
169
|
+
_model = MastercardMerchantCheckout.const_get(type).new
|
170
|
+
_model.build_from_hash(value)
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
def to_s
|
175
|
+
to_hash.to_s
|
176
|
+
end
|
177
|
+
|
178
|
+
# to_body is an alias to to_body (backward compatibility))
|
179
|
+
def to_body
|
180
|
+
to_hash
|
181
|
+
end
|
182
|
+
|
183
|
+
# return the object in the form of hash
|
184
|
+
def to_hash(include_root = false)
|
185
|
+
attributes_hash = {}
|
186
|
+
hash = {}
|
187
|
+
self.class.attribute_map.each_pair do |attr, param|
|
188
|
+
value = self.send(attr)
|
189
|
+
next if value.nil?
|
190
|
+
hash[param] = _to_hash(value)
|
191
|
+
end
|
192
|
+
attributes_hash = include_root ? { "Card" => hash } : hash
|
193
|
+
return attributes_hash
|
194
|
+
end
|
195
|
+
|
196
|
+
# Method to output non-array value in the form of hash
|
197
|
+
# For object, use to_hash. Otherwise, just return the value
|
198
|
+
def _to_hash(value)
|
199
|
+
if value.is_a?(Array)
|
200
|
+
value.compact.map{ |v| _to_hash(v) }
|
201
|
+
elsif value.is_a?(Hash)
|
202
|
+
{}.tap do |hash|
|
203
|
+
value.each { |k, v| hash[k] = _to_hash(v) }
|
204
|
+
end
|
205
|
+
elsif value.respond_to? :to_hash
|
206
|
+
value.to_hash
|
207
|
+
else
|
208
|
+
value
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
|
213
|
+
private
|
214
|
+
def after_parse
|
215
|
+
self.send(:remove_instance_variable, :@roxml_references) if defined? self.roxml_references
|
216
|
+
end
|
217
|
+
|
218
|
+
# Attribute datatype mapping.
|
219
|
+
def self.datatype_map
|
220
|
+
{
|
221
|
+
:brand_id => 'String',
|
222
|
+
:brand_name => 'String',
|
223
|
+
:account_number => 'String',
|
224
|
+
:card_holder_name => 'String',
|
225
|
+
:expiry_month => 'Integer',
|
226
|
+
:expiry_year => 'Integer',
|
227
|
+
:billing_address => 'Address'
|
228
|
+
|
229
|
+
}
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
|
234
|
+
end
|