123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122 |
- # Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
- module Whatsapp::Webhook
- class Payload
- include Whatsapp::Webhook::Concerns::HasChannel
- include Whatsapp::Webhook::Concerns::HandlesError
- def initialize(json:, uuid:, signature:)
- channel = find_channel!(uuid)
- secret = channel.options[:app_secret]
- digest = OpenSSL::Digest.new('sha256')
- raise ValidationError if OpenSSL::HMAC.hexdigest(digest, secret, json) != signature
- @channel = channel
- @data = JSON.parse(json).deep_symbolize_keys
- raise ProcessableError, __('Mismatching phone number id.') if !phone_number_id?
- end
- def process
- raise ProcessableError, __('API error.') if protocol_error?
- raise ProcessableError, __('Unsupported subscription type.') if !subscription_message?
- if status_message?
- process_status_message
- elsif message?
- process_message
- else
- # NeverShouldHappen(TM)
- raise ProcessableError, __('Unsupported webhook payload.')
- end
- end
- private
- def process_message
- if message_error?
- handle_error
- raise ProcessableError
- end
- type = @data[:entry].first[:changes].first[:value][:messages].first[:type]
- klass = "Whatsapp::Webhook::Message::#{type.capitalize}"
- raise ProcessableError, __('Unsupported message type.') if Whatsapp::Webhook::Message.descendants.map(&:to_s).exclude?(klass)
- klass.constantize.new(data: @data, channel: @channel).process
- end
- def process_status_message
- status = @data[:entry].first[:changes].first[:value][:statuses].first[:status]
- klass = "Whatsapp::Webhook::Message::Status::#{status.capitalize}"
- log_status_message
- return if Whatsapp::Webhook::Message::Status.descendants.map(&:to_s).exclude?(klass)
- klass.constantize.new(data: @data, channel: @channel).process
- end
- def phone_number_id?
- @data[:entry].first[:changes].first[:value][:metadata][:phone_number_id] == @channel.options[:phone_number_id]
- end
- def subscription_message?
- @data[:entry].first[:changes].first[:field] == 'messages'
- end
- def protocol_error?
- @data[:entry].first[:changes].first[:value].key?(:error)
- end
- def message_error?
- @data[:entry].first[:changes].first[:value][:messages].first.key?(:errors)
- end
- def error
- @data[:entry].first[:changes].first[:value][:messages].first[:errors].first
- end
- def message?
- @data[:entry].first[:changes].first[:value].key?(:messages)
- end
- def status_message?
- @data[:entry].first[:changes].first[:value].key?(:statuses)
- end
- def failed_status_message?
- @data[:entry].first[:changes].first[:value][:statuses].first[:status] == 'failed'
- end
- def log_status_message
- HttpLog.create(
- direction: 'in',
- facility: 'WhatsApp::Business',
- url: "#{Setting.get('http_type')}://#{Setting.get('fqdn')}/#{Rails.configuration.api_path}/channels_whatsapp_webhook/#{@channel.options[:callback_url_uuid]}",
- ip: @channel.options[:phone_number],
- status: 200,
- request: { content: @data },
- response: { content: {} },
- method: 'POST',
- )
- end
- class ValidationError < StandardError
- def initialize
- super(__('The WhatsApp webhook payload could not be validated.'))
- end
- end
- class ProcessableError < StandardError
- attr_reader :reason
- def initialize(reason = nil)
- @reason = reason
- super(__('The WhatsApp webhook payload could not be processed.'))
- end
- end
- end
- end
|