instant_quote 1.1.8 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2c7f021aa42714cc517f85e9828e2c34a20ba4044c4f024570338117218e68c3
4
- data.tar.gz: ba11fe1ed79bd940c613587f85481583885dd8311dd021a5cb83e3c7d9178281
3
+ metadata.gz: ec2e26453b0ed03e414feceb9e8344ff4d502d5f0962f7b509450608278c9b34
4
+ data.tar.gz: 830639812afaf22a2f67059a6fb757fc6988e99121deca26ede7eba041738a6f
5
5
  SHA512:
6
- metadata.gz: 78426747755312c4618ebc70b42b51f19435b04bbc26d2385881b82a3b9113d26d6fa13af7ad97dcdb83540a20bc0a6113d854a469c501029edbd34a5c4d9c33
7
- data.tar.gz: 76448f87df25db208b0d5c25d7e68df33f0c6c78ff7fcf58fa5cd041b50d21ae378ba5b328023155c5c95609412d4bfec98b352be79576f735f393b101626b20
6
+ metadata.gz: '0822f784c417f72c986a44ecaad692be2a53b227cf09c14a4bb7d7afe5b5db7dd3dd3d070952cc809b38f3c7cb0fe2168e0237629d5a7d70c26b24c71cf2d7f7'
7
+ data.tar.gz: '069cc5595bae6a50601f73d4403be3bcaad28b51033beec08f0488a426a51d3f8911a1828c18b00e69ecfbf3d27f24f2b3e68bd00f05091af9e334f5a6bf9e26'
data/Gemfile CHANGED
@@ -6,6 +6,7 @@ source 'https://rubygems.org'
6
6
  #
7
7
  # gem 'capital_on_tap', path: '../capital-on-tap'
8
8
  # gem 'iwoca', path: '../iwoca'
9
+ # gem 'optimum', path: '../optimum'
9
10
 
10
11
  # Specify your gem's dependencies in instant-quotes.gemspec
11
12
  gemspec
@@ -1,11 +1,12 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- instant_quote (1.1.8)
4
+ instant_quote (1.2.0)
5
5
  activesupport
6
6
  capital_on_tap
7
7
  iwoca
8
8
  money
9
+ optimum
9
10
  phonelib
10
11
 
11
12
  GEM
@@ -28,7 +29,7 @@ GEM
28
29
  multi_json
29
30
  rainbow
30
31
  coderay (1.1.2)
31
- concurrent-ruby (1.1.6)
32
+ concurrent-ruby (1.1.7)
32
33
  crack (0.4.3)
33
34
  safe_yaml (~> 1.0.0)
34
35
  diff-lcs (1.3)
@@ -49,15 +50,21 @@ GEM
49
50
  rainbow
50
51
  jaro_winkler (1.5.4)
51
52
  method_source (1.0.0)
52
- minitest (5.14.1)
53
+ minitest (5.14.2)
53
54
  money (6.13.8)
54
55
  i18n (>= 0.6.4, <= 2)
55
56
  multi_json (1.15.0)
56
57
  multipart-post (2.1.1)
58
+ optimum (1.0.0)
59
+ addressable
60
+ faraday
61
+ faraday_middleware
62
+ multi_json
63
+ rainbow
57
64
  parallel (1.19.1)
58
65
  parser (2.7.0.5)
59
66
  ast (~> 2.4.0)
60
- phonelib (0.6.44)
67
+ phonelib (0.6.45)
61
68
  pry (0.13.0)
62
69
  coderay (~> 1.1)
63
70
  method_source (~> 1.0)
@@ -38,6 +38,7 @@ Gem::Specification.new do |spec|
38
38
  spec.add_dependency 'activesupport'
39
39
  spec.add_dependency 'capital_on_tap'
40
40
  spec.add_dependency 'iwoca'
41
+ spec.add_dependency 'optimum'
41
42
  spec.add_dependency 'money'
42
43
  spec.add_dependency 'phonelib'
43
44
  end
@@ -25,7 +25,7 @@ module InstantQuote
25
25
  },
26
26
  {
27
27
  name: 'monthly_income',
28
- label: 'Applicat monthly income',
28
+ label: 'Applicant monthly income',
29
29
  type: 'number',
30
30
  required: true,
31
31
  if: ->(app) { app.borrower_organisation.sole_trader? }
@@ -0,0 +1,144 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'optimum'
4
+ require 'instant_quote/decision_parsers/optimum'
5
+
6
+ module InstantQuote
7
+ module Adapters
8
+ class Optimum < Adapter
9
+ additional_fields [
10
+ {
11
+ name: 'applicant_phone_number',
12
+ label: 'Applicant mobile phone',
13
+ required: true
14
+ },
15
+ {
16
+ name: 'expected_turnover_next_12_months',
17
+ label: 'Expected turnover next 12 months',
18
+ required: true,
19
+ type: 'money'
20
+ },
21
+ {
22
+ name: 'expected_customers',
23
+ label: 'Number of expected customers with a balance in 6 months',
24
+ required: true,
25
+ type: 'number'
26
+ },
27
+ {
28
+ name: 'industry_sector',
29
+ label: 'Industry sector',
30
+ required: true,
31
+ type: 'select',
32
+ choices: [
33
+ "Advertising / Marketing",
34
+ "Beer / Wine Merchant",
35
+ "Builders Merchant",
36
+ "Clothing & Fashion",
37
+ "Computer Hardware",
38
+ "Computer Software",
39
+ "Construction",
40
+ "Consultancy",
41
+ "Contract Cleaning",
42
+ "Courier",
43
+ "Distribution",
44
+ "Driving Agency",
45
+ "Electrical Contracting",
46
+ "Electrical Wholesaler",
47
+ "Engineering - Heavy",
48
+ "Engineering - Light",
49
+ "Engineering - Precision",
50
+ "Fabrication",
51
+ "Flooring Contractor",
52
+ "Food Manufacture",
53
+ "Franchise",
54
+ "Free Newspaper",
55
+ "Freight Forwarding",
56
+ "Fruit Importer",
57
+ "Furniture Manufacture",
58
+ "Garden Equipment",
59
+ "Haulage",
60
+ "IT Contracting",
61
+ "Jewellery",
62
+ "Maintenance",
63
+ "Manufacturing",
64
+ "Meat Trade",
65
+ "Other Services",
66
+ "Permanent Recruitment",
67
+ "Plant Hire",
68
+ "Plastic Injection Moulding",
69
+ "Plumbers Merchant",
70
+ "Printing",
71
+ "Publishing",
72
+ "Recycling",
73
+ "Security Guards",
74
+ "Shopfitting",
75
+ "Stationery",
76
+ "Steel Stockholding",
77
+ "Telecoms",
78
+ "Temporary Recruitment",
79
+ "Timber Merchant",
80
+ "Tooling Manufacture",
81
+ "Toys & Fancy Goods",
82
+ "Warehousing & Storage",
83
+ "Wholesale"
84
+ ]
85
+ },
86
+ {
87
+ name: 'credit_control',
88
+ label: 'Do you require credit control?',
89
+ required: false,
90
+ choices: {
91
+ '1' => 'Yes',
92
+ '0' => 'No'
93
+ },
94
+ type: 'radio'
95
+ },
96
+ {
97
+ name: 'applicant_consent',
98
+ label: 'Yes, I give my consent for my data to be shared',
99
+ required: true,
100
+ type: 'checkbox'
101
+ }
102
+ ]
103
+
104
+ def get_quote(params)
105
+ response = ::Optimum::Decision.create(params)
106
+
107
+ return response.data[:opportunityId] if response.success?
108
+
109
+ raise_error(response)
110
+ end
111
+
112
+ def get_status(application_id)
113
+ response = ::Optimum::Decision.update(application_id)
114
+
115
+ raise_error(response) unless response.success?
116
+
117
+ decision_parser.new(response.body)
118
+ end
119
+
120
+ # Gets the URL for the given state key.
121
+ def get_link(_)
122
+ ''
123
+ end
124
+
125
+ def get_approval(_)
126
+ true
127
+ end
128
+
129
+ private
130
+
131
+ def decision_parser
132
+ DecisionParsers::Optimum
133
+ end
134
+
135
+ def raise_error(response)
136
+ raise ApiError.new(
137
+ adapter: 'optimum',
138
+ data: response.body,
139
+ error: response.error || 'Unknown API error'
140
+ )
141
+ end
142
+ end
143
+ end
144
+ end
@@ -14,7 +14,7 @@ module InstantQuote
14
14
  @code = params[:code]
15
15
 
16
16
  # Delete the metaData key, because it's irrelevant and has long strings
17
- @data.delete(:metaData) if @data.key?(:metaData)
17
+ @data.delete(:metaData) if @data.respond_to?(:key?) && @data.key?(:metaData)
18
18
  end
19
19
  end
20
20
  end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module InstantQuote
4
+ module ConnectionTranslators
5
+ class Optimum < ConnectionTranslator
6
+ def translate
7
+ {
8
+ companyNumber: company&.company_number.to_s,
9
+ sector: extra_info['industry_sector'],
10
+ turnover: extra_info['expected_turnover_next_12_months'].to_f,
11
+ homeowners: homeowners?,
12
+ yearsTrading: company&.years_in_business,
13
+ expectedCustomers: extra_info['expected_customers'].to_i,
14
+ contactName: application&.primary_user&.name,
15
+ contactNumber: extra_info['applicant_phone_number'],
16
+ contactEmail: application&.primary_user&.email,
17
+ creditControl: extra_info['credit_control'] == '1',
18
+ phoenix: false,
19
+ quotationOnly: false
20
+ }
21
+ end
22
+
23
+ private
24
+
25
+ def company
26
+ application.borrower_organisation
27
+ end
28
+
29
+ def homeowners?
30
+ case company&.directors_home_owning&.name
31
+ when 'Home owner without mortgage', 'Home owner with mortgage'
32
+ true
33
+ else
34
+ false
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ module InstantQuote
4
+ module DecisionParsers
5
+ # Example response:
6
+ #
7
+ # {
8
+ # decision: {
9
+ # decision: "approved",
10
+ # trialPeriodMonths: 4, # trial period length
11
+ # fundingLimit: 745000,
12
+ # annualRenewalFee: 0,
13
+ # badDebtProtection: 0.85,
14
+ # cashAvailableWithinHours: 48,
15
+ # discountFee: 3.35,
16
+ # facility: "Factoring", # Factoring or Invoice Discounting or Confidential Invoice Discounting
17
+ # prepayment: 80,
18
+ # serviceFee: 1.14
19
+ # },
20
+ # opportunityId: "01234567ABCDEFGH", # the Optimum Finance opportunity ID (only supplied for successful applications that are not 'quotation only')
21
+ # companyName: "COMPANY NAME LTD" # company name from the given company number - use this to verify correct company
22
+ # }
23
+
24
+ class Optimum < DecisionParser
25
+ STATUSES = {
26
+ approved: 'approved',
27
+ declined: 'declined',
28
+ referred: 'referred',
29
+ }.freeze
30
+
31
+ LOAN_STATES = {
32
+ started: 'Closed Won'
33
+ }.freeze
34
+
35
+ def initialize(data = {})
36
+ super
37
+ end
38
+
39
+ def pending?
40
+ !STATUSES.value?(status)
41
+ end
42
+
43
+ def approved?
44
+ status == STATUSES[:approved]
45
+ end
46
+
47
+ def declined?
48
+ status == STATUSES[:declined]
49
+ end
50
+
51
+ def manual_review?
52
+ status == STATUSES[:referred]
53
+ end
54
+
55
+ def loan_started?
56
+ data[:status] == LOAN_STATES[:started]
57
+ end
58
+
59
+ def amount
60
+ amount_approved = decision[:fundingLimit] || 0
61
+
62
+ Money.new(amount_approved * 100).format
63
+ end
64
+
65
+ def credit_duration
66
+ 0
67
+ end
68
+
69
+ def credit_interval
70
+ decision[:trialPeriodMonths]
71
+ end
72
+
73
+ def monthly_interest_rate
74
+ 0
75
+ end
76
+
77
+ def status
78
+ decision[:decision]
79
+ end
80
+
81
+ private
82
+
83
+ def decision
84
+ data[:decision] || data[:originalDecision] || {}
85
+ end
86
+ end
87
+ end
88
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module InstantQuote
4
- VERSION = '1.1.8'
4
+ VERSION = '1.2.0'
5
5
  end
@@ -16,31 +16,15 @@ module InstantQuote
16
16
  class SignatureVerifyError < StandardError
17
17
  end
18
18
 
19
- def iwoca_event
20
- # Dynamically calls the following methods:
21
- # * handle_approval_status_changed
22
- # * handle_instruct_payments
23
- # * handle_customer_funded
24
- if iwoca_verify_signature
25
- return send("handle_#{iwoca_event_type}") if respond_to?("handle_#{iwoca_event_type}")
26
-
27
- render json: iwoca_payload, status: 200
28
- else
29
- iwoca_verify_signature_error
30
- end
31
- rescue SignatureVerifyError => e
32
- iwoca_verify_signature_error(e.message)
33
- end
34
-
35
19
  private
36
20
 
37
21
  def iwoca_verify_signature
38
- unless webhook_signature && iwoca_payload && iwoca_signature
22
+ unless webhook_signature && payload && iwoca_signature
39
23
  raise SignatureVerifyError, "#{SIGNATURE_HEADER} header missing"
40
24
  end
41
25
 
42
26
  digest = OpenSSL::Digest.new('sha1')
43
- hmac_digest = OpenSSL::HMAC.digest(digest, webhook_signature, iwoca_payload)
27
+ hmac_digest = OpenSSL::HMAC.digest(digest, webhook_signature, payload)
44
28
  signature = "sha1=#{Base64.strict_encode64(hmac_digest)}"
45
29
 
46
30
  Rack::Utils.secure_compare(signature, iwoca_signature)
@@ -50,8 +34,8 @@ module InstantQuote
50
34
  @webhook_signature ||= ::Iwoca.configuration.webhook_signature
51
35
  end
52
36
 
53
- def iwoca_payload
54
- @iwoca_payload ||= request.body.read
37
+ def payload
38
+ @payload ||= request.body.read
55
39
  end
56
40
 
57
41
  def iwoca_signature
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: instant_quote
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.8
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - rikas
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-08-07 00:00:00.000000000 Z
11
+ date: 2020-09-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -164,6 +164,20 @@ dependencies:
164
164
  - - ">="
165
165
  - !ruby/object:Gem::Version
166
166
  version: '0'
167
+ - !ruby/object:Gem::Dependency
168
+ name: optimum
169
+ requirement: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - ">="
172
+ - !ruby/object:Gem::Version
173
+ version: '0'
174
+ type: :runtime
175
+ prerelease: false
176
+ version_requirements: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - ">="
179
+ - !ruby/object:Gem::Version
180
+ version: '0'
167
181
  - !ruby/object:Gem::Dependency
168
182
  name: money
169
183
  requirement: !ruby/object:Gem::Requirement
@@ -217,15 +231,18 @@ files:
217
231
  - lib/instant_quote/adapters/capital_on_tap.rb
218
232
  - lib/instant_quote/adapters/iwoca.rb
219
233
  - lib/instant_quote/adapters/iwoca_cbils.rb
234
+ - lib/instant_quote/adapters/optimum.rb
220
235
  - lib/instant_quote/api_error.rb
221
236
  - lib/instant_quote/connection_translator.rb
222
237
  - lib/instant_quote/connection_translators/capital_on_tap.rb
223
238
  - lib/instant_quote/connection_translators/iwoca.rb
224
239
  - lib/instant_quote/connection_translators/iwoca_cbils.rb
240
+ - lib/instant_quote/connection_translators/optimum.rb
225
241
  - lib/instant_quote/decision_parser.rb
226
242
  - lib/instant_quote/decision_parsers/capital_on_tap.rb
227
243
  - lib/instant_quote/decision_parsers/iwoca.rb
228
244
  - lib/instant_quote/decision_parsers/iwoca_cbils.rb
245
+ - lib/instant_quote/decision_parsers/optimum.rb
229
246
  - lib/instant_quote/version.rb
230
247
  - lib/instant_quote/webhooks/iwoca.rb
231
248
  homepage: https://finpoint.co.uk