Stripe Coupon code with one-time use - ruby-on-rails

I have followed the stripe instructions to add coupons to my app. The coupons work great but I want to make a coupon that works all the time but can only be used once per user.
My coupon model
Coupon.rb
class Coupon
include Mongoid::Document
has_many :charges
has_one :manager
validates_presence_of :code, :discount_percent, :coupon_count, :description
validates_uniqueness_of :code
field :code, type: String
field :discount_percent, type: Integer
field :expires_at, type: Date
field :coupon_count, type: Integer
field :description, type: String
def self.get(code)
where(
:code => (normalize_code(code)),
:$and => [
{
:$or => [
{ :coupon_count.gte => 1 },
{ :coupon_count => nil }
]
}, {
:$or => [
{ :expires_at.gte => Date.today },
{ :expires_at => nil }
]
}
]
).first
end
def apply_discount(amount)
discount = amount * (self.discount_percent * 0.01)
(amount - discount.to_i)
end
def discount_percent_human
if discount_percent.present?
discount_percent.to_s + '%'
end
end
def generate_subscription_code(manager)
# Random, unguessable number as a base20 string
# .reverse ensures we don't use first character (which may not take all values)
raw_string = SecureRandom.random_number( 2**80 ).to_s( 20 ).reverse
# Convert Ruby base 20 to better characters for user experience
long_code = raw_string.tr( '0123456789abcdefghij', '234679QWERTYUPADFGHX' )
# Format the code for printing
short_code = long_code[0..3] + '-' + long_code[4..7] + '-' + long_code[8..11]
self.code = short_code
self.discount_percent = 100
self.expires_at = DateTime.now + 1.year
self.coupon_count = 120
self.description = "#{manager.email} Annual Subscription"
save
end
private
def self.normalize_code(code)
code.gsub(/ +/, '').upcase
end
end
Here is my charges controller also
charges_controller.rb
class Managers::ChargesController < ApplicationController
before_filter :authenticate_manager!
def new
#reportapproval = Reportapproval.find(params[:id])
#manager = current_manager
#amount = #reportapproval.manager_request_report_type
end
def create
# Amount in cents
#reportapproval = Reportapproval.find params[:reportapproval_id]
#manager = current_manager
#amount = #reportapproval.manager_request_report_type
#final_amount = #amount
#code = params[:couponCode]
if !#code.blank?
#coupon = Coupon.get(#code)
if #coupon.nil?
flash[:error] = 'Coupon code is not valid or expired.'
redirect_to new_managers_charge_path(id: #reportapproval.id)
return
elsif #coupon.discount_percent == 100
#reportapproval.report_paid = true
#reportapproval.free_coupon_used = true
#reportapproval.save!
#coupon.coupon_count = #coupon.coupon_count - 1
#coupon.save!
redirect_to managers_dashboard_path, :notice => "You have successfully requested a pre-paid report from #{#reportapproval.tenant_last_name} with a 'No-Pay' coupon."
return
else
#final_amount = #coupon.apply_discount(#amount.to_i)
#discount_amount = (#amount.to_i - #final_amount.to_i)
end
if #coupon.present?
charge_metadata = {
:coupon_code => #coupon.code,
:coupon_discount => #coupon.discount_percent_human
}
end
end
charge_metadata ||= {}
customer = Stripe::Customer.create(
:email => #manager.email,
:card => params[:stripeToken]
)
stripe_charge = Stripe::Charge.create(
:customer => customer.id,
:amount => #final_amount,
:description => 'Manager Paid Report',
:currency => 'usd',
:metadata => charge_metadata
)
#charge = Charge.create!(amount: #final_amount, coupon: #coupon, stripe_id: stripe_charge.id)
#reportapproval.report_paid = true
#reportapproval.save!
if #coupon.present?
#coupon.coupon_count = #coupon.coupon_count - 1
#coupon.save!
else
redirect_to managers_dashboard_path, :notice => "You have successfully requested a pre-paid report from #{#reportapproval.tenant_last_name}."
return
end
rescue Stripe::CardError => e
flash[:error] = e.message
redirect_to managers_charges_path
end
end
How can I add the ability to check if the manager has used the coupon before and only allow them to use once?

Related

Ruby - PG::UndefinedTable: ERROR: missing FROM-clause entry for table

I'm getting the following error when I'm trying to query another table in a model definition.
PG::UndefinedTable: ERROR: missing FROM-clause entry for table "miq_user_roles" LINE 1: ..." = $1 AND "service_templates"."display" = $2 AND "miq_user_... ^ [catalog/explorer]
Any idea how to resolve this?
scope :with_service_template_catalog_id, ->(cat_id) { where(:service_template_catalog_id => cat_id) }
scope :without_service_template_catalog_id, -> { where(:service_template_catalog_id => nil) }
scope :with_existent_service_template_catalog_id, -> { where.not(:service_template_catalog_id => nil) }
scope :displayed, -> { where(:display => true) }
scope :public_service_templates, -> { where("miq_user_roles.settings" => nil) }
Here is the full code:
class ServiceTemplate < ApplicationRecord
include SupportsFeatureMixin
DEFAULT_PROCESS_DELAY_BETWEEN_GROUPS = 120
GENERIC_ITEM_SUBTYPES = {
"custom" => N_("Custom"),
"vm" => N_("Virtual Machine"),
"playbook" => N_("Playbook"),
"hosted_database" => N_("Hosted Database"),
"load_balancer" => N_("Load Balancer"),
"storage" => N_("Storage")
}.freeze
SERVICE_TYPE_ATOMIC = 'atomic'.freeze
SERVICE_TYPE_COMPOSITE = 'composite'.freeze
RESOURCE_ACTION_UPDATE_ATTRS = [:dialog,
:dialog_id,
:fqname,
:configuration_template,
:configuration_template_id,
:configuration_template_type].freeze
include CustomActionsMixin
include ServiceMixin
include OwnershipMixin
include NewWithTypeStiMixin
include TenancyMixin
include ArchivedMixin
include CiFeatureMixin
include_concern 'Filter'
include_concern 'Copy'
validates :name, :presence => true
belongs_to :tenant
has_many :service_templates, :through => :service_resources, :source => :resource, :source_type => 'ServiceTemplate'
has_many :services
has_many :service_template_tenants, :dependent => :destroy
has_many :additional_tenants, :through => :service_template_tenants, :source => :tenant, :dependent => :destroy
has_one :picture, :dependent => :destroy, :as => :resource, :autosave => true
belongs_to :service_template_catalog
belongs_to :zone
belongs_to :currency, :inverse_of => false
has_many :dialogs, -> { distinct }, :through => :resource_actions
has_many :miq_schedules, :as => :resource, :dependent => :destroy
has_many :miq_requests, :as => :source, :dependent => :nullify
has_many :active_requests, -> { where(:request_state => MiqRequest::ACTIVE_STATES) }, :as => :source, :class_name => "MiqRequest"
virtual_column :type_display, :type => :string
virtual_column :template_valid, :type => :boolean
virtual_column :template_valid_error_message, :type => :string
virtual_column :archived, :type => :boolean
virtual_column :active, :type => :boolean
default_value_for :internal, false
default_value_for :service_type, SERVICE_TYPE_ATOMIC
default_value_for(:generic_subtype) { |st| 'custom' if st.prov_type == 'generic' }
virtual_has_one :config_info, :class_name => "Hash"
scope :with_service_template_catalog_id, ->(cat_id) { where(:service_template_catalog_id => cat_id) }
scope :without_service_template_catalog_id, -> { where(:service_template_catalog_id => nil) }
scope :with_existent_service_template_catalog_id, -> { where.not(:service_template_catalog_id => nil) }
scope :displayed, -> { where(:display => true) }
scope :public_service_templates, -> { where("miq_user_roles.settings" => nil) }
supports :order do
unsupported_reason_add(:order, 'Service template does not belong to a service catalog') unless service_template_catalog
unsupported_reason_add(:order, 'Service template is not configured to be displayed') unless display
end
alias orderable? supports_order?
alias validate_order supports_order?
def self.with_tenant(tenant_id)
tenant = Tenant.find(tenant_id)
where(:tenant_id => tenant.ancestor_ids + [tenant_id])
end
def self.with_additional_tenants
references(table_name, :tenants).includes(:service_template_tenants => :tenant)
end
def self.all_catalog_item_types
#all_catalog_item_types ||= begin
builtin_catalog_item_types = {
"generic" => N_("Generic"),
"generic_orchestration" => N_("Orchestration"),
}
ExtManagementSystem.subclasses_supporting(:catalog)
.flat_map(&:catalog_types)
.reduce(builtin_catalog_item_types, :merge)
end
end
def self.catalog_item_types
ems_classes = Rbac.filtered(ExtManagementSystem.all).collect(&:class).uniq.select { |ems| ems.supports?(:catalog) }
ci_types = Set.new(ems_classes.flat_map(&:catalog_types).reduce({}, :merge).keys)
ci_types.add('generic_orchestration') if Rbac.filtered(OrchestrationTemplate).exists?
ci_types.add('generic')
all_catalog_item_types.each.with_object({}) do |(key, description), hash|
hash[key] = {:description => description, :display => ci_types.include?(key)}
end
end
def self.create_catalog_item(options, auth_user)
transaction do
create_from_options(options).tap do |service_template|
config_info = options[:config_info].except(:provision, :retirement, :reconfigure)
workflow_class = MiqProvisionWorkflow.class_for_source(config_info[:src_vm_id])
if workflow_class
request = workflow_class.new(config_info, auth_user).make_request(nil, config_info)
service_template.add_resource(request)
end
service_template.create_resource_actions(options[:config_info])
end
end
end
def self.class_from_request_data(data)
request_type = data['prov_type']
if request_type.include?('generic_')
generic_type = request_type.split('generic_').last
"ServiceTemplate#{generic_type.camelize}".constantize
else
ServiceTemplate
end
end
def update_catalog_item(options, auth_user = nil)
config_info = validate_update_config_info(options)
unless config_info
update!(options)
return reload
end
transaction do
update_from_options(options)
update_service_resources(config_info, auth_user)
update_resource_actions(config_info)
save!
end
reload
end
def children
service_templates
end
def descendants
children.flat_map { |child| [child] + child.descendants }
end
def subtree
[self] + descendants
end
def vms_and_templates
[]
end
def destroy
if parent_services.present?
raise MiqException::MiqServiceError, _("Cannot delete a service that is the child of another service.")
end
service_resources.each do |sr|
rsc = sr.resource
rsc.destroy if rsc.kind_of?(MiqProvisionRequestTemplate)
end
super
end
def archive
raise _("Cannot archive while in use") unless active_requests.empty?
archive!
end
def retireable?
false
end
def request_class
ServiceTemplateProvisionRequest
end
def request_type
"clone_to_service"
end
def config_info
options[:config_info] || construct_config_info
end
def create_service(service_task, parent_svc = nil)
nh = attributes.dup
# Service#display was renamed to #visible in https://github.com/ManageIQ/manageiq-schema/pull/410
nh['visible'] = nh.delete('display') if nh.key?('display')
nh['options'][:dialog] = service_task.options[:dialog]
(nh.keys - Service.column_names + %w(created_at guid service_template_id updated_at id type prov_type)).each { |key| nh.delete(key) }
# Hide child services by default
nh['visible'] = false if parent_svc
# If visible is nil, set it to false
nh['visible'] ||= false
# convert template class name to service class name by naming convention
nh['type'] = self.class.name.sub('Template', '')
nh['initiator'] = service_task.options[:initiator] if service_task.options[:initiator]
service = Service.create!(nh) do |svc|
svc.service_template = self
set_ownership(svc, service_task.get_user)
service_resources.each do |sr|
nh = sr.attributes.dup
%w(id created_at updated_at service_template_id).each { |key| nh.delete(key) }
svc.add_resource(sr.resource, nh) unless sr.resource.nil?
end
end
service.tap do |svc|
if parent_svc
service_resource = ServiceResource.find_by(:id => service_task.options[:service_resource_id])
parent_svc.add_resource!(svc, service_resource)
end
end
end
def composite?
service_type.to_s.include?(self.class::SERVICE_TYPE_COMPOSITE)
end
def atomic?
service_type.to_s.include?(self.class::SERVICE_TYPE_ATOMIC)
end
def type_display
case service_type
when self.class::SERVICE_TYPE_ATOMIC then "Item"
when self.class::SERVICE_TYPE_COMPOSITE then "Bundle"
when nil then "Unknown"
else
service_type.to_s.capitalize
end
end
def create_tasks_for_service(service_task, parent_svc)
unless parent_svc
return [] unless self.class.include_service_template?(service_task,
service_task.source_id,
parent_svc)
end
svc = create_service(service_task, parent_svc)
service_task.destination = svc
create_subtasks(service_task, svc)
end
# default implementation to create subtasks from service resources
def create_subtasks(parent_service_task, parent_service)
tasks = []
service_resources.each do |child_svc_rsc|
scaling_min = child_svc_rsc.scaling_min
1.upto(scaling_min).each do |scaling_idx|
nh = parent_service_task.attributes.dup
%w(id created_on updated_on type state status message).each { |key| nh.delete(key) }
nh['options'] = parent_service_task.options.dup
nh['options'].delete(:child_tasks)
# Initial Options[:dialog] to an empty hash so we do not pass down dialog values to child services tasks
nh['options'][:dialog] = {}
next if child_svc_rsc.resource_type == "ServiceTemplate" &&
!self.class.include_service_template?(parent_service_task,
child_svc_rsc.resource.id,
parent_service)
new_task = parent_service_task.class.new(nh)
new_task.options.merge!(
:src_id => child_svc_rsc.resource.id,
:scaling_idx => scaling_idx,
:scaling_min => scaling_min,
:service_resource_id => child_svc_rsc.id,
:parent_service_id => parent_service.id,
:parent_task_id => parent_service_task.id,
)
new_task.state = 'pending'
new_task.status = 'Ok'
new_task.source = child_svc_rsc.resource
new_task.save!
new_task.after_request_task_create
parent_service_task.miq_request.miq_request_tasks << new_task
tasks << new_task
end
end
tasks
end
def set_ownership(service, user)
return if user.nil?
service.evm_owner = user
if user.current_group
$log.info("Setting Service Owning User to Name=#{user.name}, ID=#{user.id}, Group to Name=#{user.current_group.name}, ID=#{user.current_group.id}")
service.miq_group = user.current_group
else
$log.info("Setting Service Owning User to Name=#{user.name}, ID=#{user.id}")
end
end
def self.default_provisioning_entry_point(service_type)
if service_type == 'atomic'
'/Service/Provisioning/StateMachines/ServiceProvision_Template/CatalogItemInitialization'
else
'/Service/Provisioning/StateMachines/ServiceProvision_Template/CatalogBundleInitialization'
end
end
def self.default_retirement_entry_point
'/Service/Retirement/StateMachines/ServiceRetirement/Default'
end
def self.default_reconfiguration_entry_point
nil
end
def template_valid?
validate_template[:valid]
end
alias template_valid template_valid?
def template_valid_error_message
validate_template[:message]
end
def validate_template
missing_resources = service_resources.select { |sr| sr.resource.nil? }
if missing_resources.present?
missing_list = missing_resources.collect { |sr| "#{sr.resource_type}:#{sr.resource_id}" }.join(", ")
return {:valid => false,
:message => "Missing Service Resource(s): #{missing_list}"}
end
service_resources.detect do |s|
r = s.resource
r.respond_to?(:template_valid?) && !r.template_valid?
end.try(:resource).try(:validate_template) || {:valid => true, :message => nil}
end
def provision_action
resource_actions.find_by(:action => "Provision")
end
def update_resource_actions(ae_endpoints)
resource_action_list.each do |action|
resource_params = ae_endpoints[action[:param_key]]
resource_action = resource_actions.find_by(:action => action[:name])
# If the action exists in updated parameters
if resource_params
# And the resource action exists on the template already, update it
if resource_action
resource_action.update!(resource_params.slice(*RESOURCE_ACTION_UPDATE_ATTRS))
# If the resource action does not exist, create it
else
build_resource_action(resource_params, action)
end
elsif resource_action
# If the endpoint does not exist in updated parameters, but exists on the template, delete it
resource_action.destroy
end
end
end
def create_resource_actions(ae_endpoints)
ae_endpoints ||= {}
resource_action_list.each do |action|
ae_endpoint = ae_endpoints[action[:param_key]]
next unless ae_endpoint
build_resource_action(ae_endpoint, action)
end
save!
end
def self.create_from_options(options)
create!(options.except(:config_info).merge(:options => { :config_info => options[:config_info] }))
end
private_class_method :create_from_options
def provision_request(user, options = nil, request_options = {})
request_options[:provision_workflow] = true
request_options[:parent_id] = options.delete('param_parent_request_id') unless options['param_parent_request_id'].nil?
result = order(user, options, request_options)
raise result[:errors].join(", ") if result[:errors].any?
result[:request]
end
def picture=(value)
if value.kind_of?(Hash)
super(Picture.new(value))
else
super
end
end
def queue_order(user_id, options, request_options)
MiqQueue.submit_job(
:class_name => self.class.name,
:instance_id => id,
:method_name => "order",
:args => [user_id, options, request_options],
)
end
def order(user_or_id, options = nil, request_options = {}, schedule_time = nil)
user = user_or_id.kind_of?(User) ? user_or_id : User.find(user_or_id)
workflow = provision_workflow(user, options, request_options)
if schedule_time
require 'time'
time = Time.parse(schedule_time).utc
errors = workflow.validate_dialog
errors << unsupported_reason(:order)
return {:errors => errors} if errors.compact.present?
schedule = MiqSchedule.create!(
:name => "Order #{self.class.name} #{id} at #{time}",
:description => "Order #{self.class.name} #{id} at #{time}",
:sched_action => {:args => [user.id, options, request_options], :method => "queue_order"},
:resource => self,
:run_at => {
:interval => {:unit => "once"},
:start_time => time,
:tz => "UTC",
},
)
{:schedule => schedule}
else
workflow.submit_request
end
end
def provision_workflow(user, dialog_options = nil, request_options = {})
dialog_options ||= {}
request_options.delete(:provision_workflow) if request_options[:submit_workflow]
ra_options = request_options.slice(:initiator, :init_defaults, :provision_workflow, :submit_workflow).merge(:target => self)
ResourceActionWorkflow.new(dialog_options, user, provision_action, ra_options).tap do |wf|
wf.request_options = request_options
end
end
def add_resource(rsc, options = {})
super
adjust_service_type
end
def self.display_name(number = 1)
n_('Service Catalog Item', 'Service Catalog Items', number)
end
def my_zone
# Catalog items can specify a zone to run in.
# Catalog bundle are used for grouping catalog items and are therefore treated as zone-agnostic.
zone&.name if atomic?
end
private
def update_service_resources(config_info, auth_user = nil)
config_info = config_info.except(:provision, :retirement, :reconfigure)
workflow_class = MiqProvisionWorkflow.class_for_source(config_info[:src_vm_id])
if workflow_class
service_resources.find_by(:resource_type => 'MiqRequest').try(:destroy)
new_request = workflow_class.new(config_info, auth_user).make_request(nil, config_info)
add_resource!(new_request)
end
end
def build_resource_action(ae_endpoint, action)
fqname = ae_endpoint[:fqname] || self.class.send(action[:method], *action[:args]) || ""
build_options = {:action => action[:name],
:fqname => fqname,
:ae_attributes => {:service_action => action[:name]}}
build_options.merge!(ae_endpoint.slice(*RESOURCE_ACTION_UPDATE_ATTRS))
resource_actions.build(build_options)
end
def validate_update_config_info(options)
if options[:service_type] && options[:service_type] != service_type
raise _('service_type cannot be changed')
end
if options[:prov_type] && options[:prov_type] != prov_type
raise _('prov_type cannot be changed')
end
options[:config_info]
end
def resource_action_list
[
{:name => ResourceAction::PROVISION,
:param_key => :provision,
:method => 'default_provisioning_entry_point',
:args => [service_type]},
{:name => ResourceAction::RECONFIGURE,
:param_key => :reconfigure,
:method => 'default_reconfiguration_entry_point',
:args => []},
{:name => ResourceAction::RETIREMENT,
:param_key => :retirement,
:method => 'default_retirement_entry_point',
:args => []}
]
end
def update_from_options(params)
options[:config_info] = params[:config_info]
update!(params.except(:config_info))
end
def construct_config_info
config_info = {}
miq_request_resource = service_resources.find_by(:resource_type => 'MiqRequest')
config_info.merge!(miq_request_resource.resource.options.compact) if miq_request_resource
config_info.merge!(resource_actions_info)
end
def resource_actions_info
resource_actions.each_with_object({}) do |resource_action, config_info|
resource_options = resource_action.slice(:dialog_id, :configuration_template_type, :configuration_template_id).compact
resource_options[:fqname] = resource_action.fqname
config_info[resource_action.action.downcase.to_sym] = resource_options.symbolize_keys
end
end
def generic_custom_buttons
CustomButton.buttons_for("Service")
end
def adjust_service_type
self.service_type = service_resources.any? { |st| st.resource_type.in?(['Service', 'ServiceTemplate']) } ? self.class::SERVICE_TYPE_COMPOSITE : self.class::SERVICE_TYPE_ATOMIC
end
end
scope :public_service_templates, -> {
joins(:miq_user_roles).where(miq_user_roles: { settings: nil })
}
Assuming the table exists. You need to join on that table in order to query it. Note, the default joins is an inner join that will remove records that don't have an associated miq_user_roles.
This will change the query substantially. If a record has_many miq_user_roles you'll likely need to add a distinct or distinct on clause. If it's possible to have no miq_user_roles, then records without them will now dissappear when you call the scope, since the inner join didn't find any.

Active Merchant paypal recurring payments

I am using Active Merchant gem to handle payments through the site. But now i want to make these payments recurring, on a monthly basis. Is there a way using active merchant or?
subscription_controller.rb
class SubscriptionsController < ApplicationController
def new
#home_page = true
#white = true
#subscription = Subscription.new(token: params[:token])
if !logged_in?
redirect_to signup_url
end
end
def create
#subscription = Subscription.new(subscription_params)
#subscription.remote_ip = request.remote_ip
#subscription.user_id = current_user.id
if #subscription.save
if #subscription.purchase
#subscription.send_thank_you_email
redirect_to thankyou_path
else
raise ActiveRecord::Rollback
flash[:notice] = "It seems something went wrong with the paypal transaction. Please check that your credit card is valid and has credit in it and try again."
redirect_to :back
end
else
flash[:notice] = "Something went wrong with marking your purchase as complete. Please contact support to check it out."
redirect_to :back
end
end
def purchase
response = GATEWAY.setup_purchase(999,
ip: request.remote_ip,
return_url: new_subscription_url,
cancel_return_url: root_url,
currency: "USD",
items: [{name: "Order", description: "Recurring payment for ******", quantity: "1", amount: 999}]
)
redirect_to GATEWAY.redirect_url_for(response.token)
end
def thank_you
#home_page = true
#white = true
end
private
def subscription_params
params.require(:subscription).permit(:token)
end
end
subscription.rb model
def purchase
response = GATEWAY.purchase(999, express_purchase_options)
response.success?
end
def token=(token)
self[:token] = token
if new_record? && !token.blank?
# you can dump details var if you need more info from buyer
details = GATEWAY.details_for(token)
puts details.params["PayerInfo"]["PayerName"].inspect
self.payer_id = details.payer_id
self.first_name = details.params["PayerInfo"]["PayerName"]["FirstName"]
self.last_name = details.params["PayerInfo"]["PayerName"]["LastName"]
end
end
# send thank you email
def send_thank_you_email
UserMailer.thank_you(self).deliver_now
end
private
def express_purchase_options
{
:ip => remote_ip,
:token => token,
:payer_id => payer_id
}
end
production.rb environment
config.after_initialize do
ActiveMerchant::Billing::Base.mode = :production
::GATEWAY = ActiveMerchant::Billing::PaypalExpressGateway.new(
:login => ENV['PAYPAL_LOGIN'],
:password => ENV['PAYPAL_PASSWORD'],
:signature => ENV['PAYPAL_SIGNATURE']
)
end
I think ActiveMerchant used to have something like this:
subscription = PAYPAL_EXPRESS_GATEWAY.recurring(#subscription.price_in_cents, nil,
:description => 'blah',
:start_date => Date.tomorrow,
:period => 'Year',
:frequency => 1,
:amount => price,
:currency => 'USD'
)
See this answer Does ActiveMerchant support Subscription Based transaction
Also see this: https://github.com/activemerchant/active_merchant/blob/master/lib/active_merchant/billing/gateways/paypal_express.rb
https://github.com/activemerchant/active_merchant/blob/master/lib/active_merchant/billing/gateways/paypal/paypal_recurring_api.rb

PayPal Express Checkout with multiple items with active merchant in rails

I am trying to use PayPal ExpressCheckout button with multiple items but with no success.I am using NetBeans IDE, rails 4 and MySQL db.Here is what I did so far:
In my production.rb file I have:
Rails.application.configure do
config/application.rb.
config.after_initialize do
ActiveMerchant::Billing::Base.mode = :test
paypal_options = {
:login => "xxxx",
:password => "xxxx ",
:signature => "xxxx "
}
::STANDARD_GATEWAY = ActiveMerchant::Billing::PaypalGateway.new(paypal_options)
::EXPRESS_GATEWAY = ActiveMerchant::Billing::PaypalExpressGateway.new(paypal_options)
end
In my transaction.rb model I have:
def valid_purchase
if express_token.blank?
standard_purchase
else
express_token
end
def express_purchase
# price_in_cents = total
response = EXPRESS_GATEWAY.purchase(total, express_purchase_options)
if response.success?
self.status = "processed"
else
errors.add(:transactions, "---- #{response.message}.")
end
end
def express_token=(token)
self[:express_token] = token
if new_record? && !token.blank?
details = EXPRESS_GATEWAY.details_for(token)
self.express_payer_id = details.payer_id
self.ship_to_first_name = details.params["first_name"]
self.ship_to_last_name = details.params["last_name"]
end
end
private
def express_purchase_options
{
:ip => customer_ip,
:token => express_token,
:payer_id => express_payer_id
}
end
And in my transaction_controller.rb I have:
def express_checkout
order_items =[]
postage_rate=nil
item = Hash.new
#order = Order.find(session[:order_id])
#receipts = Receipt.where(:order_id=>#order.id)
#receipts.each do |r|
postage_rate = r.postage_rate * 100
end
#cart = Cart.find(#order.cart_id)
#cart.cart_items.each do |i|
#product = Product.find(i.product_id)
item = {
name: #product.product_name,
quantity: i.amount,
description: "ORDER_ID: #{#order.id}",
amount: #product.selling_price * 100 ,
shipping: postage_rate/#cart.cart_items.size
}
order_items << item
end
price_in_cents = (#order.total_p_pr * 100).round(2)
options = {
:ip => request.remote_ip,
:return_url => url_for(:action=>:new, :only_path => false),
:cancel_return_url => catalogs_traders_url,
:currency => "USD",
:allow_guest_checkout=> true,
:items => order_items # this line outputs: [{:name=>"owl potty", :quantity=>1, :description=>"ORDER_ID: 249", :amount=>2808.0, :shipping=>332.0}, {:name=>"a bag", :quantity=>1, :description=>"ORDER_ID: 249", :amount=>1260.0, :shipping=>332.0}, {:name=>"bracelet", :quantity=>1, :description=>"ORDER_ID: 249", :amount=>120.0, :shipping=>332.0}, {:name=>"beautiful woman", :quantity=>1, :description=>"ORDER_ID: 249", :amount=>74352.0, :shipping=>332.0}]
}
#passing the cost of the order
response = EXPRESS_GATEWAY.setup_purchase(price_in_cents,options )
redirect_to EXPRESS_GATEWAY.redirect_url_for(response.token)
end
def new
#transaction = Transaction.new(:express_token => params[:token])
end
I get:
Any help will be more than welcome. Thank you!
I red this post very,very carefully
setting tax amount in Active Merchant / PayPal Express Checkout
and I understood my mistakes. Here is my corrected transaction_controller:
# to redirect to PayPay site
def express_checkout
pr = nil
tp = nil
items =[]
postage_r=[]
total_p = []
order_items =[]
postage_rate=nil
item = Hash.new
#order = Order.find(session[:order_id])
#receipts = Receipt.where(:order_id=>#order.id)
#receipts.each do |r|
total_p << r.total_price
postage_r << r.postage_rate
end
tp = total_p.inject{|sum,x| sum + x }
pr = postage_r.inject{|sum,x| sum + x }
#cart = Cart.find(#order.cart_id)
#cart.cart_items.each do |i|
#product = Product.find(i.product_id)
item = {
name: #product.product_name,
quantity: i.amount,
description: "ORDER_ID: #{#order.id}",
amount: #product.selling_price * 100 ,
}
order_items << item
end
price_in_cents = (#order.total_p_pr * 100).round(2)
options = {
:subtotal => tp * 100,
:shipping => pr * 100,
:handling => 0,
:tax => 0,
:ip => request.remote_ip,
:return_url => url_for(:action=>:new, :only_path => false),
:cancel_return_url => catalogs_traders_url,
:currency => "USD",
:allow_guest_checkout=> true,
:items => order_items
}
#passing the cost of the order
response = EXPRESS_GATEWAY.setup_purchase(price_in_cents,options )
redirect_to EXPRESS_GATEWAY.redirect_url_for(response.token)
end
It worked. I hope my post will be useful for someone who want to integrate Express Checkout button. Thank you for all your help!

Podio Ruby Rails shows "nomethoderror"

I'm having problems with the Podio_rails_sample. I've included my leadsController and leads.rb files. The line that gets hung up is field['config']['settings']['allowed_values'].
Line 25 is the problematic one:
NoMethodError in LeadsController#new
undefined method `[]' for nil:NilClass
Extracted source (around line #25):
23 app = Podio::Application.find(APP_ID)
24 field = app.fields.find { |field| field['external_id'] == 'status' }
25 field['config']['settings']['allowed_values']
26 end
27
28 def self.create_from_params(params)
Rails.root: c:/Sites/podio_rails_sample
app = Podio::Application.find(APP_ID)
field = app.fields.find { |field| field['external_id'] == 'status' }
field['config']['settings']['allowed_values']
end
def self.create_from_params(params)
Rails.root: c:/Sites/podio_rails_sample
-----------------------------------
class LeadsController < ApplicationController
before_filter :load_collections, :only => [:new, :edit]
def index
#leads = Lead.all
end
def new
#lead = Lead.new
end
def create
Lead.create_from_params(params['lead'])
redirect_to leads_path, :notice => 'Lead created'
end
def edit
#lead = Lead.find_basic(params[:id])
end
def update
Lead.update_from_params(params[:id], params['lead'])
redirect_to leads_path, :notice => 'Lead updated'
end
def destroy
Lead.delete(params[:id])
redirect_to leads_path, :notice => 'Lead deleted'
end
#protected
def load_collections
#lead_contacts = Lead.space_contacts
#sales_contacts = Lead.users
#statuses = Lead.statuses
end
end
-------------------------------------
- leads.rb file
class Lead < Podio::Item
APP_ID =12328033
SPACE_ID =3204114
# Find all items in the Leads app
def self.all
collection = self.find_all(APP_ID)
collection[:all]
end
# Find valid lead contacts in the space
def self.space_contacts
Podio::Contact.find_all_for_space(SPACE_ID, :order => 'contact', :limit => 12, :contact_type => 'space,connection', :exclude_self => false) rescue []
end
# Find valid sales contacts in the space
def self.users
Podio::Contact.find_all_for_space(SPACE_ID, :order => 'contact', :limit => 12, :contact_type => 'user', :exclude_self => false) rescue []
end
# Find valid statuses
def self.statuses
app = Podio::Application.find(APP_ID)
field = app.fields.find { |field| field['external_id'] == 'status' }
field['config']['settings']['allowed_values']
end
def self.create_from_params(params)
# raise fields.inspect
self.create(APP_ID, { :fields => fields_from_params(params) })
end
def self.update_from_params(id, params)
self.update(id, { :fields => fields_from_params(params) })
end
#
# Map the field values return by the Podio API to simple getters
#
def organization
field_values_by_external_id('company-or-organisation', :simple => true)
end
def lead_contact
field_values_by_external_id('contacts', :simple => true).try(:[], 'name')
end
def sales_contact
field_values_by_external_id('sales-contact', :simple => true).try(:[], 'name')
end
def potential_revenue_value
field_values_by_external_id('potential-revenue').try(:first).try(:[], 'value').to_i
end
def potential_revenue_currency
field_values_by_external_id('potential-revenue').try(:first).try(:[], 'currency')
end
def probability
field_values_by_external_id('probability-of-sale', :simple => true)
end
def status
field_values_by_external_id('status', :simple => true)
end
def followup_at
field_values_by_external_id('next-follow-up').try(:first).try(:[], 'start').try(:to_datetime)
end
protected
def field_values_by_external_id(external_id, options = {})
if self.fields.present?
field = self.fields.find { |field| field['external_id'] == external_id }
if field
values = field['values']
if options[:simple]
values.first['value']
else
values
end
else
nil
end
else
nil
end
end
def self.fields_from_params(params)
{
'company-or-organisation' => params[:organization],
'contacts' => (params[:lead_contact].present? ? params[:lead_contact].to_i : nil),
'sales-contact' => (params[:sales_contact].present? ? params[:sales_contact].to_i : nil),
'potential-revenue' => { :value => params['potential_revenue_value'], :currency => params['potential_revenue_currency'] },
'probability-of-sale' => params[:probability].to_i,
'status' => params[:status],
'next-follow-up' => DateTime.new(params['followup_at(1i)'].to_i, params['followup_at(2i)'].to_i, params['followup_at(3i)'].to_i).to_s(:db)
}.delete_if { |k, v| v.nil? }
end
end

How to get gateway response from model into controller - Ruby on Rails

My application uses activemerchant to process payments. I'm using Eway as my payment gateway. I'm storing credit card details with Eway to keep them out of my application database.
I'm using a method store which returns a response with a customer billing id that I can use at a later time to process the order.
http://rdoc.info/github/Shopify/active_merchant/master/ActiveMerchant/Billing/EwayManagedGateway
My main issue is how do I get the response value into my controller so I can save it to the member model.
I've created a simple ruby file to test this all works and it does. I just need to convert this code to work inside my rails app.
require "rubygems"
gem 'activemerchant', '1.15.0'
require 'activemerchant'
ActiveMerchant::Billing::Base.mode = :production
gateway = ActiveMerchant::Billing::EwayManagedGateway.new(
:login => '12345678',
:username => 'mylogin#example.com',
:password => 'mypassword'
)
credit_card = ActiveMerchant::Billing::CreditCard.new(
:type => "visa",
:number => "4444333322221111",
:verification_value => "123",
:month => "11",
:year => "2011",
:first_name => "John",
:last_name => "Citizen"
)
options = {
:order_id => '1230123',
:ip => "127.0.0.1",
:email => 'john.citizen#example.com',
:billing_address => { :title => "Mr.",
:address1 => '123 Sample Street',
:city => 'Sampleville',
:state => 'NSW',
:country => 'AU',
:zip => '2000'
},
:description => 'purchased items'
}
if credit_card.valid?
response = gateway.store(credit_card, options)
if response.success?
puts "Credit Card Stored. #{response.message}"
customer = response.params['CreateCustomerResult']
puts "Customer Id: #{customer}"
else
puts "Error: #{response.message}"
end
else
puts "Error, credit card is not valid. #{credit_card.errors.full_messages.join('. ')}"
end
Here is the relevant code in my order model.
serialize :params
cattr_accessor :gateway
def response=(response)
self.success = response.success?
self.message = response.message
self.params = response.params
self.billing_id = response.params['CreateCustomerResult']
rescue ActiveMerchant::ActiveMerchantError => e
self.success = false
self.message = e.message
self.params = {}
self.billing_id = nil
end
def store
response = Order.gateway.store(credit_card, options)
end
Here is my order controller create code.
def create
#member = current_member
#order_deal = Deal.find(params[:deal_id])
#order = #order_deal.orders.build(params[:order])
#order.member_id = current_member.id
#order.ip_address = request.remote_ip
#deal = #order_deal
if #order.save
if #order.store
render :action => "success"
flash[:notice] = "Successfully processed your order."
else
render :action => "new"
end
else
render :action => 'new'
end
end
So essentially I want to get the
response.params['CreateCustomerResult']
and add it to my member model under
member.billing_id = response.params['CreateCustomerResult]
How can I do this?

Resources