var.rb
5.96 KB
1
2
3
4
5
6
7
8
9
10
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
45
46
47
48
49
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
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
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
require 'var/version'
require 'conekta'
require 'paypal-sdk-rest'
# Main Module
module Var
  # TODO: add paypal
  VALID_SERVICES = [:conekta]
  @@var_classes = []
  def self.valid_services
    VALID_SERVICES
  end
  def self.var_classes
    @@var_classes
  end
  def self.add_var_class(class_name)
    @@var_classes << class_name unless @@var_classes.include? class_name
  end
  def self.create_charge(service, object, options = {})
    return { error_message: 'Service is not supported' } unless VALID_SERVICES.include? service
    return { error_message: "#{object.class} doesn't support charges" } unless object.respond_to?(:charge_with)
    charge = object.charge_with(service, options)
    charge
  end
  def self.conekta_webhook(params)
    payment = params[:data][:object]
    object = Var.find_charge payment[:id]
    object.update_columns(var_status: payment[:status])
    true
  rescue Exception => exception
    false
  end
  def self.find_charge(id)
    if Rails.env.development?
      Rails.application.eager_load!
    end
    @@var_classes.map do |class_name|
      class_name.where(var_id: id)
    end.flatten.first
  end
end
# Module for models
module ActsAsChargeable
  extend ActiveSupport::Concern
  # Class Methods
  module ClassMethods
    def acts_as_chargeable(keys = {})
      include ChargeableInstanceMethods
      cattr_accessor :sync_attributes
      self.sync_attributes = keys
      Var.add_var_class(self)
    end
  end
  # Instance Methods
  module ChargeableInstanceMethods
    def charge_with(service, options)
      unless instance_support?(service)
        error_message = "#{self.class} doesn't support" \
                        " charges with #{service}"
        return { error_message: error_message }
      end
      send("charge_with_#{service}", options)
    rescue Exception => exception
      { error_message: exception.message }
    end
    def charge_with_conekta(options)
      charge = conekta_charge(options)
      update_columns(var_status: charge.status, var_id: charge.id,
                     var_service: 'conekta')
      update_conekta_barcode(charge) if options[:conekta_type] == 'oxxo'
      charge
    rescue Conekta::ParameterValidationError, Conekta::ProcessingError,
           Conekta::Error => e
      update_columns(var_status: 'failed')
      { error_message: e.message }
    end
    def conekta_charge(options)
      @charge ||= Conekta::Charge.create({
        description: sync(:conekta, 'description'),
        amount: sync(:conekta, 'amount'), currency: 'MXN',
        reference_id: sync(:conekta, 'reference_id'),
        details: {
          name: sync(:conekta, 'name'), email: sync(:conekta, 'email'),
          line_items: [{
            description: sync(:conekta, 'description'), quantity: 1,
            unit_price: sync(:conekta, 'amount'), name: sync(:conekta, 'name')
          }] }
      }.merge(conekta_type_of_charge(options)))
    end
    def conekta_type_of_charge(options)
      if options[:conekta_type] == 'card'
        { card: options[:card_token] }
      elsif options[:conekta_type] == 'oxxo'
        { cash: { type: 'oxxo',
                  expires_at: (Time.zone.today + 3.days).strftime('%Y-%m-%d') }
        }
      end
    end
    def update_conekta_barcode(charge)
      method = charge.payment_method
      update_columns(var_barcode: method.barcode,
                     var_barcode_url: method.barcode_url,
                     var_payment_expires_at: Time.at(method.expires_at))
    end
    def manual_charge
      self.update_columns(var_service: 'manual', var_status: 'paid',
                          var_payment_at: Time.zone.now)
      # TODO: Create a new table with transaction
    end
    # def charge_with_paypal(options)
    #   if(!options.include? :card)
    #     error_message = "Paypal needs a card sent as a third paramater"
    #     return { error_message: error_message}
    #   end
    #   @payment = PayPal::SDK::REST::Payment.new({
    #     intent: "sale",
    #     payer: {
    #       payer_info: {
    #         email: self.sync(:paypal, 'email')},
    #       payment_method: "credit_card",
    #       funding_instruments: [{
    #         credit_card: {
    #           type: options[:card][:type],
    #           number: options[:card][:number],
    #           expire_month: options[:card][:expire_month],
    #           expire_year: options[:card][:expire_year],
    #           cvv2: options[:card][:cvv2]}}]},
    #     transactions: [{
    #       item_list: {
    #         items: [{
    #           name: self.sync(:paypal, 'name'),
    #           sku: self.sync(:paypal, 'sku'),
    #           price: self.sync(:paypal, 'price'),
    #           currency: "MXN",
    #           quantity: 1 }]},
    #       amount: {
    #         total: self.sync(:paypal, 'price'),
    #         currency: "MXN" },
    #       description: self.sync(:paypal, 'description') }]})
    # end
    def find_charge
      return { error_message: 'Not charged yet' } unless var_service
      send("find_#{var_service}_charge")
    rescue Exception => exception
      { error_message: exception.message }
    end
    def charged?(service)
      charge = find_charge(service)
      charge.any? && !charge.include?(:error_message)
    end
    def find_conekta_charge
      Conekta::Charge.find(var_id)
    end
    def instance_support?(service)
      sync_attributes.include?(service)
    end
    def sync(service, key)
      service_attributes = send("#{service}_attributes")
      return send(key) unless service_attributes.include? key.to_sym
      send(service_attributes[key.to_sym])
    end
    # def sync_attribute(key)
    #   return self.send(key) unless self.sync_attributes.include? key.to_sym
    #   self.send(sync_attributes[key.to_sym])
    # end
    def conekta_attributes
      sync_attributes[:conekta] || {}
    end
    def paypal_attributes
      sync_attributes[:paypal] || {}
    end
    def var_payed?
      var_status == 'paid'
    end
  end
end
if defined? ActiveRecord::Base
  ActiveRecord::Base.send(:include, ActsAsChargeable)
end