instant_quote 1.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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: []