instant_quote 1.0.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,112 @@
1
+ # frozen_string_literal: true
2
+
3
+ module InstantQuote
4
+ module DecisionParsers
5
+ # Example response:
6
+ #
7
+ # { data:
8
+ # {
9
+ # balance: {
10
+ # principal: 0.0, interest: 0.0, product_fees: 0.0, other_fees: 0.0, total: 0.0
11
+ # },
12
+ # latest_approval_request: { status: "no_decision_possible" },
13
+ # approval_requirements: [
14
+ # { type: "cc", status: "required" },
15
+ # { type: "bus_cc", status: "required" },
16
+ # { type: "dir_guarantor", status: "required" },
17
+ # { type: "enhanced_security_check", status: "pending" }
18
+ # ],
19
+ # funding_requirements: [
20
+ # { status: "required", type: "fraud" },
21
+ # { type: "docs", status: "required" },
22
+ # { type: "enhanced_security_check", status: "pending" }
23
+ # ]
24
+ # }
25
+ # }
26
+ class Iwoca < DecisionParser
27
+ STATUSES = {
28
+ pending: 'pending',
29
+ approved: 'approved',
30
+ declined: 'declined',
31
+ no_decision: 'no_decision_possible',
32
+ not_defined: 'not_defined'
33
+ }.freeze
34
+
35
+ LOAN_STATES = {
36
+ ongoing: 'ongoing',
37
+ ended: 'ended',
38
+ topup: 'topup',
39
+ pending: 'pending',
40
+ overdue: 'overdue',
41
+ duetoday: 'duetoday'
42
+ }.freeze
43
+
44
+ # iwoca sends an extra "data" key, that is removed here, to avoid having to do data[:data]
45
+ # because it's silly :)
46
+ def initialize(data = {})
47
+ @data = super
48
+ @data = @data[:data] || {}
49
+ end
50
+
51
+ def pending?
52
+ status == STATUSES[:not_defined]
53
+ end
54
+
55
+ def approved?
56
+ status == STATUSES[:approved]
57
+ end
58
+
59
+ def declined?
60
+ status == STATUSES[:declined]
61
+ end
62
+
63
+ def manual_review?
64
+ status == STATUSES[:pending]
65
+ end
66
+
67
+ def no_decision_possible?
68
+ status == STATUSES[:no_decision]
69
+ end
70
+
71
+ def loan_started?
72
+ data[:loan_status] == LOAN_STATES[:ongoing]
73
+ end
74
+
75
+ def amount
76
+ amount_approved = status_hash.dig(:approved, :max_credit) || 0
77
+
78
+ Money.new(amount_approved * 100).format
79
+ end
80
+
81
+ def credit_duration
82
+ status_hash.dig(:approved, :duration)
83
+ end
84
+
85
+ def credit_interval
86
+ status_hash.dig(:approved, :interval)
87
+ end
88
+
89
+ def monthly_interest_rate
90
+ rates = status_hash.dig(:approved, :rate_structures)
91
+
92
+ return 0 unless rates&.any?
93
+
94
+ rate_info = rates.first.dig(:rates)&.first
95
+
96
+ return 0 unless rate_info
97
+
98
+ rate_info.dig(:rate)
99
+ end
100
+
101
+ def status
102
+ status_hash.dig(:status) || STATUSES[:not_defined]
103
+ end
104
+
105
+ private
106
+
107
+ def status_hash
108
+ data.dig(:approval_status) || data.dig(:latest_approval_request) || {}
109
+ end
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module InstantQuote
4
+ VERSION = '1.0.5'
5
+ end
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/concern'
4
+
5
+ module InstantQuote
6
+ module Webhooks
7
+ # To be included in a Rails controller. More information about iwoca webhooks:
8
+ # https://developer.iwoca.co.uk/lending-api-v1/partner-webhook-url/partner-webhook-for-iwoca-events
9
+ module Iwoca
10
+ extend ActiveSupport::Concern
11
+
12
+ SIGNATURE_HEADER = 'X-IW-Signature' # HMAC hex digest of the payload.
13
+ EVENT_TYPE_HEADER = 'X-IW-Event-Type' # The event type sent by iwoca.
14
+ EVENT_ID_HEADER = 'X-IW-Event-ID' # The event id sent by iwoca.
15
+
16
+ class SignatureVerifyError < StandardError
17
+ end
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
+ private
36
+
37
+ def iwoca_verify_signature
38
+ unless webhook_signature && iwoca_payload && iwoca_signature
39
+ raise SignatureVerifyError, "#{SIGNATURE_HEADER} header missing"
40
+ end
41
+
42
+ digest = OpenSSL::Digest.new('sha1')
43
+ hmac_digest = OpenSSL::HMAC.digest(digest, webhook_signature, iwoca_payload)
44
+ signature = "sha1=#{Base64.strict_encode64(hmac_digest)}"
45
+
46
+ Rack::Utils.secure_compare(signature, iwoca_signature)
47
+ end
48
+
49
+ def webhook_signature
50
+ @webhook_signature ||= ::Iwoca.configuration.webhook_signature
51
+ end
52
+
53
+ def iwoca_payload
54
+ @iwoca_payload ||= request.body.read
55
+ end
56
+
57
+ def iwoca_signature
58
+ request.headers[SIGNATURE_HEADER]
59
+ end
60
+
61
+ def iwoca_event_type
62
+ # Allowed Values: approval_status_changed, instruct_payments, customer_funded
63
+ request.headers[EVENT_TYPE_HEADER]
64
+ end
65
+
66
+ def iwoca_event_id
67
+ request.headers[EVENT_ID_HEADER]
68
+ end
69
+
70
+ def iwoca_verify_signature_error(message = "Wrong value in #{SIGNATURE_HEADER}")
71
+ return send(:iwoca_verification_error) if respond_to?(:iwoca_verification_error)
72
+
73
+ render json: { error: message }, status: :unauthorized
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'instant_quote/version'
4
+ require 'instant_quote/adapter'
5
+ require 'instant_quote/decision_parser'
6
+ require 'instant_quote/connection_translator'
7
+ require 'instant_quote/api_error'
8
+ require 'instant_quote/adapter_finder'
9
+
10
+ module InstantQuote
11
+ module_function
12
+
13
+ class AdapterError < StandardError
14
+ end
15
+
16
+ AVAILABLE_METHODS = %i[
17
+ get_link
18
+ get_quote
19
+ get_status
20
+ get_approval
21
+ additional_fields
22
+ parse_decision
23
+ ].freeze
24
+
25
+ # Creates the class methods to be used in the outside world:
26
+ # InstantQuote.get_link(connection)
27
+ # InstantQuote.get_quote(connection)
28
+ # InstantQuote.get_status(connection)
29
+ # InstantQuote.get_approval(connection)
30
+ # InstantQuote.additional_fields(connection)
31
+ # InstantQuote.parse_decision(connection)
32
+ AVAILABLE_METHODS.each do |method|
33
+ define_method(method) do |connection|
34
+ AdapterFinder.new(connection).send(method)
35
+ end
36
+ end
37
+ end
metadata ADDED
@@ -0,0 +1,223 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: instant_quote
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.5
5
+ platform: ruby
6
+ authors:
7
+ - rikas
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2020-03-02 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: dotenv
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: pry-byebug
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '10.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '10.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '3.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '3.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rubocop
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: simplecov
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: activesupport
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: capital_on_tap
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :runtime
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: iwoca
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ type: :runtime
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ - !ruby/object:Gem::Dependency
154
+ name: money
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - ">="
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ type: :runtime
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - ">="
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
167
+ description: Instant quote providers gem.
168
+ email:
169
+ - oterosantos@gmail.com
170
+ executables: []
171
+ extensions: []
172
+ extra_rdoc_files: []
173
+ files:
174
+ - ".gitignore"
175
+ - ".rspec"
176
+ - ".rubocop.yml"
177
+ - ".ruby-version"
178
+ - ".travis.yml"
179
+ - Gemfile
180
+ - Gemfile.lock
181
+ - README.md
182
+ - Rakefile
183
+ - bin/console
184
+ - bin/setup
185
+ - instant_quote.gemspec
186
+ - lib/instant_quote.rb
187
+ - lib/instant_quote/adapter.rb
188
+ - lib/instant_quote/adapter_finder.rb
189
+ - lib/instant_quote/adapters/capital_on_tap.rb
190
+ - lib/instant_quote/adapters/iwoca.rb
191
+ - lib/instant_quote/api_error.rb
192
+ - lib/instant_quote/connection_translator.rb
193
+ - lib/instant_quote/connection_translators/capital_on_tap.rb
194
+ - lib/instant_quote/connection_translators/iwoca.rb
195
+ - lib/instant_quote/decision_parser.rb
196
+ - lib/instant_quote/decision_parsers/capital_on_tap.rb
197
+ - lib/instant_quote/decision_parsers/iwoca.rb
198
+ - lib/instant_quote/version.rb
199
+ - lib/instant_quote/webhooks/iwoca.rb
200
+ homepage: https://finpoint.co.uk
201
+ licenses: []
202
+ metadata:
203
+ homepage_uri: https://finpoint.co.uk
204
+ post_install_message:
205
+ rdoc_options: []
206
+ require_paths:
207
+ - lib
208
+ required_ruby_version: !ruby/object:Gem::Requirement
209
+ requirements:
210
+ - - ">="
211
+ - !ruby/object:Gem::Version
212
+ version: '0'
213
+ required_rubygems_version: !ruby/object:Gem::Requirement
214
+ requirements:
215
+ - - ">="
216
+ - !ruby/object:Gem::Version
217
+ version: '0'
218
+ requirements: []
219
+ rubygems_version: 3.0.3
220
+ signing_key:
221
+ specification_version: 4
222
+ summary: Instant quote providers gem.
223
+ test_files: []