diff --git a/app/controllers/api/v1/invoice_generator/invoice_generator_controller.rb b/app/controllers/api/v1/invoice_generator/invoice_generator_controller.rb index ee5c88b..b7c1fd5 100644 --- a/app/controllers/api/v1/invoice_generator/invoice_generator_controller.rb +++ b/app/controllers/api/v1/invoice_generator/invoice_generator_controller.rb @@ -31,12 +31,27 @@ class InvoiceGeneratorController < ApplicationController param :invoice_number, String, required: true def create - InvoiceInstanceGenerator.create(params:) + Rails.logger.info( + "[BILLING-GENERATE] start invoice_number=#{params[:invoice_number].inspect} " \ + "initiator=#{params[:custom_field2].inspect} amount=#{params[:transaction_amount].inspect} " \ + "request_id=#{request.request_id}" + ) + + invoice = InvoiceInstanceGenerator.create(params:) link = EverypayLinkGenerator.create(params:) + Rails.logger.info( + "[BILLING-GENERATE] saved id=#{invoice&.id} invoice_number=#{invoice&.invoice_number} " \ + "request_id=#{request.request_id}" + ) + render json: { 'message' => 'Link created', 'everypay_link' => link }, status: :created rescue StandardError => e - Rails.logger.info e + Rails.logger.error( + "[BILLING-GENERATE] FAILED invoice_number=#{params[:invoice_number].inspect} " \ + "request_id=#{request.request_id} -> #{e.class}: #{e.message}" + ) + Rails.logger.error(e.backtrace.join("\n")) if e.backtrace end end end diff --git a/app/controllers/api/v1/invoice_generator/invoice_number_generator_controller.rb b/app/controllers/api/v1/invoice_generator/invoice_number_generator_controller.rb index 4b0f4a9..fc373c3 100644 --- a/app/controllers/api/v1/invoice_generator/invoice_number_generator_controller.rb +++ b/app/controllers/api/v1/invoice_generator/invoice_number_generator_controller.rb @@ -4,6 +4,12 @@ class Api::V1::InvoiceGenerator::InvoiceNumberGeneratorController < Api::V1::Inv def create invoice_number = InvoiceNumberService.call + Rails.logger.info( + "[BILLING-NUMBER] generated=#{invoice_number.inspect} " \ + "min=#{InvoiceNumberService::INVOICE_NUMBER_MIN} max=#{InvoiceNumberService::INVOICE_NUMBER_MAX} " \ + "db_max=#{Invoice.maximum(:invoice_number).inspect} request_id=#{request.request_id}" + ) + if invoice_number == 'out of range' return render json: { 'message' => "Number create failed. #{invoice_number}", 'error' => invoice_number }, status: :not_implemented diff --git a/app/controllers/api/v1/invoice_generator/oneoff_controller.rb b/app/controllers/api/v1/invoice_generator/oneoff_controller.rb index abbca5e..95d6a83 100644 --- a/app/controllers/api/v1/invoice_generator/oneoff_controller.rb +++ b/app/controllers/api/v1/invoice_generator/oneoff_controller.rb @@ -11,14 +11,23 @@ class OneoffController < Api::V1::InvoiceGenerator::BaseController param :reference_number, String, required: false def create + Rails.logger.info( + "[BILLING-ONEOFF] start invoice_number=#{params[:invoice_number].inspect} " \ + "customer_url=#{params[:customer_url].inspect} " \ + "found_in_db=#{Invoice.exists?(invoice_number: params[:invoice_number])} " \ + "request_id=#{request.request_id}" + ) + response = Oneoff.call(invoice_number: params[:invoice_number], customer_url: params[:customer_url], reference_number: params[:reference_number]) if response.result? + Rails.logger.info("[BILLING-ONEOFF] ok invoice_number=#{params[:invoice_number].inspect} request_id=#{request.request_id}") render json: { 'message' => 'Link created', 'oneoff_redirect_link' => response.instance['payment_link'] }, status: :created else + Rails.logger.warn("[BILLING-ONEOFF] rejected invoice_number=#{params[:invoice_number].inspect} errors=#{response.errors.inspect} request_id=#{request.request_id}") render json: { error: response.errors }, status: :unprocessable_entity end end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 612c927..91557c9 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -1,6 +1,16 @@ class ApplicationController < ActionController::API + prepend_before_action :log_api_request before_action :authorized + def log_api_request + Rails.logger.info( + "[BILLING-API] #{request.request_method} #{request.original_fullpath} " \ + "host=#{request.host} request_id=#{request.request_id} " \ + "initiator=#{(decoded_token && decoded_token[0]['initiator']).inspect} " \ + "params=#{request.filtered_parameters.except('controller', 'action', 'format').inspect}" + ) + end + def encode_token(payload) JWT.encode(payload, ENV['secret_word']) end diff --git a/app/controllers/errors_controller.rb b/app/controllers/errors_controller.rb index 32c3853..3f5e9e6 100644 --- a/app/controllers/errors_controller.rb +++ b/app/controllers/errors_controller.rb @@ -7,11 +7,39 @@ def show ActionDispatch::ExceptionWrapper.new( request.env, @exception ).status_code - render view_for_code(@status_code), status: @status_code + + log_exception + + respond_to do |format| + format.html { render view_for_code(@status_code), status: @status_code } + format.json { render json: error_payload, status: @status_code } + format.any { render json: error_payload, status: @status_code, content_type: 'application/json' } + end end private + def log_exception + return if @exception.nil? + + wrapped = @exception.cause || @exception + + Rails.logger.error( + "[ErrorsController] #{@status_code} on #{request.method} " \ + "#{request.original_fullpath} (format=#{request.format.try(:ref).inspect}) " \ + "request_id=#{request.request_id} -> " \ + "#{wrapped.class}: #{wrapped.message}" + ) + Rails.logger.error(wrapped.backtrace.join("\n")) if wrapped.backtrace + end + + def error_payload + { + error: Rack::Utils::HTTP_STATUS_CODES.fetch(@status_code, 'Error'), + status: @status_code + } + end + def view_for_code(code) supported_error_codes.fetch(code, '404') end diff --git a/app/views/errors/401.html.erb b/app/views/errors/401.html.erb new file mode 100644 index 0000000..c1c8286 --- /dev/null +++ b/app/views/errors/401.html.erb @@ -0,0 +1,3 @@ +

Unauthorized

+ +<%= link_to 'Back to home', root_path, class: 'text-indigo-500' %> diff --git a/app/views/errors/404.html.erb b/app/views/errors/404.html.erb new file mode 100644 index 0000000..5983d0c --- /dev/null +++ b/app/views/errors/404.html.erb @@ -0,0 +1,3 @@ +

Not Found

+ +<%= link_to 'Back to home', root_path, class: 'text-indigo-500' %> diff --git a/app/views/errors/500.html.erb b/app/views/errors/500.html.erb new file mode 100644 index 0000000..6f7926a --- /dev/null +++ b/app/views/errors/500.html.erb @@ -0,0 +1,3 @@ +

Internal Server Error

+ +<%= link_to 'Back to home', root_path, class: 'text-indigo-500' %> diff --git a/config/application.yml.sample b/config/application.yml.sample index 6a0919f..4ed80b6 100644 --- a/config/application.yml.sample +++ b/config/application.yml.sample @@ -76,7 +76,7 @@ development: tara_redirect_uri: 'https://eis_billing_system.test/auth/tara/callback' tara_identifier: '' tara_secret: '' -tara_scope: 'openid' +tara_scope: 'openid webauthn' tara_discovery: true diff --git a/db/schema.rb b/db/schema.rb index ff3262d..d4c543a 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.1].define(version: 2023_11_30_114315) do +ActiveRecord::Schema[7.2].define(version: 2023_11_30_114315) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql"