opentrust 0.1.1

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: de6fbba7e9eebcb29951eabb63212c48696ed16e3ee6c0fbf98e4be0f3b8ba9f
4
+ data.tar.gz: 273707de6ff17306d6a8284d02a1d1fef3e85459d5be6f27a81991f5ebfb0571
5
+ SHA512:
6
+ metadata.gz: 0e60f2550129dace80d3e3e5ae14db4f178301bacea8e470544a3cc6f3b98dbf29e0c1a7c1a8a124a76b1ff5317b64afb8106724560d398aca8e5e2a3f5302a6
7
+ data.tar.gz: e7e639909140db87ebd47b66df031e5805218420a9a5be1ded69c48d0b8c4eb1b75d2f4f346d5924ac35e7aa32df39242edd6483752c765da7abd4632c9d102f
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Parity Cube Pvt Ltd and OpenTrust contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,42 @@
1
+ # opentrust (Ruby)
2
+
3
+ Check mobile numbers against experiences shared by other Indian merchants, and share your own. No dependencies.
4
+
5
+ ```ruby
6
+ # Gemfile
7
+ gem "opentrust"
8
+ ```
9
+
10
+ ```ruby
11
+ require "opentrust"
12
+
13
+ client = OpenTrust::Client.new(api_key: ENV["OPENTRUST_API_KEY"]) # or just set OPENTRUST_API_KEY
14
+
15
+ result = client.lookup("98765 43210") # uses 1 check
16
+ result.reported? # => true / false
17
+ result.report_count # => 2
18
+ result.reports.each { |r| puts "#{r.reason_code}: #{r.notes}" }
19
+ result.notice # keep this in front of anyone who sees the result
20
+ result.checks_remaining
21
+
22
+ client.lookup_many(%w[9876543210 9000000001]) # up to 100, all or nothing
23
+ client.report(phone: "9876543210", reason_code: "cod_refusal", notes: "Refused 3 COD orders in March")
24
+ client.me # => { "checks_remaining" => ..., ... }
25
+ ```
26
+
27
+ ## Please don't block checkout on OpenTrust
28
+
29
+ Results are unverified observations from individual merchants. Use them as one input, have a person review decisions that matter, and fail open if OpenTrust is unreachable:
30
+
31
+ ```ruby
32
+ begin
33
+ result = client.lookup(order.phone)
34
+ flag_for_review(order) if result.reported?
35
+ rescue OpenTrust::Error => e
36
+ Rails.logger.warn("OpenTrust unavailable: #{e.message}") # carry on
37
+ end
38
+ ```
39
+
40
+ Errors: `AuthenticationError` (401), `NoChecksError` (402, write to us and we'll raise your limit), `NotApprovedError` (403), `DuplicateReportError` (409), `InvalidRequestError` (422), `RateLimitError` (429), `ServerError` (5xx), `ConnectionError` (network/timeout). All inherit from `OpenTrust::Error` and carry `status` and `code`.
41
+
42
+ Timeout defaults to 5 seconds (`timeout:`). Docs: https://opentrust.in/docs
@@ -0,0 +1,100 @@
1
+ require "json"
2
+ require "net/http"
3
+ require "uri"
4
+
5
+ module OpenTrust
6
+ Report = Struct.new(:reason_code, :reason, :notes, :reported_at, :reporter_verified, keyword_init: true)
7
+
8
+ # The result of checking one number. Reports are unverified observations from individual merchants,
9
+ # not a finding about anyone. Use them as one input among others.
10
+ Lookup = Struct.new(:phone_last4, :report_count, :reports, :notice, :checks_remaining, keyword_init: true) do
11
+ def reported?
12
+ report_count.to_i.positive?
13
+ end
14
+ end
15
+
16
+ Receipt = Struct.new(:id, :phone_last4, keyword_init: true)
17
+
18
+ class Client
19
+ DEFAULT_BASE_URL = "https://opentrust.in/api/v1".freeze
20
+
21
+ # adapter: optional callable (method, url, headers, body) -> [status, body_string], mainly for tests.
22
+ def initialize(api_key: ENV["OPENTRUST_API_KEY"], base_url: ENV.fetch("OPENTRUST_BASE_URL", DEFAULT_BASE_URL), timeout: 5, adapter: nil)
23
+ raise ArgumentError, "api_key is required (or set OPENTRUST_API_KEY)" if api_key.to_s.empty?
24
+
25
+ @api_key = api_key
26
+ @base_url = base_url.chomp("/")
27
+ @timeout = timeout
28
+ @adapter = adapter
29
+ end
30
+
31
+ # Check one number. Uses one check from your allowance.
32
+ def lookup(phone)
33
+ lookup_from(request(:post, "/lookups", phone: phone))
34
+ end
35
+
36
+ # Check up to 100 numbers. All or nothing: if you don't have enough checks, none are used.
37
+ def lookup_many(phones)
38
+ body = request(:post, "/lookups/batch", phones: Array(phones))
39
+ body.fetch("results").map { |r| lookup_from(r.merge("notice" => body["notice"], "checks_remaining" => body["checks_remaining"])) }
40
+ end
41
+
42
+ # Share an experience with a number. Optional, and doesn't change your allowance.
43
+ def report(phone:, reason_code:, notes: nil)
44
+ body = request(:post, "/reports", { phone: phone, reason_code: reason_code, notes: notes }.compact)
45
+ Receipt.new(id: body["id"], phone_last4: body["phone_last4"])
46
+ end
47
+
48
+ # Your remaining checks and usage, as a Hash.
49
+ def me
50
+ request(:get, "/me")
51
+ end
52
+
53
+ private
54
+
55
+ def lookup_from(h)
56
+ Lookup.new(
57
+ phone_last4: h["phone_last4"], report_count: h["report_count"], notice: h["notice"], checks_remaining: h["checks_remaining"],
58
+ reports: Array(h["reports"]).map { |r| Report.new(reason_code: r["reason_code"], reason: r["reason"], notes: r["notes"], reported_at: r["reported_at"], reporter_verified: r["reporter_verified"]) }
59
+ )
60
+ end
61
+
62
+ def request(verb, path, payload = nil)
63
+ headers = { "Authorization" => "Bearer #{@api_key}", "Accept" => "application/json", "User-Agent" => "opentrust-ruby/#{VERSION}" }
64
+ headers["Content-Type"] = "application/json" if payload
65
+ status, text = perform(verb, "#{@base_url}#{path}", headers, payload && JSON.generate(payload))
66
+ body = begin JSON.parse(text.to_s) rescue {} end
67
+ raise_for(status, body) unless (200..299).cover?(status)
68
+ body
69
+ end
70
+
71
+ def perform(verb, url, headers, body)
72
+ return @adapter.call(verb, url, headers, body) if @adapter
73
+
74
+ uri = URI(url)
75
+ klass = verb == :get ? Net::HTTP::Get : Net::HTTP::Post
76
+ req = klass.new(uri, headers)
77
+ req.body = body if body
78
+ res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == "https", open_timeout: @timeout, read_timeout: @timeout) { |http| http.request(req) }
79
+ [res.code.to_i, res.body]
80
+ rescue Timeout::Error, SystemCallError, SocketError, OpenSSL::SSL::SSLError, EOFError => e
81
+ raise ConnectionError.new("could not reach OpenTrust: #{e.class}: #{e.message}")
82
+ end
83
+
84
+ def raise_for(status, body)
85
+ err = body["error"].is_a?(Hash) ? body["error"] : {}
86
+ message = err["message"] || "OpenTrust returned HTTP #{status}"
87
+ klass = case status
88
+ when 401 then AuthenticationError
89
+ when 402 then NoChecksError
90
+ when 403 then NotApprovedError
91
+ when 409 then DuplicateReportError
92
+ when 422 then InvalidRequestError
93
+ when 429 then RateLimitError
94
+ when 500..599 then ServerError
95
+ else Error
96
+ end
97
+ raise klass.new(message, status: status, code: err["code"])
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,21 @@
1
+ module OpenTrust
2
+ # Base class. Rescue this to handle anything that goes wrong talking to OpenTrust.
3
+ class Error < StandardError
4
+ attr_reader :status, :code
5
+
6
+ def initialize(message = nil, status: nil, code: nil)
7
+ super(message)
8
+ @status = status
9
+ @code = code
10
+ end
11
+ end
12
+
13
+ class ConnectionError < Error; end # network failure or timeout
14
+ class AuthenticationError < Error; end # 401: missing or invalid API key
15
+ class NoChecksError < Error; end # 402: allowance used up
16
+ class NotApprovedError < Error; end # 403: account not approved or suspended
17
+ class DuplicateReportError < Error; end # 409: you already shared this number
18
+ class InvalidRequestError < Error; end # 422: bad number, reason code or note
19
+ class RateLimitError < Error; end # 429
20
+ class ServerError < Error; end # 5xx
21
+ end
@@ -0,0 +1,3 @@
1
+ module OpenTrust
2
+ VERSION = "0.1.1"
3
+ end
data/lib/opentrust.rb ADDED
@@ -0,0 +1,3 @@
1
+ require_relative "opentrust/version"
2
+ require_relative "opentrust/errors"
3
+ require_relative "opentrust/client"
metadata ADDED
@@ -0,0 +1,51 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: opentrust
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - OpenTrust
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2026-10-09 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Check mobile numbers against experiences shared by other Indian merchants,
14
+ and share your own. A small, dependency-free client for https://opentrust.in.
15
+ email:
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - LICENSE
21
+ - README.md
22
+ - lib/opentrust.rb
23
+ - lib/opentrust/client.rb
24
+ - lib/opentrust/errors.rb
25
+ - lib/opentrust/version.rb
26
+ homepage: https://opentrust.in/docs
27
+ licenses:
28
+ - MIT
29
+ metadata:
30
+ documentation_uri: https://opentrust.in/docs
31
+ rubygems_mfa_required: 'true'
32
+ post_install_message:
33
+ rdoc_options: []
34
+ require_paths:
35
+ - lib
36
+ required_ruby_version: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '2.7'
41
+ required_rubygems_version: !ruby/object:Gem::Requirement
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ requirements: []
47
+ rubygems_version: 3.4.10
48
+ signing_key:
49
+ specification_version: 4
50
+ summary: Ruby client for the OpenTrust API
51
+ test_files: []