instant_quote 1.5.2 → 1.6.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +14 -0
- data/.travis.yml +3 -0
- data/Gemfile.lock +72 -53
- data/bin/console +6 -0
- data/instant_quote.gemspec +5 -1
- data/lib/instant_quote.rb +2 -0
- data/lib/instant_quote/adapter.rb +36 -15
- data/lib/instant_quote/adapter_finder.rb +9 -3
- data/lib/instant_quote/adapters/capital_on_tap.rb +4 -12
- data/lib/instant_quote/adapters/iwoca.rb +3 -6
- data/lib/instant_quote/adapters/optimum.rb +1 -10
- data/lib/instant_quote/adapters/youlend.rb +83 -0
- data/lib/instant_quote/api_error.rb +2 -0
- data/lib/instant_quote/connection_translator.rb +6 -1
- data/lib/instant_quote/connection_translators/capital_on_tap.rb +6 -2
- data/lib/instant_quote/connection_translators/iwoca.rb +4 -2
- data/lib/instant_quote/connection_translators/optimum.rb +2 -0
- data/lib/instant_quote/connection_translators/youlend.rb +97 -0
- data/lib/instant_quote/decision_parsers/capital_on_tap.rb +3 -3
- data/lib/instant_quote/decision_parsers/iwoca.rb +4 -4
- data/lib/instant_quote/decision_parsers/optimum.rb +16 -13
- data/lib/instant_quote/decision_parsers/youlend.rb +43 -0
- data/lib/instant_quote/version.rb +1 -1
- metadata +50 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c46963e390b87e65c4e598adb90a56dcbc9c8d4e9757816b0734af085cd098f6
|
4
|
+
data.tar.gz: abfa20a481247b1ddc9aa3abbda4de1c858bbf3d50584ef9fe7ac904c1f6a948
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3e42e95e2474ad1e4b04838ace2371e409ce811b4c129fe7cc65375536febd85816338b4b943f5c3133ad19d3643817d4a466fe636c843a1fb36e6542c2c63b5
|
7
|
+
data.tar.gz: 26257fb6401faf86be51b53dc7c0ebacd787f8ebb187cdf330e646318cea2a113dd7131871eac972f28272b9f363dfeb209a6f2f3a330a080c526ac36c1c0c38
|
data/.rubocop.yml
CHANGED
@@ -1,3 +1,14 @@
|
|
1
|
+
AllCops:
|
2
|
+
TargetRubyVersion: 2.6
|
3
|
+
NewCops: enable
|
4
|
+
|
5
|
+
require:
|
6
|
+
- rubocop-rake
|
7
|
+
- rubocop-rspec
|
8
|
+
|
9
|
+
Gemspec/RequiredRubyVersion:
|
10
|
+
Enabled: false
|
11
|
+
|
1
12
|
Layout/LineLength:
|
2
13
|
Max: 100
|
3
14
|
|
@@ -24,3 +35,6 @@ Style/HashTransformKeys:
|
|
24
35
|
|
25
36
|
Style/HashTransformValues:
|
26
37
|
Enabled: true
|
38
|
+
|
39
|
+
RSpec/MultipleExpectations:
|
40
|
+
Enabled: false
|
data/.travis.yml
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,47 +1,50 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
instant_quote (1.
|
4
|
+
instant_quote (1.6.1)
|
5
5
|
activesupport
|
6
6
|
capital_on_tap
|
7
7
|
iwoca
|
8
8
|
money
|
9
9
|
optimum
|
10
10
|
phonelib
|
11
|
+
youlend
|
11
12
|
|
12
13
|
GEM
|
13
14
|
remote: https://rubygems.org/
|
14
15
|
specs:
|
15
|
-
activesupport (6.
|
16
|
+
activesupport (6.1.1)
|
16
17
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
17
|
-
i18n (>=
|
18
|
-
minitest (
|
19
|
-
tzinfo (~>
|
20
|
-
zeitwerk (~> 2.
|
18
|
+
i18n (>= 1.6, < 2)
|
19
|
+
minitest (>= 5.1)
|
20
|
+
tzinfo (~> 2.0)
|
21
|
+
zeitwerk (~> 2.3)
|
21
22
|
addressable (2.7.0)
|
22
23
|
public_suffix (>= 2.0.2, < 5.0)
|
23
|
-
ast (2.4.
|
24
|
-
byebug (11.1.
|
24
|
+
ast (2.4.2)
|
25
|
+
byebug (11.1.3)
|
25
26
|
capital_on_tap (1.0.0)
|
26
27
|
addressable
|
27
28
|
faraday
|
28
29
|
faraday_middleware
|
29
30
|
multi_json
|
30
31
|
rainbow
|
31
|
-
coderay (1.1.
|
32
|
-
concurrent-ruby (1.1.
|
33
|
-
crack (0.4.
|
34
|
-
|
35
|
-
diff-lcs (1.
|
36
|
-
docile (1.3.
|
37
|
-
dotenv (2.7.
|
38
|
-
faraday (1.
|
32
|
+
coderay (1.1.3)
|
33
|
+
concurrent-ruby (1.1.8)
|
34
|
+
crack (0.4.5)
|
35
|
+
rexml
|
36
|
+
diff-lcs (1.4.4)
|
37
|
+
docile (1.3.5)
|
38
|
+
dotenv (2.7.6)
|
39
|
+
faraday (1.3.0)
|
40
|
+
faraday-net_http (~> 1.0)
|
39
41
|
multipart-post (>= 1.2, < 3)
|
40
42
|
ruby2_keywords
|
43
|
+
faraday-net_http (1.0.1)
|
41
44
|
faraday_middleware (1.0.0)
|
42
45
|
faraday (~> 1.0)
|
43
46
|
hashdiff (1.0.1)
|
44
|
-
i18n (1.8.
|
47
|
+
i18n (1.8.8)
|
45
48
|
concurrent-ruby (~> 1.0)
|
46
49
|
iwoca (1.0.0)
|
47
50
|
addressable
|
@@ -49,10 +52,9 @@ GEM
|
|
49
52
|
faraday_middleware
|
50
53
|
multi_json
|
51
54
|
rainbow
|
52
|
-
jaro_winkler (1.5.4)
|
53
55
|
method_source (1.0.0)
|
54
|
-
minitest (5.14.
|
55
|
-
money (6.
|
56
|
+
minitest (5.14.3)
|
57
|
+
money (6.14.0)
|
56
58
|
i18n (>= 0.6.4, <= 2)
|
57
59
|
multi_json (1.15.0)
|
58
60
|
multipart-post (2.1.1)
|
@@ -62,57 +64,72 @@ GEM
|
|
62
64
|
faraday_middleware
|
63
65
|
multi_json
|
64
66
|
rainbow
|
65
|
-
parallel (1.
|
66
|
-
parser (
|
67
|
-
ast (~> 2.4.
|
68
|
-
phonelib (0.6.
|
69
|
-
pry (0.13.
|
67
|
+
parallel (1.20.1)
|
68
|
+
parser (3.0.0.0)
|
69
|
+
ast (~> 2.4.1)
|
70
|
+
phonelib (0.6.48)
|
71
|
+
pry (0.13.1)
|
70
72
|
coderay (~> 1.1)
|
71
73
|
method_source (~> 1.0)
|
72
74
|
pry-byebug (3.9.0)
|
73
75
|
byebug (~> 11.0)
|
74
76
|
pry (~> 0.13.0)
|
75
|
-
public_suffix (4.0.
|
77
|
+
public_suffix (4.0.6)
|
76
78
|
rainbow (3.0.0)
|
77
79
|
rake (10.5.0)
|
80
|
+
regexp_parser (2.0.3)
|
78
81
|
rexml (3.2.4)
|
79
|
-
rspec (3.
|
80
|
-
rspec-core (~> 3.
|
81
|
-
rspec-expectations (~> 3.
|
82
|
-
rspec-mocks (~> 3.
|
83
|
-
rspec-core (3.
|
84
|
-
rspec-support (~> 3.
|
85
|
-
rspec-expectations (3.
|
82
|
+
rspec (3.10.0)
|
83
|
+
rspec-core (~> 3.10.0)
|
84
|
+
rspec-expectations (~> 3.10.0)
|
85
|
+
rspec-mocks (~> 3.10.0)
|
86
|
+
rspec-core (3.10.1)
|
87
|
+
rspec-support (~> 3.10.0)
|
88
|
+
rspec-expectations (3.10.1)
|
86
89
|
diff-lcs (>= 1.2.0, < 2.0)
|
87
|
-
rspec-support (~> 3.
|
88
|
-
rspec-mocks (3.
|
90
|
+
rspec-support (~> 3.10.0)
|
91
|
+
rspec-mocks (3.10.2)
|
89
92
|
diff-lcs (>= 1.2.0, < 2.0)
|
90
|
-
rspec-support (~> 3.
|
91
|
-
rspec-support (3.
|
92
|
-
rubocop (
|
93
|
-
jaro_winkler (~> 1.5.1)
|
93
|
+
rspec-support (~> 3.10.0)
|
94
|
+
rspec-support (3.10.2)
|
95
|
+
rubocop (1.9.1)
|
94
96
|
parallel (~> 1.10)
|
95
|
-
parser (>=
|
97
|
+
parser (>= 3.0.0.0)
|
96
98
|
rainbow (>= 2.2.2, < 4.0)
|
99
|
+
regexp_parser (>= 1.8, < 3.0)
|
97
100
|
rexml
|
101
|
+
rubocop-ast (>= 1.2.0, < 2.0)
|
98
102
|
ruby-progressbar (~> 1.7)
|
99
|
-
unicode-display_width (>= 1.4.0, <
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
103
|
+
unicode-display_width (>= 1.4.0, < 3.0)
|
104
|
+
rubocop-ast (1.4.1)
|
105
|
+
parser (>= 2.7.1.5)
|
106
|
+
rubocop-rake (0.5.1)
|
107
|
+
rubocop
|
108
|
+
rubocop-rspec (2.2.0)
|
109
|
+
rubocop (~> 1.0)
|
110
|
+
rubocop-ast (>= 1.1.0)
|
111
|
+
ruby-progressbar (1.11.0)
|
112
|
+
ruby2_keywords (0.0.4)
|
113
|
+
simplecov (0.21.2)
|
104
114
|
docile (~> 1.1)
|
105
115
|
simplecov-html (~> 0.11)
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
116
|
+
simplecov_json_formatter (~> 0.1)
|
117
|
+
simplecov-html (0.12.3)
|
118
|
+
simplecov_json_formatter (0.1.2)
|
119
|
+
tzinfo (2.0.4)
|
120
|
+
concurrent-ruby (~> 1.0)
|
121
|
+
unicode-display_width (2.0.0)
|
122
|
+
webmock (3.11.2)
|
112
123
|
addressable (>= 2.3.6)
|
113
124
|
crack (>= 0.3.2)
|
114
125
|
hashdiff (>= 0.4.0, < 2.0.0)
|
115
|
-
|
126
|
+
youlend (1.1.3)
|
127
|
+
addressable
|
128
|
+
faraday
|
129
|
+
faraday_middleware
|
130
|
+
multi_json
|
131
|
+
rainbow
|
132
|
+
zeitwerk (2.4.2)
|
116
133
|
|
117
134
|
PLATFORMS
|
118
135
|
ruby
|
@@ -125,8 +142,10 @@ DEPENDENCIES
|
|
125
142
|
rake (~> 10.0)
|
126
143
|
rspec (~> 3.0)
|
127
144
|
rubocop
|
145
|
+
rubocop-rake
|
146
|
+
rubocop-rspec
|
128
147
|
simplecov
|
129
148
|
webmock
|
130
149
|
|
131
150
|
BUNDLED WITH
|
132
|
-
2.
|
151
|
+
2.2.5
|
data/bin/console
CHANGED
@@ -24,6 +24,12 @@ Iwoca.configure do |config|
|
|
24
24
|
config.webhook_signature = ENV['IWOCA_WEBHOOK_SIGNATURE']
|
25
25
|
end
|
26
26
|
|
27
|
+
Youlend.configure do |config|
|
28
|
+
config.client_id = ENV['YOULEND_CLIENT_ID']
|
29
|
+
config.client_secret = ENV['YOULEND_CLIENT_SECRET']
|
30
|
+
config.debug = true
|
31
|
+
end
|
32
|
+
|
27
33
|
send(:include, InstantQuote)
|
28
34
|
|
29
35
|
require 'pry'
|
data/instant_quote.gemspec
CHANGED
@@ -6,6 +6,7 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
6
6
|
require 'instant_quote/version'
|
7
7
|
|
8
8
|
Gem::Specification.new do |spec|
|
9
|
+
spec.required_ruby_version = '>= 2.5.0'
|
9
10
|
spec.name = 'instant_quote'
|
10
11
|
spec.version = InstantQuote::VERSION
|
11
12
|
spec.authors = ['rikas']
|
@@ -32,13 +33,16 @@ Gem::Specification.new do |spec|
|
|
32
33
|
spec.add_development_dependency 'rake', '~> 10.0'
|
33
34
|
spec.add_development_dependency 'rspec', '~> 3.0'
|
34
35
|
spec.add_development_dependency 'rubocop'
|
36
|
+
spec.add_development_dependency 'rubocop-rake'
|
37
|
+
spec.add_development_dependency 'rubocop-rspec'
|
35
38
|
spec.add_development_dependency 'simplecov'
|
36
39
|
spec.add_development_dependency 'webmock'
|
37
40
|
|
38
41
|
spec.add_dependency 'activesupport'
|
39
42
|
spec.add_dependency 'capital_on_tap'
|
40
43
|
spec.add_dependency 'iwoca'
|
41
|
-
spec.add_dependency 'optimum'
|
42
44
|
spec.add_dependency 'money'
|
45
|
+
spec.add_dependency 'optimum'
|
43
46
|
spec.add_dependency 'phonelib'
|
47
|
+
spec.add_dependency 'youlend'
|
44
48
|
end
|
data/lib/instant_quote.rb
CHANGED
@@ -17,6 +17,7 @@ module InstantQuote
|
|
17
17
|
get_link
|
18
18
|
get_quote
|
19
19
|
get_status
|
20
|
+
get_preapproval
|
20
21
|
get_approval
|
21
22
|
additional_fields
|
22
23
|
parse_decision
|
@@ -26,6 +27,7 @@ module InstantQuote
|
|
26
27
|
# InstantQuote.get_link(connection)
|
27
28
|
# InstantQuote.get_quote(connection)
|
28
29
|
# InstantQuote.get_status(connection)
|
30
|
+
# InstantQuote.get_preapproval(connection)
|
29
31
|
# InstantQuote.get_approval(connection)
|
30
32
|
# InstantQuote.additional_fields(connection)
|
31
33
|
# InstantQuote.parse_decision(connection)
|
@@ -2,42 +2,63 @@
|
|
2
2
|
|
3
3
|
module InstantQuote
|
4
4
|
class Adapter
|
5
|
+
# This is one additional field that we always want to ask, since we're using it for phone
|
6
|
+
# validating the applicant.
|
7
|
+
DEFAULT_ADDIONAL_FIELDS = [
|
8
|
+
{
|
9
|
+
name: 'applicant_phone_number',
|
10
|
+
label: 'Applicant mobile phone',
|
11
|
+
required: true
|
12
|
+
}
|
13
|
+
].freeze
|
14
|
+
|
5
15
|
def self.additional_fields(fields = [])
|
6
|
-
return (@additional_fields ||
|
16
|
+
return (@additional_fields || DEFAULT_ADDIONAL_FIELDS) if fields.empty?
|
17
|
+
|
18
|
+
@additional_fields ||= DEFAULT_ADDIONAL_FIELDS
|
19
|
+
@additional_fields += fields
|
20
|
+
end
|
7
21
|
|
8
|
-
|
22
|
+
def self.get_link(connection)
|
23
|
+
new.get_link(connection)
|
9
24
|
end
|
10
25
|
|
11
|
-
def self.
|
12
|
-
new.
|
26
|
+
def self.get_quote(params, connection = nil)
|
27
|
+
new.get_quote(params, connection)
|
13
28
|
end
|
14
29
|
|
15
|
-
def self.
|
16
|
-
new.
|
30
|
+
def self.get_status(application_id, connection = nil)
|
31
|
+
new.get_status(application_id, connection)
|
17
32
|
end
|
18
33
|
|
19
|
-
def self.
|
20
|
-
new.
|
34
|
+
def self.get_preapproval(params, connection = nil)
|
35
|
+
new.get_preapproval(params, connection)
|
21
36
|
end
|
22
37
|
|
23
|
-
def self.get_approval(application_id)
|
24
|
-
new.get_approval(application_id)
|
38
|
+
def self.get_approval(application_id, connection = nil)
|
39
|
+
new.get_approval(application_id, connection)
|
25
40
|
end
|
26
41
|
|
27
|
-
def get_link(_application_id)
|
42
|
+
def get_link(_application_id, _connection = nil)
|
28
43
|
raise NotImplementedError, "You must implement '#{self.class.name}#get_link'"
|
29
44
|
end
|
30
45
|
|
31
|
-
def get_quote(_params)
|
46
|
+
def get_quote(_params, _connection = nil)
|
32
47
|
raise NotImplementedError, "You must implement '#{self.class.name}#get_quote'"
|
33
48
|
end
|
34
49
|
|
35
|
-
def get_status(_application_id)
|
50
|
+
def get_status(_application_id, _connection = nil)
|
36
51
|
raise NotImplementedError, "You must implement '#{self.class.name}#get_status'"
|
37
52
|
end
|
38
53
|
|
39
|
-
|
40
|
-
|
54
|
+
# Default for pre approval will be true, override it in the adapter class if needed.
|
55
|
+
def get_preapproval(_params, _connection = nil)
|
56
|
+
true
|
57
|
+
end
|
58
|
+
|
59
|
+
# Default for approval will be true, override it in the adapter class if needed.
|
60
|
+
def get_approval(_application_id, _connection = nil)
|
61
|
+
true
|
41
62
|
end
|
42
63
|
end
|
43
64
|
end
|
@@ -36,17 +36,23 @@ module InstantQuote
|
|
36
36
|
end
|
37
37
|
|
38
38
|
def get_link
|
39
|
-
adapter.get_link(connection
|
39
|
+
adapter.get_link(connection)
|
40
40
|
end
|
41
41
|
|
42
42
|
def get_quote
|
43
43
|
params = translator.translate(connection)
|
44
44
|
|
45
|
-
adapter.get_quote(params)
|
45
|
+
adapter.get_quote(params, connection)
|
46
46
|
end
|
47
47
|
|
48
48
|
def get_status
|
49
|
-
adapter.get_status(connection.external_id)
|
49
|
+
adapter.get_status(connection.external_id, connection)
|
50
|
+
end
|
51
|
+
|
52
|
+
def get_preapproval
|
53
|
+
params = translator.translate(connection)
|
54
|
+
|
55
|
+
adapter.get_preapproval(params, connection)
|
50
56
|
end
|
51
57
|
|
52
58
|
def get_approval
|
@@ -6,6 +6,7 @@ require 'instant_quote/decision_parsers/capital_on_tap'
|
|
6
6
|
module InstantQuote
|
7
7
|
module Adapters
|
8
8
|
class CapitalOnTap < Adapter
|
9
|
+
# rubocop:disable Layout/LineLength
|
9
10
|
additional_fields [
|
10
11
|
{
|
11
12
|
name: 'date_of_birth',
|
@@ -13,11 +14,6 @@ module InstantQuote
|
|
13
14
|
type: 'date',
|
14
15
|
required: true
|
15
16
|
},
|
16
|
-
{
|
17
|
-
name: 'applicant_phone_number',
|
18
|
-
label: 'Applicant mobile phone',
|
19
|
-
required: true
|
20
|
-
},
|
21
17
|
{
|
22
18
|
name: 'monthly_income',
|
23
19
|
label: 'Applicant monthly income',
|
@@ -71,6 +67,7 @@ module InstantQuote
|
|
71
67
|
type: 'checkbox'
|
72
68
|
}
|
73
69
|
]
|
70
|
+
# rubocop:enable Layout/LineLength
|
74
71
|
|
75
72
|
# Creates the quote with the information from instant connection
|
76
73
|
def get_quote(params)
|
@@ -79,7 +76,7 @@ module InstantQuote
|
|
79
76
|
# CoT responses can be 200 OK but still have an error!
|
80
77
|
raise_error(response) unless response.success? && !response.error
|
81
78
|
|
82
|
-
response.body
|
79
|
+
response.body[:result]
|
83
80
|
end
|
84
81
|
|
85
82
|
# Get the status for a given application ID
|
@@ -106,15 +103,10 @@ module InstantQuote
|
|
106
103
|
end
|
107
104
|
|
108
105
|
# The link is always the same for Capital On Tap
|
109
|
-
def get_link(
|
106
|
+
def get_link(_connection)
|
110
107
|
'https://account.capitalontap.com/apps/portal/login/'
|
111
108
|
end
|
112
109
|
|
113
|
-
# Capital On Tap doesn't need an approval request
|
114
|
-
def get_approval(_application_id)
|
115
|
-
true
|
116
|
-
end
|
117
|
-
|
118
110
|
private
|
119
111
|
|
120
112
|
def raise_error(response)
|
@@ -44,11 +44,6 @@ module InstantQuote
|
|
44
44
|
label: 'Country',
|
45
45
|
type: 'country'
|
46
46
|
},
|
47
|
-
{
|
48
|
-
name: 'applicant_phone_number',
|
49
|
-
label: 'Applicant mobile number',
|
50
|
-
required: true
|
51
|
-
},
|
52
47
|
{
|
53
48
|
name: 'applicant_consent',
|
54
49
|
label: "I agree to iwoca running soft credit checks on my personal information with \
|
@@ -81,7 +76,9 @@ module InstantQuote
|
|
81
76
|
end
|
82
77
|
|
83
78
|
# Gets the URL for the given state key.
|
84
|
-
def get_link(
|
79
|
+
def get_link(connection)
|
80
|
+
state_key = connection.external_id
|
81
|
+
|
85
82
|
response = ::Iwoca::Quote.login_link(state_key)
|
86
83
|
|
87
84
|
return response.data[:login_link] if response.success?
|
@@ -7,11 +7,6 @@ module InstantQuote
|
|
7
7
|
module Adapters
|
8
8
|
class Optimum < Adapter
|
9
9
|
additional_fields [
|
10
|
-
{
|
11
|
-
name: 'applicant_phone_number',
|
12
|
-
label: 'Applicant mobile phone',
|
13
|
-
required: true
|
14
|
-
},
|
15
10
|
{
|
16
11
|
name: 'expected_turnover_next_12_months',
|
17
12
|
label: 'Expected turnover next 12 months',
|
@@ -118,14 +113,10 @@ module InstantQuote
|
|
118
113
|
end
|
119
114
|
|
120
115
|
# Gets the URL for the given state key.
|
121
|
-
def get_link(
|
116
|
+
def get_link(_connection)
|
122
117
|
''
|
123
118
|
end
|
124
119
|
|
125
|
-
def get_approval(_)
|
126
|
-
true
|
127
|
-
end
|
128
|
-
|
129
120
|
private
|
130
121
|
|
131
122
|
def decision_parser
|
@@ -0,0 +1,83 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'youlend'
|
4
|
+
require 'instant_quote/decision_parsers/youlend'
|
5
|
+
|
6
|
+
module InstantQuote
|
7
|
+
module Adapters
|
8
|
+
class Youlend < Adapter
|
9
|
+
additional_fields [
|
10
|
+
{
|
11
|
+
name: 'monthly_card_revenue',
|
12
|
+
label: 'Avg. monthly revenue from card payments',
|
13
|
+
required: true,
|
14
|
+
type: 'money'
|
15
|
+
},
|
16
|
+
{
|
17
|
+
name: 'monthly_revenue_from_invoices',
|
18
|
+
label: 'Avg. monthly revenue from invoices',
|
19
|
+
required: true,
|
20
|
+
type: 'money'
|
21
|
+
},
|
22
|
+
{
|
23
|
+
name: 'applicant_consent',
|
24
|
+
label: 'Yes, I give my consent for my data to be shared',
|
25
|
+
required: true,
|
26
|
+
type: 'checkbox'
|
27
|
+
}
|
28
|
+
]
|
29
|
+
|
30
|
+
# For Youlend we first need to do the pre_qualification and then, if we get a positive
|
31
|
+
# response we can create the lead. We need to call both endpoints here, so it might take a
|
32
|
+
# while.
|
33
|
+
def get_quote(params, connection)
|
34
|
+
response = ::Youlend::Lead.create(params)
|
35
|
+
|
36
|
+
if response.success?
|
37
|
+
connection.update(link: response.data[:signUpURL])
|
38
|
+
|
39
|
+
return response.data[:leadId]
|
40
|
+
end
|
41
|
+
|
42
|
+
raise_error(response)
|
43
|
+
end
|
44
|
+
|
45
|
+
def get_status(_lead_id, connection)
|
46
|
+
DecisionParsers::Youlend.new(connection.approval_decision_info)
|
47
|
+
end
|
48
|
+
|
49
|
+
# Gets the URL for the given state key.
|
50
|
+
def get_link(connection)
|
51
|
+
connection.link
|
52
|
+
end
|
53
|
+
|
54
|
+
# Checks if the application is eligible for a loan. If it is we need to update the
|
55
|
+
# connection's approval_decision_info right away, since it's the only place where we get it.
|
56
|
+
def get_preapproval(params, connection)
|
57
|
+
# The preapproval endpoint doesn't work with all params, we need to remove some
|
58
|
+
params.delete(:companyName)
|
59
|
+
params.delete(:monthsTrading)
|
60
|
+
params.delete(:keyContactName)
|
61
|
+
params.delete(:registeredAddress)
|
62
|
+
params.delete(:thirdPartyLeadId)
|
63
|
+
params.delete(:contactPhoneNumber)
|
64
|
+
|
65
|
+
response = ::Youlend::Prequalification.verify(params)
|
66
|
+
|
67
|
+
connection.update(approval_decision_info: response.data) if response.success?
|
68
|
+
|
69
|
+
response
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def raise_error(response)
|
75
|
+
raise ApiError.new(
|
76
|
+
adapter: 'youlend',
|
77
|
+
data: response.body,
|
78
|
+
error: response.error || '[Youlend] API error'
|
79
|
+
)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -1,6 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'active_support/core_ext/hash/indifferent_access'
|
4
|
+
|
3
5
|
module InstantQuote
|
6
|
+
# Inherit from this class to create the translation from Finpoint's app to the JSON that needs to
|
7
|
+
# be sent to the service you're integrating. You'll have access to `application` and `extra_info`
|
8
|
+
# from the application's record.
|
4
9
|
class ConnectionTranslator
|
5
10
|
attr_accessor :application, :extra_info
|
6
11
|
|
@@ -10,7 +15,7 @@ module InstantQuote
|
|
10
15
|
|
11
16
|
def initialize(connection)
|
12
17
|
@application = connection.finance_application
|
13
|
-
@extra_info = connection.extra_info
|
18
|
+
@extra_info = ActiveSupport::HashWithIndifferentAccess.new(connection.extra_info)
|
14
19
|
end
|
15
20
|
|
16
21
|
def translate
|
@@ -117,10 +117,14 @@ module InstantQuote
|
|
117
117
|
|
118
118
|
def question_from_extra_info(name, question_type)
|
119
119
|
field = Adapters::CapitalOnTap.additional_fields.find do |f|
|
120
|
-
f[:name] == name
|
120
|
+
f[:name].to_sym == name.to_sym
|
121
121
|
end
|
122
122
|
|
123
|
-
{
|
123
|
+
{
|
124
|
+
question: field[:label],
|
125
|
+
answer: extra_info[field[:name]],
|
126
|
+
questionType: question_type
|
127
|
+
}
|
124
128
|
end
|
125
129
|
end
|
126
130
|
end
|
@@ -78,7 +78,7 @@ module InstantQuote
|
|
78
78
|
application.borrower_organisation
|
79
79
|
end
|
80
80
|
|
81
|
-
# rubocop:disable
|
81
|
+
# rubocop:disable Naming/VariableNumber
|
82
82
|
def company_address
|
83
83
|
{
|
84
84
|
postcode: company.address.postcode,
|
@@ -88,7 +88,7 @@ module InstantQuote
|
|
88
88
|
country: company.address.country
|
89
89
|
}
|
90
90
|
end
|
91
|
-
# rubocop:enable
|
91
|
+
# rubocop:enable Naming/VariableNumber
|
92
92
|
|
93
93
|
def person_information
|
94
94
|
{
|
@@ -123,6 +123,7 @@ module InstantQuote
|
|
123
123
|
}
|
124
124
|
end
|
125
125
|
|
126
|
+
# rubocop:disable Naming/VariableNumber
|
126
127
|
def person_residential_address
|
127
128
|
{
|
128
129
|
uid: SecureRandom.uuid,
|
@@ -135,6 +136,7 @@ module InstantQuote
|
|
135
136
|
residential_status: home_owning_translated
|
136
137
|
}
|
137
138
|
end
|
139
|
+
# rubocop:enable Naming/VariableNumber
|
138
140
|
|
139
141
|
def home_owning_translated
|
140
142
|
case company.directors_home_owning.name
|
@@ -3,6 +3,7 @@
|
|
3
3
|
module InstantQuote
|
4
4
|
module ConnectionTranslators
|
5
5
|
class Optimum < ConnectionTranslator
|
6
|
+
# rubocop:disable Metrics/AbcSize
|
6
7
|
def translate
|
7
8
|
{
|
8
9
|
companyNumber: company&.company_number.to_s,
|
@@ -19,6 +20,7 @@ module InstantQuote
|
|
19
20
|
quotationOnly: false
|
20
21
|
}
|
21
22
|
end
|
23
|
+
# rubocop:enable Metrics/AbcSize
|
22
24
|
|
23
25
|
private
|
24
26
|
|
@@ -0,0 +1,97 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module InstantQuote
|
4
|
+
module ConnectionTranslators
|
5
|
+
CURRENCY = 'GBP'
|
6
|
+
|
7
|
+
class Youlend < ConnectionTranslator
|
8
|
+
# rubocop:disable Metrics/AbcSize
|
9
|
+
def translate
|
10
|
+
{
|
11
|
+
companyName: company.name,
|
12
|
+
companyType: company_type_translated,
|
13
|
+
monthsTrading: company.total_months_in_business,
|
14
|
+
keyContactName: primary_user_name,
|
15
|
+
thirdPartyLeadId: application.code,
|
16
|
+
financialData: financial_data,
|
17
|
+
countryISOCode: 'GBR',
|
18
|
+
loanCurrencyISOCode: CURRENCY,
|
19
|
+
thirdPartyMerchantId: company.id,
|
20
|
+
thirdPartyCustomerId: application&.primary_user&.id,
|
21
|
+
registeredAddress: company_address,
|
22
|
+
contactPhoneNumber: extra_info[:applicant_phone_number],
|
23
|
+
monthlyCardRevenue: extra_info[:monthly_card_revenue].to_i,
|
24
|
+
contactEmailAddress: application&.primary_user&.email # optional
|
25
|
+
}
|
26
|
+
end
|
27
|
+
# rubocop:enable Metrics/AbcSize
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def financial_data
|
32
|
+
{
|
33
|
+
monthlyCardRevenue: extra_info[:monthly_card_revenue].to_i,
|
34
|
+
monthlyRevenueFromInvoices: extra_info[:monthly_revenue_from_invoices].to_i,
|
35
|
+
paymentData: payment_data
|
36
|
+
}
|
37
|
+
end
|
38
|
+
|
39
|
+
def payment_data
|
40
|
+
(1..12).to_a.map do |month|
|
41
|
+
formatted_month = format('%02<month>d', month: month)
|
42
|
+
|
43
|
+
{
|
44
|
+
paymentDate: "#{Date.today.year - 1}-#{formatted_month}-01",
|
45
|
+
amount: total_amount,
|
46
|
+
currencyISOCode: CURRENCY
|
47
|
+
}
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def total_amount
|
52
|
+
@total_amount ||= begin
|
53
|
+
card = extra_info[:monthly_card_revenue].to_i || 0
|
54
|
+
invoices = extra_info[:monthly_revenue_from_invoices].to_i || 0
|
55
|
+
|
56
|
+
card + invoices
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def company_address
|
61
|
+
address = company.address
|
62
|
+
|
63
|
+
{
|
64
|
+
line1: address.line_1,
|
65
|
+
line2: address.line_2,
|
66
|
+
city: address.town,
|
67
|
+
region: address.town,
|
68
|
+
areaCode: address.postcode,
|
69
|
+
country: address.country
|
70
|
+
}
|
71
|
+
end
|
72
|
+
|
73
|
+
def company
|
74
|
+
@company ||= application.borrower_organisation
|
75
|
+
end
|
76
|
+
|
77
|
+
def primary_user_name
|
78
|
+
application.primary_user.name
|
79
|
+
end
|
80
|
+
|
81
|
+
def company_type_translated
|
82
|
+
case company.company_type.name
|
83
|
+
when /LTD/
|
84
|
+
'ltd'
|
85
|
+
when /LLP/
|
86
|
+
'llp'
|
87
|
+
when /PLC/
|
88
|
+
'plc'
|
89
|
+
when 'Sole Trader'
|
90
|
+
'soleTrader'
|
91
|
+
else
|
92
|
+
'other'
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -42,7 +42,7 @@ module InstantQuote
|
|
42
42
|
end
|
43
43
|
|
44
44
|
def status
|
45
|
-
credit_decision
|
45
|
+
credit_decision[:status] || STATUSES[:could_not_submit]
|
46
46
|
end
|
47
47
|
|
48
48
|
# The applicationStage can be one of the following:
|
@@ -52,13 +52,13 @@ module InstantQuote
|
|
52
52
|
# ApprovedCredit, ApprovedPrepaid, DdSetup, AddOnSelection, DoneHavFailed,
|
53
53
|
# DoneIdVerificationRequired, Done, Portal
|
54
54
|
def stage
|
55
|
-
data
|
55
|
+
data[:applicationStage] || {}
|
56
56
|
end
|
57
57
|
|
58
58
|
private
|
59
59
|
|
60
60
|
def credit_decision
|
61
|
-
data
|
61
|
+
data[:creditDecision] || {}
|
62
62
|
end
|
63
63
|
end
|
64
64
|
end
|
@@ -91,21 +91,21 @@ module InstantQuote
|
|
91
91
|
|
92
92
|
return 0 unless rates&.any?
|
93
93
|
|
94
|
-
rate_info = rates.first
|
94
|
+
rate_info = rates.first[:rates]&.first
|
95
95
|
|
96
96
|
return 0 unless rate_info
|
97
97
|
|
98
|
-
rate_info
|
98
|
+
rate_info[:rate]
|
99
99
|
end
|
100
100
|
|
101
101
|
def status
|
102
|
-
status_hash
|
102
|
+
status_hash[:status] || STATUSES[:not_defined]
|
103
103
|
end
|
104
104
|
|
105
105
|
private
|
106
106
|
|
107
107
|
def status_hash
|
108
|
-
data
|
108
|
+
data[:approval_status] || data[:latest_approval_request] || {}
|
109
109
|
end
|
110
110
|
end
|
111
111
|
end
|
@@ -6,26 +6,29 @@ module InstantQuote
|
|
6
6
|
#
|
7
7
|
# {
|
8
8
|
# decision: {
|
9
|
-
#
|
10
|
-
#
|
11
|
-
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
17
|
-
#
|
18
|
-
#
|
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 / Invoice Discounting / Confidential Invoice Discounting
|
17
|
+
# prepayment: 80,
|
18
|
+
# serviceFee: 1.14
|
19
19
|
# },
|
20
|
-
# opportunityId: "01234567ABCDEFGH", # the Optimum Finance opportunity ID (only supplied for
|
21
|
-
#
|
20
|
+
# opportunityId: "01234567ABCDEFGH", # the Optimum Finance opportunity ID (only supplied for
|
21
|
+
# successful applications that are not 'quotation only')
|
22
|
+
#
|
23
|
+
# companyName: "COMPANY NAME LTD" # company name from the given company number - use this to
|
24
|
+
# verify correct company
|
22
25
|
# }
|
23
26
|
|
24
27
|
class Optimum < DecisionParser
|
25
28
|
STATUSES = {
|
26
29
|
approved: 'approved',
|
27
30
|
declined: 'declined',
|
28
|
-
referred: 'referred'
|
31
|
+
referred: 'referred'
|
29
32
|
}.freeze
|
30
33
|
|
31
34
|
LOAN_STATES = {
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module InstantQuote
|
4
|
+
module DecisionParsers
|
5
|
+
# Example response:
|
6
|
+
#
|
7
|
+
# {
|
8
|
+
# thirdPartyMerchantId: "b97b0834-74e5-4da6-a3df-c8dbe017fe09",
|
9
|
+
# mid: nil,
|
10
|
+
# companyName: "Test Company",
|
11
|
+
# overallCreditRiskScore: 3.5,
|
12
|
+
# overrideCreditRiskScore: 1.0,
|
13
|
+
# loanOptions: [
|
14
|
+
# { fundedAmount: 0.0, fee: 0.0, loanAmount: 0.0, sweep: 0.0, currencyISOCode: "GBP" },
|
15
|
+
# { fundedAmount: 0.0, fee: 0.0, loanAmount: 0.0, sweep: 0.0, currencyISOCode: "GBP" },
|
16
|
+
# { fundedAmount: 0.0, fee: 0.0, loanAmount: 0.0, sweep: 0.0, currencyISOCode: "GBP" }
|
17
|
+
# ]
|
18
|
+
# }
|
19
|
+
class Youlend < DecisionParser
|
20
|
+
def approved?
|
21
|
+
status_hash[:loanOptions].any?
|
22
|
+
end
|
23
|
+
|
24
|
+
def declined?
|
25
|
+
status_hash[:loanOptions].empty?
|
26
|
+
end
|
27
|
+
|
28
|
+
def amount
|
29
|
+
option = status_hash[:loanOptions].first
|
30
|
+
|
31
|
+
amount_approved = option[:fundedAmount].to_f + option[:fee].to_f
|
32
|
+
|
33
|
+
Money.new(amount_approved * 100).format
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def status_hash
|
39
|
+
data.presence || { loanOptions: [] }
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
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.
|
4
|
+
version: 1.6.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- rikas
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-02-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -94,6 +94,34 @@ dependencies:
|
|
94
94
|
- - ">="
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rubocop-rake
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: rubocop-rspec
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
97
125
|
- !ruby/object:Gem::Dependency
|
98
126
|
name: simplecov
|
99
127
|
requirement: !ruby/object:Gem::Requirement
|
@@ -165,7 +193,7 @@ dependencies:
|
|
165
193
|
- !ruby/object:Gem::Version
|
166
194
|
version: '0'
|
167
195
|
- !ruby/object:Gem::Dependency
|
168
|
-
name:
|
196
|
+
name: money
|
169
197
|
requirement: !ruby/object:Gem::Requirement
|
170
198
|
requirements:
|
171
199
|
- - ">="
|
@@ -179,7 +207,7 @@ dependencies:
|
|
179
207
|
- !ruby/object:Gem::Version
|
180
208
|
version: '0'
|
181
209
|
- !ruby/object:Gem::Dependency
|
182
|
-
name:
|
210
|
+
name: optimum
|
183
211
|
requirement: !ruby/object:Gem::Requirement
|
184
212
|
requirements:
|
185
213
|
- - ">="
|
@@ -206,6 +234,20 @@ dependencies:
|
|
206
234
|
- - ">="
|
207
235
|
- !ruby/object:Gem::Version
|
208
236
|
version: '0'
|
237
|
+
- !ruby/object:Gem::Dependency
|
238
|
+
name: youlend
|
239
|
+
requirement: !ruby/object:Gem::Requirement
|
240
|
+
requirements:
|
241
|
+
- - ">="
|
242
|
+
- !ruby/object:Gem::Version
|
243
|
+
version: '0'
|
244
|
+
type: :runtime
|
245
|
+
prerelease: false
|
246
|
+
version_requirements: !ruby/object:Gem::Requirement
|
247
|
+
requirements:
|
248
|
+
- - ">="
|
249
|
+
- !ruby/object:Gem::Version
|
250
|
+
version: '0'
|
209
251
|
description: Instant quote providers gem.
|
210
252
|
email:
|
211
253
|
- oterosantos@gmail.com
|
@@ -232,17 +274,20 @@ files:
|
|
232
274
|
- lib/instant_quote/adapters/iwoca.rb
|
233
275
|
- lib/instant_quote/adapters/iwoca_cbils.rb
|
234
276
|
- lib/instant_quote/adapters/optimum.rb
|
277
|
+
- lib/instant_quote/adapters/youlend.rb
|
235
278
|
- lib/instant_quote/api_error.rb
|
236
279
|
- lib/instant_quote/connection_translator.rb
|
237
280
|
- lib/instant_quote/connection_translators/capital_on_tap.rb
|
238
281
|
- lib/instant_quote/connection_translators/iwoca.rb
|
239
282
|
- lib/instant_quote/connection_translators/iwoca_cbils.rb
|
240
283
|
- lib/instant_quote/connection_translators/optimum.rb
|
284
|
+
- lib/instant_quote/connection_translators/youlend.rb
|
241
285
|
- lib/instant_quote/decision_parser.rb
|
242
286
|
- lib/instant_quote/decision_parsers/capital_on_tap.rb
|
243
287
|
- lib/instant_quote/decision_parsers/iwoca.rb
|
244
288
|
- lib/instant_quote/decision_parsers/iwoca_cbils.rb
|
245
289
|
- lib/instant_quote/decision_parsers/optimum.rb
|
290
|
+
- lib/instant_quote/decision_parsers/youlend.rb
|
246
291
|
- lib/instant_quote/version.rb
|
247
292
|
- lib/instant_quote/webhooks/iwoca.rb
|
248
293
|
homepage: https://finpoint.co.uk
|
@@ -257,7 +302,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
257
302
|
requirements:
|
258
303
|
- - ">="
|
259
304
|
- !ruby/object:Gem::Version
|
260
|
-
version:
|
305
|
+
version: 2.5.0
|
261
306
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
262
307
|
requirements:
|
263
308
|
- - ">="
|