straddle_pay 0.1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 23aebc721853de383064f4e317d3f9a6e73705d06f2f4d0e8376873dbf05bace
4
+ data.tar.gz: 1996288b18fc0434b255e7638e8e75b83db350687eeca1dc93cd46f091525ac9
5
+ SHA512:
6
+ metadata.gz: 36203c68e86e4df953735bdcb7b811a7d9db9c7867a8fa10c1ace55663ce894146621983a7d56f5490e8b1dc9cd62be65a27bb314bd4aed467e63215d78e0967
7
+ data.tar.gz: ff082a8507a7146e55ddee23a5286834212d7b97924821ce55f7a6c4c516c9653897b480173d5e363c333d6050504cd13399450972405ff2665d31bfb3ef9b72
data/CHANGELOG.md ADDED
@@ -0,0 +1,10 @@
1
+ ## [0.1.0] - 2026-02-19
2
+
3
+ ### Added
4
+ - Initial release
5
+ - Full Straddle API v1 coverage (59 endpoints)
6
+ - Faraday-based HTTP client with configurable timeouts
7
+ - Global config with per-instance overrides
8
+ - Error hierarchy with validation detail accessors
9
+ - Auto-unwrapped response envelope
10
+ - Rails Engine support
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2026 dpaluy
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
13
+ all 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
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,229 @@
1
+ # StraddlePay
2
+
3
+ Ruby client for the [Straddle](https://straddle.dev) payment infrastructure API.
4
+
5
+ [![Gem Version](https://badge.fury.io/rb/straddle_pay.svg)](https://badge.fury.io/rb/straddle_pay)
6
+ [![ci](https://github.com/dpaluy/straddle_pay/actions/workflows/ci.yml/badge.svg)](https://github.com/dpaluy/straddle_pay/actions/workflows/ci.yml)
7
+
8
+ Lightweight, idiomatic Ruby wrapper using Faraday. No auto-generated code, no type system overhead — just clean Hash responses.
9
+
10
+ ## Installation
11
+
12
+ ```
13
+ bundle add straddle_pay
14
+ ```
15
+
16
+ ## Usage
17
+
18
+ Configure once:
19
+
20
+ ```ruby
21
+ StraddlePay.configure do |config|
22
+ config.api_key = ENV.fetch("STRADDLE_API_KEY")
23
+ config.base_url = StraddlePay::Config::PRODUCTION_URL # default: sandbox
24
+ end
25
+ ```
26
+
27
+ Per-instance overrides:
28
+
29
+ ```ruby
30
+ client = StraddlePay::Client.new(
31
+ api_key: "sk_different_key",
32
+ base_url: "https://production.straddle.io"
33
+ )
34
+ ```
35
+
36
+ ### Environments
37
+
38
+ | Environment | URL |
39
+ |-------------|-----|
40
+ | Sandbox (default) | `https://sandbox.straddle.io` |
41
+ | Production | `https://production.straddle.io` |
42
+
43
+ ### Rails
44
+
45
+ Create `config/initializers/straddle_pay.rb`:
46
+
47
+ ```ruby
48
+ StraddlePay.configure do |config|
49
+ config.api_key = Rails.application.credentials.straddle_api_key
50
+ config.base_url = StraddlePay::Config::PRODUCTION_URL
51
+ config.logger = Rails.logger
52
+ end
53
+ ```
54
+
55
+ ### Charges
56
+
57
+ ```ruby
58
+ client = StraddlePay::Client.new
59
+
60
+ charge = client.charges.create(
61
+ paykey: "pk_abc123",
62
+ amount: 10_000,
63
+ currency: "usd",
64
+ description: "Monthly subscription",
65
+ payment_date: "2026-03-01",
66
+ consent_type: "internet",
67
+ device: { ip_address: "192.168.1.1" },
68
+ external_id: "order_456"
69
+ )
70
+ charge["id"] # => "ch_..."
71
+
72
+ client.charges.get("ch_abc123")
73
+ client.charges.update("ch_abc123", amount: 15_000)
74
+ client.charges.cancel("ch_abc123")
75
+ client.charges.hold("ch_abc123")
76
+ client.charges.release("ch_abc123")
77
+ client.charges.unmask("ch_abc123")
78
+ ```
79
+
80
+ ### Payouts
81
+
82
+ ```ruby
83
+ payout = client.payouts.create(
84
+ paykey: "pk_abc123",
85
+ amount: 5_000,
86
+ currency: "usd",
87
+ description: "Vendor payment",
88
+ payment_date: "2026-03-01",
89
+ device: { ip_address: "192.168.1.1" },
90
+ external_id: "payout_789"
91
+ )
92
+
93
+ # Cancel/hold/release require a reason
94
+ client.payouts.cancel("po_abc123", reason: "Duplicate payment")
95
+ client.payouts.hold("po_abc123", reason: "Under review")
96
+ client.payouts.release("po_abc123", reason: "Review complete")
97
+ ```
98
+
99
+ ### Customers
100
+
101
+ ```ruby
102
+ customer = client.customers.create(
103
+ name: "John Doe",
104
+ type: "individual",
105
+ email: "john@example.com",
106
+ phone: "+15551234567",
107
+ device: { ip_address: "192.168.1.1" }
108
+ )
109
+
110
+ customers = client.customers.list(page_number: 1, page_size: 25)
111
+
112
+ # Identity review
113
+ review = client.customers.reviews.get("cust_abc123")
114
+ client.customers.reviews.decision("cust_abc123", status: "approved")
115
+ client.customers.reviews.refresh("cust_abc123")
116
+ ```
117
+
118
+ ### Bridge (Bank Account Linking)
119
+
120
+ ```ruby
121
+ session = client.bridge.initialize_session(customer_id: "cust_abc123")
122
+
123
+ # Direct bank account link
124
+ client.bridge.links.bank_account(
125
+ customer_id: "cust_abc123",
126
+ account_number: "1234567890",
127
+ routing_number: "021000021",
128
+ account_type: "checking"
129
+ )
130
+
131
+ # Or via Plaid/Quiltt
132
+ client.bridge.links.plaid(customer_id: "cust_abc123", plaid_token: "plaid_token")
133
+ client.bridge.links.quiltt(customer_id: "cust_abc123", quiltt_token: "quiltt_token")
134
+ ```
135
+
136
+ ### Paykeys
137
+
138
+ ```ruby
139
+ client.paykeys.get("pk_abc123")
140
+ client.paykeys.list
141
+ client.paykeys.reveal("pk_abc123")
142
+ client.paykeys.cancel("pk_abc123")
143
+ ```
144
+
145
+ ### Embedded Accounts
146
+
147
+ ```ruby
148
+ org = client.embed.organizations.create(name: "Partner Corp")
149
+
150
+ account = client.embed.accounts.create(
151
+ organization_id: org["id"],
152
+ account_type: "standard",
153
+ business_profile: { name: "Partner Corp" },
154
+ access_level: "standard"
155
+ )
156
+ client.embed.accounts.onboard(account["id"], terms_of_service: { accepted: true })
157
+
158
+ client.embed.representatives.create(
159
+ account_id: account["id"],
160
+ first_name: "Jane", last_name: "Smith",
161
+ email: "jane@partner.com", dob: "1985-06-15",
162
+ mobile_number: "+15559876543", relationship: "owner", ssn_last4: "4321"
163
+ )
164
+
165
+ client.embed.linked_bank_accounts.create(
166
+ account_id: account["id"],
167
+ bank_account: { account_number: "9876543210", routing_number: "021000021" },
168
+ description: "Operating account"
169
+ )
170
+ ```
171
+
172
+ ### Scoped Requests
173
+
174
+ Pass `straddle_account_id` to scope requests to an embedded account:
175
+
176
+ ```ruby
177
+ client.charges.create(
178
+ paykey: "pk_abc",
179
+ amount: 10_000,
180
+ currency: "usd",
181
+ description: "Scoped charge",
182
+ payment_date: "2026-03-01",
183
+ consent_type: "internet",
184
+ device: { ip_address: "1.2.3.4" },
185
+ external_id: "ext_1",
186
+ straddle_account_id: "acct_partner123"
187
+ )
188
+ ```
189
+
190
+ Headers `request_id`, `correlation_id`, and `idempotency_key` are also supported on all methods.
191
+
192
+ ## Error Handling
193
+
194
+ ```ruby
195
+ begin
196
+ client.charges.create(...)
197
+ rescue StraddlePay::AuthenticationError => e
198
+ # 401 or 403
199
+ rescue StraddlePay::ClientError => e
200
+ # 400, 404, 409, 422
201
+ e.error_type # => "validation_error"
202
+ e.error_items # => [{"reference" => "email", "detail" => "is invalid"}]
203
+ rescue StraddlePay::RateLimitError
204
+ # 429 - retry with backoff
205
+ rescue StraddlePay::ServerError
206
+ # 500+
207
+ rescue StraddlePay::NetworkError
208
+ # Timeout or connection failure
209
+ end
210
+ ```
211
+
212
+ ### API Reference
213
+
214
+ Full endpoint details: [straddle.dev/api-reference](https://straddle.dev/api-reference)
215
+
216
+ ## Development
217
+
218
+ ```
219
+ bundle exec rake test
220
+ bundle exec rubocop
221
+ ```
222
+
223
+ ## Contributing
224
+
225
+ Bug reports and pull requests are welcome on GitHub at https://github.com/dpaluy/straddle_pay.
226
+
227
+ ## License
228
+
229
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -0,0 +1,136 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "faraday"
4
+ require "json"
5
+
6
+ module StraddlePay
7
+ class Client
8
+ attr_reader :api_key, :base_url
9
+
10
+ def initialize(api_key: nil, base_url: nil, logger: nil, open_timeout: nil, read_timeout: nil)
11
+ configuration = StraddlePay.config
12
+ @api_key = api_key || configuration.api_key
13
+ @base_url = base_url || configuration.base_url
14
+ @logger = logger || configuration.logger
15
+ @open_timeout = open_timeout || configuration.open_timeout
16
+ @read_timeout = read_timeout || configuration.read_timeout
17
+
18
+ raise ArgumentError, "Missing API key for StraddlePay" if @api_key.to_s.empty?
19
+ end
20
+
21
+ def customers = @customers ||= Resources::Customers.new(self)
22
+ def bridge = @bridge ||= Resources::Bridge.new(self)
23
+ def paykeys = @paykeys ||= Resources::Paykeys.new(self)
24
+ def charges = @charges ||= Resources::Charges.new(self)
25
+ def payouts = @payouts ||= Resources::Payouts.new(self)
26
+ def payments = @payments ||= Resources::Payments.new(self)
27
+ def funding_events = @funding_events ||= Resources::FundingEvents.new(self)
28
+ def reports = @reports ||= Resources::Reports.new(self)
29
+ def embed = @embed ||= Resources::Embed.new(self)
30
+
31
+ def get(path, params: nil, headers: {})
32
+ request(:get, path, params: params, headers: headers)
33
+ end
34
+
35
+ def post(path, body = nil, headers: {})
36
+ request(:post, path, body: body, headers: headers)
37
+ end
38
+
39
+ def put(path, body = nil, headers: {})
40
+ request(:put, path, body: body, headers: headers)
41
+ end
42
+
43
+ def patch(path, body = nil, headers: {})
44
+ request(:patch, path, body: body, headers: headers)
45
+ end
46
+
47
+ def delete(path, body = nil, headers: {})
48
+ request(:delete, path, body: body, headers: headers)
49
+ end
50
+
51
+ private
52
+
53
+ def request(method, path, body: nil, params: nil, headers: {})
54
+ response = connection.public_send(method, path) do |req|
55
+ apply_headers(req, headers)
56
+ apply_body(req, body) if body
57
+ req.params.update(params) if params
58
+ end
59
+ log_response(method, path, response)
60
+ handle_response(response)
61
+ rescue Faraday::TimeoutError, Faraday::ConnectionFailed => e
62
+ raise NetworkError, "Network error: #{e.message}"
63
+ end
64
+
65
+ def connection
66
+ @connection ||= Faraday.new(url: @base_url) do |f|
67
+ f.options.timeout = @read_timeout
68
+ f.options.open_timeout = @open_timeout
69
+ f.adapter Faraday.default_adapter
70
+ end
71
+ end
72
+
73
+ def apply_headers(req, extra = {})
74
+ req.headers["Authorization"] = "Bearer #{@api_key}"
75
+ req.headers["Content-Type"] = "application/json"
76
+ req.headers["Accept"] = "application/json"
77
+ extra.each { |k, v| req.headers[k.to_s] = v.to_s if v }
78
+ end
79
+
80
+ def apply_body(req, body)
81
+ req.headers["Content-Type"] = "application/json"
82
+ req.body = JSON.generate(body)
83
+ end
84
+
85
+ def handle_response(response)
86
+ body = parse_body(response.body)
87
+ return unwrap_data(body) if success?(response.status)
88
+
89
+ raise_error(response.status, body)
90
+ end
91
+
92
+ def unwrap_data(body)
93
+ return body unless body.is_a?(Hash) && body.key?("data")
94
+
95
+ body["data"]
96
+ end
97
+
98
+ def success?(status)
99
+ (200..299).cover?(status)
100
+ end
101
+
102
+ def parse_body(body)
103
+ return {} if body.nil? || (body.is_a?(String) && body.empty?)
104
+ return body unless body.is_a?(String)
105
+
106
+ JSON.parse(body)
107
+ rescue JSON::ParserError
108
+ body
109
+ end
110
+
111
+ def raise_error(status, body)
112
+ message = error_message(status, body)
113
+ case status
114
+ when 401, 403 then raise AuthenticationError.new(message, status: status, body: body)
115
+ when 400, 404, 409, 422 then raise ClientError.new(message, status: status, body: body)
116
+ when 429 then raise RateLimitError.new(message, status: status, body: body)
117
+ when 500..599 then raise ServerError.new(message, status: status, body: body)
118
+ else raise Error.new(message, status: status, body: body)
119
+ end
120
+ end
121
+
122
+ def error_message(status, body)
123
+ detail = if body.is_a?(Hash)
124
+ err = body["error"] || {}
125
+ err.is_a?(Hash) ? (err["detail"] || err["title"] || err.to_json) : err.to_s
126
+ else
127
+ body.to_s
128
+ end
129
+ "HTTP #{status}: #{detail}"
130
+ end
131
+
132
+ def log_response(method, path, response)
133
+ @logger&.debug { "StraddlePay #{method.upcase} #{path} -> #{response.status}" }
134
+ end
135
+ end
136
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "logger"
4
+
5
+ module StraddlePay
6
+ class Config
7
+ SANDBOX_URL = "https://sandbox.straddle.io"
8
+ PRODUCTION_URL = "https://production.straddle.io"
9
+
10
+ attr_accessor :api_key, :base_url, :open_timeout, :read_timeout
11
+ attr_writer :logger
12
+
13
+ def initialize
14
+ @api_key = ENV.fetch("STRADDLE_API_KEY", nil)
15
+ @base_url = ENV.fetch("STRADDLE_BASE_URL", SANDBOX_URL)
16
+ @open_timeout = integer_or_default("STRADDLE_OPEN_TIMEOUT", 5)
17
+ @read_timeout = integer_or_default("STRADDLE_READ_TIMEOUT", 30)
18
+ end
19
+
20
+ def logger
21
+ @logger ||= (defined?(Rails) && Rails.respond_to?(:logger) && Rails.logger) || Logger.new($stderr)
22
+ end
23
+
24
+ private
25
+
26
+ def integer_or_default(key, default)
27
+ Integer(ENV.fetch(key, default))
28
+ rescue StandardError
29
+ default
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module StraddlePay
4
+ if defined?(Rails)
5
+ class Engine < ::Rails::Engine
6
+ isolate_namespace StraddlePay
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module StraddlePay
4
+ class Error < StandardError
5
+ attr_reader :status, :body, :error_type, :error_items
6
+
7
+ def initialize(message = nil, status: nil, body: nil)
8
+ error_hash = body.is_a?(Hash) ? (body["error"] || {}) : {}
9
+ @error_type = error_hash["type"] if error_hash.is_a?(Hash)
10
+ @error_items = (error_hash.is_a?(Hash) ? error_hash["items"] : nil) || []
11
+ super(message)
12
+ @status = status
13
+ @body = body
14
+ end
15
+ end
16
+
17
+ class AuthenticationError < Error; end
18
+ class ClientError < Error; end
19
+ class RateLimitError < Error; end
20
+ class ServerError < Error; end
21
+ class NetworkError < Error; end
22
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module StraddlePay
4
+ module Resources
5
+ class Base
6
+ HEADER_KEYS = {
7
+ straddle_account_id: "Straddle-Account-Id",
8
+ request_id: "Request-Id",
9
+ correlation_id: "Correlation-Id",
10
+ idempotency_key: "Idempotency-Key"
11
+ }.freeze
12
+
13
+ def initialize(client)
14
+ @client = client
15
+ end
16
+
17
+ private
18
+
19
+ def extract_headers(params)
20
+ headers = {}
21
+ HEADER_KEYS.each do |key, header|
22
+ headers[header] = params.delete(key).to_s if params.key?(key)
23
+ end
24
+ headers
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module StraddlePay
4
+ module Resources
5
+ class Bridge < Base
6
+ def links = @links ||= BridgeLinks.new(@client)
7
+
8
+ def initialize_session(customer_id:, **options)
9
+ payload = { customer_id: customer_id, **options }.compact
10
+ headers = extract_headers(payload)
11
+ @client.post("v1/bridge/initialize", payload, headers: headers)
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module StraddlePay
4
+ module Resources
5
+ class BridgeLinks < Base
6
+ def bank_account(customer_id:, account_number:, routing_number:, account_type:, **options)
7
+ payload = {
8
+ customer_id: customer_id, account_number: account_number,
9
+ routing_number: routing_number, account_type: account_type, **options
10
+ }.compact
11
+ headers = extract_headers(payload)
12
+ @client.post("v1/bridge/bank_account", payload, headers: headers)
13
+ end
14
+
15
+ def plaid(customer_id:, plaid_token:, **options)
16
+ payload = { customer_id: customer_id, plaid_token: plaid_token, **options }.compact
17
+ headers = extract_headers(payload)
18
+ @client.post("v1/bridge/plaid", payload, headers: headers)
19
+ end
20
+
21
+ def quiltt(customer_id:, quiltt_token:, **options)
22
+ payload = { customer_id: customer_id, quiltt_token: quiltt_token, **options }.compact
23
+ headers = extract_headers(payload)
24
+ @client.post("v1/bridge/quiltt", payload, headers: headers)
25
+ end
26
+
27
+ def tan(customer_id:, routing_number:, account_type:, tan:, **options)
28
+ payload = {
29
+ customer_id: customer_id, routing_number: routing_number,
30
+ account_type: account_type, tan: tan, **options
31
+ }.compact
32
+ headers = extract_headers(payload)
33
+ @client.post("v1/bridge/tan", payload, headers: headers)
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module StraddlePay
4
+ module Resources
5
+ class Charges < Base
6
+ def create(paykey:, amount:, currency:, description:, payment_date:, consent_type:, device:, external_id:,
7
+ **options)
8
+ payload = {
9
+ paykey: paykey, amount: amount, currency: currency, description: description,
10
+ payment_date: payment_date, consent_type: consent_type, device: device,
11
+ external_id: external_id, **options
12
+ }.compact
13
+ headers = extract_headers(payload)
14
+ @client.post("v1/charges", payload, headers: headers)
15
+ end
16
+
17
+ def get(id, **options)
18
+ headers = extract_headers(options)
19
+ @client.get("v1/charges/#{id}", headers: headers)
20
+ end
21
+
22
+ def update(id, **options)
23
+ headers = extract_headers(options)
24
+ @client.put("v1/charges/#{id}", options, headers: headers)
25
+ end
26
+
27
+ def cancel(id, **options)
28
+ headers = extract_headers(options)
29
+ @client.put("v1/charges/#{id}/cancel", options, headers: headers)
30
+ end
31
+
32
+ def hold(id, **options)
33
+ headers = extract_headers(options)
34
+ @client.put("v1/charges/#{id}/hold", options, headers: headers)
35
+ end
36
+
37
+ def release(id, **options)
38
+ headers = extract_headers(options)
39
+ @client.put("v1/charges/#{id}/release", options, headers: headers)
40
+ end
41
+
42
+ def unmask(id, **options)
43
+ headers = extract_headers(options)
44
+ @client.get("v1/charges/#{id}/unmask", headers: headers)
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module StraddlePay
4
+ module Resources
5
+ class CustomerReviews < Base
6
+ def get(customer_id, **options)
7
+ headers = extract_headers(options)
8
+ @client.get("v1/customers/#{customer_id}/review", headers: headers)
9
+ end
10
+
11
+ def decision(customer_id, status:, **options)
12
+ payload = { status: status, **options }.compact
13
+ headers = extract_headers(payload)
14
+ @client.patch("v1/customers/#{customer_id}/review", payload, headers: headers)
15
+ end
16
+
17
+ def refresh(customer_id, **options)
18
+ headers = extract_headers(options)
19
+ @client.put("v1/customers/#{customer_id}/refresh_review", options, headers: headers)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module StraddlePay
4
+ module Resources
5
+ class Customers < Base
6
+ def reviews = @reviews ||= CustomerReviews.new(@client)
7
+
8
+ def create(name:, type:, email:, phone:, device:, **options)
9
+ payload = { name: name, type: type, email: email, phone: phone, device: device, **options }.compact
10
+ headers = extract_headers(payload)
11
+ @client.post("v1/customers", payload, headers: headers)
12
+ end
13
+
14
+ def get(id, **options)
15
+ headers = extract_headers(options)
16
+ @client.get("v1/customers/#{id}", headers: headers)
17
+ end
18
+
19
+ def list(**options)
20
+ headers = extract_headers(options)
21
+ @client.get("v1/customers", params: options, headers: headers)
22
+ end
23
+
24
+ def update(id, **options)
25
+ headers = extract_headers(options)
26
+ @client.put("v1/customers/#{id}", options, headers: headers)
27
+ end
28
+
29
+ def delete(id, **options)
30
+ headers = extract_headers(options)
31
+ @client.delete("v1/customers/#{id}", headers: headers)
32
+ end
33
+
34
+ def unmasked(id, **options)
35
+ headers = extract_headers(options)
36
+ @client.get("v1/customers/#{id}/unmasked", headers: headers)
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module StraddlePay
4
+ module Resources
5
+ class Embed < Base
6
+ def accounts = @accounts ||= EmbedAccounts.new(@client)
7
+ def organizations = @organizations ||= EmbedOrganizations.new(@client)
8
+ def representatives = @representatives ||= EmbedRepresentatives.new(@client)
9
+ def linked_bank_accounts = @linked_bank_accounts ||= EmbedLinkedBankAccounts.new(@client)
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module StraddlePay
4
+ module Resources
5
+ class EmbedAccounts < Base
6
+ def create(organization_id:, account_type:, business_profile:, access_level:, **options)
7
+ payload = {
8
+ organization_id: organization_id, account_type: account_type,
9
+ business_profile: business_profile, access_level: access_level, **options
10
+ }.compact
11
+ headers = extract_headers(payload)
12
+ @client.post("v1/accounts", payload, headers: headers)
13
+ end
14
+
15
+ def get(id, **options)
16
+ headers = extract_headers(options)
17
+ @client.get("v1/accounts/#{id}", headers: headers)
18
+ end
19
+
20
+ def list(**options)
21
+ headers = extract_headers(options)
22
+ @client.get("v1/accounts", params: options, headers: headers)
23
+ end
24
+
25
+ def update(id, **options)
26
+ headers = extract_headers(options)
27
+ @client.put("v1/accounts/#{id}", options, headers: headers)
28
+ end
29
+
30
+ def onboard(id, terms_of_service:, **options)
31
+ payload = { terms_of_service: terms_of_service, **options }.compact
32
+ headers = extract_headers(payload)
33
+ @client.post("v1/accounts/#{id}/onboard", payload, headers: headers)
34
+ end
35
+
36
+ def simulate(id, final_status:, **options)
37
+ payload = { final_status: final_status, **options }.compact
38
+ headers = extract_headers(payload)
39
+ @client.post("v1/accounts/#{id}/simulate", payload, headers: headers)
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module StraddlePay
4
+ module Resources
5
+ class EmbedLinkedBankAccounts < Base
6
+ def create(account_id:, bank_account:, description:, **options)
7
+ payload = {
8
+ account_id: account_id, bank_account: bank_account,
9
+ description: description, **options
10
+ }.compact
11
+ headers = extract_headers(payload)
12
+ @client.post("v1/linked_bank_accounts", payload, headers: headers)
13
+ end
14
+
15
+ def get(id, **options)
16
+ headers = extract_headers(options)
17
+ @client.get("v1/linked_bank_accounts/#{id}", headers: headers)
18
+ end
19
+
20
+ def list(**options)
21
+ headers = extract_headers(options)
22
+ @client.get("v1/linked_bank_accounts", params: options, headers: headers)
23
+ end
24
+
25
+ def update(id, **options)
26
+ headers = extract_headers(options)
27
+ @client.put("v1/linked_bank_accounts/#{id}", options, headers: headers)
28
+ end
29
+
30
+ def cancel(id, **options)
31
+ headers = extract_headers(options)
32
+ @client.patch("v1/linked_bank_accounts/#{id}/cancel", options.empty? ? nil : options, headers: headers)
33
+ end
34
+
35
+ def unmask(id, **options)
36
+ headers = extract_headers(options)
37
+ @client.get("v1/linked_bank_accounts/#{id}/unmask", headers: headers)
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module StraddlePay
4
+ module Resources
5
+ class EmbedOrganizations < Base
6
+ def create(name:, **options)
7
+ payload = { name: name, **options }.compact
8
+ headers = extract_headers(payload)
9
+ @client.post("v1/organizations", payload, headers: headers)
10
+ end
11
+
12
+ def get(id, **options)
13
+ headers = extract_headers(options)
14
+ @client.get("v1/organizations/#{id}", headers: headers)
15
+ end
16
+
17
+ def list(**options)
18
+ headers = extract_headers(options)
19
+ @client.get("v1/organizations", params: options, headers: headers)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module StraddlePay
4
+ module Resources
5
+ class EmbedRepresentatives < Base
6
+ def create(account_id:, first_name:, last_name:, email:, dob:, mobile_number:, relationship:, ssn_last4:,
7
+ **options)
8
+ payload = {
9
+ account_id: account_id, first_name: first_name, last_name: last_name,
10
+ email: email, dob: dob, mobile_number: mobile_number,
11
+ relationship: relationship, ssn_last4: ssn_last4, **options
12
+ }.compact
13
+ headers = extract_headers(payload)
14
+ @client.post("v1/representatives", payload, headers: headers)
15
+ end
16
+
17
+ def get(id, **options)
18
+ headers = extract_headers(options)
19
+ @client.get("v1/representatives/#{id}", headers: headers)
20
+ end
21
+
22
+ def list(**options)
23
+ headers = extract_headers(options)
24
+ @client.get("v1/representatives", params: options, headers: headers)
25
+ end
26
+
27
+ def update(id, **options)
28
+ headers = extract_headers(options)
29
+ @client.put("v1/representatives/#{id}", options, headers: headers)
30
+ end
31
+
32
+ def unmask(id, **options)
33
+ headers = extract_headers(options)
34
+ @client.get("v1/representatives/#{id}/unmask", headers: headers)
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module StraddlePay
4
+ module Resources
5
+ class FundingEvents < Base
6
+ def list(**options)
7
+ headers = extract_headers(options)
8
+ @client.get("v1/funding-events", params: options, headers: headers)
9
+ end
10
+
11
+ def get(id, **options)
12
+ headers = extract_headers(options)
13
+ @client.get("v1/funding-events/#{id}", headers: headers)
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module StraddlePay
4
+ module Resources
5
+ class Paykeys < Base
6
+ def get(id, **options)
7
+ headers = extract_headers(options)
8
+ @client.get("v1/paykeys/#{id}", headers: headers)
9
+ end
10
+
11
+ def list(**options)
12
+ headers = extract_headers(options)
13
+ @client.get("v1/paykeys", params: options, headers: headers)
14
+ end
15
+
16
+ def unmasked(id, **options)
17
+ headers = extract_headers(options)
18
+ @client.get("v1/paykeys/#{id}/unmasked", headers: headers)
19
+ end
20
+
21
+ def reveal(id, **options)
22
+ headers = extract_headers(options)
23
+ @client.get("v1/paykeys/#{id}/reveal", headers: headers)
24
+ end
25
+
26
+ def cancel(id, **options)
27
+ headers = extract_headers(options)
28
+ @client.put("v1/paykeys/#{id}/cancel", options, headers: headers)
29
+ end
30
+
31
+ def review(id, **options)
32
+ headers = extract_headers(options)
33
+ @client.patch("v1/paykeys/#{id}/review", options, headers: headers)
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module StraddlePay
4
+ module Resources
5
+ class Payments < Base
6
+ def list(**options)
7
+ headers = extract_headers(options)
8
+ @client.get("v1/payments", params: options, headers: headers)
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module StraddlePay
4
+ module Resources
5
+ class Payouts < Base
6
+ def create(paykey:, amount:, currency:, description:, payment_date:, device:, external_id:, **options)
7
+ payload = {
8
+ paykey: paykey, amount: amount, currency: currency, description: description,
9
+ payment_date: payment_date, device: device, external_id: external_id, **options
10
+ }.compact
11
+ headers = extract_headers(payload)
12
+ @client.post("v1/payouts", payload, headers: headers)
13
+ end
14
+
15
+ def get(id, **options)
16
+ headers = extract_headers(options)
17
+ @client.get("v1/payouts/#{id}", headers: headers)
18
+ end
19
+
20
+ def update(id, **options)
21
+ headers = extract_headers(options)
22
+ @client.put("v1/payouts/#{id}", options, headers: headers)
23
+ end
24
+
25
+ def cancel(id, reason:, **options)
26
+ payload = { reason: reason, **options }.compact
27
+ headers = extract_headers(payload)
28
+ @client.put("v1/payouts/#{id}/cancel", payload, headers: headers)
29
+ end
30
+
31
+ def hold(id, reason:, **options)
32
+ payload = { reason: reason, **options }.compact
33
+ headers = extract_headers(payload)
34
+ @client.put("v1/payouts/#{id}/hold", payload, headers: headers)
35
+ end
36
+
37
+ def release(id, reason:, **options)
38
+ payload = { reason: reason, **options }.compact
39
+ headers = extract_headers(payload)
40
+ @client.put("v1/payouts/#{id}/release", payload, headers: headers)
41
+ end
42
+
43
+ def unmask(id, **options)
44
+ headers = extract_headers(options)
45
+ @client.get("v1/payouts/#{id}/unmask", headers: headers)
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module StraddlePay
4
+ module Resources
5
+ class Reports < Base
6
+ def total_customers_by_status(**options)
7
+ headers = extract_headers(options)
8
+ @client.post("v1/reports/total_customers_by_status", options.empty? ? nil : options, headers: headers)
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module StraddlePay
4
+ VERSION = "0.1.0"
5
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "straddle_pay/version"
4
+
5
+ module StraddlePay
6
+ autoload :Config, "straddle_pay/config"
7
+ autoload :Client, "straddle_pay/client"
8
+ autoload :Error, "straddle_pay/errors"
9
+ autoload :AuthenticationError, "straddle_pay/errors"
10
+ autoload :ClientError, "straddle_pay/errors"
11
+ autoload :RateLimitError, "straddle_pay/errors"
12
+ autoload :ServerError, "straddle_pay/errors"
13
+ autoload :NetworkError, "straddle_pay/errors"
14
+
15
+ module Resources
16
+ autoload :Base, "straddle_pay/resources/base"
17
+ autoload :Charges, "straddle_pay/resources/charges"
18
+ autoload :Payouts, "straddle_pay/resources/payouts"
19
+ autoload :Customers, "straddle_pay/resources/customers"
20
+ autoload :CustomerReviews, "straddle_pay/resources/customer_reviews"
21
+ autoload :Bridge, "straddle_pay/resources/bridge"
22
+ autoload :BridgeLinks, "straddle_pay/resources/bridge_links"
23
+ autoload :Paykeys, "straddle_pay/resources/paykeys"
24
+ autoload :Payments, "straddle_pay/resources/payments"
25
+ autoload :FundingEvents, "straddle_pay/resources/funding_events"
26
+ autoload :Reports, "straddle_pay/resources/reports"
27
+ autoload :Embed, "straddle_pay/resources/embed"
28
+ autoload :EmbedAccounts, "straddle_pay/resources/embed_accounts"
29
+ autoload :EmbedOrganizations, "straddle_pay/resources/embed_organizations"
30
+ autoload :EmbedRepresentatives, "straddle_pay/resources/embed_representatives"
31
+ autoload :EmbedLinkedBankAccounts, "straddle_pay/resources/embed_linked_bank_accounts"
32
+ end
33
+
34
+ class << self
35
+ def config
36
+ @config ||= Config.new
37
+ end
38
+
39
+ def configure
40
+ yield(config)
41
+ end
42
+
43
+ def reset_configuration!
44
+ @config = nil
45
+ end
46
+ end
47
+ end
48
+
49
+ require_relative "straddle_pay/engine"
metadata ADDED
@@ -0,0 +1,90 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: straddle_pay
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - dpaluy
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: faraday
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '2.9'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '2.9'
26
+ description: StraddlePay provides a lightweight Faraday-based wrapper for Straddle's
27
+ payment API including charges, payouts, customers, bridge, paykeys, and embedded
28
+ account management.
29
+ email:
30
+ - dpaluy@users.noreply.github.com
31
+ executables: []
32
+ extensions: []
33
+ extra_rdoc_files:
34
+ - CHANGELOG.md
35
+ - LICENSE.txt
36
+ - README.md
37
+ files:
38
+ - CHANGELOG.md
39
+ - LICENSE.txt
40
+ - README.md
41
+ - lib/straddle_pay.rb
42
+ - lib/straddle_pay/client.rb
43
+ - lib/straddle_pay/config.rb
44
+ - lib/straddle_pay/engine.rb
45
+ - lib/straddle_pay/errors.rb
46
+ - lib/straddle_pay/resources/base.rb
47
+ - lib/straddle_pay/resources/bridge.rb
48
+ - lib/straddle_pay/resources/bridge_links.rb
49
+ - lib/straddle_pay/resources/charges.rb
50
+ - lib/straddle_pay/resources/customer_reviews.rb
51
+ - lib/straddle_pay/resources/customers.rb
52
+ - lib/straddle_pay/resources/embed.rb
53
+ - lib/straddle_pay/resources/embed_accounts.rb
54
+ - lib/straddle_pay/resources/embed_linked_bank_accounts.rb
55
+ - lib/straddle_pay/resources/embed_organizations.rb
56
+ - lib/straddle_pay/resources/embed_representatives.rb
57
+ - lib/straddle_pay/resources/funding_events.rb
58
+ - lib/straddle_pay/resources/paykeys.rb
59
+ - lib/straddle_pay/resources/payments.rb
60
+ - lib/straddle_pay/resources/payouts.rb
61
+ - lib/straddle_pay/resources/reports.rb
62
+ - lib/straddle_pay/version.rb
63
+ homepage: https://github.com/dpaluy/straddle_pay
64
+ licenses:
65
+ - MIT
66
+ metadata:
67
+ rubygems_mfa_required: 'true'
68
+ homepage_uri: https://github.com/dpaluy/straddle_pay
69
+ documentation_uri: https://rubydoc.info/gems/straddle_pay
70
+ source_code_uri: https://github.com/dpaluy/straddle_pay
71
+ changelog_uri: https://github.com/dpaluy/straddle_pay/blob/master/CHANGELOG.md
72
+ bug_tracker_uri: https://github.com/dpaluy/straddle_pay/issues
73
+ rdoc_options: []
74
+ require_paths:
75
+ - lib
76
+ required_ruby_version: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ version: 3.2.0
81
+ required_rubygems_version: !ruby/object:Gem::Requirement
82
+ requirements:
83
+ - - ">="
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ requirements: []
87
+ rubygems_version: 4.0.3
88
+ specification_version: 4
89
+ summary: Ruby client for the Straddle payment infrastructure API.
90
+ test_files: []