Class: Receptionist::CreditCardChecker

Inherits:
Object
  • Object
show all
Includes:
ElasticAPM::SpanHelpers
Defined in:
app/my_lib/receptionist/credit_card_checker.rb

Overview

Manage credit card

TODO This class should be refactored by create another class that resposible to handle OmiseToken and HungryHub user relationship myabe we could name it 'CreditCardLinker'

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(reservation, token = nil, use_3d_secure: false, ip_address: nil, provider: nil) ⇒ CreditCardChecker

Returns a new instance of CreditCardChecker.



14
15
16
17
18
19
20
21
22
23
24
25
# File 'app/my_lib/receptionist/credit_card_checker.rb', line 14

def initialize(reservation, token = nil, use_3d_secure: false, ip_address: nil, provider: nil)
  @reservation = reservation
  @restaurant = reservation.restaurant
  @user = reservation.user
  @token = token
  @ip_address = ip_address
  @provider = provider

  # TODO: REMOVE use_3d_secure later
  @use_3d_secure = use_3d_secure.to_s == 'true'
  BUSINESS_LOGGER.set_business_context({ reservation_id: reservation.id })
end

Instance Attribute Details

#ip_addressObject

Returns the value of attribute ip_address.



12
13
14
# File 'app/my_lib/receptionist/credit_card_checker.rb', line 12

def ip_address
  @ip_address
end

#providerObject

Returns the value of attribute provider.



12
13
14
# File 'app/my_lib/receptionist/credit_card_checker.rb', line 12

def provider
  @provider
end

#reservationObject

Returns the value of attribute reservation.



12
13
14
# File 'app/my_lib/receptionist/credit_card_checker.rb', line 12

def reservation
  @reservation
end

#restaurantObject

Returns the value of attribute restaurant.



12
13
14
# File 'app/my_lib/receptionist/credit_card_checker.rb', line 12

def restaurant
  @restaurant
end

#tokenObject

Returns the value of attribute token.



12
13
14
# File 'app/my_lib/receptionist/credit_card_checker.rb', line 12

def token
  @token
end

#use_3d_secureObject

Returns the value of attribute use_3d_secure.



12
13
14
# File 'app/my_lib/receptionist/credit_card_checker.rb', line 12

def use_3d_secure
  @use_3d_secure
end

#userObject

Returns the value of attribute user.



12
13
14
# File 'app/my_lib/receptionist/credit_card_checker.rb', line 12

def user
  @user
end

Class Method Details

.execute(reservation, token, use_3d_secure: false, ip_address: nil, provider: nil, &block) ⇒ Object



138
139
140
141
# File 'app/my_lib/receptionist/credit_card_checker.rb', line 138

def self.execute(reservation, token, use_3d_secure: false, ip_address: nil, provider: nil, &block)
  rcpt = new(reservation, token, use_3d_secure: use_3d_secure, ip_address: ip_address, provider: provider)
  rcpt.check(&block)
end

Instance Method Details

#card_token_valid_based_on_provider?Boolean

Returns:

  • (Boolean)


41
42
43
# File 'app/my_lib/receptionist/credit_card_checker.rb', line 41

def card_token_valid_based_on_provider?
  reservation.restaurant.selected_cc_payment_provider.to_sym == provider&.to_sym
end

#checkObject

reservation should be saved to database in block code

::Receptionist::CreditCardChecker.execute(reservation, token) do
  reservation.save
end


50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
# File 'app/my_lib/receptionist/credit_card_checker.rb', line 50

def check
  if not_confirmed?
    BUSINESS_LOGGER.info(
      'Reject to charge reservation because the status is not confirmed yet. ',
      reservation_id: reservation.id,
    )
    raise CreditCardInvalid, "Sorry we can't confirm your booking with restaurant. Please contact #{SUPPORT_EMAIL}"
  end

  unless required?
    yield if block_given?
    return true
  end

  raise CreditCardInvalid, 'This reservation requires credit card' unless valid?

  validate_card_provider!

  result = false
  error_message = "failed to charge Reservation ID: #{reservation.id}"
  ActiveRecord::Base.transaction do
    raise ActiveRecord::Rollback, 'Failed to process reservation' if block_given? && !yield

    if reservation.using_charge_directly_channel?
      my_merchant = setup_my_merchant
      my_merchant.store_card(token)

      charge_amount = reservation.charge_amount.presence ||
        Money.new(reservation.restaurant.cc_hold_amount, reservation.restaurant.default_currency).amount
      money_amount = Money.from_amount(charge_amount, reservation.restaurant.default_currency)

      if reservation.charges.present? && reservation.charges.select(&:persisted?).present?
        BUSINESS_LOGGER.info('Reservation has been charged before', reservation_id: reservation.id)
        return true
      end

      if charge_amount.positive? && !my_merchant.charge(money_amount, use_3d_secure: use_3d_secure,
                                                                      ip_address: ip_address)
        error_message = my_merchant.error_message_simple
        error_message = I18n.t('errors.use_different_card') if error_message.include?('fraud')
        BUSINESS_LOGGER.error(error_message, reservation_id: reservation.id)

        # send error to rollbar for monitoring in staging
        err_msg = "failed to charge Reservation ID: #{reservation.id}"
        Rollbar.error(err_msg, reservation_id: reservation.id,
                               original_error_message: my_merchant.error_message_simple,
                               kind: 'using_charge_directly')

        raise ActiveRecord::Rollback, error_message
      end
    elsif reservation.using_on_hold_channel?
      my_merchant = setup_my_merchant
      my_merchant.store_card(token)
      unless my_merchant.hold
        error_message = my_merchant.error_message_simple
        BUSINESS_LOGGER.error(error_message, reservation_id: reservation.id)

        # send error to rollbar for monitoring in staging
        err_msg = "failed to charge Reservation ID: #{reservation.id}"
        Rollbar.error(err_msg, reservation_id: reservation.id,
                               original_error_message: error_message,
                               kind: 'using_on_hold')

        raise ActiveRecord::Rollback, error_message
      end
    else
      BUSINESS_LOGGER.error('Package has wrong charge setting', reservation_id: reservation.id)
      raise ActiveRecord::Rollback, 'Package has wrong charge setting'
    end
    BUSINESS_LOGGER.info("Reservation has been charged with 3d secure #{use_3d_secure}",
                         reservation_id: reservation.id)
    result = true
  end

  raise CreditCardInvalid, error_message unless result

  result
rescue ActiveRecord::RecordInvalid, CreditCardInvalid => e
  # send error to APM for monitoring
  APMErrorHandler.report("Credit Card Invalid for Reservation ID: #{reservation.id}", e: e)

  raise(CreditCardInvalid, e.message)
rescue StandardError => e
  APMErrorHandler.report e
  raise(CreditCardError, 'Sorry, something went wrong')
end

#credit_card_given?Boolean

Returns:

  • (Boolean)


31
32
33
34
35
# File 'app/my_lib/receptionist/credit_card_checker.rb', line 31

def credit_card_given?
  return true if token.present?

  false
end

#required?Boolean

Returns:

  • (Boolean)


27
28
29
# File 'app/my_lib/receptionist/credit_card_checker.rb', line 27

def required?
  reservation.require_prepayment?
end

#valid?Boolean

Returns:

  • (Boolean)


37
38
39
# File 'app/my_lib/receptionist/credit_card_checker.rb', line 37

def valid?
  required? && credit_card_given?
end