instant_quote 1.7.18 → 1.7.20

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: ea32f26600ebacbcea2bbb802e81cfdfdbc6f3fd8a63c5114450bd757aa7bcfe
4
- data.tar.gz: 3690d3afa1a8bef07787712907ae74e68141ff948191aec6dc15515ef56733eb
3
+ metadata.gz: d31fe21346d1dcdc18f60859f3bb71f248df1ebe76bc10b1ef8e77717c522210
4
+ data.tar.gz: 16913cdcbf765609b081cc39a1dacf655dba67faf1e2cd4ffd6a93b8d9c279eb
5
5
  SHA512:
6
- metadata.gz: a6ae792406151bc13bbd707f7bcc70f1baeb45a3e3c358fed1a528be3b0099e747c976ef056307170e636fcbe0d28e09cef9b0e79480e690c9aed95889fc1672
7
- data.tar.gz: 5ed602cde2830a7a84225605a44bd68f6573fddaaf905617130b0da9d931c9a752edfab2880e07e64ba95d6dab6638160cb95679a565c5d6d2469f54d42351e0
6
+ metadata.gz: 7179adf5e3ca6dd13788e072d9f5abd4c2fd2b8ca5dfc6d38ecb1839d18cbea3d89e0ef47ad30f45166d65ad76583b86f0d881e2ed50ca6f03e6f2d38ca8597d
7
+ data.tar.gz: f0e046542bf8ee4cc71aabbba509ca270e36dc3a39c9a5f37009c0406f88d17b4fc45e8c3892d0d54da70e8779da58ad3daaaf03e8c2e99e127ecab09e67b5fc
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- instant_quote (1.7.18)
4
+ instant_quote (1.7.20)
5
5
  activesupport
6
6
  capital_on_tap (~> 1.0.1)
7
7
  iwoca (~> 1.1.0)
data/bin/console CHANGED
@@ -32,5 +32,53 @@ end
32
32
 
33
33
  send(:include, InstantQuote)
34
34
 
35
+ # Configuration needed for the Money gem
36
+ Money.default_currency = :gbp
37
+ I18n.available_locales = [:en]
38
+ I18n.locale = :en
39
+ Money.locale_backend = :i18n
40
+ Money.rounding_mode = BigDecimal::ROUND_HALF_EVEN
41
+
42
+ def declined_v1_json
43
+ json = '{
44
+ "data": {
45
+ "state_key": "[FILTERED]",
46
+ "approval_requirements": [
47
+ { "type": "cc", "status": "required" },
48
+ { "type": "enhanced_security_check", "status": "pending" }
49
+ ],
50
+ "approval_status": { "status": "declined" },
51
+ "latest_approval_request": { "status": "decision_made" }
52
+ }
53
+ }'
54
+
55
+ JSON.parse(json)
56
+ end
57
+
58
+ def approved_v1_json
59
+ json = '{
60
+ "data": {
61
+ "state_key": "[FILTERED]",
62
+ "approval_status": {
63
+ "approved": {
64
+ "duration": 24,
65
+ "interval": "1m",
66
+ "max_credit": 21000.0,
67
+ "min_credit": 1.0,
68
+ "rate_structures": [
69
+ { "effective_on_day": 0, "rates": [{ "max_principal": 21000.0, "rate": 0.0515 }] }
70
+ ],
71
+ "time_out": "2023-09-24T00:00:00Z",
72
+ "top_up_allowed": false
73
+ },
74
+ "status": "approved"
75
+ },
76
+ "latest_approval_request": { "status": "decision_made" }
77
+ }
78
+ }'
79
+
80
+ JSON.parse(json)
81
+ end
82
+
35
83
  require 'pry'
36
84
  Pry.start
@@ -98,6 +98,16 @@ module InstantQuote
98
98
  # offers - the response of the available offers
99
99
  def initialize(data = {})
100
100
  @data = super
101
+
102
+ # Just in case (v1 has a lot of key 'data' nesting)
103
+ @data = @data.dig(:data) if @data.key?(:data)
104
+
105
+ # It's a v1 JSON response, so we need to translate it to v2 first!
106
+ if @data.key?(:state_key)
107
+ puts "Translating v1 JSON to v2..."
108
+ @data = DecisionParsers::IwocaV2Translator.translate(@data)
109
+ end
110
+
101
111
  @status = @data[:status] if @data.key?(:status)
102
112
  @offers = @data[:offers] if @data.key?(:offers)
103
113
  end
@@ -153,7 +163,7 @@ module InstantQuote
153
163
  end
154
164
 
155
165
  def monthly_interest_rate
156
- first_offer&.interest_rate
166
+ first_offer[:interest_rate]
157
167
  end
158
168
 
159
169
  def status
@@ -0,0 +1,141 @@
1
+ # frozen_string_literal: true
2
+
3
+ module InstantQuote
4
+ module DecisionParsers
5
+ # Example responses:
6
+ #
7
+ # {
8
+ # "data": {
9
+ # "state_key": "[FILTERED]",
10
+ # "approval_requirements": [
11
+ # { "type": "enhanced_security_check", "status": "pending" }
12
+ # ],
13
+ # "approval_status": { "status": "declined" },
14
+ # "latest_approval_request": { "status": "decision_made" }
15
+ # }
16
+ # }
17
+ #
18
+ # {
19
+ # "data": {
20
+ # "state_key": "[FILTERED]",
21
+ # "approval_requirements": [
22
+ # { "type": "cc", "status": "required" },
23
+ # { "type": "enhanced_security_check", "status": "pending" }
24
+ # ],
25
+ # "approval_status": { "status": "declined" },
26
+ # "latest_approval_request": { "status": "decision_made" }
27
+ # }
28
+ # }
29
+ #
30
+ # {
31
+ # "data": {
32
+ # "state_key": "[FILTERED]",
33
+ # "approval_status": {
34
+ # "approved": {
35
+ # "duration": 24,
36
+ # "interval": "1m",
37
+ # "max_credit": 21000.0,
38
+ # "min_credit": 1.0,
39
+ # "rate_structures": [
40
+ # { "effective_on_day": 0, "rates": [{ "max_principal": 21000.0, "rate": 0.0515 }] }
41
+ # ],
42
+ # "time_out": "2023-09-24T00:00:00Z",
43
+ # "top_up_allowed": false
44
+ # },
45
+ # "status": "approved"
46
+ # },
47
+ # "latest_approval_request": { "status": "decision_made" }
48
+ # }
49
+ # }
50
+ class IwocaV1 < DecisionParser
51
+ STATUSES = {
52
+ pending: 'pending',
53
+ approved: 'approved',
54
+ declined: 'declined',
55
+ no_decision: 'no_decision_possible',
56
+ not_defined: 'not_defined'
57
+ }.freeze
58
+
59
+ LOAN_STATES = {
60
+ ongoing: 'ongoing',
61
+ ended: 'ended',
62
+ topup: 'topup',
63
+ pending: 'pending',
64
+ overdue: 'overdue',
65
+ duetoday: 'duetoday'
66
+ }.freeze
67
+
68
+ # iwoca sends an extra "data" key, that is removed here, to avoid having to do data[:data]
69
+ # because it's silly :)
70
+ def initialize(data = {})
71
+ @data = super
72
+ @data = @data[:data] if @data.key?(:data)
73
+ end
74
+
75
+ def pending?
76
+ status == STATUSES[:not_defined]
77
+ end
78
+
79
+ def approved?
80
+ status == STATUSES[:approved]
81
+ end
82
+
83
+ def declined?
84
+ status == STATUSES[:declined]
85
+ end
86
+
87
+ def manual_review?
88
+ status == STATUSES[:pending]
89
+ end
90
+
91
+ def no_decision_possible?
92
+ status == STATUSES[:no_decision]
93
+ end
94
+
95
+ def loan_started?
96
+ data[:loan_status] == LOAN_STATES[:ongoing]
97
+ end
98
+
99
+ def amount
100
+ amount_approved = status_hash.dig(:approved, :max_credit) || 0
101
+
102
+ Money.new(amount_approved * 100).format
103
+ end
104
+
105
+ # Only used by the new iwoca2 translator
106
+ def amount_unformatted
107
+ status_hash.dig(:approved, :max_credit) || 0
108
+ end
109
+
110
+ def credit_duration
111
+ status_hash.dig(:approved, :duration)
112
+ end
113
+
114
+ def credit_interval
115
+ status_hash.dig(:approved, :interval)
116
+ end
117
+
118
+ def monthly_interest_rate
119
+ rates = status_hash.dig(:approved, :rate_structures)
120
+
121
+ return 0 unless rates&.any?
122
+
123
+ rate_info = rates.first[:rates]&.first
124
+
125
+ return 0 unless rate_info
126
+
127
+ rate_info[:rate]
128
+ end
129
+
130
+ def status
131
+ status_hash[:status] || STATUSES[:not_defined]
132
+ end
133
+
134
+ private
135
+
136
+ def status_hash
137
+ data[:approval_status] || data[:latest_approval_request] || {}
138
+ end
139
+ end
140
+ end
141
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'instant_quote/decision_parsers/iwoca'
4
+
5
+ # This class is used to translate iwoca's v1 decision format to v2. In the app we might have some
6
+ # decisions stored with the v1 format that need translation but the webhook at the moment is also
7
+ # using the old format (with state_key instead of customer_id).
8
+ module InstantQuote
9
+ module DecisionParsers
10
+ class IwocaV2Translator
11
+ def initialize(v1_json)
12
+ @decision = InstantQuote::DecisionParsers::IwocaV1.new(v1_json)
13
+ end
14
+
15
+ def self.translate(decision)
16
+ new(decision).translate
17
+ end
18
+
19
+ # Translates the v1 JSON into v2 format.
20
+ def translate
21
+ {
22
+ status: {
23
+ status: translate_status(@decision.status),
24
+ requests: [
25
+ {
26
+ suggested_product: {
27
+ amount: @decision.amount_unformatted,
28
+ duration: {
29
+ unit: translate_interval(@decision.credit_interval),
30
+ amount: @decision.credit_duration
31
+ },
32
+ }
33
+ }
34
+ ]
35
+ },
36
+ offers: [
37
+ {
38
+ interest_rate: @decision.monthly_interest_rate
39
+ }
40
+ ],
41
+ }
42
+ end
43
+
44
+ private
45
+
46
+ def translate_status(v1_status)
47
+ case v1_status
48
+ when 'approved'
49
+ 'funded'
50
+ else
51
+ v1_status
52
+ end
53
+ end
54
+
55
+ def translate_interval(v1_interval)
56
+ case v1_interval
57
+ when '1m'
58
+ 'months'
59
+ when '1w'
60
+ 'weeks'
61
+ when '1d'
62
+ 'days'
63
+ else
64
+ 'months'
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module InstantQuote
4
- VERSION = '1.7.18'
4
+ VERSION = '1.7.20'
5
5
  end
@@ -42,6 +42,24 @@ module InstantQuote
42
42
  request.headers[SIGNATURE_HEADER]
43
43
  end
44
44
 
45
+ # The list of possible event types is:
46
+ # approval_status_changed
47
+ # customer_funded
48
+ # application_offered
49
+ # application_declined
50
+ # application_deferred
51
+ # cashflow_added
52
+ # mca_context_changed
53
+ # bank_account_setup
54
+ # application_status_changed
55
+ # application_attributed
56
+ # indicative_offer_created
57
+ # shopify_payment_resolve
58
+ # shopify_payment_reject
59
+ # shopify_refund_resolve
60
+ #
61
+ # More info:
62
+ # https://iwoca.stoplight.io/docs/lapi-notifications/b59ca8c181611-get-available-webhook-event-types
45
63
  def iwoca_event_type
46
64
  # Allowed Values: approval_status_changed, instruct_payments, customer_funded
47
65
  request.headers[EVENT_TYPE_HEADER]
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.7.18
4
+ version: 1.7.20
5
5
  platform: ruby
6
6
  authors:
7
7
  - rikas
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-08-24 00:00:00.000000000 Z
11
+ date: 2023-08-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -158,6 +158,8 @@ files:
158
158
  - lib/instant_quote/decision_parsers/iwoca_flexi24.rb
159
159
  - lib/instant_quote/decision_parsers/iwoca_recovery_loans.rb
160
160
  - lib/instant_quote/decision_parsers/iwoca_revenue_based_loans.rb
161
+ - lib/instant_quote/decision_parsers/iwoca_v1.rb
162
+ - lib/instant_quote/decision_parsers/iwoca_v2_translator.rb
161
163
  - lib/instant_quote/decision_parsers/optimum.rb
162
164
  - lib/instant_quote/decision_parsers/youlend.rb
163
165
  - lib/instant_quote/version.rb