activemerchant 1.87.0 → 1.88.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 +4 -4
- data/CHANGELOG +5 -0
- data/lib/active_merchant/billing/gateways/authorize_net.rb +15 -15
- data/lib/active_merchant/billing/gateways/authorize_net_cim.rb +33 -33
- data/lib/active_merchant/billing/gateways/cardknox.rb +10 -10
- data/lib/active_merchant/billing/gateways/cyber_source.rb +13 -13
- data/lib/active_merchant/billing/gateways/epay.rb +9 -9
- data/lib/active_merchant/billing/gateways/eway_managed.rb +18 -18
- data/lib/active_merchant/billing/gateways/exact.rb +10 -10
- data/lib/active_merchant/billing/gateways/litle.rb +1 -1
- data/lib/active_merchant/billing/gateways/merchant_one.rb +3 -3
- data/lib/active_merchant/billing/gateways/merchant_ware.rb +4 -4
- data/lib/active_merchant/billing/gateways/mundipagg.rb +1 -1
- data/lib/active_merchant/billing/gateways/netbilling.rb +8 -8
- data/lib/active_merchant/billing/gateways/ogone.rb +2 -2
- data/lib/active_merchant/billing/gateways/omise.rb +5 -5
- data/lib/active_merchant/billing/gateways/opp.rb +1 -1
- data/lib/active_merchant/billing/gateways/paymill.rb +5 -5
- data/lib/active_merchant/billing/gateways/paypal.rb +1 -1
- data/lib/active_merchant/billing/gateways/paystation.rb +76 -76
- data/lib/active_merchant/billing/gateways/payu_latam.rb +1 -1
- data/lib/active_merchant/billing/gateways/quickbooks.rb +2 -2
- data/lib/active_merchant/billing/gateways/quickpay/quickpay_v10.rb +150 -150
- data/lib/active_merchant/billing/gateways/sage.rb +11 -11
- data/lib/active_merchant/billing/gateways/sage_pay.rb +5 -5
- data/lib/active_merchant/billing/gateways/wirecard.rb +5 -5
- data/lib/active_merchant/version.rb +1 -1
- metadata +2 -8
@@ -408,8 +408,8 @@ module ActiveMerchant #:nodoc:
|
|
408
408
|
|
409
409
|
def add_signature(parameters)
|
410
410
|
if @options[:signature].blank?
|
411
|
-
|
412
|
-
|
411
|
+
ActiveMerchant.deprecated(OGONE_NO_SIGNATURE_DEPRECATION_MESSAGE) unless(@options[:signature_encryptor] == 'none')
|
412
|
+
return
|
413
413
|
end
|
414
414
|
|
415
415
|
add_pair parameters, 'SHASign', calculate_signature(parameters, @options[:signature_encryptor], @options[:signature])
|
@@ -247,15 +247,15 @@ module ActiveMerchant #:nodoc:
|
|
247
247
|
message = response['message'] if response['code'] == 'invalid_card'
|
248
248
|
case message
|
249
249
|
when /brand not supported/
|
250
|
-
|
250
|
+
STANDARD_ERROR_CODE[:invalid_number]
|
251
251
|
when /number is invalid/
|
252
|
-
|
252
|
+
STANDARD_ERROR_CODE[:incorrect_number]
|
253
253
|
when /expiration date cannot be in the past/
|
254
|
-
|
254
|
+
STANDARD_ERROR_CODE[:expired_card]
|
255
255
|
when /expiration \w+ is invalid/
|
256
|
-
|
256
|
+
STANDARD_ERROR_CODE[:invalid_expiry_date]
|
257
257
|
else
|
258
|
-
|
258
|
+
STANDARD_ERROR_CODE[:processing_error]
|
259
259
|
end
|
260
260
|
end
|
261
261
|
|
@@ -189,7 +189,7 @@ module ActiveMerchant #:nodoc:
|
|
189
189
|
end
|
190
190
|
|
191
191
|
def add_authentication(post)
|
192
|
-
|
192
|
+
post[:authentication] = { entityId: @options[:entity_id], password: @options[:password], userId: @options[:user_id]}
|
193
193
|
end
|
194
194
|
|
195
195
|
def add_customer_data(post, payment, options)
|
@@ -129,12 +129,12 @@ module ActiveMerchant #:nodoc:
|
|
129
129
|
options[:money] = money
|
130
130
|
case payment_method
|
131
131
|
when String
|
132
|
-
|
132
|
+
self.send("#{action}_with_token", money, payment_method, options)
|
133
133
|
else
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
134
|
+
MultiResponse.run do |r|
|
135
|
+
r.process { save_card(payment_method, options) }
|
136
|
+
r.process { self.send("#{action}_with_token", money, r.authorization, options) }
|
137
|
+
end
|
138
138
|
end
|
139
139
|
end
|
140
140
|
|
@@ -100,101 +100,101 @@ module ActiveMerchant #:nodoc:
|
|
100
100
|
|
101
101
|
private
|
102
102
|
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
def add_customer_data(post, options)
|
114
|
-
post[:mc] = options[:customer]
|
115
|
-
end
|
103
|
+
def new_request
|
104
|
+
{
|
105
|
+
:pi => @options[:paystation_id], # paystation account id
|
106
|
+
:gi => @options[:gateway_id], # paystation gateway id
|
107
|
+
'2p' => 't', # two-party transaction type
|
108
|
+
:nr => 't', # -- redirect??
|
109
|
+
:df => 'yymm' # date format: optional sometimes, required others
|
110
|
+
}
|
111
|
+
end
|
116
112
|
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
post[:mr] = options[:order_id]
|
121
|
-
end
|
113
|
+
def add_customer_data(post, options)
|
114
|
+
post[:mc] = options[:customer]
|
115
|
+
end
|
122
116
|
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
end
|
117
|
+
def add_invoice(post, options)
|
118
|
+
post[:ms] = generate_unique_id
|
119
|
+
post[:mo] = options[:description]
|
120
|
+
post[:mr] = options[:order_id]
|
121
|
+
end
|
129
122
|
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
123
|
+
def add_credit_card(post, credit_card)
|
124
|
+
post[:cn] = credit_card.number
|
125
|
+
post[:ct] = credit_card.brand
|
126
|
+
post[:ex] = format_date(credit_card.month, credit_card.year)
|
127
|
+
post[:cc] = credit_card.verification_value if credit_card.verification_value?
|
128
|
+
end
|
134
129
|
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
end
|
130
|
+
def add_token(post, token)
|
131
|
+
post[:fp] = 't' # turn on "future payments" - what paystation calls Token Billing
|
132
|
+
post[:ft] = token
|
133
|
+
end
|
140
134
|
|
141
|
-
|
142
|
-
|
143
|
-
|
135
|
+
def store_credit_card(post, options)
|
136
|
+
post[:fp] = 't' # turn on "future payments" - what paystation calls Token Billing
|
137
|
+
post[:fs] = 't' # tells paystation to store right now, not bill
|
138
|
+
post[:ft] = options[:token] if options[:token] # specify a token to use that, or let Paystation generate one
|
139
|
+
end
|
144
140
|
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
end
|
141
|
+
def add_authorize_flag(post, options)
|
142
|
+
post[:pa] = 't' # tells Paystation that this is a pre-auth authorisation payment (account must be in pre-auth mode)
|
143
|
+
end
|
149
144
|
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
end
|
145
|
+
def add_refund_specific_fields(post, authorization)
|
146
|
+
post[:rc] = 't'
|
147
|
+
post[:rt] = authorization
|
148
|
+
end
|
155
149
|
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
150
|
+
def add_authorization_token(post, auth_token, verification_value = nil)
|
151
|
+
post[:cp] = 't' # Capture Payment flag – tells Paystation this transaction should be treated as a capture payment
|
152
|
+
post[:cx] = auth_token
|
153
|
+
post[:cc] = verification_value
|
154
|
+
end
|
160
155
|
|
161
|
-
|
162
|
-
|
156
|
+
def add_amount(post, money, options)
|
157
|
+
post[:am] = amount(money)
|
158
|
+
post[:cu] = options[:currency] || currency(money)
|
159
|
+
end
|
163
160
|
|
164
|
-
|
161
|
+
def parse(xml_response)
|
162
|
+
response = {}
|
165
163
|
|
166
|
-
|
167
|
-
response[element.name.underscore.to_sym] = element.text
|
168
|
-
end
|
164
|
+
xml = REXML::Document.new(xml_response)
|
169
165
|
|
170
|
-
|
166
|
+
xml.elements.each("#{xml.root.name}/*") do |element|
|
167
|
+
response[element.name.underscore.to_sym] = element.text
|
171
168
|
end
|
172
169
|
|
173
|
-
|
174
|
-
|
175
|
-
pstn_prefix_params = post.collect { |key, value| "pstn_#{key}=#{CGI.escape(value.to_s)}" }.join('&')
|
170
|
+
response
|
171
|
+
end
|
176
172
|
|
177
|
-
|
178
|
-
|
179
|
-
|
173
|
+
def commit(post)
|
174
|
+
post[:tm] = 'T' if test?
|
175
|
+
pstn_prefix_params = post.collect { |key, value| "pstn_#{key}=#{CGI.escape(value.to_s)}" }.join('&')
|
180
176
|
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
)
|
185
|
-
end
|
177
|
+
data = ssl_post(self.live_url, "#{pstn_prefix_params}&paystation=_empty")
|
178
|
+
response = parse(data)
|
179
|
+
message = message_from(response)
|
186
180
|
|
187
|
-
|
188
|
-
|
189
|
-
|
181
|
+
PaystationResponse.new(success?(response), message, response,
|
182
|
+
:test => (response[:tm]&.casecmp('t')&.zero?),
|
183
|
+
:authorization => response[:paystation_transaction_id]
|
184
|
+
)
|
185
|
+
end
|
190
186
|
|
191
|
-
|
192
|
-
|
193
|
-
|
187
|
+
def success?(response)
|
188
|
+
(response[:ec] == SUCCESSFUL_RESPONSE_CODE) || (response[:ec] == SUCCESSFUL_FUTURE_PAYMENT)
|
189
|
+
end
|
194
190
|
|
195
|
-
|
196
|
-
|
197
|
-
|
191
|
+
def message_from(response)
|
192
|
+
response[:em]
|
193
|
+
end
|
194
|
+
|
195
|
+
def format_date(month, year)
|
196
|
+
"#{format(year, :two_digits)}#{format(month, :two_digits)}"
|
197
|
+
end
|
198
198
|
|
199
199
|
end
|
200
200
|
|
@@ -365,7 +365,7 @@ module ActiveMerchant #:nodoc:
|
|
365
365
|
when 'verify_credentials'
|
366
366
|
response['code'] == 'SUCCESS'
|
367
367
|
when 'refund', 'void'
|
368
|
-
|
368
|
+
response['code'] == 'SUCCESS' && response['transactionResponse'] && (response['transactionResponse']['state'] == 'PENDING' || response['transactionResponse']['state'] == 'APPROVED')
|
369
369
|
else
|
370
370
|
response['code'] == 'SUCCESS' && response['transactionResponse'] && (response['transactionResponse']['state'] == 'APPROVED')
|
371
371
|
end
|
@@ -252,9 +252,9 @@ module ActiveMerchant #:nodoc:
|
|
252
252
|
end
|
253
253
|
|
254
254
|
def success?(response)
|
255
|
-
|
255
|
+
return FRAUD_WARNING_CODES.concat(['0']).include?(response['errors'].first['code']) if response['errors']
|
256
256
|
|
257
|
-
|
257
|
+
!['DECLINED', 'CANCELLED'].include?(response['status'])
|
258
258
|
end
|
259
259
|
|
260
260
|
def message_from(response)
|
@@ -99,197 +99,197 @@ module ActiveMerchant
|
|
99
99
|
|
100
100
|
private
|
101
101
|
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
add_amount(post, money, options)
|
106
|
-
add_credit_card_or_reference(post, credit_card_or_reference)
|
107
|
-
add_additional_params(:authorize, post, options)
|
102
|
+
def authorization_params(money, credit_card_or_reference, options = {})
|
103
|
+
post = {}
|
108
104
|
|
109
|
-
|
110
|
-
|
105
|
+
add_amount(post, money, options)
|
106
|
+
add_credit_card_or_reference(post, credit_card_or_reference)
|
107
|
+
add_additional_params(:authorize, post, options)
|
111
108
|
|
112
|
-
|
113
|
-
|
109
|
+
post
|
110
|
+
end
|
114
111
|
|
115
|
-
|
116
|
-
|
112
|
+
def capture_params(money, credit_card, options = {})
|
113
|
+
post = {}
|
117
114
|
|
118
|
-
|
119
|
-
|
115
|
+
add_amount(post, money, options)
|
116
|
+
add_additional_params(:capture, post, options)
|
120
117
|
|
121
|
-
|
122
|
-
|
123
|
-
commit('/cards', post)
|
124
|
-
end
|
118
|
+
post
|
119
|
+
end
|
125
120
|
|
126
|
-
|
127
|
-
|
121
|
+
def create_store(options = {})
|
122
|
+
post = {}
|
123
|
+
commit('/cards', post)
|
124
|
+
end
|
128
125
|
|
129
|
-
|
130
|
-
|
131
|
-
end
|
126
|
+
def authorize_store(identification, credit_card, options = {})
|
127
|
+
post = {}
|
132
128
|
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
end
|
129
|
+
add_credit_card_or_reference(post, credit_card, options)
|
130
|
+
commit(synchronized_path("/cards/#{identification}/authorize"), post)
|
131
|
+
end
|
137
132
|
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
commit('/payments', post)
|
143
|
-
end
|
133
|
+
def create_token(identification, options)
|
134
|
+
post = {}
|
135
|
+
commit(synchronized_path("/cards/#{identification}/tokens"), post)
|
136
|
+
end
|
144
137
|
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
response = response_error(e.response.body)
|
152
|
-
rescue JSON::ParserError
|
153
|
-
response = json_error(response)
|
154
|
-
end
|
138
|
+
def create_payment(money, options = {})
|
139
|
+
post = {}
|
140
|
+
add_currency(post, money, options)
|
141
|
+
add_invoice(post, options)
|
142
|
+
commit('/payments', post)
|
143
|
+
end
|
155
144
|
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
)
|
145
|
+
def commit(action, params = {})
|
146
|
+
success = false
|
147
|
+
begin
|
148
|
+
response = parse(ssl_post(self.live_url + action, params.to_json, headers))
|
149
|
+
success = successful?(response)
|
150
|
+
rescue ResponseError => e
|
151
|
+
response = response_error(e.response.body)
|
152
|
+
rescue JSON::ParserError
|
153
|
+
response = json_error(response)
|
160
154
|
end
|
161
155
|
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
end
|
168
|
-
end
|
156
|
+
Response.new(success, message_from(success, response), response,
|
157
|
+
:test => test?,
|
158
|
+
:authorization => authorization_from(response)
|
159
|
+
)
|
160
|
+
end
|
169
161
|
|
170
|
-
|
171
|
-
|
162
|
+
def authorization_from(response)
|
163
|
+
if response['token']
|
164
|
+
response['token'].to_s
|
165
|
+
else
|
166
|
+
response['id'].to_s
|
172
167
|
end
|
168
|
+
end
|
173
169
|
|
174
|
-
|
175
|
-
|
176
|
-
|
170
|
+
def add_currency(post, money, options)
|
171
|
+
post[:currency] = options[:currency] || currency(money)
|
172
|
+
end
|
177
173
|
|
178
|
-
|
179
|
-
|
180
|
-
|
174
|
+
def add_amount(post, money, options)
|
175
|
+
post[:amount] = options[:amount] || amount(money)
|
176
|
+
end
|
181
177
|
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
end
|
178
|
+
def add_autocapture(post, value)
|
179
|
+
post[:auto_capture] = value
|
180
|
+
end
|
186
181
|
|
187
|
-
|
188
|
-
|
182
|
+
def add_order_id(post, options)
|
183
|
+
requires!(options, :order_id)
|
184
|
+
post[:order_id] = format_order_id(options[:order_id])
|
185
|
+
end
|
189
186
|
|
190
|
-
|
191
|
-
|
192
|
-
end
|
187
|
+
def add_invoice(post, options)
|
188
|
+
add_order_id(post, options)
|
193
189
|
|
194
|
-
|
195
|
-
|
196
|
-
|
190
|
+
if options[:billing_address]
|
191
|
+
post[:invoice_address] = map_address(options[:billing_address])
|
192
|
+
end
|
197
193
|
|
198
|
-
|
199
|
-
|
200
|
-
end
|
194
|
+
if options[:shipping_address]
|
195
|
+
post[:shipping_address] = map_address(options[:shipping_address])
|
201
196
|
end
|
202
197
|
|
203
|
-
|
204
|
-
|
205
|
-
key = key.to_sym
|
206
|
-
post[key] = options[key] if options[key]
|
207
|
-
end
|
198
|
+
[:metadata, :branding_id, :variables].each do |field|
|
199
|
+
post[field] = options[field] if options[field]
|
208
200
|
end
|
201
|
+
end
|
209
202
|
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
else
|
215
|
-
post[:card][:number] = credit_card_or_reference.number
|
216
|
-
post[:card][:cvd] = credit_card_or_reference.verification_value
|
217
|
-
post[:card][:expiration] = expdate(credit_card_or_reference)
|
218
|
-
post[:card][:issued_to] = credit_card_or_reference.name
|
219
|
-
end
|
203
|
+
def add_additional_params(action, post, options = {})
|
204
|
+
MD5_CHECK_FIELDS[API_VERSION][action].each do |key|
|
205
|
+
key = key.to_sym
|
206
|
+
post[key] = options[key] if options[key]
|
220
207
|
end
|
208
|
+
end
|
221
209
|
|
222
|
-
|
223
|
-
|
210
|
+
def add_credit_card_or_reference(post, credit_card_or_reference, options = {})
|
211
|
+
post[:card] ||= {}
|
212
|
+
if credit_card_or_reference.is_a?(String)
|
213
|
+
post[:card][:token] = credit_card_or_reference
|
214
|
+
else
|
215
|
+
post[:card][:number] = credit_card_or_reference.number
|
216
|
+
post[:card][:cvd] = credit_card_or_reference.verification_value
|
217
|
+
post[:card][:expiration] = expdate(credit_card_or_reference)
|
218
|
+
post[:card][:issued_to] = credit_card_or_reference.name
|
224
219
|
end
|
220
|
+
end
|
225
221
|
|
226
|
-
|
227
|
-
|
228
|
-
|
222
|
+
def parse(body)
|
223
|
+
JSON.parse(body)
|
224
|
+
end
|
229
225
|
|
230
|
-
|
231
|
-
|
226
|
+
def successful?(response)
|
227
|
+
has_error = response['errors']
|
228
|
+
invalid_code = invalid_operation_code?(response)
|
232
229
|
|
233
|
-
|
234
|
-
|
235
|
-
end
|
230
|
+
!(has_error || invalid_code)
|
231
|
+
end
|
236
232
|
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
operation && operation['qp_status_code'] != '20000'
|
241
|
-
end
|
242
|
-
end
|
233
|
+
def message_from(success, response)
|
234
|
+
success ? 'OK' : (response['message'] || invalid_operation_message(response) || 'Unknown error - please contact QuickPay')
|
235
|
+
end
|
243
236
|
|
244
|
-
|
245
|
-
|
237
|
+
def invalid_operation_code?(response)
|
238
|
+
if response['operations']
|
239
|
+
operation = response['operations'].last
|
240
|
+
operation && operation['qp_status_code'] != '20000'
|
246
241
|
end
|
242
|
+
end
|
247
243
|
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
country = Country.find(address[:country])
|
252
|
-
mapped = {
|
253
|
-
:name => address[:name],
|
254
|
-
:street => address[:address1],
|
255
|
-
:city => address[:city],
|
256
|
-
:region => address[:address2],
|
257
|
-
:zip_code => address[:zip],
|
258
|
-
:country_code => country.code(:alpha3).value
|
259
|
-
}
|
260
|
-
mapped
|
261
|
-
end
|
244
|
+
def invalid_operation_message(response)
|
245
|
+
response['operations'] && response['operations'].last['qp_status_msg']
|
246
|
+
end
|
262
247
|
|
263
|
-
|
264
|
-
|
265
|
-
|
248
|
+
def map_address(address)
|
249
|
+
return {} if address.nil?
|
250
|
+
requires!(address, :name, :address1, :city, :zip, :country)
|
251
|
+
country = Country.find(address[:country])
|
252
|
+
mapped = {
|
253
|
+
:name => address[:name],
|
254
|
+
:street => address[:address1],
|
255
|
+
:city => address[:city],
|
256
|
+
:region => address[:address2],
|
257
|
+
:zip_code => address[:zip],
|
258
|
+
:country_code => country.code(:alpha3).value
|
259
|
+
}
|
260
|
+
mapped
|
261
|
+
end
|
266
262
|
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
'Authorization' => 'Basic ' + auth,
|
271
|
-
'User-Agent' => "Quickpay-v#{API_VERSION} ActiveMerchantBindings/#{ActiveMerchant::VERSION}",
|
272
|
-
'Accept' => 'application/json',
|
273
|
-
'Accept-Version' => "v#{API_VERSION}",
|
274
|
-
'Content-Type' => 'application/json'
|
275
|
-
}
|
276
|
-
end
|
263
|
+
def format_order_id(order_id)
|
264
|
+
truncate(order_id.to_s.gsub(/#/, ''), 20)
|
265
|
+
end
|
277
266
|
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
267
|
+
def headers
|
268
|
+
auth = Base64.strict_encode64(":#{@options[:api_key]}")
|
269
|
+
{
|
270
|
+
'Authorization' => 'Basic ' + auth,
|
271
|
+
'User-Agent' => "Quickpay-v#{API_VERSION} ActiveMerchantBindings/#{ActiveMerchant::VERSION}",
|
272
|
+
'Accept' => 'application/json',
|
273
|
+
'Accept-Version' => "v#{API_VERSION}",
|
274
|
+
'Content-Type' => 'application/json'
|
275
|
+
}
|
276
|
+
end
|
283
277
|
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
278
|
+
def response_error(raw_response)
|
279
|
+
parse(raw_response)
|
280
|
+
rescue JSON::ParserError
|
281
|
+
json_error(raw_response)
|
282
|
+
end
|
289
283
|
|
290
|
-
|
291
|
-
|
292
|
-
|
284
|
+
def json_error(raw_response)
|
285
|
+
msg = 'Invalid response received from the Quickpay API.'
|
286
|
+
msg += " (The raw response returned by the API was #{raw_response.inspect})"
|
287
|
+
{ 'message' => msg }
|
288
|
+
end
|
289
|
+
|
290
|
+
def synchronized_path(path)
|
291
|
+
"#{path}?synchronized"
|
292
|
+
end
|
293
293
|
end
|
294
294
|
end
|
295
295
|
end
|