Refactor QuickBooks webhook handling to use ActiveJob for processing; improve security with signature verification and streamline entity processing

This commit is contained in:
2026-02-26 20:30:20 -05:00
parent a64016eb95
commit 4f55751500
2 changed files with 88 additions and 62 deletions

View File

@@ -86,68 +86,6 @@ class QboController < ApplicationController
notice: I18n.t(:label_billing_enqueued) + " #{issue.customer.name}"
}
end
# Quickbooks Webhook Callback
def webhook
logger.info "Quickbooks is calling webhook"
# check the payload
signature = request.headers['intuit-signature']
key = Setting.plugin_redmine_qbo['settingsWebhookToken']
data = request.body.read
hash = Base64.encode64(OpenSSL::HMAC.digest(OpenSSL::Digest::Digest.new('sha256'), key, data)).strip()
# proceed if the request is good
if hash.eql? signature
Thread.new do
if request.headers['content-type'] == 'application/json'
data = JSON.parse(data)
else
# application/x-www-form-urlencoded
data = params.as_json
end
# Process the information
entities = data['eventNotifications'][0]['dataChangeEvent']['entities']
entities.each do |entity|
id = entity['id'].to_i
name = entity['name']
logger.info "Casting #{name.constantize} to obj"
# Magicly initialize the correct class
obj = name.constantize
# for merge events
obj.destroy(entity['deletedId']) if entity['deletedId']
#Check to see if we are deleting a record
if entity['operation'].eql? "Delete"
obj.destroy(id)
#if not then update!
else
begin
obj.sync_by_id(id)
rescue => e
logger.error "Failed to call sync_by_id on obj"
logger.error e.message
logger.error e.backtrace.join("\n")
end
end
end
# Record that last time we updated
Qbo.update_time_stamp
ActiveRecord::Base.connection.close
end
# The webhook doesn't require a response but let's make sure we don't send anything
render nothing: true, status: 200
else
render nothing: true, status: 400
end
logger.info "Quickbooks webhook complete"
end
#
# Synchronizes the QboCustomer table with QBO
@@ -170,4 +108,33 @@ class QboController < ApplicationController
redirect_to :home, flash: { notice: I18n.t(:label_syncing) }
end
# QuickBooks Webhook Callback
def webhook
logger.info "QBO: Webhook received"
signature = request.headers['intuit-signature']
key = Setting.plugin_redmine_qbo['settingsWebhookToken']
body = request.raw_post
digest = OpenSSL::Digest.new('sha256')
computed = Base64.strict_encode64(OpenSSL::HMAC.digest(digest, key, body))
unless secure_compare(computed, signature)
logger.warn "QBO: Invalid webhook signature"
head :unauthorized
return
end
WebhookProcessJob.perform_later(body)
head :ok
end
private
def secure_compare(a, b)
return false if a.blank? || b.blank?
ActiveSupport::SecurityUtils.secure_compare(a, b)
end
end