Skip to content

Commit

Permalink
Direct deposit update (#12812)
Browse files Browse the repository at this point in the history
* Add update function to direct deposit service and standardize error responses

* Linting fixes

---------

Co-authored-by: Jeremy Weiland <jeremy6d@users.noreply.github.com>
  • Loading branch information
tpharrison and jeremy6d authored May 31, 2023
1 parent 04d681e commit 2e5fc61
Show file tree
Hide file tree
Showing 25 changed files with 993 additions and 123 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# frozen_string_literal: true

require 'lighthouse/service_exception'
require 'lighthouse/direct_deposit/client'
require 'lighthouse/direct_deposit/error_parser'
require 'lighthouse/direct_deposit/payment_account'

module V0
Expand All @@ -10,6 +12,13 @@ class DisabilityCompensationsController < ApplicationController
before_action :controller_enabled?
before_action { authorize :lighthouse, :access_disability_compensations? }

rescue_from(*Lighthouse::ServiceException::ERROR_MAP.values) do |exception|
error = { status: exception.status_code, body: exception.errors.first }
response = Lighthouse::DirectDeposit::ErrorParser.parse(error)

render status: response.status, json: response.body
end

def show
response = client.get_payment_info

Expand All @@ -18,6 +27,15 @@ def show
serializer: DisabilityCompensationsSerializer
end

def update
response = client.update_payment_info(payment_account)
send_confirmation_email

render status: response.status,
json: response.body,
serializer: DisabilityCompensationsSerializer
end

private

def controller_enabled?
Expand Down
122 changes: 101 additions & 21 deletions app/swagger/swagger/requests/profile.rb
Original file line number Diff line number Diff line change
Expand Up @@ -74,26 +74,26 @@ class Profile
end

response 400 do
key :description, ' Bad Request'
key :description, 'Bad Request'
schema do
key :required, [:errors]

property :errors do
key :type, :array
items do
key :required, %i[title detail code status]
key :required, %i[title detail code source]
property :title, type: :string,
example: 'Invalid field value',
description: 'Error title'
property :detail, type: :string,
example: 'getDirectDeposit.icn size must be between 17 and 17, getDirectDeposit.icn must match \"^\\d{10}V\\d{6}$\"',
description: 'Description of error (optional)'
property :code, type: :string,
example: 'LIGHTHOUSE_DIRECT_DEPOSIT400',
example: 'cnp.payment.icn.invalid',
description: 'Service name with code appended'
property :status, type: :string,
example: '400',
description: 'http status code'
property :source, type: %i[string object],
example: 'Lighthouse Direct Deposit',
description: 'Service name'
end
end
end
Expand All @@ -107,12 +107,13 @@ class Profile
property :errors do
key :type, :array
items do
key :required, %i[title detail code status]
key :required, %i[title detail code source]
property :title, type: :string, example: 'Invalid token.', description: 'Error title'
property :detail, type: %i[string null], description: 'Description of error (optional)'
property :code, type: :string, example: 'LIGHTHOUSE_DIRECT_DEPOSIT401',
description: 'Service name with code appended'
property :status, type: :string, example: '401', description: 'http status code'
property :code, type: :string, example: 'cnp.payment.invalid.token',
description: 'Error code'
property :source, type: %i[string object], example: 'Lighthouse Direct Deposit',
description: 'Service name'
end
end
end
Expand All @@ -126,31 +127,110 @@ class Profile
property :errors do
key :type, :array
items do
key :required, %i[title detail code status]
key :required, %i[title detail code source]
property :title, type: :string, example: 'Person for ICN not found', description: 'Error title'
property :detail, type: :string, example: 'No data found for ICN', description: 'Description of error (optional)'
property :code, type: :string, example: 'LIGHTHOUSE_DIRECT_DEPOSIT404',
property :code, type: :string, example: 'cnp.payment.icn.not.found',
description: 'Service name with code appended'
property :status, type: :string, example: '404', description: 'http status code'
property :source, type: %i[string object], example: 'Lighthouse Direct Deposit',
description: 'Service name'
end
end
end
end
end

response 502 do
key :description, 'Bad Gateway'
operation :put do
key :produces, ['application/json']
key :consumes, ['application/json']
key :description, 'Update a veterans direct deposit information for compensation and pension benefits'
key :tags, %w[
profile
]

parameter :authorization

parameter do
key :name, :body
key :in, :body
key :description, 'Attributes to update a payment account.'
key :required, true

schema do
property :account_number, type: :string, example: '1234567890'
property :account_type, type: :string, example: 'Checking'
property :routing_number, type: :string, example: '031000503'
end
end

response 200 do
key :description, 'Direct deposit information for a users compensation and pension benefits.'
schema do
key :type, :object
property(:data) do
key :type, :object
property :id, type: :string
property :type, type: :string
property :attributes do
key :type, :object

property :control_information do
key :required, %i[
can_update_direct_deposit
is_corp_available
is_corp_rec_found
has_no_bdn_payments
has_identity
has_index
is_competent
has_mailing_address
has_no_fiduciary_assigned
is_not_deceased
has_payment_address
]
property :can_update_direct_deposit, type: :boolean, example: true, description: 'Must be true to view payment account information'
property :is_corp_available, type: :boolean, example: true, description: ''
property :is_corp_rec_found, type: :boolean, example: true, description: ''
property :has_no_bdn_payments, type: :boolean, example: true, description: ''
property :has_identity, type: :boolean, example: true, description: ''
property :has_index, type: :boolean, example: true, description: ''
property :is_competent, type: :boolean, example: true, description: ''
property :has_mailing_addres, type: :boolean, example: true, description: ''
property :has_no_fiduciary_assigne, type: :boolean, example: true, description: ''
property :is_not_decease, type: :boolean, example: true, description: ''
property :has_payment_address, type: :boolean, example: true, description: ''
end

property :payment_account do
key :required, %i[
name
account_type
account_number
routing_number
]
property :name, type: :string, example: 'WELLS FARGO BANK', description: 'Bank name'
property :account_type, type: :string, enum: %w[CHECKING SAVINGS], example: 'CHECKING', description: 'Bank account type'
property :account_number, type: :string, example: '******7890', description: 'Bank account number (masked)'
property :routing_number, type: :string, example: '*****0503', description: 'Bank routing number (masked)'
end
end
end
end
end

response 400 do
key :description, 'Routing number related to fraud'
schema do
key :required, [:errors]

property :errors do
key :type, :array
items do
key :required, %i[title detail code status]
property :title, type: :string, example: 'Required Backend Connection Error', description: 'Error title'
property :detail, type: :string, example: 'e9e04329-b211-11ed-9449-732003342465', description: 'Description of error (optional)'
property :code, type: :string, example: 'LIGHTHOUSE_DIRECT_DEPOSIT502',
description: 'Service name with code appended'
property :status, type: :string, example: '502', description: 'http status code'
key :required, %i[title detail code source]
property :title, type: :string, example: 'Bad Request', description: 'Error title'
property :detail, type: :string, example: 'Routing number related to potential fraud', description: 'Description of error (optional)'
property :code, type: :string, example: 'cnp.payment.routing.number.fraud.message', description: 'Service name with code appended'
property :source, type: %i[string object], example: 'Lighthouse Direct Deposit', description: 'Service name'
end
end
end
Expand Down
3 changes: 3 additions & 0 deletions lib/common/exceptions/forbidden.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,12 @@ class Forbidden < BaseError
def initialize(options = {})
@detail = options[:detail]
@source = options[:source]
@errors = options[:errors]
end

def errors
return @errors if @errors.present?

Array(SerializableError.new(i18n_data.merge(detail: @detail, source: @source)))
end
end
Expand Down
3 changes: 3 additions & 0 deletions lib/common/exceptions/unauthorized.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,12 @@ class Unauthorized < BaseError
def initialize(options = {})
@detail = options[:detail]
@source = options[:source]
@errors = options[:errors]
end

def errors
return @errors if @errors.present?

Array(SerializableError.new(i18n_data.merge(detail: @detail, source: @source)))
end
end
Expand Down
20 changes: 20 additions & 0 deletions lib/lighthouse/direct_deposit/client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,15 @@ def get_payment_info
handle_error(e, config.settings.client_id, config.base_path)
end

def update_payment_info(params)
body = build_request_body(params)

response = config.put("?icn=#{@icn}", body)
handle_response(response)
rescue Faraday::ClientError, Faraday::ServerError => e
handle_error(e, config.settings.client_id, config.base_path)
end

private

def handle_response(response)
Expand All @@ -38,5 +47,16 @@ def handle_error(error, lighthouse_client_id, base_path)
base_path
)
end

def build_request_body(payment_account)
{
'paymentAccount' =>
{
'accountNumber' => payment_account.account_number,
'accountType' => payment_account.account_type,
'financialInstitutionRoutingNumber' => payment_account.routing_number
}
}.to_json
end
end
end
84 changes: 84 additions & 0 deletions lib/lighthouse/direct_deposit/error_parser.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# frozen_string_literal: true

require 'lighthouse/direct_deposit/error_response'

module Lighthouse
module DirectDeposit
class ErrorParser
def self.parse(response)
status = response[:status]
body = response[:body].with_indifferent_access
detail = parse_detail(body)

errors = [
{
title: parse_title(body),
detail:,
code: parse_code(detail),
source: data_source
}
]

Lighthouse::DirectDeposit::ErrorResponse.new(status, errors)
end

def self.parse_title(body)
body[:error] || body[:title] || status_message_from(body[:status]) || 'Unknown error'
end

def self.parse_detail(body)
return parse_first_error_code(body) if error_codes?(body)

body[:error_description] || body[:error] || body[:detail] || body[:message] || 'Unknown error'
end

def self.parse_code(detail)
return 'cnp.payment.api.rate.limit.exceeded' if detail.include? 'API rate limit exceeded'
return 'cnp.payment.invalid.authentication.creds' if detail.include? 'Invalid authentication credentials'
return 'cnp.payment.invalid.token' if detail.include? 'Invalid token'
return 'cnp.payment.invalid.scopes' if detail.include? 'scopes are not configured'
return 'cnp.payment.icn.not.found' if detail.include? 'No data found for ICN'
return 'cnp.payment.icn.invalid' if detail.include? 'getDirectDeposit.icn size'
return 'cnp.payment.account.number.invalid' if detail.include? 'payment.accountNumber.invalid'
return 'cnp.payment.routing.number.invalid' if detail.include? 'payment.accountRoutingNumber.invalid'
return 'cnp.payment.account.type.invalid' if detail.include? 'payment.accountType.invalid'
return 'cnp.payment.routing.number.invalid.checksum' if detail.include? 'accountRoutingNumber.invalidCheckSum'
return 'cnp.payment.restriction.indicators.present' if detail.include? 'restriction.indicators.present'
return 'cnp.payment.routing.number.fraud' if detail.include? 'Routing number related to potential fraud'
return 'cnp.payment.accounting.number.fraud' if detail.include? 'Flashes on record'
return 'cnp.payment.unspecified.error' if detail.include? 'GUIE50022'

'cnp.payment.generic.error'
end

def self.data_source
'Lighthouse Direct Deposit'
end

def self.parse_first_error_code(body)
body[:error_codes][0][:error_code]
end

def self.parse_first_error_detail(body)
body[:error_codes][0][:detail]
end

def self.error_codes?(body)
body[:error_codes].present?
end

def self.status_message_from(code)
case code
when 401
'Not Authorized'
when 403
'Forbidden'
when 413
'Payload too large'
when 429
'Too many requests'
end
end
end
end
end
Loading

0 comments on commit 2e5fc61

Please sign in to comment.