cash_app_pay 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/lib/cash_app_pay/api_operations/create.rb +16 -0
- data/lib/cash_app_pay/api_operations/delete.rb +27 -0
- data/lib/cash_app_pay/api_operations/list.rb +17 -0
- data/lib/cash_app_pay/api_operations/request.rb +45 -0
- data/lib/cash_app_pay/api_operations/retrieve.rb +24 -0
- data/lib/cash_app_pay/api_operations/save.rb +16 -0
- data/lib/cash_app_pay/api_operations/update.rb +31 -0
- data/lib/cash_app_pay/api_operations/upsert.rb +22 -0
- data/lib/cash_app_pay/api_resource.rb +58 -0
- data/lib/cash_app_pay/cash_app_pay_client.rb +117 -0
- data/lib/cash_app_pay/cash_app_pay_configuration.rb +19 -0
- data/lib/cash_app_pay/cash_app_pay_object.rb +77 -0
- data/lib/cash_app_pay/cash_app_pay_response.rb +17 -0
- data/lib/cash_app_pay/connection_manager.rb +79 -0
- data/lib/cash_app_pay/endpoint.rb +8 -0
- data/lib/cash_app_pay/error_object.rb +6 -0
- data/lib/cash_app_pay/errors.rb +45 -0
- data/lib/cash_app_pay/helpers/symbolize.rb +31 -0
- data/lib/cash_app_pay/list_object.rb +64 -0
- data/lib/cash_app_pay/persistent_http_client.rb +48 -0
- data/lib/cash_app_pay/resources/api_key.rb +19 -0
- data/lib/cash_app_pay/resources/brand.rb +20 -0
- data/lib/cash_app_pay/resources/customer.rb +56 -0
- data/lib/cash_app_pay/resources/customer_request.rb +18 -0
- data/lib/cash_app_pay/resources/dispute.rb +142 -0
- data/lib/cash_app_pay/resources/dispute_evidence.rb +9 -0
- data/lib/cash_app_pay/resources/fee_plan.rb +16 -0
- data/lib/cash_app_pay/resources/grant.rb +13 -0
- data/lib/cash_app_pay/resources/merchant.rb +20 -0
- data/lib/cash_app_pay/resources/payment.rb +63 -0
- data/lib/cash_app_pay/resources/refund.rb +62 -0
- data/lib/cash_app_pay/resources/webhook.rb +20 -0
- data/lib/cash_app_pay/resources/webhook_event.rb +15 -0
- data/lib/cash_app_pay/version.rb +5 -0
- data/lib/cash_app_pay.rb +69 -0
- metadata +85 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 56ca23a3952f68fcf4c16941a887ee4a66016774074405b6cadcc5ce63cc8168
|
4
|
+
data.tar.gz: 57edbf0f525f96d03da5e838b31192472bd9dc643b49cc6514dd5e227e93329e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 8cb314461eea62e42789ffc4a0de5ddd88d1c6b45c64ec30575e0f9ff36e996852cb2d3717fa46de7de492928cfc980ce26ee6edbc60aded6d7c4101816d1b16
|
7
|
+
data.tar.gz: 0755fbff98ba362c1756e23420701f7c2a186dec0fb210bb259db713267b86303ed7e5ca578535236b1ed6e95a07af713f511a5f20dc06ed1ad649a36ac5e2bf
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CashAppPay
|
4
|
+
module APIOperations
|
5
|
+
module Create
|
6
|
+
def create(params = {}, opts = {})
|
7
|
+
request_cash_app_pay_object(
|
8
|
+
method: :post,
|
9
|
+
path: resource_url,
|
10
|
+
params: params,
|
11
|
+
opts: opts
|
12
|
+
)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CashAppPay
|
4
|
+
module APIOperations
|
5
|
+
module Delete
|
6
|
+
def delete(opts = {})
|
7
|
+
delete_cash_app_pay_object(
|
8
|
+
path: resource_url,
|
9
|
+
opts: opts
|
10
|
+
)
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.included(base)
|
14
|
+
base.extend(ClassMethods)
|
15
|
+
end
|
16
|
+
|
17
|
+
module ClassMethods
|
18
|
+
def delete(resource, _params = {}, opts = {})
|
19
|
+
delete_cash_app_pay_object(
|
20
|
+
path: "#{resource_url}/#{CGI.escape(resource)}",
|
21
|
+
opts: opts
|
22
|
+
)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CashAppPay
|
4
|
+
module APIOperations
|
5
|
+
module List
|
6
|
+
def list(filters = {}, opts = {})
|
7
|
+
response, opts = execute_resource_request(
|
8
|
+
method: :get,
|
9
|
+
url: resource_url,
|
10
|
+
url_params: filters,
|
11
|
+
opts: opts
|
12
|
+
)
|
13
|
+
ListObject.initialize_from_response(self, response, opts, filters)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CashAppPay
|
4
|
+
module APIOperations
|
5
|
+
module Request
|
6
|
+
def request_cash_app_pay_object(method:, path:, params:, opts: {})
|
7
|
+
body = self.class.encode_body(params) unless params.nil?
|
8
|
+
response, opts = self.class.execute_resource_request(method: method, url: path, body_params: body, opts: opts)
|
9
|
+
initialize_from(response.data, opts)
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.included(base)
|
13
|
+
base.extend(ClassMethods)
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def delete_cash_app_pay_object(path:, opts: {})
|
19
|
+
response, = execute_resource_request(method: :delete, url: path, opts: opts)
|
20
|
+
response.http_status == 200
|
21
|
+
end
|
22
|
+
|
23
|
+
module ClassMethods
|
24
|
+
def execute_resource_request(method:, url:, url_params: nil, body_params: nil, opts: {})
|
25
|
+
response = CashAppPay::CashAppPayClient.execute_request(method_name: method, path: url,
|
26
|
+
url_params: url_params, body_params: body_params, opts: opts)
|
27
|
+
[response, opts]
|
28
|
+
end
|
29
|
+
|
30
|
+
def request_cash_app_pay_object(method:, path:, params:, opts: {})
|
31
|
+
body = encode_body(params) unless params.nil?
|
32
|
+
response, opts = execute_resource_request(method: method, url: path, body_params: body, opts: opts)
|
33
|
+
initialize_from_net_response(response, opts)
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def delete_cash_app_pay_object(path:, opts: {})
|
39
|
+
response, = execute_resource_request(method: :delete, url: path, opts: opts)
|
40
|
+
response.http_status == 200
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CashAppPay
|
4
|
+
module APIOperations
|
5
|
+
module Retrieve
|
6
|
+
def refresh
|
7
|
+
response, opts = self.class.execute_resource_request(method: :get, url: resource_url, opts: @opts)
|
8
|
+
initialize_from(response.data, opts)
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.included(base)
|
12
|
+
base.extend(ClassMethods)
|
13
|
+
end
|
14
|
+
|
15
|
+
module ClassMethods
|
16
|
+
def retrieve(id, opts = {})
|
17
|
+
instance = new({ id: id }, opts)
|
18
|
+
instance.refresh
|
19
|
+
instance
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CashAppPay
|
4
|
+
module APIOperations
|
5
|
+
module Save
|
6
|
+
def save(opts = {})
|
7
|
+
request_cash_app_pay_object(
|
8
|
+
method: :post,
|
9
|
+
path: self.class.resource_url,
|
10
|
+
params: @values,
|
11
|
+
opts: opts
|
12
|
+
)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CashAppPay
|
4
|
+
module APIOperations
|
5
|
+
module Update
|
6
|
+
def update(params = {}, opts = {})
|
7
|
+
request_cash_app_pay_object(
|
8
|
+
method: :patch,
|
9
|
+
path: resource_url,
|
10
|
+
params: params,
|
11
|
+
opts: opts
|
12
|
+
)
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.included(base)
|
16
|
+
base.extend(ClassMethods)
|
17
|
+
end
|
18
|
+
|
19
|
+
module ClassMethods
|
20
|
+
def update(resource, params = {}, opts = {})
|
21
|
+
request_cash_app_pay_object(
|
22
|
+
method: :patch,
|
23
|
+
path: "#{resource_url}/#{CGI.escape(resource)}",
|
24
|
+
params: params,
|
25
|
+
opts: opts
|
26
|
+
)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CashAppPay
|
4
|
+
module APIOperations
|
5
|
+
module Upsert
|
6
|
+
def self.included(base)
|
7
|
+
base.extend(ClassMethods)
|
8
|
+
end
|
9
|
+
|
10
|
+
module ClassMethods
|
11
|
+
def upsert(resource, params = {}, opts = {})
|
12
|
+
request_cash_app_pay_object(
|
13
|
+
method: :put,
|
14
|
+
path: "#{resource_url}/#{CGI.escape(resource)}",
|
15
|
+
params: params,
|
16
|
+
opts: opts
|
17
|
+
)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CashAppPay
|
4
|
+
class APIResource < CashAppPayObject
|
5
|
+
include CashAppPay::APIOperations::Request
|
6
|
+
|
7
|
+
attr_reader :opts
|
8
|
+
|
9
|
+
def initialize(values = {}, opts = {})
|
10
|
+
super(values)
|
11
|
+
@opts = opts
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.resource_url
|
15
|
+
raise NotImplementedError, 'API Resource is an abstract class'
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def self.initialize_from_net_response(response, opts)
|
21
|
+
instance = new
|
22
|
+
instance.send(:initialize_from, response.data, opts)
|
23
|
+
instance
|
24
|
+
end
|
25
|
+
|
26
|
+
def initialize_from(values, opts)
|
27
|
+
@values = values.fetch(self.class.object_name, {})
|
28
|
+
@opts = opts
|
29
|
+
self
|
30
|
+
end
|
31
|
+
|
32
|
+
def resource_url
|
33
|
+
unless (id = self.id)
|
34
|
+
raise InvalidRequestError.new(
|
35
|
+
"Could not determine which URL to request: #{self.class} instance has invalid ID: #{id.inspect}",
|
36
|
+
'id'
|
37
|
+
)
|
38
|
+
end
|
39
|
+
"#{self.class.resource_url}/#{CGI.escape(id)}"
|
40
|
+
end
|
41
|
+
|
42
|
+
# Encode the params for the body of the request.
|
43
|
+
# If the params contains `idempotency_key` then put this at the root
|
44
|
+
# e.g. { request: { id: 1 }, idempotency_key: 'key' }
|
45
|
+
# params: Hash of params
|
46
|
+
def self.encode_body(params)
|
47
|
+
idempotency_key = params.delete(:idempotency_key) || params.delete('idempotency_key')
|
48
|
+
body = if params.empty? && !idempotency_key.nil?
|
49
|
+
{ idempotency_key: idempotency_key }
|
50
|
+
else
|
51
|
+
named_body_params = Hash[object_name, params]
|
52
|
+
named_body_params[:idempotency_key] = idempotency_key unless idempotency_key.nil?
|
53
|
+
named_body_params
|
54
|
+
end
|
55
|
+
body.to_json
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'cash_app_pay/version'
|
4
|
+
|
5
|
+
module CashAppPay
|
6
|
+
class CashAppPayClient
|
7
|
+
def self.execute_request(method_name:, path:, url_params:, body_params: nil, opts: {})
|
8
|
+
base_uri = opts[:api_base] || CashAppPay.api_base
|
9
|
+
client_id = opts[:client_id] || CashAppPay.client_id
|
10
|
+
|
11
|
+
check_client_id!(client_id)
|
12
|
+
|
13
|
+
method = method_name.to_s.upcase
|
14
|
+
url = URI::HTTPS.build(host: base_uri, path: path)
|
15
|
+
url.query = URI.encode_www_form(url_params) if !url_params.nil? && !url_params.empty?
|
16
|
+
http = CashAppPay::PersistentHttpClient.get(url)
|
17
|
+
|
18
|
+
headers = if path.start_with?(NETWORK_API_PATH_PREFIX) || path.start_with?(MANAGE_API_PATH_PREFIX)
|
19
|
+
network_api_headers(client_id, opts)
|
20
|
+
elsif path.start_with?(CUSTOMER_REQUEST_API_PATH_PREFIX)
|
21
|
+
customer_request_api_headers(client_id)
|
22
|
+
else
|
23
|
+
raise InvalidRequestError.new('path', '')
|
24
|
+
end
|
25
|
+
|
26
|
+
request = Net::HTTPGenericRequest.new(method, !body_params.nil?, true, url, headers)
|
27
|
+
request.body = body_params unless body_params.nil?
|
28
|
+
|
29
|
+
response = http.request(request)
|
30
|
+
|
31
|
+
begin
|
32
|
+
resp = CashAppPayResponse.from_net_http(response)
|
33
|
+
handle_error_response(resp.error_data) if resp.error_data
|
34
|
+
rescue JSON::ParserError
|
35
|
+
raise general_api_error(response.code.to_i, response.body)
|
36
|
+
end
|
37
|
+
|
38
|
+
resp
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.handle_error_response(response_errors)
|
42
|
+
return unless (error_response = response_errors.first)
|
43
|
+
|
44
|
+
raise APIResponseError, error_response
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.check_client_id!(client_id)
|
48
|
+
return if client_id
|
49
|
+
|
50
|
+
raise AuthenticationError, 'No Client ID provided. Set your API key using CashAppPay.client_id = <CLIENT-ID>'
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.check_api_base!(api_base)
|
54
|
+
return if api_base
|
55
|
+
|
56
|
+
raise AuthenticationError, 'No API base provided. Set your API key using CashAppPay.api_base = <API-BASE>'
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.check_region!(region)
|
60
|
+
raise AuthenticationError, 'No Region provided. Set your API key using CashAppPay.region = <REGION>' unless region
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.check_signature!(signature)
|
64
|
+
return if signature
|
65
|
+
|
66
|
+
raise AuthenticationError, 'No Signature provided. Set your API key using CashAppPay.signature = <SIGNATURE>'
|
67
|
+
end
|
68
|
+
|
69
|
+
def self.check_api_key!(api_key)
|
70
|
+
return if api_key
|
71
|
+
|
72
|
+
raise AuthenticationError, 'No Region provided. Set your API key using CashAppPay.api_key = <API-KEY>'
|
73
|
+
end
|
74
|
+
|
75
|
+
def self.general_api_error(status, body)
|
76
|
+
APIError.new("Invalid response object from API: #{body.inspect} (HTTP response code was #{status})",
|
77
|
+
http_status: status, http_body: body)
|
78
|
+
end
|
79
|
+
|
80
|
+
def self.user_agent
|
81
|
+
"cash-app-pay-ruby/v#{CashAppPay::VERSION} RubyBindings (#{RUBY_VERSION} p#{RUBY_PATCHLEVEL} (#{RUBY_RELEASE_DATE})) RUBY_PLATFORM #{defined?(RUBY_ENGINE) ? "(#{RUBY_ENGINE})" : ''}"
|
82
|
+
end
|
83
|
+
|
84
|
+
CUSTOMER_REQUEST_API_PATH_PREFIX = '/customer-request/'
|
85
|
+
NETWORK_API_PATH_PREFIX = '/network/'
|
86
|
+
MANAGE_API_PATH_PREFIX = '/management/'
|
87
|
+
|
88
|
+
def self.customer_request_api_headers(client_id)
|
89
|
+
{
|
90
|
+
"Authorization": ['Client', client_id].join(' '),
|
91
|
+
"Accept": 'application/json',
|
92
|
+
"Content-Type": 'application/json',
|
93
|
+
"User-Agent": user_agent
|
94
|
+
}
|
95
|
+
end
|
96
|
+
|
97
|
+
def self.network_api_headers(client_id, opts)
|
98
|
+
api_key = opts[:api_key] || CashAppPay.api_key
|
99
|
+
signature = opts[:signature] || CashAppPay.signature
|
100
|
+
region = opts[:region] || CashAppPay.region
|
101
|
+
|
102
|
+
check_api_key!(api_key)
|
103
|
+
check_signature!(signature)
|
104
|
+
check_region!(region)
|
105
|
+
|
106
|
+
authorization = ['Client', client_id, api_key].join(' ')
|
107
|
+
{
|
108
|
+
"Authorization": authorization,
|
109
|
+
"X-Region": region,
|
110
|
+
"X-Signature": signature,
|
111
|
+
"Accept": 'application/json',
|
112
|
+
"Content-Type": 'application/json',
|
113
|
+
"User-Agent": user_agent
|
114
|
+
}
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'cash_app_pay/endpoint'
|
4
|
+
|
5
|
+
module CashAppPay
|
6
|
+
class CashAppPayConfiguration
|
7
|
+
attr_accessor :client_id, :api_base, :region, :signature, :api_key
|
8
|
+
|
9
|
+
def self.setup
|
10
|
+
new.tap do |instance|
|
11
|
+
yield(instance) if block_given?
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize
|
16
|
+
@api_base = Endpoint::PRODUCTION
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CashAppPay
|
4
|
+
class CashAppPayObject
|
5
|
+
using CashAppPay::Helpers::Symbolize
|
6
|
+
|
7
|
+
attr_accessor :values
|
8
|
+
|
9
|
+
def initialize(values)
|
10
|
+
@values = values.deep_symbolize_keys
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_h(*_args)
|
14
|
+
@values
|
15
|
+
end
|
16
|
+
|
17
|
+
alias to_hash to_h
|
18
|
+
alias as_json to_h
|
19
|
+
|
20
|
+
def to_json(*_args)
|
21
|
+
@values.to_json
|
22
|
+
end
|
23
|
+
|
24
|
+
def to_s(*_args)
|
25
|
+
JSON.pretty_generate(to_h)
|
26
|
+
end
|
27
|
+
|
28
|
+
def to_str
|
29
|
+
self['id'].to_s
|
30
|
+
end
|
31
|
+
|
32
|
+
def inspect
|
33
|
+
id_string = !id.nil? ? " id=#{id}" : ''
|
34
|
+
"#<#{self.class}:0x#{object_id.to_s(16)}#{id_string}> JSON: " +
|
35
|
+
JSON.pretty_generate(@values)
|
36
|
+
end
|
37
|
+
|
38
|
+
def [](name)
|
39
|
+
data = @values[typed_key(name)]
|
40
|
+
if data.is_a?(Hash)
|
41
|
+
CashAppPay::CashAppPayObject.new(data)
|
42
|
+
elsif data.is_a?(Array)
|
43
|
+
data.map do |item|
|
44
|
+
item.is_a?(Hash) ? CashAppPay::CashAppPayObject.new(item) : item
|
45
|
+
end
|
46
|
+
else
|
47
|
+
data
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def ==(other)
|
52
|
+
if other.is_a?(CashAppPay::CashAppPayObject)
|
53
|
+
@values == other.values
|
54
|
+
else
|
55
|
+
false
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def []=(name, value)
|
60
|
+
@values[typed_key(name)] = value
|
61
|
+
end
|
62
|
+
|
63
|
+
def method_missing(name, *_args)
|
64
|
+
if name.to_s.end_with?('=')
|
65
|
+
attr = name.to_s[0...-1].to_sym
|
66
|
+
val = _args.first
|
67
|
+
self[attr] = val
|
68
|
+
else
|
69
|
+
self[name]
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def typed_key(key)
|
74
|
+
key.to_sym
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CashAppPay
|
4
|
+
class CashAppPayResponse
|
5
|
+
attr_accessor :http_headers, :http_status, :data, :error_data, :http_body
|
6
|
+
|
7
|
+
def self.from_net_http(http_resp)
|
8
|
+
resp = CashAppPayResponse.new
|
9
|
+
resp.http_headers = http_resp.each_header.to_h
|
10
|
+
resp.http_status = http_resp.code.to_i
|
11
|
+
resp.http_body = http_resp.body
|
12
|
+
resp.data = JSON.parse(http_resp.read_body, symbolize_names: true) unless resp.http_body.empty?
|
13
|
+
resp.error_data = resp&.data&.fetch(:errors, nil)
|
14
|
+
resp
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# connection manager represents
|
4
|
+
# a cache of all keep-alive connections
|
5
|
+
# in a current thread
|
6
|
+
module CashAppPay
|
7
|
+
class ConnectionManager
|
8
|
+
DEFAULT_OPTIONS = { read_timeout: 80, open_timeout: 30 }.freeze
|
9
|
+
# if a client wasn't used within this time range
|
10
|
+
# it gets removed from the cache and the connection closed.
|
11
|
+
# This helps to make sure there are no memory leaks.
|
12
|
+
STALE_AFTER = 60 * 5 # 5.minutes
|
13
|
+
|
14
|
+
# Seconds to reuse the connection of the previous request. If the idle time is less than this Keep-Alive Timeout, Net::HTTP reuses the TCP/IP socket used by the previous communication. Source: Ruby docs
|
15
|
+
KEEP_ALIVE_TIMEOUT = 30 # seconds
|
16
|
+
|
17
|
+
# KEEP_ALIVE_TIMEOUT vs STALE_AFTER
|
18
|
+
# STALE_AFTER - how long an Net::HTTP client object is cached in ruby
|
19
|
+
# KEEP_ALIVE_TIMEOUT - how long that client keeps TCP/IP socket open.
|
20
|
+
|
21
|
+
attr_accessor :clients_store, :last_used
|
22
|
+
|
23
|
+
def initialize
|
24
|
+
self.clients_store = {}
|
25
|
+
self.last_used = Time.now
|
26
|
+
end
|
27
|
+
|
28
|
+
def get_client(uri, options)
|
29
|
+
mutex.synchronize do
|
30
|
+
# refresh the last time a client was used,
|
31
|
+
# this prevents the client from becoming stale
|
32
|
+
self.last_used = Time.now
|
33
|
+
|
34
|
+
# we use params as a cache key for clients.
|
35
|
+
# 2 connections to the same host but with different
|
36
|
+
# options are going to use different HTTP clients
|
37
|
+
params = [uri.host, uri.port, options]
|
38
|
+
client = clients_store[params]
|
39
|
+
|
40
|
+
return client if client
|
41
|
+
|
42
|
+
client = Net::HTTP.new(uri.host, uri.port)
|
43
|
+
client.keep_alive_timeout = KEEP_ALIVE_TIMEOUT
|
44
|
+
|
45
|
+
# set SSL to true if a scheme is https
|
46
|
+
client.use_ssl = uri.scheme == 'https'
|
47
|
+
|
48
|
+
# dynamically set Net::HTTP options
|
49
|
+
DEFAULT_OPTIONS.merge(options).each_pair do |key, value|
|
50
|
+
client.public_send("#{key}=", value)
|
51
|
+
end
|
52
|
+
|
53
|
+
# open connection
|
54
|
+
client.start
|
55
|
+
|
56
|
+
# cache the client
|
57
|
+
clients_store[params] = client
|
58
|
+
|
59
|
+
client
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# close connections for each client
|
64
|
+
def close_connections!
|
65
|
+
mutex.synchronize do
|
66
|
+
clients_store.each_value(&:finish)
|
67
|
+
self.clients_store = {}
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def stale?
|
72
|
+
Time.now - last_used > STALE_AFTER
|
73
|
+
end
|
74
|
+
|
75
|
+
def mutex
|
76
|
+
@mutex ||= Mutex.new
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CashAppPay
|
4
|
+
# CashAppPayError is the base error from which all other more specific errors derive.
|
5
|
+
class CashAppPayError < StandardError
|
6
|
+
attr_reader :message
|
7
|
+
|
8
|
+
def initialize(message)
|
9
|
+
@message = message
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
# InvalidRequestError is raised when a request is initiated with invalid
|
14
|
+
# parameters.
|
15
|
+
class InvalidRequestError < CashAppPayError
|
16
|
+
attr_accessor :param
|
17
|
+
|
18
|
+
def initialize(message, param)
|
19
|
+
super(message)
|
20
|
+
@param = param
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# AuthenticationError is raised when invalid credentials are used to connect
|
25
|
+
# to Cash App servers.
|
26
|
+
class AuthenticationError < CashAppPayError
|
27
|
+
end
|
28
|
+
|
29
|
+
class APIError < CashAppPayError
|
30
|
+
attr_reader :http_status, :http_body
|
31
|
+
|
32
|
+
def initialize(message, http_status:, http_body:)
|
33
|
+
super(message)
|
34
|
+
@http_status = http_status
|
35
|
+
@http_body = http_body
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class APIResponseError < CashAppPayError
|
40
|
+
def initialize(response)
|
41
|
+
error_object = ErrorObject.new(response)
|
42
|
+
super(error_object.to_s)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CashAppPay
|
4
|
+
module Helpers
|
5
|
+
module Symbolize
|
6
|
+
extend self
|
7
|
+
|
8
|
+
def symbolize_recursive(hash)
|
9
|
+
{}.tap do |h|
|
10
|
+
hash.each { |key, value| h[key.to_sym] = transform(value) }
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def transform(thing)
|
17
|
+
case thing
|
18
|
+
when Hash then symbolize_recursive(thing)
|
19
|
+
when Array then thing.map { |v| transform(v) }
|
20
|
+
else; thing
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
refine Hash do
|
25
|
+
def deep_symbolize_keys
|
26
|
+
Symbolize.symbolize_recursive(self)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|