Class: MyActiveMerchants::OmiseGateway

Inherits:
Object
  • Object
show all
Includes:
ElasticAPM::SpanHelpers, OmiseHelper
Defined in:
app/my_lib/my_active_merchants/omise_gateway.rb

Class Method Summary collapse

Instance Method Summary collapse

Methods included from OmiseHelper

#configure_omise_keys, #omise_public_key

Constructor Details

#initialize(reservation, token) ⇒ OmiseGateway

Returns a new instance of OmiseGateway.

Parameters:



11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# File 'app/my_lib/my_active_merchants/omise_gateway.rb', line 11

def initialize(reservation, token)
  @omise_token = token
  @reservation = reservation

  # Determine country code based on reservation type
  restaurant_country_code = if voucher_transaction_adapter?(reservation)
                              # For voucher transactions, use adapter's country resolution
                              reservation.country.alpha3
                            elsif reservation.restaurant.present?
                              # For reservations and tickets, get from restaurant
                              reservation.restaurant.country.alpha3
                            else
                              # Fallback to Thailand
                              ApiV5::Constants::COUNTRY_CODE_TH
                            end

  # currently for credit card payment, use omise th for sg restaurant
  default_country_code = if restaurant_country_code == ApiV5::Constants::COUNTRY_CODE_SG &&
      Flipper.enabled?(:enable_omise_th_payment_for_sg_cc)
                           ApiV5::Constants::COUNTRY_CODE_TH
                         else
                           restaurant_country_code
                         end

  @country_code = default_country_code.presence || restaurant_country_code

  @customer = if reservation.customer.present?
                reservation.customer
              elsif reservation.user_id.present?
                create_or_update_user_omise
              else
                add_omise_record_to_guest
              end
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(method, *args, &block) ⇒ Object (private)

hook to ensure configure_omise_keys is called before any method



414
415
416
417
418
419
# File 'app/my_lib/my_active_merchants/omise_gateway.rb', line 414

def method_missing(method, *args, &block)
  # Configure Omise keys based on the determined country code
  configure_omise_keys(@country_code)

  super
end

Class Method Details

.void(charge_id) ⇒ Object



46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'app/my_lib/my_active_merchants/omise_gateway.rb', line 46

def self.void(charge_id)
  return false if charge_id.blank?

  hh_charge = Externals::Omise::Charge.find_by(omise_charge_id: charge_id)

  if hh_charge.blank?
    raise CreditCardInvalid, "Failed refund credit card, charge not found with omise_charge_id: #{charge_id}"
  end

  charge = Omise::Charge.retrieve(hh_charge.omise_charge_id)
  if charge.refundable
    return charge.refunds.create(amount: hh_charge.amount_v2_cents, metadata: {
                                   refund_for: "Refund-#{hh_charge.reservation_id}",
                                 })
  end

  false
end

Instance Method Details

#capture_card(charge_amount, ip_address: nil, use_3d_secure: true) ⇒ Object

Parameters:

  • charge_amount (Money)


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
136
137
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
166
167
168
169
# File 'app/my_lib/my_active_merchants/omise_gateway.rb', line 75

def capture_card(charge_amount, ip_address: nil, use_3d_secure: true)
  return false unless valid?

  customer = Omise::Customer.retrieve @customer.customer_id
  card_id = customer.cards.last.id
  amount = charge_amount.cents
  currency = charge_amount.currency.iso_code
  capture = reservation.using_charge_directly_channel?

  charge_data = {
    amount: amount,
    description: "HungryHub-#{reservation.id}",
    currency: currency,
    customer: @customer.customer_id,
    card: card_id,
    capture: capture,
  }

  if ip_address.blank?
    APMErrorHandler.report 'Failed to charge, IP address is blank', charge_data: charge_data
    reservation.errors.add(:base, 'Something went wrong')
    return false
  end

  charge_data[:ip] = ip_address

  # set return_uri for enable 3DS
  if use_3d_secure.to_s == 'true'
    return_uri = reservation.payment_success_url.start_with?('https://') ? reservation.payment_success_url : "https://#{reservation.payment_success_url}"
    charge_data[:return_uri] = return_uri
  end

  omise_charge = ::Omise::Charge.create(charge_data)

  if omise_charge[:authorize_uri].present?
    firebase = MyFirebase.new
    # Handle both Reservation and TicketTransaction (via adapter) cases
    # For ticket transactions, Firebase record already exists from update_firebase call
    # We're just adding the authorization_url field to the existing record
    firebase_path = resolve_firebase_path(reservation)
    firebase.update(firebase_path, { authorization_url: omise_charge[:authorize_uri] })
  end

  unless ApiV5::Constants::VALID_OMISE_CHARGE_STATUSES.include?(omise_charge.status)
    APMErrorHandler.report('can not charge', charge_data: charge_data, attributes: omise_charge.attributes)
    reservation.errors.add(:base, omise_charge.failure_message)
    return false
  end

  # save card info to our DB
  save_card(omise_charge[:card])

  if omise_charge.status == ApiV5::Constants::OMISE_CHARGE_PENDING
    # void charge if system failed to store the reference into our DB
    VoidFailedReservationWorker.void_later(:omise, omise_charge.id) if omise_charge.captured

    # When this gateway is reused for TicketTransaction payments we receive a
    # TicketTransactionAdapter instead of a Reservation AR model. The
    # Externals::Omise::Charge model (charges table) supports association to
    # ticket_transaction (used elsewhere in ticket flows). Passing the adapter
    # as reservation causes ActiveRecord::AssociationTypeMismatch. Detection performed
    # via ticket_transaction_adapter?(reservation) below; we detect adapter usage (duck-typing)
    # and associate properly.
    charge_base_attrs = {
      customer: @customer,
      transaction_id: omise_charge['transaction'],
      amount_v2: charge_amount,
      omise_charge_id: omise_charge.id,
      status: omise_charge.status,
    }

    charge = build_charge_with_association(charge_base_attrs, reservation)

    if charge.save
      true
    else
      APMErrorHandler.report 'Card already captured but we can not save transaction id',
                             charge: omise_charge.attributes
      true
    end
  else
    unless omise_charge.status == ApiV5::Constants::OMISE_CHARGE_PENDING
      APMErrorHandler.report('failed to charge', attrs: omise_charge.attributes)
      reservation.errors.add(:base, 'something went wrong')
      false
    end

    APMErrorHandler.report 'Charge success but status is pending, waiting authorization_url call by frontend',
                           attrs: omise_charge.attributes
    true
  end
rescue Omise::Error => e
  reservation.errors.add(:base, e.message)
  false
end

#customer_created?Boolean

Returns:

  • (Boolean)


65
66
67
68
69
70
71
72
# File 'app/my_lib/my_active_merchants/omise_gateway.rb', line 65

def customer_created?
  if customer.blank?
    APMErrorHandler.report('failed to hold charge omise, customer is blank', reservation_id: reservation.id)
    reservation.errors.add(:base, 'something went wrong')
    return false
  end
  true
end