Class: CloverSandboxSimulator::Services::Clover::EcommerceService

Inherits:
BaseService
  • Object
show all
Defined in:
lib/clover_sandbox_simulator/services/clover/ecommerce_service.rb

Overview

Manages Clover Ecommerce API operations Handles card tokenization, charges, and refunds via the Ecommerce API

The Ecommerce API is separate from the Platform API and uses different endpoints:

Requires PUBLIC_TOKEN (for tokenization) and PRIVATE_TOKEN (for charges/refunds)

Constant Summary collapse

TEST_CARDS =

Test card numbers for sandbox

{
  visa: "4242424242424242",
  visa_debit: "4005562231212123",
  mastercard: "5200828282828210",
  amex: "378282246310005",
  discover: "6011111111111117",
  # Cards that simulate specific responses
  decline: "4000000000000002",
  insufficient_funds: "4000000000009995"
}.freeze

Instance Attribute Summary

Attributes inherited from BaseService

#config, #logger

Instance Method Summary collapse

Methods inherited from BaseService

#initialize

Constructor Details

This class inherits a constructor from CloverSandboxSimulator::Services::BaseService

Instance Method Details

#create_card_token(card_number:, exp_month:, exp_year:, cvv: "123") ⇒ Hash?

Create a card token for use in charges

Parameters:

  • card_number (String)

    Card number (use TEST_CARDS for sandbox)

  • exp_month (String)

    Expiration month (01-12)

  • exp_year (String)

    Expiration year (4 digits)

  • cvv (String) (defaults to: "123")

    CVV code

Returns:

  • (Hash, nil)

    Token response with id, or nil on failure



34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/clover_sandbox_simulator/services/clover/ecommerce_service.rb', line 34

def create_card_token(card_number:, exp_month:, exp_year:, cvv: "123")
  validate_ecommerce_config!

  logger.info "Creating card token for card ending in #{card_number[-4..]}"

  payload = {
    "card" => {
      "number" => card_number,
      "exp_month" => exp_month.to_s.rjust(2, "0"),
      "exp_year" => exp_year.to_s,
      "cvv" => cvv.to_s
    }
  }

  response = ecommerce_request(:post, "#{config.tokenizer_environment}v1/tokens",
                               payload: payload,
                               auth_type: :public)

  if response && response["id"]
    logger.info "Card token created: #{response['id']} (#{response.dig('card', 'brand')})"
  end

  response
end

#create_charge(amount:, source:, currency: "usd", description: nil, order_id: nil) ⇒ Hash?

Create a charge (card payment)

Parameters:

  • amount (Integer)

    Amount in cents

  • source (String)

    Card token ID (clv_…)

  • currency (String) (defaults to: "usd")

    Currency code (default: usd)

  • description (String, nil) (defaults to: nil)

    Optional description

  • order_id (String, nil) (defaults to: nil)

    Optional order ID to link the charge

Returns:

  • (Hash, nil)

    Charge response with id, or nil on failure



90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/clover_sandbox_simulator/services/clover/ecommerce_service.rb', line 90

def create_charge(amount:, source:, currency: "usd", description: nil, order_id: nil)
  validate_ecommerce_config!

  logger.info "Creating charge: $#{amount / 100.0} with token #{source[0..20]}..."

  payload = {
    "amount" => amount,
    "currency" => currency,
    "source" => source
  }
  payload["description"] = description if description
  payload["order_id"] = order_id if order_id

  # Generate unique idempotency key
  idempotency_key = "charge-#{SecureRandom.uuid}"

  response = ecommerce_request(:post, "#{config.ecommerce_environment}v1/charges",
                               payload: payload,
                               auth_type: :private,
                               idempotency_key: idempotency_key)

  if response && response["id"]
    logger.info "Charge successful: #{response['id']} - $#{response['amount'] / 100.0} (#{response['status']})"
  else
    logger.error "Charge failed: #{response.inspect}"
  end

  response
end

#create_refund(charge_id:, amount: nil, reason: nil) ⇒ Hash?

Create a refund for a charge

Parameters:

  • charge_id (String)

    The charge ID to refund

  • amount (Integer, nil) (defaults to: nil)

    Amount to refund in cents (nil for full refund)

  • reason (String, nil) (defaults to: nil)

    Reason for refund

Returns:

  • (Hash, nil)

    Refund response



138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
# File 'lib/clover_sandbox_simulator/services/clover/ecommerce_service.rb', line 138

def create_refund(charge_id:, amount: nil, reason: nil)
  validate_ecommerce_config!

  if amount
    logger.info "Creating partial refund for charge #{charge_id}: $#{amount / 100.0}"
  else
    logger.info "Creating full refund for charge #{charge_id}"
  end

  payload = { "charge" => charge_id }
  payload["amount"] = amount if amount
  payload["reason"] = reason if reason

  idempotency_key = "refund-#{SecureRandom.uuid}"

  response = ecommerce_request(:post, "#{config.ecommerce_environment}v1/refunds",
                               payload: payload,
                               auth_type: :private,
                               idempotency_key: idempotency_key)

  if response && response["id"]
    logger.info "Refund successful: #{response['id']} - $#{(response['amount'] || 0) / 100.0}"
  else
    logger.error "Refund failed: #{response.inspect}"
  end

  response
end

#create_test_card_token(card_type: nil) ⇒ Hash?

Create a random test card token Rotates between different card types to avoid rate limits

Parameters:

  • card_type (Symbol, nil) (defaults to: nil)

    Type of card (nil for random rotation)

Returns:

  • (Hash, nil)

    Token response



64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# File 'lib/clover_sandbox_simulator/services/clover/ecommerce_service.rb', line 64

def create_test_card_token(card_type: nil)
  # Rotate between different card types to avoid "sale count per card" limits
  card_types = [:visa, :visa_debit, :mastercard, :discover]
  card_type ||= card_types.sample

  card_number = TEST_CARDS[card_type] || TEST_CARDS[:visa]
  exp_year = (Time.now.year + 3).to_s
  # Vary expiration month to create different card fingerprints
  exp_month = format("%02d", rand(1..12))

  create_card_token(
    card_number: card_number,
    exp_month: exp_month,
    exp_year: exp_year,
    cvv: card_type == :amex ? "1234" : "123"
  )
end

#create_test_charge(amount:, card_type: :visa) ⇒ Hash?

Create a charge with a new test card in one step

Parameters:

  • amount (Integer)

    Amount in cents

  • card_type (Symbol) (defaults to: :visa)

    Type of test card

Returns:

  • (Hash, nil)

    Charge response



125
126
127
128
129
130
# File 'lib/clover_sandbox_simulator/services/clover/ecommerce_service.rb', line 125

def create_test_charge(amount:, card_type: :visa)
  token = create_test_card_token(card_type: card_type)
  return nil unless token && token["id"]

  create_charge(amount: amount, source: token["id"])
end

#ecommerce_available?Boolean

Check if Ecommerce API is available

Returns:

  • (Boolean)


190
191
192
# File 'lib/clover_sandbox_simulator/services/clover/ecommerce_service.rb', line 190

def ecommerce_available?
  config.ecommerce_enabled?
end

#get_charge(charge_id) ⇒ Hash?

Get a charge by ID

Parameters:

  • charge_id (String)

    The charge ID

Returns:

  • (Hash, nil)

    Charge details



171
172
173
174
175
176
# File 'lib/clover_sandbox_simulator/services/clover/ecommerce_service.rb', line 171

def get_charge(charge_id)
  validate_ecommerce_config!

  ecommerce_request(:get, "#{config.ecommerce_environment}v1/charges/#{charge_id}",
                    auth_type: :private)
end

#get_refund(refund_id) ⇒ Hash?

Get a refund by ID

Parameters:

  • refund_id (String)

    The refund ID

Returns:

  • (Hash, nil)

    Refund details



182
183
184
185
186
187
# File 'lib/clover_sandbox_simulator/services/clover/ecommerce_service.rb', line 182

def get_refund(refund_id)
  validate_ecommerce_config!

  ecommerce_request(:get, "#{config.ecommerce_environment}v1/refunds/#{refund_id}",
                    auth_type: :private)
end