sift 4.5.1 → 4.7.0
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.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +2 -2
- data/HISTORY +11 -0
- data/README.md +72 -1
- data/lib/sift/client/decision/apply_to.rb +7 -3
- data/lib/sift/client/decision.rb +7 -6
- data/lib/sift/client.rb +49 -32
- data/lib/sift/router.rb +7 -4
- data/lib/sift/version.rb +1 -1
- data/sift.gemspec +7 -5
- data/spec/unit/client/decision/apply_to_spec.rb +1 -1
- data/spec/unit/client/decision_spec.rb +1 -1
- data/spec/unit/client_structured_fields_spec.rb +60 -0
- data/spec/unit/client_validationapi_spec.rb +46 -0
- data/spec/unit/configuration_spec.rb +67 -0
- data/test_integration_app/events_api/test_events_api.rb +102 -12
- metadata +11 -9
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: '0316139e47e2c117655c7f8f75497c78e7f657552e816f2ec40f1e2c0a616d67'
|
|
4
|
+
data.tar.gz: 628cddc8debde4d8ad15bafb96a266a6cfbaaa2e1e78ee9202d9a4388a978fb9
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c9e69e75d82c24f624ae4049e539cf127966de795196cff8a7025a861deb54a7f53a5e21ab90611d3c1de4c1cc5c300905a7bffd6afff3fe16f2942c1b494565
|
|
7
|
+
data.tar.gz: 9997e80b62404f3677b60bf43021b0c700cf26d60d6aff7fa8a447a3757338a2fd988bccb83aa87ddcfe44ea476c5c76e116a99e5de69c8b86bffbf796aff7d2
|
data/.github/workflows/ci.yml
CHANGED
|
@@ -20,7 +20,7 @@ jobs:
|
|
|
20
20
|
runs-on: ubuntu-latest
|
|
21
21
|
strategy:
|
|
22
22
|
matrix:
|
|
23
|
-
ruby-version: ['2.
|
|
23
|
+
ruby-version: ['2.7', '3.1', '3.2', '3.3']
|
|
24
24
|
steps:
|
|
25
25
|
- uses: actions/checkout@v4
|
|
26
26
|
- name: Set up Ruby
|
|
@@ -36,7 +36,7 @@ jobs:
|
|
|
36
36
|
if: ${{ github.ref == 'refs/heads/master' }}
|
|
37
37
|
strategy:
|
|
38
38
|
matrix:
|
|
39
|
-
ruby-version: [
|
|
39
|
+
ruby-version: ['2.7', '3.1', '3.2', '3.3']
|
|
40
40
|
steps:
|
|
41
41
|
- uses: actions/checkout@v4
|
|
42
42
|
- name: Set up Ruby
|
data/HISTORY
CHANGED
|
@@ -1,3 +1,14 @@
|
|
|
1
|
+
=== 4.7.0 2026-08-27
|
|
2
|
+
- Added support for $nationality, $year_of_birth fields on $create_account, $update_account events
|
|
3
|
+
- Added support for $kyc field on $create_account, $update_account, $transaction, $create_order, $update_order, $verification events
|
|
4
|
+
- Added support for $geo, $bot_identification fields on $create_account, $update_account, $login, $transaction, $create_order, $update_order events
|
|
5
|
+
|
|
6
|
+
=== 4.6.0 2026-02-10
|
|
7
|
+
- Bump the minimum version of httparty to 0.23.3 to ensure protection against CVE-2025-68696
|
|
8
|
+
- Refactor Client to use dedicated internal HTTP clients for different API endpoints
|
|
9
|
+
- Fix Verification API methods to use correct version parameter
|
|
10
|
+
- Increase minimum required version for Ruby to 2.7.0
|
|
11
|
+
|
|
1
12
|
=== 4.5.1 2025-04-07
|
|
2
13
|
- Fix Verification URLs
|
|
3
14
|
|
data/README.md
CHANGED
|
@@ -4,7 +4,7 @@ The official Ruby bindings for the latest version (v205) of the [Sift API](https
|
|
|
4
4
|
|
|
5
5
|
## Requirements
|
|
6
6
|
|
|
7
|
-
* Ruby 2.
|
|
7
|
+
* Ruby 2.7.0 or above.
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
## Installation
|
|
@@ -87,6 +87,77 @@ response.api_error_message # Error message associated with status Error Code
|
|
|
87
87
|
response = client.score(user_id)
|
|
88
88
|
```
|
|
89
89
|
|
|
90
|
+
### New KYC Signals, Geo & Bot Detection
|
|
91
|
+
|
|
92
|
+
```ruby
|
|
93
|
+
# $verification event with KYC outcome
|
|
94
|
+
response = client.track("$verification", {
|
|
95
|
+
"$user_id" => "23056",
|
|
96
|
+
"$verification_type" => "$kyc",
|
|
97
|
+
"$status" => "$success",
|
|
98
|
+
"$kyc" => {
|
|
99
|
+
"$names_match" => true,
|
|
100
|
+
"$kyc_level" => "$basic",
|
|
101
|
+
"$bin_nationality_match" => false,
|
|
102
|
+
"$provider" => "lexisnexis"
|
|
103
|
+
}
|
|
104
|
+
})
|
|
105
|
+
|
|
106
|
+
# $create_account with account-level identity attributes
|
|
107
|
+
response = client.track("$create_account", {
|
|
108
|
+
"$user_id" => "23056",
|
|
109
|
+
"$nationality" => "US",
|
|
110
|
+
"$year_of_birth" => 1985,
|
|
111
|
+
"$kyc" => {
|
|
112
|
+
"$names_match" => true,
|
|
113
|
+
"$kyc_level" => "$full",
|
|
114
|
+
"$provider" => "prove"
|
|
115
|
+
},
|
|
116
|
+
"$geo" => {
|
|
117
|
+
"$uuid" => "gc-abc-123",
|
|
118
|
+
"$provider" => "geocomply"
|
|
119
|
+
},
|
|
120
|
+
"$bot_identification" => {
|
|
121
|
+
"$result" => "$human",
|
|
122
|
+
"$provider" => "datadome"
|
|
123
|
+
}
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
# $login event with geo and bot-detection signals
|
|
127
|
+
response = client.track("$login", {
|
|
128
|
+
"$user_id" => "23056",
|
|
129
|
+
"$login_status" => "$success",
|
|
130
|
+
"$geo" => {
|
|
131
|
+
"$uuid" => "gc-abc-123",
|
|
132
|
+
"$provider" => "geocomply"
|
|
133
|
+
},
|
|
134
|
+
"$bot_identification" => {
|
|
135
|
+
"$result" => "$human",
|
|
136
|
+
"$provider" => "datadome"
|
|
137
|
+
}
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
# $transaction event with KYC, geo, and bot-detection signals
|
|
141
|
+
response = client.track("$transaction", {
|
|
142
|
+
"$user_id" => "23056",
|
|
143
|
+
"$amount" => 15230000,
|
|
144
|
+
"$currency_code" => "USD",
|
|
145
|
+
"$kyc" => {
|
|
146
|
+
"$names_match" => true,
|
|
147
|
+
"$bin_nationality_match" => true,
|
|
148
|
+
"$provider" => "lexisnexis"
|
|
149
|
+
},
|
|
150
|
+
"$geo" => {
|
|
151
|
+
"$uuid" => "gc-abc-123",
|
|
152
|
+
"$provider" => "geocomply"
|
|
153
|
+
},
|
|
154
|
+
"$bot_identification" => {
|
|
155
|
+
"$result" => "$suspected",
|
|
156
|
+
"$provider" => "human_security"
|
|
157
|
+
}
|
|
158
|
+
})
|
|
159
|
+
```
|
|
160
|
+
|
|
90
161
|
## Decisions
|
|
91
162
|
|
|
92
163
|
To learn more about the decisions endpoint visit our [developer docs](https://sift.com/developers/docs/ruby/decisions-api/get-decisions).
|
|
@@ -21,7 +21,7 @@ module Sift
|
|
|
21
21
|
time
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
attr_reader :decision_id, :configs, :getter, :api_key
|
|
24
|
+
attr_reader :decision_id, :configs, :getter, :api_key, :client_class
|
|
25
25
|
|
|
26
26
|
PROPERTIES.each do |attribute|
|
|
27
27
|
class_eval %{
|
|
@@ -31,11 +31,12 @@ module Sift
|
|
|
31
31
|
}
|
|
32
32
|
end
|
|
33
33
|
|
|
34
|
-
def initialize(api_key, decision_id, configs)
|
|
34
|
+
def initialize(api_key, decision_id, configs, client_class = Sift::Client)
|
|
35
35
|
@api_key = api_key
|
|
36
36
|
@decision_id = decision_id
|
|
37
37
|
@configs = configs
|
|
38
38
|
@getter = Utils::HashGetter.new(configs)
|
|
39
|
+
@client_class = client_class
|
|
39
40
|
end
|
|
40
41
|
|
|
41
42
|
def run
|
|
@@ -58,7 +59,8 @@ module Sift
|
|
|
58
59
|
def send_request
|
|
59
60
|
Router.post(path, {
|
|
60
61
|
body: request_body,
|
|
61
|
-
headers: headers
|
|
62
|
+
headers: headers,
|
|
63
|
+
client_class: client_class
|
|
62
64
|
})
|
|
63
65
|
end
|
|
64
66
|
|
|
@@ -79,6 +81,8 @@ module Sift
|
|
|
79
81
|
validator.valid_order?
|
|
80
82
|
elsif applying_to_session?
|
|
81
83
|
validator.valid_session?
|
|
84
|
+
elsif applying_to_content?
|
|
85
|
+
validator.valid_content?
|
|
82
86
|
else
|
|
83
87
|
validator.valid_user?
|
|
84
88
|
end
|
data/lib/sift/client/decision.rb
CHANGED
|
@@ -10,11 +10,12 @@ module Sift
|
|
|
10
10
|
class Decision
|
|
11
11
|
FILTER_PARAMS = %w{ limit entity_type abuse_types from }
|
|
12
12
|
|
|
13
|
-
attr_reader :account_id, :api_key
|
|
13
|
+
attr_reader :account_id, :api_key, :client_class
|
|
14
14
|
|
|
15
|
-
def initialize(api_key, account_id)
|
|
15
|
+
def initialize(api_key, account_id, client_class = Sift::Client)
|
|
16
16
|
@account_id = account_id
|
|
17
17
|
@api_key = api_key
|
|
18
|
+
@client_class = client_class
|
|
18
19
|
end
|
|
19
20
|
|
|
20
21
|
def list(options = {})
|
|
@@ -25,7 +26,8 @@ module Sift
|
|
|
25
26
|
else
|
|
26
27
|
Router.get(index_path, {
|
|
27
28
|
query: build_query(getter),
|
|
28
|
-
headers: auth_header
|
|
29
|
+
headers: auth_header,
|
|
30
|
+
client_class: client_class
|
|
29
31
|
})
|
|
30
32
|
end
|
|
31
33
|
end
|
|
@@ -44,7 +46,7 @@ module Sift
|
|
|
44
46
|
getter = Utils::HashGetter.new(configs)
|
|
45
47
|
configs[:account_id] = account_id
|
|
46
48
|
|
|
47
|
-
ApplyTo.new(api_key, getter.get(:decision_id), configs).run
|
|
49
|
+
ApplyTo.new(api_key, getter.get(:decision_id), configs, client_class).run
|
|
48
50
|
end
|
|
49
51
|
|
|
50
52
|
def index_path
|
|
@@ -54,7 +56,7 @@ module Sift
|
|
|
54
56
|
private
|
|
55
57
|
|
|
56
58
|
def request_next_page(path)
|
|
57
|
-
Router.get(path, headers: auth_header)
|
|
59
|
+
Router.get(path, headers: auth_header, client_class: client_class)
|
|
58
60
|
end
|
|
59
61
|
|
|
60
62
|
def auth_header
|
|
@@ -63,4 +65,3 @@ module Sift
|
|
|
63
65
|
end
|
|
64
66
|
end
|
|
65
67
|
end
|
|
66
|
-
|
data/lib/sift/client.rb
CHANGED
|
@@ -2,9 +2,6 @@ require "httparty"
|
|
|
2
2
|
require "multi_json"
|
|
3
3
|
require "base64"
|
|
4
4
|
|
|
5
|
-
require_relative "./client/decision"
|
|
6
|
-
require_relative "./error"
|
|
7
|
-
|
|
8
5
|
module Sift
|
|
9
6
|
|
|
10
7
|
# Represents the payload returned from a call through the track API
|
|
@@ -94,17 +91,36 @@ module Sift
|
|
|
94
91
|
API_ENDPOINT = ENV["SIFT_RUBY_API_URL"] || 'https://api.siftscience.com'
|
|
95
92
|
API3_ENDPOINT = ENV["SIFT_RUBY_API3_URL"] || 'https://api3.siftscience.com'
|
|
96
93
|
|
|
94
|
+
# Maintain backward compatibility for users who may rely on HTTParty methods
|
|
97
95
|
include HTTParty
|
|
98
96
|
base_uri API_ENDPOINT
|
|
99
97
|
|
|
100
98
|
attr_reader :api_key, :account_id
|
|
101
99
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
100
|
+
class << self
|
|
101
|
+
def build_auth_header(api_key)
|
|
102
|
+
{ "Authorization" => "Basic #{Base64.strict_encode64(api_key + ":")}" }
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def user_agent
|
|
106
|
+
"sift-ruby/#{VERSION}"
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# Factory methods for internal API executors that inherit from the current class context.
|
|
110
|
+
# This ensures that subclasses of Client propagate their HTTParty configuration
|
|
111
|
+
# to these internal clients.
|
|
105
112
|
|
|
106
|
-
|
|
107
|
-
|
|
113
|
+
def api_client
|
|
114
|
+
@api_client ||= Class.new(self) do
|
|
115
|
+
base_uri API_ENDPOINT
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def api3_client
|
|
120
|
+
@api3_client ||= Class.new(self) do
|
|
121
|
+
base_uri API3_ENDPOINT
|
|
122
|
+
end
|
|
123
|
+
end
|
|
108
124
|
end
|
|
109
125
|
|
|
110
126
|
# Constructor
|
|
@@ -256,7 +272,7 @@ module Sift
|
|
|
256
272
|
}
|
|
257
273
|
options.merge!(:timeout => timeout) unless timeout.nil?
|
|
258
274
|
|
|
259
|
-
response = self.class.post(path, options)
|
|
275
|
+
response = self.class.api_client.post(path, options)
|
|
260
276
|
Response.new(response.body, response.code, response.response)
|
|
261
277
|
end
|
|
262
278
|
|
|
@@ -319,7 +335,7 @@ module Sift
|
|
|
319
335
|
}
|
|
320
336
|
options.merge!(:timeout => timeout) unless timeout.nil?
|
|
321
337
|
|
|
322
|
-
response = self.class.get(Sift.score_api_path(user_id, version), options)
|
|
338
|
+
response = self.class.api_client.get(Sift.score_api_path(user_id, version), options)
|
|
323
339
|
Response.new(response.body, response.code, response.response)
|
|
324
340
|
end
|
|
325
341
|
|
|
@@ -364,6 +380,7 @@ module Sift
|
|
|
364
380
|
abuse_types = opts[:abuse_types]
|
|
365
381
|
api_key = opts[:api_key] || @api_key
|
|
366
382
|
timeout = opts[:timeout] || @timeout
|
|
383
|
+
version = opts[:version] || @version
|
|
367
384
|
include_score_percentiles = opts[:include_score_percentiles]
|
|
368
385
|
|
|
369
386
|
raise("user_id must be a non-empty string") if (!user_id.is_a? String) || user_id.to_s.empty?
|
|
@@ -382,7 +399,7 @@ module Sift
|
|
|
382
399
|
}
|
|
383
400
|
options.merge!(:timeout => timeout) unless timeout.nil?
|
|
384
401
|
|
|
385
|
-
response = self.class.get(Sift.user_score_api_path(user_id,
|
|
402
|
+
response = self.class.api_client.get(Sift.user_score_api_path(user_id, version), options)
|
|
386
403
|
Response.new(response.body, response.code, response.response)
|
|
387
404
|
end
|
|
388
405
|
|
|
@@ -420,6 +437,7 @@ module Sift
|
|
|
420
437
|
abuse_types = opts[:abuse_types]
|
|
421
438
|
api_key = opts[:api_key] || @api_key
|
|
422
439
|
timeout = opts[:timeout] || @timeout
|
|
440
|
+
version = opts[:version] || @version
|
|
423
441
|
|
|
424
442
|
raise("user_id must be a non-empty string") if (!user_id.is_a? String) || user_id.to_s.empty?
|
|
425
443
|
raise("Bad api_key parameter") if api_key.empty?
|
|
@@ -434,7 +452,7 @@ module Sift
|
|
|
434
452
|
}
|
|
435
453
|
options.merge!(:timeout => timeout) unless timeout.nil?
|
|
436
454
|
|
|
437
|
-
response = self.class.post(Sift.user_score_api_path(user_id,
|
|
455
|
+
response = self.class.api_client.post(Sift.user_score_api_path(user_id, version), options)
|
|
438
456
|
Response.new(response.body, response.code, response.response)
|
|
439
457
|
end
|
|
440
458
|
|
|
@@ -532,7 +550,7 @@ module Sift
|
|
|
532
550
|
}
|
|
533
551
|
options.merge!(:timeout => timeout) unless timeout.nil?
|
|
534
552
|
|
|
535
|
-
response = self.class.delete(Sift.users_label_api_path(user_id, version), options)
|
|
553
|
+
response = self.class.api_client.delete(Sift.users_label_api_path(user_id, version), options)
|
|
536
554
|
Response.new(response.body, response.code, response.response)
|
|
537
555
|
end
|
|
538
556
|
|
|
@@ -569,8 +587,7 @@ module Sift
|
|
|
569
587
|
}
|
|
570
588
|
options.merge!(:timeout => timeout) unless timeout.nil?
|
|
571
589
|
|
|
572
|
-
|
|
573
|
-
response = self.class.get(uri, options)
|
|
590
|
+
response = self.class.api3_client.get(Sift.workflow_status_path(account_id, run_id), options)
|
|
574
591
|
Response.new(response.body, response.code, response.response)
|
|
575
592
|
end
|
|
576
593
|
|
|
@@ -607,8 +624,7 @@ module Sift
|
|
|
607
624
|
}
|
|
608
625
|
options.merge!(:timeout => timeout) unless timeout.nil?
|
|
609
626
|
|
|
610
|
-
|
|
611
|
-
response = self.class.get(uri, options)
|
|
627
|
+
response = self.class.api3_client.get(Sift.user_decisions_api_path(account_id, user_id), options)
|
|
612
628
|
Response.new(response.body, response.code, response.response)
|
|
613
629
|
end
|
|
614
630
|
|
|
@@ -645,8 +661,7 @@ module Sift
|
|
|
645
661
|
}
|
|
646
662
|
options.merge!(:timeout => timeout) unless timeout.nil?
|
|
647
663
|
|
|
648
|
-
|
|
649
|
-
response = self.class.get(uri, options)
|
|
664
|
+
response = self.class.api3_client.get(Sift.order_decisions_api_path(account_id, order_id), options)
|
|
650
665
|
Response.new(response.body, response.code, response.response)
|
|
651
666
|
end
|
|
652
667
|
|
|
@@ -685,8 +700,7 @@ module Sift
|
|
|
685
700
|
}
|
|
686
701
|
options.merge!(:timeout => timeout) unless timeout.nil?
|
|
687
702
|
|
|
688
|
-
|
|
689
|
-
response = self.class.get(uri, options)
|
|
703
|
+
response = self.class.api3_client.get(Sift.session_decisions_api_path(account_id, user_id, session_id), options)
|
|
690
704
|
Response.new(response.body, response.code, response.response)
|
|
691
705
|
end
|
|
692
706
|
|
|
@@ -725,8 +739,7 @@ module Sift
|
|
|
725
739
|
}
|
|
726
740
|
options.merge!(:timeout => timeout) unless timeout.nil?
|
|
727
741
|
|
|
728
|
-
|
|
729
|
-
response = self.class.get(uri, options)
|
|
742
|
+
response = self.class.api3_client.get(Sift.content_decisions_api_path(account_id, user_id, content_id), options)
|
|
730
743
|
Response.new(response.body, response.code, response.response)
|
|
731
744
|
end
|
|
732
745
|
|
|
@@ -748,7 +761,7 @@ module Sift
|
|
|
748
761
|
|
|
749
762
|
def build_default_headers_post(api_key)
|
|
750
763
|
{
|
|
751
|
-
"Authorization" => "Basic #{Base64.
|
|
764
|
+
"Authorization" => "Basic #{Base64.strict_encode64(api_key+":")}",
|
|
752
765
|
"User-Agent" => "SiftScience/v#{@version} sift-ruby/#{VERSION}",
|
|
753
766
|
"Content-Type" => "application/json"
|
|
754
767
|
}
|
|
@@ -768,7 +781,7 @@ module Sift
|
|
|
768
781
|
:headers => build_default_headers_post(api_key)
|
|
769
782
|
}
|
|
770
783
|
options.merge!(:timeout => timeout) unless timeout.nil?
|
|
771
|
-
response = self.class.post(Sift.verification_api_send_path(
|
|
784
|
+
response = self.class.api_client.post(Sift.verification_api_send_path(version), options)
|
|
772
785
|
Response.new(response.body, response.code, response.response)
|
|
773
786
|
end
|
|
774
787
|
|
|
@@ -787,7 +800,7 @@ module Sift
|
|
|
787
800
|
}
|
|
788
801
|
options.merge!(:timeout => timeout) unless timeout.nil?
|
|
789
802
|
|
|
790
|
-
response = self.class.post(Sift.verification_api_resend_path(
|
|
803
|
+
response = self.class.api_client.post(Sift.verification_api_resend_path(version), options)
|
|
791
804
|
Response.new(response.body, response.code, response.response)
|
|
792
805
|
end
|
|
793
806
|
|
|
@@ -806,7 +819,7 @@ module Sift
|
|
|
806
819
|
}
|
|
807
820
|
options.merge!(:timeout => timeout) unless timeout.nil?
|
|
808
821
|
|
|
809
|
-
response = self.class.post(Sift.verification_api_check_path(
|
|
822
|
+
response = self.class.api_client.post(Sift.verification_api_check_path(version), options)
|
|
810
823
|
Response.new(response.body, response.code, response.response)
|
|
811
824
|
end
|
|
812
825
|
|
|
@@ -831,7 +844,7 @@ module Sift
|
|
|
831
844
|
:basic_auth => { :username => api_key, :password => "" }
|
|
832
845
|
}
|
|
833
846
|
options.merge!(:timeout => timeout) unless timeout.nil?
|
|
834
|
-
response = self.class.post(
|
|
847
|
+
response = self.class.api_client.post(Sift.psp_merchant_api_path(account_id), options)
|
|
835
848
|
Response.new(response.body, response.code, response.response)
|
|
836
849
|
end
|
|
837
850
|
|
|
@@ -858,7 +871,7 @@ module Sift
|
|
|
858
871
|
:basic_auth => { :username => api_key, :password => "" }
|
|
859
872
|
}
|
|
860
873
|
options.merge!(:timeout => timeout) unless timeout.nil?
|
|
861
|
-
response = self.class.put(
|
|
874
|
+
response = self.class.api_client.put(Sift.psp_merchant_id_api_path(account_id, merchant_id), options)
|
|
862
875
|
Response.new(response.body, response.code, response.response)
|
|
863
876
|
end
|
|
864
877
|
|
|
@@ -882,7 +895,7 @@ module Sift
|
|
|
882
895
|
:basic_auth => { :username => api_key, :password => "" }
|
|
883
896
|
}
|
|
884
897
|
options.merge!(:timeout => timeout) unless timeout.nil?
|
|
885
|
-
response = self.class.get(
|
|
898
|
+
response = self.class.api_client.get(Sift.psp_merchant_id_api_path(account_id, merchant_id), options)
|
|
886
899
|
Response.new(response.body, response.code, response.response)
|
|
887
900
|
end
|
|
888
901
|
|
|
@@ -911,7 +924,7 @@ module Sift
|
|
|
911
924
|
:query => query
|
|
912
925
|
}
|
|
913
926
|
options.merge!(:timeout => timeout) unless timeout.nil?
|
|
914
|
-
response = self.class.get(
|
|
927
|
+
response = self.class.api_client.get(Sift.psp_merchant_api_path(account_id), options)
|
|
915
928
|
Response.new(response.body, response.code, response.response)
|
|
916
929
|
end
|
|
917
930
|
|
|
@@ -926,7 +939,7 @@ module Sift
|
|
|
926
939
|
end
|
|
927
940
|
|
|
928
941
|
def decision_instance
|
|
929
|
-
@decision_instance ||= Decision.new(api_key, account_id)
|
|
942
|
+
@decision_instance ||= Decision.new(api_key, account_id, self.class)
|
|
930
943
|
end
|
|
931
944
|
|
|
932
945
|
def delete_nils(properties)
|
|
@@ -943,4 +956,8 @@ module Sift
|
|
|
943
956
|
end
|
|
944
957
|
end
|
|
945
958
|
end
|
|
959
|
+
|
|
960
|
+
require_relative "./client/decision"
|
|
961
|
+
require_relative "./error"
|
|
962
|
+
|
|
946
963
|
end
|
data/lib/sift/router.rb
CHANGED
|
@@ -2,20 +2,23 @@ require_relative "./version"
|
|
|
2
2
|
require_relative "./client"
|
|
3
3
|
|
|
4
4
|
module Sift
|
|
5
|
-
class Router
|
|
6
|
-
include HTTParty
|
|
5
|
+
class Router < Client
|
|
7
6
|
|
|
8
7
|
class << self
|
|
9
8
|
def get(path, options = {})
|
|
9
|
+
client_class = options.delete(:client_class) || Sift::Client
|
|
10
|
+
options[:base_uri] = nil
|
|
10
11
|
serialize_body(options)
|
|
11
12
|
add_default_headers(options)
|
|
12
|
-
wrap_response(
|
|
13
|
+
wrap_response(client_class.api3_client.get(path, options))
|
|
13
14
|
end
|
|
14
15
|
|
|
15
16
|
def post(path, options = {})
|
|
17
|
+
client_class = options.delete(:client_class) || Sift::Client
|
|
18
|
+
options[:base_uri] = nil
|
|
16
19
|
serialize_body(options)
|
|
17
20
|
add_default_headers(options)
|
|
18
|
-
wrap_response(
|
|
21
|
+
wrap_response(client_class.api3_client.post(path, options))
|
|
19
22
|
end
|
|
20
23
|
|
|
21
24
|
def serialize_body(options)
|
data/lib/sift/version.rb
CHANGED
data/sift.gemspec
CHANGED
|
@@ -7,10 +7,10 @@ Gem::Specification.new do |s|
|
|
|
7
7
|
s.version = Sift::VERSION
|
|
8
8
|
s.platform = Gem::Platform::RUBY
|
|
9
9
|
s.authors = ["Fred Sadaghiani", "Yoav Schatzberg", "Jacob Burnim"]
|
|
10
|
-
s.email = ["support@
|
|
11
|
-
s.homepage = "http://
|
|
12
|
-
s.summary = %q{Sift
|
|
13
|
-
s.description = %q{Sift
|
|
10
|
+
s.email = ["support@sift.com"]
|
|
11
|
+
s.homepage = "http://sift.com"
|
|
12
|
+
s.summary = %q{Sift Ruby API Gem}
|
|
13
|
+
s.description = %q{Sift Ruby API. Please see http://sift.com for more details.}
|
|
14
14
|
|
|
15
15
|
s.rubyforge_project = "sift"
|
|
16
16
|
|
|
@@ -19,6 +19,8 @@ Gem::Specification.new do |s|
|
|
|
19
19
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
|
20
20
|
s.require_paths = ["lib"]
|
|
21
21
|
|
|
22
|
+
s.required_ruby_version = '>= 2.7.0'
|
|
23
|
+
|
|
22
24
|
# Gems that must be intalled for sift to compile and build
|
|
23
25
|
s.add_development_dependency "rspec", "~> 3.5"
|
|
24
26
|
s.add_development_dependency "rspec_junit_formatter"
|
|
@@ -26,7 +28,7 @@ Gem::Specification.new do |s|
|
|
|
26
28
|
s.add_development_dependency "webmock", ">= 1.16.0", "< 2"
|
|
27
29
|
|
|
28
30
|
# Gems that must be intalled for sift to work
|
|
29
|
-
s.add_dependency "httparty", ">= 0.
|
|
31
|
+
s.add_dependency "httparty", ">= 0.23.3"
|
|
30
32
|
s.add_dependency "multi_json", ">= 1.0"
|
|
31
33
|
|
|
32
34
|
s.add_development_dependency("rake")
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
require_relative '../spec_helper'
|
|
2
|
+
require 'sift'
|
|
3
|
+
|
|
4
|
+
describe Sift::Client do
|
|
5
|
+
|
|
6
|
+
before :each do
|
|
7
|
+
Sift.api_key = nil
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
it "Successfully submits a $create_account event with KYC, geo, and bot-detection structured fields" do
|
|
11
|
+
response_json = { :status => 0, :error_message => "OK" }
|
|
12
|
+
stub_request(:post, "https://api.siftscience.com/v205/events").
|
|
13
|
+
with { |request|
|
|
14
|
+
parsed_body = JSON.parse(request.body)
|
|
15
|
+
expect(parsed_body["$nationality"]).to eq("US")
|
|
16
|
+
expect(parsed_body["$year_of_birth"]).to eq(1985)
|
|
17
|
+
expect(parsed_body["$kyc"]).to eq({
|
|
18
|
+
"$names_match" => true,
|
|
19
|
+
"$kyc_level" => "$basic",
|
|
20
|
+
"$bin_nationality_match" => true,
|
|
21
|
+
"$provider" => "lexisnexis"
|
|
22
|
+
})
|
|
23
|
+
expect(parsed_body["$geo"]).to eq({
|
|
24
|
+
"$uuid" => "gc-abc-123",
|
|
25
|
+
"$provider" => "geocomply"
|
|
26
|
+
})
|
|
27
|
+
expect(parsed_body["$bot_identification"]).to eq({
|
|
28
|
+
"$result" => "$human",
|
|
29
|
+
"$provider" => "datadome"
|
|
30
|
+
})
|
|
31
|
+
}.to_return(:status => 200, :body => MultiJson.dump(response_json), :headers => {})
|
|
32
|
+
|
|
33
|
+
api_key = "foobar"
|
|
34
|
+
properties = {
|
|
35
|
+
:$type => "$create_account",
|
|
36
|
+
:$user_id => "23056",
|
|
37
|
+
:$nationality => "US",
|
|
38
|
+
:$year_of_birth => 1985,
|
|
39
|
+
:$kyc => {
|
|
40
|
+
:$names_match => true,
|
|
41
|
+
:$kyc_level => "$basic",
|
|
42
|
+
:$bin_nationality_match => true,
|
|
43
|
+
:$provider => "lexisnexis"
|
|
44
|
+
},
|
|
45
|
+
:$geo => {
|
|
46
|
+
:$uuid => "gc-abc-123",
|
|
47
|
+
:$provider => "geocomply"
|
|
48
|
+
},
|
|
49
|
+
:$bot_identification => {
|
|
50
|
+
:$result => "$human",
|
|
51
|
+
:$provider => "datadome"
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
response = Sift::Client.new(:api_key => api_key, :version => "205")
|
|
56
|
+
.track("$create_account", properties)
|
|
57
|
+
expect(response.ok?).to eq(true)
|
|
58
|
+
expect(response.api_status).to eq(0)
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -88,4 +88,50 @@ describe Sift::Client do
|
|
|
88
88
|
|
|
89
89
|
end
|
|
90
90
|
|
|
91
|
+
# Regression tests for verification API version bug fix
|
|
92
|
+
# These tests ensure verification methods use verification_version (1.1)
|
|
93
|
+
# instead of the events API version (205)
|
|
94
|
+
|
|
95
|
+
it "Uses verification API version (1.1) not events API version (205) for verification_send" do
|
|
96
|
+
api_key = "test_key"
|
|
97
|
+
response_json = { :status => 0, :error_message => "OK"}
|
|
98
|
+
|
|
99
|
+
# Should call v1.1, NOT v205
|
|
100
|
+
stub_request(:post, "https://test_key:@api.siftscience.com/v1.1/verification/send")
|
|
101
|
+
.to_return(:status => 200, :body => MultiJson.dump(response_json))
|
|
102
|
+
|
|
103
|
+
# Client defaults: version=205 (events), verification_version=1.1
|
|
104
|
+
response = Sift::Client.new(:api_key => api_key).verification_send(valid_send_properties)
|
|
105
|
+
|
|
106
|
+
expect(response.ok?).to eq(true)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
it "Uses verification API version (1.1) not events API version (205) for verification_resend" do
|
|
110
|
+
api_key = "test_key"
|
|
111
|
+
response_json = { :status => 0, :error_message => "OK"}
|
|
112
|
+
|
|
113
|
+
# Should call v1.1, NOT v205
|
|
114
|
+
stub_request(:post, "https://test_key:@api.siftscience.com/v1.1/verification/resend")
|
|
115
|
+
.to_return(:status => 200, :body => MultiJson.dump(response_json))
|
|
116
|
+
|
|
117
|
+
# Client defaults: version=205 (events), verification_version=1.1
|
|
118
|
+
response = Sift::Client.new(:api_key => api_key).verification_resend(valid_resend_properties)
|
|
119
|
+
|
|
120
|
+
expect(response.ok?).to eq(true)
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
it "Uses verification API version (1.1) not events API version (205) for verification_check" do
|
|
124
|
+
api_key = "test_key"
|
|
125
|
+
response_json = { :status => 0, :error_message => "OK"}
|
|
126
|
+
|
|
127
|
+
# Should call v1.1, NOT v205
|
|
128
|
+
stub_request(:post, "https://test_key:@api.siftscience.com/v1.1/verification/check")
|
|
129
|
+
.to_return(:status => 200, :body => MultiJson.dump(response_json))
|
|
130
|
+
|
|
131
|
+
# Client defaults: version=205 (events), verification_version=1.1
|
|
132
|
+
response = Sift::Client.new(:api_key => api_key).verification_check(valid_check_properties)
|
|
133
|
+
|
|
134
|
+
expect(response.ok?).to eq(true)
|
|
135
|
+
end
|
|
136
|
+
|
|
91
137
|
end
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
require_relative "../spec_helper"
|
|
2
|
+
require "sift"
|
|
3
|
+
require "logger"
|
|
4
|
+
|
|
5
|
+
describe "Sift::Client Configuration Patterns" do
|
|
6
|
+
let(:api_key) { "test_api_key" }
|
|
7
|
+
|
|
8
|
+
it "propagates global Sift::Client configuration to internal clients" do
|
|
9
|
+
Sift::Client.default_timeout 5
|
|
10
|
+
|
|
11
|
+
# Internal executors should inherit this
|
|
12
|
+
expect(Sift::Client.api_client.default_options[:timeout]).to eq(5)
|
|
13
|
+
expect(Sift::Client.api3_client.default_options[:timeout]).to eq(5)
|
|
14
|
+
|
|
15
|
+
# Reset
|
|
16
|
+
Sift::Client.default_timeout 2
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
it "allows independent subclass configurations" do
|
|
20
|
+
class SubclassA < Sift::Client; end
|
|
21
|
+
class SubclassB < Sift::Client; end
|
|
22
|
+
|
|
23
|
+
SubclassA.default_timeout 10
|
|
24
|
+
SubclassB.default_timeout 20
|
|
25
|
+
|
|
26
|
+
expect(SubclassA.api_client.default_options[:timeout]).to eq(10)
|
|
27
|
+
expect(SubclassB.api_client.default_options[:timeout]).to eq(20)
|
|
28
|
+
|
|
29
|
+
# Ensure they didn't leak to parent
|
|
30
|
+
expect(Sift::Client.api_client.default_options[:timeout]).to be <= 5
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
it "propagates complex settings like loggers to subclasses" do
|
|
34
|
+
class LoggingClient < Sift::Client; end
|
|
35
|
+
|
|
36
|
+
logger = Logger.new(nil)
|
|
37
|
+
LoggingClient.logger logger, :debug, :curl
|
|
38
|
+
|
|
39
|
+
expect(LoggingClient.api_client.default_options[:logger]).to eq(logger)
|
|
40
|
+
expect(LoggingClient.api_client.default_options[:log_level]).to eq(:debug)
|
|
41
|
+
expect(LoggingClient.api_client.default_options[:log_format]).to eq(:curl)
|
|
42
|
+
|
|
43
|
+
# Ensure Sift::Client remains untouched
|
|
44
|
+
expect(Sift::Client.api_client.default_options[:logger]).to be_nil
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
it "respects inheritance chain for Decisions and Router" do
|
|
48
|
+
class DecisionClient < Sift::Client; end
|
|
49
|
+
DecisionClient.default_timeout 15
|
|
50
|
+
|
|
51
|
+
client = DecisionClient.new(api_key: api_key, account_id: "acc")
|
|
52
|
+
|
|
53
|
+
# Verify the executor used by the Router is the one from DecisionClient
|
|
54
|
+
expect(DecisionClient.api3_client.default_options[:timeout]).to eq(15)
|
|
55
|
+
|
|
56
|
+
# We want to ensure that when Router.get is called, it uses DecisionClient.api3_client
|
|
57
|
+
# In lib/sift/router.rb, we have:
|
|
58
|
+
# wrap_response(client_class.api3_client.get(path, options))
|
|
59
|
+
|
|
60
|
+
expect(DecisionClient.api3_client).to receive(:get).and_call_original
|
|
61
|
+
|
|
62
|
+
# Mock the request to avoid network calls
|
|
63
|
+
stub_request(:get, /api3.siftscience.com/)
|
|
64
|
+
|
|
65
|
+
client.get_user_decisions("user_1")
|
|
66
|
+
end
|
|
67
|
+
end
|
|
@@ -24,7 +24,16 @@ class EventsAPI
|
|
|
24
24
|
"$brand_name" => "sift",
|
|
25
25
|
"$site_domain" => "sift.com",
|
|
26
26
|
"$site_country" => "US",
|
|
27
|
-
|
|
27
|
+
|
|
28
|
+
"$geo" => {
|
|
29
|
+
"$uuid" => "gc-abc-123",
|
|
30
|
+
"$provider" => "geocomply"
|
|
31
|
+
},
|
|
32
|
+
"$bot_identification" => {
|
|
33
|
+
"$result" => "$human",
|
|
34
|
+
"$provider" => "datadome"
|
|
35
|
+
},
|
|
36
|
+
|
|
28
37
|
# Send this information with a login from a BROWSER client.
|
|
29
38
|
"$browser" => {
|
|
30
39
|
"$user_agent" => "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36",
|
|
@@ -107,13 +116,27 @@ class EventsAPI
|
|
|
107
116
|
|
|
108
117
|
# For marketplaces, use $seller_user_id to identify the seller
|
|
109
118
|
"$seller_user_id" => "slinkys_emporium",
|
|
110
|
-
|
|
119
|
+
|
|
120
|
+
"$kyc" => {
|
|
121
|
+
"$names_match" => true,
|
|
122
|
+
"$bin_nationality_match" => false,
|
|
123
|
+
"$provider" => "prove"
|
|
124
|
+
},
|
|
125
|
+
"$geo" => {
|
|
126
|
+
"$uuid" => "gc-abc-123",
|
|
127
|
+
"$provider" => "geocomply"
|
|
128
|
+
},
|
|
129
|
+
"$bot_identification" => {
|
|
130
|
+
"$result" => "$human",
|
|
131
|
+
"$provider" => "human_security"
|
|
132
|
+
},
|
|
133
|
+
|
|
111
134
|
# Sample Custom Fields
|
|
112
135
|
"digital_wallet" => "apple_pay", # "google_wallet", etc.
|
|
113
136
|
"coupon_code" => "dollarMadness",
|
|
114
137
|
"shipping_choice" => "FedEx Ground Courier",
|
|
115
138
|
"is_first_time_buyer" => false,
|
|
116
|
-
|
|
139
|
+
|
|
117
140
|
# Send this information from a BROWSER client.
|
|
118
141
|
"$browser" => {
|
|
119
142
|
"$user_agent" => "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36",
|
|
@@ -121,7 +144,7 @@ class EventsAPI
|
|
|
121
144
|
"$content_language" => "en-GB"
|
|
122
145
|
}
|
|
123
146
|
}
|
|
124
|
-
|
|
147
|
+
|
|
125
148
|
return @@client.track("$transaction", properties, :warnings => 'true')
|
|
126
149
|
end
|
|
127
150
|
|
|
@@ -233,7 +256,21 @@ class EventsAPI
|
|
|
233
256
|
"coupon_code" => "dollarMadness",
|
|
234
257
|
"shipping_choice" => "FedEx Ground Courier",
|
|
235
258
|
"is_first_time_buyer" => false,
|
|
236
|
-
|
|
259
|
+
|
|
260
|
+
"$kyc" => {
|
|
261
|
+
"$names_match" => true,
|
|
262
|
+
"$kyc_level" => "$basic",
|
|
263
|
+
"$provider" => "lexisnexis"
|
|
264
|
+
},
|
|
265
|
+
"$geo" => {
|
|
266
|
+
"$uuid" => "gc-abc-123",
|
|
267
|
+
"$provider" => "geocomply"
|
|
268
|
+
},
|
|
269
|
+
"$bot_identification" => {
|
|
270
|
+
"$result" => "$human",
|
|
271
|
+
"$provider" => "datadome"
|
|
272
|
+
},
|
|
273
|
+
|
|
237
274
|
# Send this information from a BROWSER client.
|
|
238
275
|
"$browser" => {
|
|
239
276
|
"$user_agent" => "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36",
|
|
@@ -241,7 +278,7 @@ class EventsAPI
|
|
|
241
278
|
"$content_language" => "en-GB"
|
|
242
279
|
}
|
|
243
280
|
}
|
|
244
|
-
|
|
281
|
+
|
|
245
282
|
return @@client.track("$create_order", properties)
|
|
246
283
|
end
|
|
247
284
|
|
|
@@ -457,7 +494,23 @@ class EventsAPI
|
|
|
457
494
|
|
|
458
495
|
"$social_sign_on_type" => "$twitter",
|
|
459
496
|
"$account_types" => ["merchant", "premium"],
|
|
460
|
-
|
|
497
|
+
|
|
498
|
+
"$nationality" => "US",
|
|
499
|
+
"$year_of_birth" => 1985,
|
|
500
|
+
"$kyc" => {
|
|
501
|
+
"$names_match" => true,
|
|
502
|
+
"$kyc_level" => "$full",
|
|
503
|
+
"$provider" => "prove"
|
|
504
|
+
},
|
|
505
|
+
"$geo" => {
|
|
506
|
+
"$uuid" => "gc-abc-123",
|
|
507
|
+
"$provider" => "geocomply"
|
|
508
|
+
},
|
|
509
|
+
"$bot_identification" => {
|
|
510
|
+
"$result" => "$human",
|
|
511
|
+
"$provider" => "datadome"
|
|
512
|
+
},
|
|
513
|
+
|
|
461
514
|
# Send this information from a BROWSER client.
|
|
462
515
|
"$browser" => {
|
|
463
516
|
"$user_agent" => "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36",
|
|
@@ -465,7 +518,7 @@ class EventsAPI
|
|
|
465
518
|
"$content_language" => "en-GB"
|
|
466
519
|
}
|
|
467
520
|
}
|
|
468
|
-
|
|
521
|
+
|
|
469
522
|
return @@client.track("$update_account", properties)
|
|
470
523
|
end
|
|
471
524
|
|
|
@@ -585,7 +638,23 @@ class EventsAPI
|
|
|
585
638
|
|
|
586
639
|
"$social_sign_on_type" => "$twitter",
|
|
587
640
|
"$account_types" => ["merchant", "premium"],
|
|
588
|
-
|
|
641
|
+
|
|
642
|
+
"$nationality" => "US",
|
|
643
|
+
"$year_of_birth" => 1985,
|
|
644
|
+
"$kyc" => {
|
|
645
|
+
"$names_match" => true,
|
|
646
|
+
"$kyc_level" => "$basic",
|
|
647
|
+
"$provider" => "lexisnexis"
|
|
648
|
+
},
|
|
649
|
+
"$geo" => {
|
|
650
|
+
"$uuid" => "gc-abc-123",
|
|
651
|
+
"$provider" => "geocomply"
|
|
652
|
+
},
|
|
653
|
+
"$bot_identification" => {
|
|
654
|
+
"$result" => "$human",
|
|
655
|
+
"$provider" => "datadome"
|
|
656
|
+
},
|
|
657
|
+
|
|
589
658
|
# Suggested Custom Fields
|
|
590
659
|
"twitter_handle" => "billyjones",
|
|
591
660
|
"work_phone" => "1-347-555-5921",
|
|
@@ -618,7 +687,14 @@ class EventsAPI
|
|
|
618
687
|
"$verified_event" => "$login",
|
|
619
688
|
"$reason" => "$automated_rule", # Verification was triggered based on risk score
|
|
620
689
|
"$verification_type" => "$sms",
|
|
621
|
-
"$verified_value" => "14155551212"
|
|
690
|
+
"$verified_value" => "14155551212",
|
|
691
|
+
|
|
692
|
+
"$kyc" => {
|
|
693
|
+
"$names_match" => true,
|
|
694
|
+
"$kyc_level" => "$basic",
|
|
695
|
+
"$bin_nationality_match" => false,
|
|
696
|
+
"$provider" => "lexisnexis"
|
|
697
|
+
}
|
|
622
698
|
}
|
|
623
699
|
|
|
624
700
|
return @@client.track("$verification", properties)
|
|
@@ -828,7 +904,21 @@ class EventsAPI
|
|
|
828
904
|
"coupon_code" => "dollarMadness",
|
|
829
905
|
"shipping_choice" => "FedEx Ground Courier",
|
|
830
906
|
"is_first_time_buyer" => false,
|
|
831
|
-
|
|
907
|
+
|
|
908
|
+
"$kyc" => {
|
|
909
|
+
"$names_match" => true,
|
|
910
|
+
"$kyc_level" => "$basic",
|
|
911
|
+
"$provider" => "lexisnexis"
|
|
912
|
+
},
|
|
913
|
+
"$geo" => {
|
|
914
|
+
"$uuid" => "gc-abc-123",
|
|
915
|
+
"$provider" => "geocomply"
|
|
916
|
+
},
|
|
917
|
+
"$bot_identification" => {
|
|
918
|
+
"$result" => "$human",
|
|
919
|
+
"$provider" => "datadome"
|
|
920
|
+
},
|
|
921
|
+
|
|
832
922
|
# Send this information from a BROWSER client.
|
|
833
923
|
"$browser" => {
|
|
834
924
|
"$user_agent" => "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36",
|
|
@@ -836,7 +926,7 @@ class EventsAPI
|
|
|
836
926
|
"$content_language" => "en-GB"
|
|
837
927
|
}
|
|
838
928
|
}
|
|
839
|
-
|
|
929
|
+
|
|
840
930
|
return @@client.track("$update_order", properties)
|
|
841
931
|
end
|
|
842
932
|
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: sift
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 4.
|
|
4
|
+
version: 4.7.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Fred Sadaghiani
|
|
@@ -10,7 +10,7 @@ authors:
|
|
|
10
10
|
autorequire:
|
|
11
11
|
bindir: bin
|
|
12
12
|
cert_chain: []
|
|
13
|
-
date:
|
|
13
|
+
date: 2026-09-30 00:00:00.000000000 Z
|
|
14
14
|
dependencies:
|
|
15
15
|
- !ruby/object:Gem::Dependency
|
|
16
16
|
name: rspec
|
|
@@ -80,14 +80,14 @@ dependencies:
|
|
|
80
80
|
requirements:
|
|
81
81
|
- - ">="
|
|
82
82
|
- !ruby/object:Gem::Version
|
|
83
|
-
version: 0.
|
|
83
|
+
version: 0.23.3
|
|
84
84
|
type: :runtime
|
|
85
85
|
prerelease: false
|
|
86
86
|
version_requirements: !ruby/object:Gem::Requirement
|
|
87
87
|
requirements:
|
|
88
88
|
- - ">="
|
|
89
89
|
- !ruby/object:Gem::Version
|
|
90
|
-
version: 0.
|
|
90
|
+
version: 0.23.3
|
|
91
91
|
- !ruby/object:Gem::Dependency
|
|
92
92
|
name: multi_json
|
|
93
93
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -116,9 +116,9 @@ dependencies:
|
|
|
116
116
|
- - ">="
|
|
117
117
|
- !ruby/object:Gem::Version
|
|
118
118
|
version: '0'
|
|
119
|
-
description: Sift
|
|
119
|
+
description: Sift Ruby API. Please see http://sift.com for more details.
|
|
120
120
|
email:
|
|
121
|
-
- support@
|
|
121
|
+
- support@sift.com
|
|
122
122
|
executables: []
|
|
123
123
|
extensions: []
|
|
124
124
|
extra_rdoc_files: []
|
|
@@ -156,7 +156,9 @@ files:
|
|
|
156
156
|
- spec/unit/client_label_spec.rb
|
|
157
157
|
- spec/unit/client_psp_merchant_spec.rb
|
|
158
158
|
- spec/unit/client_spec.rb
|
|
159
|
+
- spec/unit/client_structured_fields_spec.rb
|
|
159
160
|
- spec/unit/client_validationapi_spec.rb
|
|
161
|
+
- spec/unit/configuration_spec.rb
|
|
160
162
|
- spec/unit/router_spec.rb
|
|
161
163
|
- spec/unit/validate/decision_spec.rb
|
|
162
164
|
- spec/unit/validate/primitive_spec.rb
|
|
@@ -167,7 +169,7 @@ files:
|
|
|
167
169
|
- test_integration_app/psp_merchants_api/test_psp_merchant_api.rb
|
|
168
170
|
- test_integration_app/score_api/test_score_api.rb
|
|
169
171
|
- test_integration_app/verification_api/test_verification_api.rb
|
|
170
|
-
homepage: http://
|
|
172
|
+
homepage: http://sift.com
|
|
171
173
|
licenses: []
|
|
172
174
|
metadata: {}
|
|
173
175
|
post_install_message:
|
|
@@ -178,7 +180,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
178
180
|
requirements:
|
|
179
181
|
- - ">="
|
|
180
182
|
- !ruby/object:Gem::Version
|
|
181
|
-
version:
|
|
183
|
+
version: 2.7.0
|
|
182
184
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
183
185
|
requirements:
|
|
184
186
|
- - ">="
|
|
@@ -188,5 +190,5 @@ requirements: []
|
|
|
188
190
|
rubygems_version: 3.1.6
|
|
189
191
|
signing_key:
|
|
190
192
|
specification_version: 4
|
|
191
|
-
summary: Sift
|
|
193
|
+
summary: Sift Ruby API Gem
|
|
192
194
|
test_files: []
|