Ricardo Garcia

Adds rspec for gem TDD

source 'https://rubygems.org'
# Specify your gem's dependencies in var.gemspec
group :development do
gem "sqlite3"
end
gemspec
......
require 'rspec/core/rake_task'
require 'bundler/gem_tasks'
# Default directory to look in is `/specs`
# # Run with `rake spec`
RSpec::Core::RakeTask.new(:spec) do |task|
task.rspec_opts = ['--color', '--format d']
task.verbose = false
end
task default: :spec
......
#!/usr/bin/env ruby
require 'bundler/setup'
require 'active_record'
require 'active_support'
require_relative '../spec/mocks/var_database_mock.rb'
require_relative '../spec/mocks/var_models_mock.rb'
require 'var'
# You can add fixtures and/or initialization code here to make experimenting
# with your gem easier. You can also use a different console, if you like.
ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => ":memory:")
ActiveRecord::Schema.verbose = false
VarDatabaseMock.setup_db
# (If you use this, don't forget to add pry to your Gemfile!)
# require "pry"
# Pry.start
require 'irb'
IRB.start
require "pry"
Pry.start
......
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)
amount = charge.amount / 100.0
fee = charge.fee / 100.0
update_columns(var_status: charge.status, var_id: charge.id,
var_service: 'conekta', var_fee: fee,
var_paid_amount: amount,
var_payment_method: charge.payment_method.object)
update_conekta_barcode(charge) if options[:conekta_type] == 'oxxo'
if var_payed?
time = Time.zone.now
update_columns(var_payment_at: time)
end
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
update_columns(var_service: 'manual', var_status: 'paid',
var_payment_at: Time.zone.now)
# TODO: Create a new table with transaction
end
def manual_discharge
update_columns(var_service: 'manual', var_status: 'pending',
var_payment_at: nil)
# 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 conekta_attributes
sync_attributes[:conekta] || {}
end
def paypal_attributes
sync_attributes[:paypal] || {}
end
def var_payed?
var_status == 'paid'
end
def cancel_oxxo_payment
return { error_message: 'Already paid' } if var_payed?
return { error_message: 'Not charged yet' } unless var_service
return { error_message: 'Not charged with oxxo' } unless var_barcode
charge = find_charge
if charge.status == 'paid'
update_columns(var_status: 'paid',
var_payment_at: Time.zone.at(charge.paid_at))
return { error_message: 'Already paid' }
end
clean_var_variables
end
def var_expired_by(time)
return false unless var_payment_expires_at
var_payment_expires_at + time < Time.zone.now && !var_payed?
end
private
def clean_var_variables
if update_columns(var_status: nil, var_barcode: nil, var_barcode_url: nil,
var_id: nil, var_service: nil, var_payment_at: nil,
var_payment_expires_at: nil)
{ object: code }
else
{ error_message: 'Something went wrong' }
end
end
end
end
......@@ -7,9 +7,9 @@ class AddVarStatusTo<%=@model_name.pluralize.camelize%> < ActiveRecord::Migratio
add_column :<%=@model_name.pluralize.underscore%>, :var_payment_expires_at, :datetime
add_column :<%=@model_name.pluralize.underscore%>, :var_id, :string
add_column :<%=@model_name.pluralize.underscore%>, :var_service, :string
add_column :<%=model_name.pluralize.underscore%>, :var_fee, :float
add_column :<%=model_name.pluralize.underscore%>, :var_paid_amount, :float
add_column :<%=model_name.pluralize.underscore%>, :var_payment_at, :datetime
add_column :<%=model_name.pluralize.underscore%>, :var_payment_method, :string
add_column :<%=@model_name.pluralize.underscore%>, :var_fee, :float
add_column :<%=@model_name.pluralize.underscore%>, :var_paid_amount, :float
add_column :<%=@model_name.pluralize.underscore%>, :var_payment_at, :datetime
add_column :<%=@model_name.pluralize.underscore%>, :var_payment_method, :string
end
end
\ No newline at end of file
end
......
......@@ -8,6 +8,6 @@ class VarModelGenerator < Rails::Generators::NamedBase
d = Time.now.strftime('%Y%m%d%H%M%S')
@model_name = name
template 'var_model.rb',
"db/migrate/#{d}_add_var_status_to_#{name.pluralize.underscore}.rb"
"db/migrate/#{d}_add_var_attributes_to_#{name.pluralize.underscore}.rb"
end
end
......
require 'var/version'
require 'conekta'
require 'paypal-sdk-rest'
require 'acts_as_chargeable'
# 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
class << self
def self.conekta_webhook(params)
payment = params[:data][:object]
object = Var.find_charge payment[:id]
object.update_columns(var_status: payment[:status])
if object.var_payed?
time = Time.strptime payment[:paid_at].to_s, '%s'
amount = payment[:amount].to_f / 100.0
fee = payment[:fee].to_f / 100.0
object.update({var_fee: fee, var_paid_amount: amount, var_payment_method: payment[:payment_method][:object], var_payment_at: time})
def valid_services
VALID_SERVICES
end
object
rescue Exception => exception
puts exception
false
end
def self.find_charge(id)
Rails.application.eager_load! if Rails.env.development?
@@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 }
def var_classes
@@var_classes
end
def charge_with_conekta(options)
charge = conekta_charge(options)
amount = charge.amount / 100.0
fee = charge.fee / 100.0
update_columns(var_status: charge.status, var_id: charge.id,
var_service: 'conekta', var_fee: fee,
var_paid_amount: amount,
var_payment_method: charge.payment_method.object)
update_conekta_barcode(charge) if options[:conekta_type] == 'oxxo'
if var_payed?
time = Time.zone.now
update_columns(var_payment_at: time)
end
charge
rescue Conekta::ParameterValidationError, Conekta::ProcessingError,
Conekta::Error => e
update_columns(var_status: 'failed')
{ error_message: e.message }
def add_var_class(class_name)
@@var_classes << class_name unless @@var_classes.include? class_name
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)))
def 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 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') }
}
def conekta_webhook(params)
payment = params[:data][:object]
object = Var.find_charge payment[:id]
object.update_columns(var_status: payment[:status])
if object.var_payed?
time = Time.strptime payment[:paid_at].to_s, '%s'
amount = payment[:amount].to_f / 100.0
fee = payment[:fee].to_f / 100.0
object.update(var_fee: fee, var_paid_amount: amount, var_payment_method: payment[:payment_method][:object], var_payment_at: time)
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
update_columns(var_service: 'manual', var_status: 'paid',
var_payment_at: Time.zone.now)
# TODO: Create a new table with transaction
end
def manual_discharge
update_columns(var_service: 'manual', var_status: 'pending',
var_payment_at: nil)
# 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")
object
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 conekta_attributes
sync_attributes[:conekta] || {}
end
def paypal_attributes
sync_attributes[:paypal] || {}
end
def var_payed?
var_status == 'paid'
end
def cancel_oxxo_payment
return { error_message: 'Already paid' } if var_payed?
return { error_message: 'Not charged yet' } unless var_service
return { error_message: 'Not charged with oxxo' } unless var_barcode
charge = find_charge
if charge.status == 'paid'
update_columns(var_status: 'paid',
var_payment_at: Time.zone.at(charge.paid_at))
return { error_message: 'Already paid' }
end
clean_var_variables
end
def var_expired_by(time)
return false unless var_payment_expires_at
return var_payment_expires_at + time < Time.zone.now && !var_payed?
puts exception
false
end
private
def clean_var_variables
if update_columns(var_status: nil, var_barcode: nil, var_barcode_url: nil,
var_id: nil, var_service: nil, var_payment_at: nil,
var_payment_expires_at: nil)
{ object: code }
else
{ error_message: 'Something went wrong' }
end
def find_charge(id)
Rails.application.eager_load! if Rails.env.development?
@@var_classes.map do |class_name|
class_name.where(var_id: id)
end.flatten.first
end
end
end
......
# Var Version
module Var
VERSION = '0.2.22'
VERSION = '0.3.0'
end
......
class VarDatabaseMock
class << self
def setup_db
ActiveRecord::Schema.define(version: 1) do
create_table :products do |t|
t.string :code
t.string :var_status, default: 'pending'
t.string :var_barcode_url
t.string :var_barcode
t.datetime :var_payment
t.datetime :var_payment_expires_at
t.string :var_id
t.string :var_service
t.float :var_fee
t.float :var_paid_amount
t.datetime :var_payment_at
t.string :var_payment_method
t.timestamps null: false
end
end
end
end
end
class Product < ActiveRecord::Base
end
require 'pry'
require 'active_record'
require 'active_support'
require 'mocks/var_database_mock'
require 'mocks/var_models_mock'
require 'Var'
ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
ActiveRecord::Schema.verbose = false
VarDatabaseMock.setup_db
require 'spec_helper'
describe Var do
context 'as a class' do
it { is_expected.to respond_to(:valid_services) }
it { is_expected.to respond_to(:var_classes) }
it { is_expected.to respond_to(:add_var_class) }
it { is_expected.to respond_to(:create_charge) }
it { is_expected.to respond_to(:conekta_webhook) }
it { is_expected.to respond_to(:find_charge) }
it 'returns conekta as a valid service' do
expect(Var.valid_services).to eq([:conekta])
end
end
end
# coding: utf-8
lib = File.expand_path('../lib', __FILE__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
require 'var/version'
......@@ -6,12 +5,12 @@ require 'var/version'
Gem::Specification.new do |spec|
spec.name = 'var'
spec.version = Var::VERSION
spec.authors = ['abrahamrq']
spec.authors = ['abrahamrq', 'chelord', 'rgp']
spec.email = ['abraham.rq03@gmail.com']
spec.summary = 'summary of var'
spec.description = 'description of var'
spec.homepage = 'https://github.com/abrahamrq'
spec.summary = 'Payment gateway for Conekta, Paypal'
spec.description = 'Just another Payment Gateway for Conekta and Paypal and stuff for rails apps'
spec.homepage = 'http://git.ukko.mx/gems/var'
# Prevent pushing this gem to RubyGems.org by setting 'allowed_push_host', or
# delete this section to allow pushing this gem to any host.
......@@ -28,6 +27,16 @@ Gem::Specification.new do |spec|
spec.add_dependency 'conekta', '~> 0.5'
spec.add_dependency 'paypal-sdk-rest'
spec.add_dependency 'activerecord', '>= 4.0', '< 5.1'
spec.add_dependency "activesupport"
spec.add_development_dependency 'bundler', '~> 1.10.a'
spec.add_development_dependency 'rake', '~> 10.0'
spec.add_development_dependency "rspec"
spec.add_development_dependency "rspec-nc"
spec.add_development_dependency "guard"
spec.add_development_dependency "guard-rspec"
spec.add_development_dependency "pry"
spec.add_development_dependency "pry-remote"
spec.add_development_dependency "pry-nav"
end
......