var.rb 5.12 KB
require 'var/version'
require 'conekta'
require 'paypal-sdk-rest'

# Main Module
module Var
  # TODO: add paypal
  # @@valid_services = [:conekta, :paypal]
  VALID_SERVICES = [:conekta]

  def self.valid_services
    VALID_SERVICES
  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
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
    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 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
  end
end

if defined? ActiveRecord::Base
  ActiveRecord::Base.send(:include, ActsAsChargeable)
end