clover_sandbox_simulator 1.0.0 ā 1.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 +4 -4
- data/bin/simulate +243 -3
- data/lib/clover_sandbox_simulator/configuration.rb +122 -2
- data/lib/clover_sandbox_simulator/data/restaurant/combos.json +242 -0
- data/lib/clover_sandbox_simulator/data/restaurant/coupon_codes.json +266 -0
- data/lib/clover_sandbox_simulator/data/restaurant/discounts.json +166 -7
- data/lib/clover_sandbox_simulator/data/restaurant/gift_cards.json +82 -0
- data/lib/clover_sandbox_simulator/generators/data_loader.rb +59 -0
- data/lib/clover_sandbox_simulator/generators/order_generator.rb +583 -54
- data/lib/clover_sandbox_simulator/parallel_executor.rb +319 -0
- data/lib/clover_sandbox_simulator/services/clover/discount_service.rb +786 -1
- data/lib/clover_sandbox_simulator/services/clover/ecommerce_service.rb +282 -0
- data/lib/clover_sandbox_simulator/services/clover/gift_card_service.rb +224 -0
- data/lib/clover_sandbox_simulator/services/clover/oauth_service.rb +265 -0
- data/lib/clover_sandbox_simulator/services/clover/order_service.rb +60 -3
- data/lib/clover_sandbox_simulator/services/clover/payment_service.rb +95 -2
- data/lib/clover_sandbox_simulator/services/clover/refund_service.rb +367 -0
- data/lib/clover_sandbox_simulator/services/clover/services_manager.rb +31 -0
- metadata +38 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 72dfd2be038d2febf2b311a2b5a076060141e3d3d32fd8030c60b7c5a96b2560
|
|
4
|
+
data.tar.gz: 6eb8ef64591c7fc9296f9839961d1865d2dcb98e27ea9165f020cda7cc2940ed
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 124c9d70cf3b8c993ad7f5f348099ab4a8fcab54b26ddcd301191d8705a9168b3fdeef8473cf500f006430d87952d74ba2ff9e787424041014245e44983ae657
|
|
7
|
+
data.tar.gz: 74734957ee1a1a8c0e935d554039b836d9e5990218095c66c3c64166848f85006dfbcbf242b47c526649fb3bf3d465594d6c49525955acb31be6ed9ede5ce7ec
|
data/bin/simulate
CHANGED
|
@@ -26,13 +26,15 @@ module CloverSandboxSimulator
|
|
|
26
26
|
|
|
27
27
|
desc "generate", "Generate orders for today"
|
|
28
28
|
option :count, type: :numeric, aliases: "-n", desc: "Number of orders to generate"
|
|
29
|
+
option :refund_percentage, type: :numeric, aliases: "-r", default: 5,
|
|
30
|
+
desc: "Percentage of orders to refund (0-100, default 5)"
|
|
29
31
|
def generate
|
|
30
32
|
configure_logging
|
|
31
33
|
|
|
32
34
|
puts "š½ļø Clover Sandbox Simulator - Order Generation"
|
|
33
35
|
puts "=" * 50
|
|
34
36
|
|
|
35
|
-
generator = Generators::OrderGenerator.new
|
|
37
|
+
generator = Generators::OrderGenerator.new(refund_percentage: options[:refund_percentage])
|
|
36
38
|
count = options[:count]
|
|
37
39
|
|
|
38
40
|
orders = generator.generate_today(count: count)
|
|
@@ -42,13 +44,15 @@ module CloverSandboxSimulator
|
|
|
42
44
|
|
|
43
45
|
desc "day", "Generate a realistic full day of restaurant operations"
|
|
44
46
|
option :multiplier, type: :numeric, aliases: "-m", default: 1.0, desc: "Order multiplier (0.5 = slow day, 2.0 = busy day)"
|
|
47
|
+
option :refund_percentage, type: :numeric, aliases: "-r", default: 5,
|
|
48
|
+
desc: "Percentage of orders to refund (0-100, default 5)"
|
|
45
49
|
def day
|
|
46
50
|
configure_logging
|
|
47
51
|
|
|
48
52
|
puts "š½ļø Clover Sandbox Simulator - Realistic Restaurant Day"
|
|
49
53
|
puts "=" * 50
|
|
50
54
|
|
|
51
|
-
generator = Generators::OrderGenerator.new
|
|
55
|
+
generator = Generators::OrderGenerator.new(refund_percentage: options[:refund_percentage])
|
|
52
56
|
orders = generator.generate_realistic_day(multiplier: options[:multiplier])
|
|
53
57
|
|
|
54
58
|
puts "\nā
Generated #{orders.size} orders!"
|
|
@@ -96,6 +100,8 @@ module CloverSandboxSimulator
|
|
|
96
100
|
desc "full", "Run full simulation (setup + generate orders)"
|
|
97
101
|
option :count, type: :numeric, aliases: "-n", desc: "Number of orders to generate"
|
|
98
102
|
option :business_type, type: :string, default: "restaurant", desc: "Business type"
|
|
103
|
+
option :refund_percentage, type: :numeric, aliases: "-r", default: 5,
|
|
104
|
+
desc: "Percentage of orders to refund (0-100, default 5)"
|
|
99
105
|
def full
|
|
100
106
|
configure_logging
|
|
101
107
|
|
|
@@ -109,7 +115,7 @@ module CloverSandboxSimulator
|
|
|
109
115
|
puts "\n"
|
|
110
116
|
|
|
111
117
|
# Generate orders
|
|
112
|
-
order_gen = Generators::OrderGenerator.new
|
|
118
|
+
order_gen = Generators::OrderGenerator.new(refund_percentage: options[:refund_percentage])
|
|
113
119
|
orders = order_gen.generate_today(count: options[:count])
|
|
114
120
|
|
|
115
121
|
puts "\nā
Full simulation complete!"
|
|
@@ -150,6 +156,7 @@ module CloverSandboxSimulator
|
|
|
150
156
|
employees = services.employee.get_employees
|
|
151
157
|
customers = services.customer.get_customers
|
|
152
158
|
discounts = services.discount.get_discounts
|
|
159
|
+
refunds = services.refund.fetch_refunds
|
|
153
160
|
|
|
154
161
|
puts "Categories: #{categories.size}"
|
|
155
162
|
puts "Menu Items: #{items.size}"
|
|
@@ -157,6 +164,7 @@ module CloverSandboxSimulator
|
|
|
157
164
|
puts "Employees: #{employees.size}"
|
|
158
165
|
puts "Customers: #{customers.size}"
|
|
159
166
|
puts "Discounts: #{discounts.size}"
|
|
167
|
+
puts "Refunds: #{refunds.size}"
|
|
160
168
|
|
|
161
169
|
puts "\nš Categories:"
|
|
162
170
|
categories.each { |c| puts " - #{c['name']}" }
|
|
@@ -165,13 +173,245 @@ module CloverSandboxSimulator
|
|
|
165
173
|
tenders.each { |t| puts " - #{t['label']}" }
|
|
166
174
|
end
|
|
167
175
|
|
|
176
|
+
desc "refunds", "List all refunds"
|
|
177
|
+
option :limit, type: :numeric, aliases: "-l", desc: "Maximum number of refunds to show"
|
|
178
|
+
def refunds
|
|
179
|
+
configure_logging
|
|
180
|
+
|
|
181
|
+
puts "š½ļø Clover Sandbox Simulator - Refunds"
|
|
182
|
+
puts "=" * 50
|
|
183
|
+
|
|
184
|
+
services = Services::Clover::ServicesManager.new
|
|
185
|
+
refunds = services.refund.fetch_refunds(limit: options[:limit])
|
|
186
|
+
|
|
187
|
+
if refunds.empty?
|
|
188
|
+
puts "No refunds found."
|
|
189
|
+
return
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
puts "Found #{refunds.size} refunds:\n\n"
|
|
193
|
+
|
|
194
|
+
total_amount = 0
|
|
195
|
+
refunds.each do |refund|
|
|
196
|
+
amount = refund["amount"] || 0
|
|
197
|
+
total_amount += amount
|
|
198
|
+
payment_id = refund.dig("payment", "id") || "N/A"
|
|
199
|
+
created_at = refund["createdTime"] ? Time.at(refund["createdTime"] / 1000).strftime("%Y-%m-%d %H:%M") : "N/A"
|
|
200
|
+
|
|
201
|
+
puts " ID: #{refund['id']}"
|
|
202
|
+
puts " Amount: $#{'%.2f' % (amount / 100.0)}"
|
|
203
|
+
puts " Payment: #{payment_id}"
|
|
204
|
+
puts " Created: #{created_at}"
|
|
205
|
+
puts ""
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
puts "-" * 40
|
|
209
|
+
puts "Total refunded: $#{'%.2f' % (total_amount / 100.0)}"
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
desc "refund", "Create a refund for a payment"
|
|
213
|
+
option :payment_id, type: :string, aliases: "-p", required: true, desc: "Payment ID to refund"
|
|
214
|
+
option :amount, type: :numeric, aliases: "-a", desc: "Amount in cents (omit for full refund)"
|
|
215
|
+
option :reason, type: :string, aliases: "-r", default: "customer_request",
|
|
216
|
+
desc: "Refund reason (customer_request, quality_issue, wrong_order, duplicate_charge)"
|
|
217
|
+
def refund
|
|
218
|
+
configure_logging
|
|
219
|
+
|
|
220
|
+
puts "š½ļø Clover Sandbox Simulator - Create Refund"
|
|
221
|
+
puts "=" * 50
|
|
222
|
+
|
|
223
|
+
services = Services::Clover::ServicesManager.new
|
|
224
|
+
|
|
225
|
+
if options[:amount]
|
|
226
|
+
puts "Creating partial refund of $#{'%.2f' % (options[:amount] / 100.0)}..."
|
|
227
|
+
result = services.refund.create_partial_refund(
|
|
228
|
+
payment_id: options[:payment_id],
|
|
229
|
+
amount: options[:amount],
|
|
230
|
+
reason: options[:reason]
|
|
231
|
+
)
|
|
232
|
+
else
|
|
233
|
+
puts "Creating full refund..."
|
|
234
|
+
result = services.refund.create_full_refund(
|
|
235
|
+
payment_id: options[:payment_id],
|
|
236
|
+
reason: options[:reason]
|
|
237
|
+
)
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
if result && result["id"]
|
|
241
|
+
puts "\nā
Refund created successfully!"
|
|
242
|
+
puts " Refund ID: #{result['id']}"
|
|
243
|
+
puts " Amount: $#{'%.2f' % ((result['amount'] || 0) / 100.0)}"
|
|
244
|
+
else
|
|
245
|
+
puts "\nā Failed to create refund."
|
|
246
|
+
end
|
|
247
|
+
end
|
|
248
|
+
|
|
168
249
|
desc "version", "Show version"
|
|
169
250
|
def version
|
|
170
251
|
puts "Clover Sandbox Simulator v1.0.0"
|
|
171
252
|
end
|
|
172
253
|
|
|
254
|
+
# ============================================
|
|
255
|
+
# Gift Card Commands
|
|
256
|
+
# ============================================
|
|
257
|
+
|
|
258
|
+
desc "gift_cards", "List all gift cards and their balances"
|
|
259
|
+
def gift_cards
|
|
260
|
+
configure_logging
|
|
261
|
+
|
|
262
|
+
puts "š Clover Sandbox Simulator - Gift Cards"
|
|
263
|
+
puts "=" * 50
|
|
264
|
+
|
|
265
|
+
services = Services::Clover::ServicesManager.new
|
|
266
|
+
cards = services.gift_card.fetch_gift_cards
|
|
267
|
+
|
|
268
|
+
if cards.empty?
|
|
269
|
+
puts "No gift cards found."
|
|
270
|
+
puts "\nCreate one with: simulate gift_card_create --amount 5000"
|
|
271
|
+
return
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
puts "\n#{'ID'.ljust(20)} #{'Card Number'.ljust(20)} #{'Balance'.rjust(12)} #{'Status'.ljust(10)}"
|
|
275
|
+
puts "-" * 65
|
|
276
|
+
|
|
277
|
+
total_balance = 0
|
|
278
|
+
cards.each do |card|
|
|
279
|
+
id = card["id"] || "N/A"
|
|
280
|
+
number = mask_card_number(card["cardNumber"] || "Unknown")
|
|
281
|
+
balance = card["balance"] || 0
|
|
282
|
+
status = card["status"] || "UNKNOWN"
|
|
283
|
+
|
|
284
|
+
total_balance += balance if status == "ACTIVE"
|
|
285
|
+
|
|
286
|
+
balance_str = "$#{'%.2f' % (balance / 100.0)}"
|
|
287
|
+
puts "#{id.ljust(20)} #{number.ljust(20)} #{balance_str.rjust(12)} #{status.ljust(10)}"
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
puts "-" * 65
|
|
291
|
+
puts "Total Active Balance: $#{'%.2f' % (total_balance / 100.0)}"
|
|
292
|
+
puts "\nā
Found #{cards.size} gift cards"
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
desc "gift_card_create", "Create a new gift card"
|
|
296
|
+
option :amount, type: :numeric, aliases: "-a", required: true, desc: "Initial balance in cents (e.g., 5000 for $50)"
|
|
297
|
+
option :card_number, type: :string, aliases: "-n", desc: "Optional 16-digit card number"
|
|
298
|
+
def gift_card_create
|
|
299
|
+
configure_logging
|
|
300
|
+
|
|
301
|
+
puts "š Clover Sandbox Simulator - Create Gift Card"
|
|
302
|
+
puts "=" * 50
|
|
303
|
+
|
|
304
|
+
services = Services::Clover::ServicesManager.new
|
|
305
|
+
|
|
306
|
+
amount = options[:amount]
|
|
307
|
+
card_number = options[:card_number]
|
|
308
|
+
|
|
309
|
+
puts "Creating gift card with balance: $#{'%.2f' % (amount / 100.0)}"
|
|
310
|
+
|
|
311
|
+
result = services.gift_card.create_gift_card(
|
|
312
|
+
amount: amount,
|
|
313
|
+
card_number: card_number
|
|
314
|
+
)
|
|
315
|
+
|
|
316
|
+
if result && result["id"]
|
|
317
|
+
puts "\nā
Gift card created successfully!"
|
|
318
|
+
puts " ID: #{result['id']}"
|
|
319
|
+
puts " Card Number: #{mask_card_number(result['cardNumber'])}"
|
|
320
|
+
puts " Balance: $#{'%.2f' % ((result['balance'] || amount) / 100.0)}"
|
|
321
|
+
puts " Status: #{result['status']}"
|
|
322
|
+
else
|
|
323
|
+
puts "\nā Failed to create gift card"
|
|
324
|
+
end
|
|
325
|
+
end
|
|
326
|
+
|
|
327
|
+
desc "gift_card_balance", "Check gift card balance"
|
|
328
|
+
option :id, type: :string, aliases: "-i", required: true, desc: "Gift card ID"
|
|
329
|
+
def gift_card_balance
|
|
330
|
+
configure_logging
|
|
331
|
+
|
|
332
|
+
puts "š Clover Sandbox Simulator - Gift Card Balance"
|
|
333
|
+
puts "=" * 50
|
|
334
|
+
|
|
335
|
+
services = Services::Clover::ServicesManager.new
|
|
336
|
+
|
|
337
|
+
card = services.gift_card.get_gift_card(options[:id])
|
|
338
|
+
|
|
339
|
+
if card
|
|
340
|
+
puts "\nGift Card Details:"
|
|
341
|
+
puts " ID: #{card['id']}"
|
|
342
|
+
puts " Card Number: #{mask_card_number(card['cardNumber'])}"
|
|
343
|
+
puts " Balance: $#{'%.2f' % ((card['balance'] || 0) / 100.0)}"
|
|
344
|
+
puts " Status: #{card['status']}"
|
|
345
|
+
else
|
|
346
|
+
puts "\nā Gift card not found: #{options[:id]}"
|
|
347
|
+
end
|
|
348
|
+
end
|
|
349
|
+
|
|
350
|
+
desc "gift_card_reload", "Add balance to a gift card"
|
|
351
|
+
option :id, type: :string, aliases: "-i", required: true, desc: "Gift card ID"
|
|
352
|
+
option :amount, type: :numeric, aliases: "-a", required: true, desc: "Amount to add in cents"
|
|
353
|
+
def gift_card_reload
|
|
354
|
+
configure_logging
|
|
355
|
+
|
|
356
|
+
puts "š Clover Sandbox Simulator - Reload Gift Card"
|
|
357
|
+
puts "=" * 50
|
|
358
|
+
|
|
359
|
+
services = Services::Clover::ServicesManager.new
|
|
360
|
+
|
|
361
|
+
gc_id = options[:id]
|
|
362
|
+
amount = options[:amount]
|
|
363
|
+
|
|
364
|
+
puts "Adding $#{'%.2f' % (amount / 100.0)} to gift card #{gc_id}"
|
|
365
|
+
|
|
366
|
+
result = services.gift_card.reload_gift_card(gc_id, amount: amount)
|
|
367
|
+
|
|
368
|
+
if result
|
|
369
|
+
puts "\nā
Gift card reloaded successfully!"
|
|
370
|
+
puts " New Balance: $#{'%.2f' % ((result['balance'] || 0) / 100.0)}"
|
|
371
|
+
else
|
|
372
|
+
puts "\nā Failed to reload gift card"
|
|
373
|
+
end
|
|
374
|
+
end
|
|
375
|
+
|
|
376
|
+
desc "gift_card_redeem", "Redeem/use balance from a gift card"
|
|
377
|
+
option :id, type: :string, aliases: "-i", required: true, desc: "Gift card ID"
|
|
378
|
+
option :amount, type: :numeric, aliases: "-a", required: true, desc: "Amount to redeem in cents"
|
|
379
|
+
def gift_card_redeem
|
|
380
|
+
configure_logging
|
|
381
|
+
|
|
382
|
+
puts "š Clover Sandbox Simulator - Redeem Gift Card"
|
|
383
|
+
puts "=" * 50
|
|
384
|
+
|
|
385
|
+
services = Services::Clover::ServicesManager.new
|
|
386
|
+
|
|
387
|
+
gc_id = options[:id]
|
|
388
|
+
amount = options[:amount]
|
|
389
|
+
|
|
390
|
+
puts "Redeeming $#{'%.2f' % (amount / 100.0)} from gift card #{gc_id}"
|
|
391
|
+
|
|
392
|
+
result = services.gift_card.redeem_gift_card(gc_id, amount: amount)
|
|
393
|
+
|
|
394
|
+
if result[:success]
|
|
395
|
+
puts "\nā
Redemption successful!"
|
|
396
|
+
puts " Amount Redeemed: $#{'%.2f' % (result[:amount_redeemed] / 100.0)}"
|
|
397
|
+
puts " Remaining Balance: $#{'%.2f' % (result[:remaining_balance] / 100.0)}"
|
|
398
|
+
|
|
399
|
+
if result[:shortfall] > 0
|
|
400
|
+
puts " ā ļø Shortfall: $#{'%.2f' % (result[:shortfall] / 100.0)} (insufficient balance)"
|
|
401
|
+
end
|
|
402
|
+
else
|
|
403
|
+
puts "\nā Redemption failed: #{result[:message]}"
|
|
404
|
+
end
|
|
405
|
+
end
|
|
406
|
+
|
|
173
407
|
private
|
|
174
408
|
|
|
409
|
+
def mask_card_number(card_number)
|
|
410
|
+
return card_number if card_number.nil? || card_number.length < 8
|
|
411
|
+
|
|
412
|
+
"#{card_number[0..3]}********#{card_number[-4..]}"
|
|
413
|
+
end
|
|
414
|
+
|
|
175
415
|
def configure_logging
|
|
176
416
|
if options[:verbose]
|
|
177
417
|
CloverSandboxSimulator.configuration.logger.level = Logger::DEBUG
|
|
@@ -2,20 +2,114 @@
|
|
|
2
2
|
|
|
3
3
|
module CloverSandboxSimulator
|
|
4
4
|
class Configuration
|
|
5
|
-
attr_accessor :merchant_id, :api_token, :environment, :log_level, :tax_rate, :business_type
|
|
5
|
+
attr_accessor :merchant_id, :merchant_name, :api_token, :environment, :log_level, :tax_rate, :business_type,
|
|
6
|
+
:public_token, :private_token, :ecommerce_environment, :tokenizer_environment,
|
|
7
|
+
:app_id, :app_secret, :refresh_token
|
|
8
|
+
|
|
9
|
+
# Path to merchants JSON file
|
|
10
|
+
MERCHANTS_FILE = File.join(File.dirname(__FILE__), "..", "..", ".env.json")
|
|
6
11
|
|
|
7
12
|
def initialize
|
|
8
13
|
@merchant_id = ENV.fetch("CLOVER_MERCHANT_ID", nil)
|
|
14
|
+
@merchant_name = ENV.fetch("CLOVER_MERCHANT_NAME", nil)
|
|
9
15
|
@api_token = ENV.fetch("CLOVER_API_TOKEN", nil)
|
|
10
16
|
@environment = normalize_url(ENV.fetch("CLOVER_ENVIRONMENT", "https://sandbox.dev.clover.com/"))
|
|
11
17
|
@log_level = parse_log_level(ENV.fetch("LOG_LEVEL", "INFO"))
|
|
12
18
|
@tax_rate = ENV.fetch("TAX_RATE", "8.25").to_f
|
|
13
19
|
@business_type = ENV.fetch("BUSINESS_TYPE", "restaurant").to_sym
|
|
20
|
+
|
|
21
|
+
# Ecommerce API tokens (for card payments and refunds)
|
|
22
|
+
@public_token = ENV.fetch("PUBLIC_TOKEN", nil)
|
|
23
|
+
@private_token = ENV.fetch("PRIVATE_TOKEN", nil)
|
|
24
|
+
@ecommerce_environment = normalize_url(ENV.fetch("ECOMMERCE_ENVIRONMENT", "https://scl-sandbox.dev.clover.com/"))
|
|
25
|
+
@tokenizer_environment = normalize_url(ENV.fetch("TOKENIZER_ENVIRONMENT", "https://token-sandbox.dev.clover.com/"))
|
|
26
|
+
|
|
27
|
+
# OAuth credentials (for token refresh)
|
|
28
|
+
@app_id = ENV.fetch("CLOVER_APP_ID", nil)
|
|
29
|
+
@app_secret = ENV.fetch("CLOVER_APP_SECRET", nil)
|
|
30
|
+
@refresh_token = ENV.fetch("CLOVER_REFRESH_TOKEN", nil)
|
|
31
|
+
|
|
32
|
+
# Load from .env.json if merchant_id not set in ENV
|
|
33
|
+
load_from_merchants_file if @merchant_id.nil? || @merchant_id.empty?
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Check if OAuth is configured for token refresh
|
|
37
|
+
def oauth_enabled?
|
|
38
|
+
!app_id.nil? && !app_id.empty? &&
|
|
39
|
+
!app_secret.nil? && !app_secret.empty?
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Load configuration for a specific merchant from .env.json
|
|
43
|
+
#
|
|
44
|
+
# @param merchant_id [String, nil] Merchant ID to load (nil for first merchant)
|
|
45
|
+
# @param index [Integer, nil] Index of merchant in the list (0-based)
|
|
46
|
+
# @return [self]
|
|
47
|
+
def load_merchant(merchant_id: nil, index: nil)
|
|
48
|
+
merchants = load_merchants_file
|
|
49
|
+
return self if merchants.empty?
|
|
50
|
+
|
|
51
|
+
merchant = if merchant_id
|
|
52
|
+
merchants.find { |m| m["CLOVER_MERCHANT_ID"] == merchant_id }
|
|
53
|
+
elsif index
|
|
54
|
+
merchants[index]
|
|
55
|
+
else
|
|
56
|
+
merchants.first
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
if merchant
|
|
60
|
+
apply_merchant_config(merchant)
|
|
61
|
+
logger.info "Loaded merchant: #{@merchant_name} (#{@merchant_id})"
|
|
62
|
+
else
|
|
63
|
+
logger.warn "Merchant not found: #{merchant_id || "index #{index}"}"
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
self
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# List all available merchants from .env.json
|
|
70
|
+
#
|
|
71
|
+
# @return [Array<Hash>] Array of merchant configs
|
|
72
|
+
def available_merchants
|
|
73
|
+
load_merchants_file.map do |m|
|
|
74
|
+
{
|
|
75
|
+
id: m["CLOVER_MERCHANT_ID"],
|
|
76
|
+
name: m["CLOVER_MERCHANT_NAME"],
|
|
77
|
+
has_ecommerce: !m["PUBLIC_TOKEN"].to_s.empty? && !m["PRIVATE_TOKEN"].to_s.empty?
|
|
78
|
+
}
|
|
79
|
+
end
|
|
14
80
|
end
|
|
15
81
|
|
|
16
82
|
def validate!
|
|
17
83
|
raise ConfigurationError, "CLOVER_MERCHANT_ID is required" if merchant_id.nil? || merchant_id.empty?
|
|
18
|
-
|
|
84
|
+
|
|
85
|
+
# API token is only required for Platform API operations
|
|
86
|
+
# Ecommerce-only operations can work without it
|
|
87
|
+
true
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Validate for Platform API operations (requires OAuth token)
|
|
91
|
+
def validate_platform!
|
|
92
|
+
validate!
|
|
93
|
+
raise ConfigurationError, "CLOVER_API_TOKEN is required for Platform API" if api_token.nil? || api_token.empty?
|
|
94
|
+
|
|
95
|
+
true
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# Check if Platform API is configured (has OAuth token)
|
|
99
|
+
def platform_enabled?
|
|
100
|
+
!api_token.nil? && !api_token.empty? && api_token != "NEEDS_REFRESH"
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# Check if Ecommerce API is configured
|
|
104
|
+
def ecommerce_enabled?
|
|
105
|
+
!public_token.nil? && !public_token.empty? &&
|
|
106
|
+
!private_token.nil? && !private_token.empty?
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# Validate Ecommerce configuration
|
|
110
|
+
def validate_ecommerce!
|
|
111
|
+
raise ConfigurationError, "PUBLIC_TOKEN is required for Ecommerce API" if public_token.nil? || public_token.empty?
|
|
112
|
+
raise ConfigurationError, "PRIVATE_TOKEN is required for Ecommerce API" if private_token.nil? || private_token.empty?
|
|
19
113
|
|
|
20
114
|
true
|
|
21
115
|
end
|
|
@@ -32,6 +126,32 @@ module CloverSandboxSimulator
|
|
|
32
126
|
|
|
33
127
|
private
|
|
34
128
|
|
|
129
|
+
def load_from_merchants_file
|
|
130
|
+
merchants = load_merchants_file
|
|
131
|
+
return if merchants.empty?
|
|
132
|
+
|
|
133
|
+
# Use first merchant by default
|
|
134
|
+
apply_merchant_config(merchants.first)
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def load_merchants_file
|
|
138
|
+
return [] unless File.exist?(MERCHANTS_FILE)
|
|
139
|
+
|
|
140
|
+
JSON.parse(File.read(MERCHANTS_FILE))
|
|
141
|
+
rescue JSON::ParserError => e
|
|
142
|
+
warn "Failed to parse #{MERCHANTS_FILE}: #{e.message}"
|
|
143
|
+
[]
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def apply_merchant_config(merchant)
|
|
147
|
+
@merchant_id = merchant["CLOVER_MERCHANT_ID"]
|
|
148
|
+
@merchant_name = merchant["CLOVER_MERCHANT_NAME"]
|
|
149
|
+
@api_token = merchant["CLOVER_API_TOKEN"] if merchant["CLOVER_API_TOKEN"].to_s.length > 10
|
|
150
|
+
@refresh_token = merchant["CLOVER_REFRESH_TOKEN"] if merchant["CLOVER_REFRESH_TOKEN"].to_s.length > 10
|
|
151
|
+
@public_token = merchant["PUBLIC_TOKEN"] unless merchant["PUBLIC_TOKEN"].to_s.empty?
|
|
152
|
+
@private_token = merchant["PRIVATE_TOKEN"] unless merchant["PRIVATE_TOKEN"].to_s.empty?
|
|
153
|
+
end
|
|
154
|
+
|
|
35
155
|
def normalize_url(url)
|
|
36
156
|
url = url.strip
|
|
37
157
|
url.end_with?("/") ? url : "#{url}/"
|