synapse_pay_rest 0.0.15 → 2.0.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/.gitignore +7 -2
- data/Gemfile.lock +10 -6
- data/LICENSE +20 -0
- data/README.md +80 -22
- data/lib/synapse_pay_rest.rb +65 -21
- data/lib/synapse_pay_rest/api/nodes.rb +93 -19
- data/lib/synapse_pay_rest/api/transactions.rb +103 -0
- data/lib/synapse_pay_rest/api/users.rb +101 -41
- data/lib/synapse_pay_rest/client.rb +49 -0
- data/lib/synapse_pay_rest/error.rb +8 -2
- data/lib/synapse_pay_rest/http_client.rb +94 -27
- data/lib/synapse_pay_rest/models/node/ach_us_node.rb +111 -0
- data/lib/synapse_pay_rest/models/node/base_node.rb +192 -0
- data/lib/synapse_pay_rest/models/node/eft_ind_node.rb +19 -0
- data/lib/synapse_pay_rest/models/node/eft_node.rb +27 -0
- data/lib/synapse_pay_rest/models/node/eft_np_node.rb +19 -0
- data/lib/synapse_pay_rest/models/node/iou_node.rb +27 -0
- data/lib/synapse_pay_rest/models/node/node.rb +99 -0
- data/lib/synapse_pay_rest/models/node/reserve_us_node.rb +23 -0
- data/lib/synapse_pay_rest/models/node/synapse_ind_node.rb +22 -0
- data/lib/synapse_pay_rest/models/node/synapse_node.rb +25 -0
- data/lib/synapse_pay_rest/models/node/synapse_np_node.rb +22 -0
- data/lib/synapse_pay_rest/models/node/synapse_us_node.rb +23 -0
- data/lib/synapse_pay_rest/models/node/unverified_node.rb +73 -0
- data/lib/synapse_pay_rest/models/node/wire_int_node.rb +23 -0
- data/lib/synapse_pay_rest/models/node/wire_node.rb +38 -0
- data/lib/synapse_pay_rest/models/node/wire_us_node.rb +23 -0
- data/lib/synapse_pay_rest/models/transaction/transaction.rb +212 -0
- data/lib/synapse_pay_rest/models/user/base_document.rb +346 -0
- data/lib/synapse_pay_rest/models/user/document.rb +71 -0
- data/lib/synapse_pay_rest/models/user/physical_document.rb +29 -0
- data/lib/synapse_pay_rest/models/user/question.rb +45 -0
- data/lib/synapse_pay_rest/models/user/social_document.rb +7 -0
- data/lib/synapse_pay_rest/models/user/user.rb +593 -0
- data/lib/synapse_pay_rest/models/user/virtual_document.rb +77 -0
- data/lib/synapse_pay_rest/version.rb +2 -1
- data/samples.md +391 -219
- data/synapse_pay_rest.gemspec +13 -12
- metadata +78 -24
- data/lib/synapse_pay_rest/api/trans.rb +0 -38
@@ -0,0 +1,71 @@
|
|
1
|
+
module SynapsePayRest
|
2
|
+
# Ancestor of physical/social/virtual document types.
|
3
|
+
#
|
4
|
+
# @todo refactor this as a mixin since it shouldn't be instantiated.
|
5
|
+
class Document
|
6
|
+
|
7
|
+
# @!attribute [rw] base_document
|
8
|
+
# @return [SynapsePayRest::BaseDocument] the base document to which the document belongs
|
9
|
+
# @!attribute [rw] status
|
10
|
+
# @return [String] https://docs.synapsepay.com/docs/user-resources#section-document-status
|
11
|
+
attr_accessor :base_document, :status, :id, :type, :value, :last_updated
|
12
|
+
|
13
|
+
class << self
|
14
|
+
# Creates a document instances but does not submit it to the API. Use
|
15
|
+
# BaseDocument#create/#update/#add_physical_documents or related methods
|
16
|
+
# to submit the document to the API.
|
17
|
+
#
|
18
|
+
# @note This should only be called on subclasses of Document, not on
|
19
|
+
# Document itself.
|
20
|
+
#
|
21
|
+
# @param type [String]
|
22
|
+
# @param value [String]
|
23
|
+
#
|
24
|
+
# @return [SynapsePayRest::Document]
|
25
|
+
#
|
26
|
+
# @see https://docs.synapsepay.com/docs/user-resources#section-physical-document-types physical document types
|
27
|
+
# @see https://docs.synapsepay.com/docs/user-resources#section-social-document-types social document types
|
28
|
+
# @see https://docs.synapsepay.com/docs/user-resources#section-virtual-document-types virtual document types
|
29
|
+
def create(type:, value:)
|
30
|
+
raise ArgumentError, 'type must be a String' unless type.is_a?(String)
|
31
|
+
raise ArgumentError, 'value must be a String' unless type.is_a?(String)
|
32
|
+
|
33
|
+
self.new(type: type, value: value)
|
34
|
+
end
|
35
|
+
|
36
|
+
# @note Do not call this method. It is used by child classes only.
|
37
|
+
def create_from_response(data)
|
38
|
+
self.new(
|
39
|
+
type: data['document_type'],
|
40
|
+
id: data['id'],
|
41
|
+
status: data['status'],
|
42
|
+
last_updated: data['last_updated']
|
43
|
+
)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# @note Do not instantiate directly. User #create on subclasses.
|
48
|
+
def initialize(type:, **options)
|
49
|
+
@type = type.upcase
|
50
|
+
# only exist for created (not for fetched)
|
51
|
+
@id = options[:id]
|
52
|
+
@value = options[:value]
|
53
|
+
# only exist for fetched data
|
54
|
+
@status = options[:status]
|
55
|
+
@last_updated = options[:last_updated]
|
56
|
+
end
|
57
|
+
|
58
|
+
# Checks if two Document instances have same id (different instances of same record).
|
59
|
+
def ==(other)
|
60
|
+
other.instance_of?(self.class) && !id.nil? && id == other.id
|
61
|
+
end
|
62
|
+
|
63
|
+
# Converts the document into hash format for use in request JSON.
|
64
|
+
# @note You shouldn't need to call this directly.
|
65
|
+
#
|
66
|
+
# @return [Hash]
|
67
|
+
def to_hash
|
68
|
+
{'document_value' => value, 'document_type' => type}
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module SynapsePayRest
|
2
|
+
# Represents physical documents that can be added to a base document.
|
3
|
+
#
|
4
|
+
# @see https://docs.synapsepay.com/docs/user-resources#section-physical-document-types
|
5
|
+
# physical document types
|
6
|
+
class PhysicalDocument < Document
|
7
|
+
# Converts the document into hash format for use in request JSON.
|
8
|
+
# @note You shouldn't need to call this directly.
|
9
|
+
#
|
10
|
+
# @return [Hash]
|
11
|
+
def to_hash
|
12
|
+
{'document_value' => to_base64(value), 'document_type' => type}
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
# Converts the supplied file to base64 encoding so it can be uploaded to API.
|
18
|
+
def to_base64(file_path)
|
19
|
+
raise ArgumentError, 'file_path must be a String' unless file_path.is_a?(String)
|
20
|
+
|
21
|
+
content_types = MIME::Types.type_for(file_path)
|
22
|
+
file_type = content_types.first.content_type if content_types.any?
|
23
|
+
file_contents = open(file_path) { |f| f.read }
|
24
|
+
encoded = Base64.encode64(file_contents)
|
25
|
+
mime_padding = "data:#{file_type};base64,"
|
26
|
+
mime_padding + encoded
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module SynapsePayRest
|
2
|
+
# Represents a question that is triggered when a document is returned with
|
3
|
+
# status MFA|PENDING.
|
4
|
+
class Question
|
5
|
+
# @!attribute [r] question
|
6
|
+
# @return [String] the text of the question
|
7
|
+
# @!attribute [r] answers
|
8
|
+
# @return [Hash{Integer=>String}] the answer choice numbers and text
|
9
|
+
# @!attribute [r] id
|
10
|
+
# @return [Integer] the number of the question in the question_set
|
11
|
+
# @!attribute [r] choice
|
12
|
+
# @return [String,void] the chosen answer (starts out nil)
|
13
|
+
attr_reader :question, :answers, :id, :choice
|
14
|
+
|
15
|
+
# @note This is initialized automatically by SynapsePayRest::VirtualDocument.
|
16
|
+
def initialize(id:, question:, answers:)
|
17
|
+
@id = id
|
18
|
+
@question = question
|
19
|
+
@answers = answers
|
20
|
+
@choice = nil
|
21
|
+
end
|
22
|
+
|
23
|
+
# Selects the user's answer choice for this question. This does not submit
|
24
|
+
# it to the API - you must call VirtualDocument#submit once all questions
|
25
|
+
# have been answered.
|
26
|
+
#
|
27
|
+
# @param answer_number [Integer] the user's chosen answer
|
28
|
+
#
|
29
|
+
# @return [Integer] the answer chosen
|
30
|
+
def choice=(answer_number)
|
31
|
+
raise ArgumentError, 'answer_number must be an Integer' unless answer_number.is_a?(Integer)
|
32
|
+
unless answers.keys.include? answer_number
|
33
|
+
raise ArgumentError, "answer given must be in #{answers.keys}"
|
34
|
+
end
|
35
|
+
|
36
|
+
@choice = answer_number
|
37
|
+
end
|
38
|
+
|
39
|
+
# Converts the question/answer to a hash for use in JSON.
|
40
|
+
# @note You should not need to call this directly.
|
41
|
+
def to_hash
|
42
|
+
{'question_id' => id, 'answer_id' => choice}
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,593 @@
|
|
1
|
+
module SynapsePayRest
|
2
|
+
# Represents a user record and holds methods for constructing user instances
|
3
|
+
# from API calls. This is built on top of the SynapsePayRest::Users class and
|
4
|
+
# is intended to make it easier to use the API without knowing payload formats
|
5
|
+
# or knowledge of REST.
|
6
|
+
#
|
7
|
+
# @todo use mixins to remove duplication between Node and BaseNode. May be
|
8
|
+
# better to refactor this into a mixin altogether since this shouldn't be instantiated.
|
9
|
+
# @todo reduce duplicated logic between User/BaseNode/Transaction
|
10
|
+
class User
|
11
|
+
# @!attribute [rw] base_documents
|
12
|
+
# @return [Array<SynapsePayRest::BaseDocument>]
|
13
|
+
# @!attribute [r] permission
|
14
|
+
# @return [String] https://docs.synapsepay.com/docs/user-resources#section-user-permissions
|
15
|
+
attr_reader :client, :id, :logins, :phone_numbers, :legal_names, :note,
|
16
|
+
:supp_id, :is_business, :cip_tag, :permission
|
17
|
+
attr_accessor :refresh_token, :base_documents
|
18
|
+
|
19
|
+
class << self
|
20
|
+
# Creates a new user in the API and returns a User instance from the
|
21
|
+
# response data.
|
22
|
+
#
|
23
|
+
# @param client [SynapsePayRest::Client]
|
24
|
+
# @param logins [Array<Hash>]
|
25
|
+
# @param phone_numbers [Array<String>]
|
26
|
+
# @param legal_names [Array<String>]
|
27
|
+
# @param note [String] (optional)
|
28
|
+
# @param supp_id [String] (optional)
|
29
|
+
# @param is_business [Boolean] (optional) API defaults to false
|
30
|
+
# @param cip_tag [Integer] (optional) the CIP tag to use in this users CIP doc
|
31
|
+
#
|
32
|
+
# @example logins argument (only :email is required)
|
33
|
+
# [{
|
34
|
+
# email: "test@test.com",
|
35
|
+
# password: "letmein",
|
36
|
+
# read_only: false
|
37
|
+
# }]
|
38
|
+
#
|
39
|
+
# @raise [SynapsePayRest::Error] if HTTP error or invalid argument format
|
40
|
+
#
|
41
|
+
# @return [SynapsePayRest::User]
|
42
|
+
#
|
43
|
+
# @todo simplify the logins argument somehow. separate class?
|
44
|
+
def create(client:, logins:, phone_numbers:, legal_names:, **options)
|
45
|
+
raise ArgumentError, 'client must be a SynapsePayRest::Client' unless client.is_a?(Client)
|
46
|
+
if [logins, phone_numbers, legal_names].any? { |arg| !arg.is_a? Array}
|
47
|
+
raise ArgumentError, 'logins/phone_numbers/legal_names must be Array'
|
48
|
+
end
|
49
|
+
if [logins, phone_numbers, legal_names].any?(&:empty?)
|
50
|
+
raise ArgumentError, 'logins/phone_numbers/legal_names cannot be empty'
|
51
|
+
end
|
52
|
+
unless logins.first.is_a? Hash
|
53
|
+
raise ArgumentError, 'logins must contain at least one hash {email: (required), password:, read_only:}'
|
54
|
+
end
|
55
|
+
unless logins.first[:email].is_a?(String) && logins.first[:email].length > 0
|
56
|
+
raise ArgumentError, 'logins must contain at least one hash with :email key'
|
57
|
+
end
|
58
|
+
|
59
|
+
payload = payload_for_create(logins: logins, phone_numbers: phone_numbers, legal_names: legal_names, **options)
|
60
|
+
response = client.users.create(payload: payload)
|
61
|
+
create_from_response(client, response)
|
62
|
+
end
|
63
|
+
|
64
|
+
# Queries the API for a user by id and returns a User instances if found.
|
65
|
+
#
|
66
|
+
# @param client [SynapsePayRest::Client]
|
67
|
+
# @param id [String] id of the user to find
|
68
|
+
#
|
69
|
+
# @raise [SynapsePayRest::Error] if user not found or invalid client credentials
|
70
|
+
#
|
71
|
+
# @return [SynapsePayRest::User]
|
72
|
+
def find(client:, id:)
|
73
|
+
raise ArgumentError, 'client must be a SynapsePayRest::Client' unless client.is_a?(Client)
|
74
|
+
raise ArgumentError, 'id must be a String' unless id.is_a?(String)
|
75
|
+
|
76
|
+
response = client.users.get(user_id: id)
|
77
|
+
create_from_response(client, response)
|
78
|
+
end
|
79
|
+
|
80
|
+
# Queries the API for all users (with optional filters) and returns them
|
81
|
+
# as User instances.
|
82
|
+
#
|
83
|
+
# @param client [SynapsePayRest::Client]
|
84
|
+
# @param query [String] (optional) response will be filtered to
|
85
|
+
# users with matching name/email
|
86
|
+
# @param page [String,Integer] (optional) response will default to 1
|
87
|
+
# @param per_page [String,Integer] (optional) response will default to 20
|
88
|
+
#
|
89
|
+
# @raise [SynapsePayRest::Error] if HTTP error or invalid argument format
|
90
|
+
#
|
91
|
+
# @return [Array<SynapsePayRest::User>]
|
92
|
+
def all(client:, page: nil, per_page: nil, query: nil)
|
93
|
+
raise ArgumentError, 'client must be a SynapsePayRest::Client' unless client.is_a?(Client)
|
94
|
+
[page, per_page].each do |arg|
|
95
|
+
if arg && (!arg.is_a?(Integer) || arg < 1)
|
96
|
+
raise ArgumentError, "#{arg} must be nil or an Integer >= 1"
|
97
|
+
end
|
98
|
+
end
|
99
|
+
if query && !query.is_a?(String)
|
100
|
+
raise ArgumentError, 'query must be a String'
|
101
|
+
end
|
102
|
+
|
103
|
+
response = client.users.get(page: page, per_page: per_page, query: query)
|
104
|
+
create_multiple_from_response(client, response['users'])
|
105
|
+
end
|
106
|
+
|
107
|
+
# Queries the API for all users with name/email matching the given query
|
108
|
+
# and returns them as User instances.
|
109
|
+
#
|
110
|
+
# @param client [SynapsePayRest::Client]
|
111
|
+
# @param query [String] response will be filtered to
|
112
|
+
# users with matching name/email
|
113
|
+
# @param page [String,Integer] (optional) response will default to 1
|
114
|
+
# @param per_page [String,Integer] (optional) response will default to 20
|
115
|
+
#
|
116
|
+
# @raise [SynapsePayRest::Error] if HTTP error or invalid argument format
|
117
|
+
#
|
118
|
+
# @return [Array<SynapsePayRest::User>]
|
119
|
+
def search(client:, query:, page: nil, per_page: nil)
|
120
|
+
all(client: client, query: query, page: page, per_page: per_page)
|
121
|
+
end
|
122
|
+
|
123
|
+
# Maps args to API payload format.
|
124
|
+
# @note Do not call directly.
|
125
|
+
def payload_for_create(logins:, phone_numbers:, legal_names:, **options)
|
126
|
+
payload = {
|
127
|
+
'logins' => logins,
|
128
|
+
'phone_numbers' => phone_numbers,
|
129
|
+
'legal_names' => legal_names,
|
130
|
+
}
|
131
|
+
# optional payload fields
|
132
|
+
extra = {}
|
133
|
+
extra['note'] = options[:note] if options[:note]
|
134
|
+
extra['supp_id'] = options[:supp_id] if options[:supp_id]
|
135
|
+
extra['is_business'] = options[:is_business] if options[:is_business]
|
136
|
+
extra['cip_tag'] = options[:cip_tag] if options[:cip_tag]
|
137
|
+
payload['extra'] = extra if extra.any?
|
138
|
+
|
139
|
+
payload
|
140
|
+
end
|
141
|
+
|
142
|
+
# Constructs a user instance from a user response.
|
143
|
+
# @note Do not call directly.
|
144
|
+
def create_from_response(client, response)
|
145
|
+
user = self.new(
|
146
|
+
client: client,
|
147
|
+
id: response['_id'],
|
148
|
+
refresh_token: response['refresh_token'],
|
149
|
+
logins: response['logins'],
|
150
|
+
phone_numbers: response['phone_numbers'],
|
151
|
+
legal_names: response['legal_names'],
|
152
|
+
permission: response['permission'],
|
153
|
+
note: response['extra']['note'],
|
154
|
+
supp_id: response['extra']['supp_id'],
|
155
|
+
is_business: response['extra']['is_business'],
|
156
|
+
cip_tag: response['extra']['cip_tag']
|
157
|
+
)
|
158
|
+
|
159
|
+
unless response['documents'].empty?
|
160
|
+
base_documents = BaseDocument.create_from_response(user, response)
|
161
|
+
user.base_documents = base_documents
|
162
|
+
end
|
163
|
+
user
|
164
|
+
end
|
165
|
+
|
166
|
+
# Calls create_from_response on each member of a response collection.
|
167
|
+
def create_multiple_from_response(client, response)
|
168
|
+
return [] if response.empty?
|
169
|
+
response.map { |user_data| create_from_response(client, user_data)}
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
# @note Do not call directly. Use User.create or other class method
|
174
|
+
# to instantiate via API action.
|
175
|
+
def initialize(**options)
|
176
|
+
options.each { |key, value| instance_variable_set("@#{key}", value) }
|
177
|
+
@client.http_client.user_id = id
|
178
|
+
@base_documents ||= []
|
179
|
+
end
|
180
|
+
|
181
|
+
# Updates the given key value pairs.
|
182
|
+
#
|
183
|
+
# @param login [Hash]
|
184
|
+
# @param phone_number [String]
|
185
|
+
# @param legal_name [String]
|
186
|
+
# @param remove_login [Hash]
|
187
|
+
# @param remove_phone_number [String]
|
188
|
+
# @param read_only [Boolean]
|
189
|
+
#
|
190
|
+
# @example login/remove_login argument (only email is required)
|
191
|
+
# {
|
192
|
+
# email: "test@test.com",
|
193
|
+
# password: "letmein",
|
194
|
+
# read_only: false
|
195
|
+
# }
|
196
|
+
#
|
197
|
+
# @raise [SynapsePayRest::Error] if HTTP error or invalid argument format
|
198
|
+
#
|
199
|
+
# @return [SynapsePayRest::User] new instance corresponding to same API record
|
200
|
+
def update(**options)
|
201
|
+
if options.empty?
|
202
|
+
raise ArgumentError, 'must provide a key-value pair to update. keys: login,
|
203
|
+
read_only, phone_number, legal_name, remove_phone_number, remove_login'
|
204
|
+
end
|
205
|
+
|
206
|
+
response = client.users.update(payload: payload_for_update(options))
|
207
|
+
# return an updated user instance
|
208
|
+
self.class.create_from_response(client, response)
|
209
|
+
end
|
210
|
+
|
211
|
+
# Creates a new base document for the user. To update an existing base
|
212
|
+
# document, see SynapsePay::BaseDocument#update.
|
213
|
+
#
|
214
|
+
# @param email [String]
|
215
|
+
# @param phone_number [String]
|
216
|
+
# @param ip [String]
|
217
|
+
# @param name [String]
|
218
|
+
# @param aka [String] corresponds to 'alias' in docs, use name if no alias
|
219
|
+
# @param entity_type [String] consult your organization's CIP for valid options
|
220
|
+
# @see https://docs.synapsepay.com/docs/user-resources#section-supported-entity-types all supported entity_type values
|
221
|
+
# @param entity_scope [String] consult your organization's CIP for valid options
|
222
|
+
# @see https://docs.synapsepay.com/docs/user-resources#section-supported-entity-scope all entity_scope options
|
223
|
+
# @param birth_day [Integer]
|
224
|
+
# @param birth_month [Integer]
|
225
|
+
# @param birth_year [Integer]
|
226
|
+
# @param address_street [String]
|
227
|
+
# @param address_city [String]
|
228
|
+
# @param address_subdivision [String]
|
229
|
+
# @param address_postal_code [String]
|
230
|
+
# @param address_country_code [String]
|
231
|
+
# @param physical_documents [Array<SynapsePayRest::PhysicalDocument>] (optional)
|
232
|
+
# @param social_documents [Array<SynapsePayRest::SocialDocument>] (optional)
|
233
|
+
# @param virtual_documents [Array<SynapsePayRest::VirtualDocument>] (optional)
|
234
|
+
#
|
235
|
+
# @raise [SynapsePayRest::Error]
|
236
|
+
#
|
237
|
+
# @return [SynapsePayRest::User] new instance corresponding to same API record
|
238
|
+
def create_base_document(**args)
|
239
|
+
BaseDocument.create(user: self, **args)
|
240
|
+
end
|
241
|
+
|
242
|
+
# Adds a login for the user.
|
243
|
+
#
|
244
|
+
# @param email [String]
|
245
|
+
# @param password [String] (optional)
|
246
|
+
# @param read_only [Boolean] (optional)
|
247
|
+
#
|
248
|
+
# @raise [SynapsePayRest::Error]
|
249
|
+
#
|
250
|
+
# @return [SynapsePayRest::User] (self)
|
251
|
+
def add_login(email:, password: nil, read_only: nil)
|
252
|
+
raise ArgumentError, 'email must be a String' unless email.is_a?(String)
|
253
|
+
raise ArgumentError, 'password must be nil or String' if password && !password.is_a?(String)
|
254
|
+
if read_only && ![true, false].include?(read_only)
|
255
|
+
raise ArgumentError, 'read_only must be nil or Boolean'
|
256
|
+
end
|
257
|
+
|
258
|
+
login = {'email' => email}
|
259
|
+
# optional
|
260
|
+
login['password'] = password if password
|
261
|
+
login['read_only'] = read_only if read_only
|
262
|
+
update(login: login)
|
263
|
+
end
|
264
|
+
|
265
|
+
# Removes a login from the user.
|
266
|
+
#
|
267
|
+
# @param email [String]
|
268
|
+
#
|
269
|
+
# @raise [SynapsePayRest::Error]
|
270
|
+
#
|
271
|
+
# @return [SynapsePayRest::User] new instance corresponding to same API record
|
272
|
+
def remove_login(email:)
|
273
|
+
raise ArgumentError, 'email must be a String' unless email.is_a? String
|
274
|
+
|
275
|
+
login = {email: email}
|
276
|
+
update(remove_login: login)
|
277
|
+
end
|
278
|
+
|
279
|
+
# Add a phone_number to the user.
|
280
|
+
#
|
281
|
+
# @param phone_number [String]
|
282
|
+
#
|
283
|
+
# @raise [SynapsePayRest::Error]
|
284
|
+
#
|
285
|
+
# @return [SynapsePayRest::User] new instance corresponding to same API record
|
286
|
+
def add_phone_number(phone_number)
|
287
|
+
raise ArgumentError, 'phone_number must be a String' unless phone_number.is_a? String
|
288
|
+
|
289
|
+
update(phone_number: phone_number)
|
290
|
+
end
|
291
|
+
|
292
|
+
# Removes a phone_number from the user.
|
293
|
+
#
|
294
|
+
# @param phone_number [String]
|
295
|
+
#
|
296
|
+
# @raise [SynapsePayRest::Error]
|
297
|
+
#
|
298
|
+
# @return [SynapsePayRest::User] new instance corresponding to same API record
|
299
|
+
def remove_phone_number(phone_number)
|
300
|
+
raise ArgumentError, 'phone_number must be a String' unless phone_number.is_a? String
|
301
|
+
|
302
|
+
update(remove_phone_number: phone_number)
|
303
|
+
end
|
304
|
+
|
305
|
+
# Updates the user's oauth token.
|
306
|
+
#
|
307
|
+
# @raise [SynapsePayRest::Error]
|
308
|
+
#
|
309
|
+
# @return [SynapsePayRest::User] (self)
|
310
|
+
def authenticate
|
311
|
+
client.users.refresh(payload: payload_for_refresh)
|
312
|
+
self
|
313
|
+
end
|
314
|
+
|
315
|
+
# Step 1 of fingerprint registration. Requests a new fingerprint be
|
316
|
+
# registered to the user.
|
317
|
+
#
|
318
|
+
# @param fingerprint [String]
|
319
|
+
#
|
320
|
+
# @raise [SynapsePayRest::Error]
|
321
|
+
#
|
322
|
+
# @return [Array<String>] array of devices (phone number / email)
|
323
|
+
def register_fingerprint(fingerprint)
|
324
|
+
raise ArgumentError, 'fingerprint must be a String' unless fingerprint.is_a?(String)
|
325
|
+
|
326
|
+
client.http_client.update_headers(fingerprint: fingerprint)
|
327
|
+
response = client.users.refresh(payload: payload_for_refresh)
|
328
|
+
response['phone_numbers']
|
329
|
+
end
|
330
|
+
|
331
|
+
# Step 2 of fingerprint registration. Sends a request to the API to send a
|
332
|
+
# 2FA PIN to the device specified. The device must be selected from return
|
333
|
+
# value of #register_fingerprint.
|
334
|
+
#
|
335
|
+
# @param device [String]
|
336
|
+
#
|
337
|
+
# @raise [SynapsePayRest::Error]
|
338
|
+
#
|
339
|
+
# @return [:success] if successful
|
340
|
+
def select_2fa_device(device)
|
341
|
+
raise ArgumentError, 'device must be a String' unless device.is_a?(String)
|
342
|
+
|
343
|
+
payload = payload_for_refresh
|
344
|
+
payload['phone_number'] = device
|
345
|
+
client.users.refresh(payload: payload)
|
346
|
+
:success
|
347
|
+
end
|
348
|
+
|
349
|
+
# Step 3 (final) step of fingerprint registration. Confirms the PIN sent to
|
350
|
+
# the device after calling #select_2fa_device (step 2).
|
351
|
+
#
|
352
|
+
# @param pin [String]
|
353
|
+
# @param device [String]
|
354
|
+
#
|
355
|
+
# @raise [SynapsePayRest::Error]
|
356
|
+
#
|
357
|
+
# @return [:success] if successful
|
358
|
+
def confirm_2fa_pin(pin:, device:)
|
359
|
+
raise ArgumentError, 'pin must be a String' unless pin.is_a?(String)
|
360
|
+
raise ArgumentError, 'device must be a String' unless device.is_a?(String)
|
361
|
+
|
362
|
+
payload = payload_for_refresh
|
363
|
+
payload['phone_number'] = device
|
364
|
+
payload['validation_pin'] = pin
|
365
|
+
client.users.refresh(payload: payload)
|
366
|
+
:success
|
367
|
+
end
|
368
|
+
|
369
|
+
# Queries the API for all nodes belonging to this user and returns them as
|
370
|
+
# node (SynapsePayRest::BaseNode) instances.
|
371
|
+
#
|
372
|
+
# @param page [String,Integer] (optional) response will default to 1
|
373
|
+
# @param per_page [String,Integer] (optional) response will default to 20
|
374
|
+
# @param type [String] (optional)
|
375
|
+
# @see https://docs.synapsepay.com/docs/node-resources node types
|
376
|
+
#
|
377
|
+
# @raise [SynapsePayRest::Error]
|
378
|
+
#
|
379
|
+
# @return [Array<SynapsePayRest::BaseNode>] subclass d`epends on node types
|
380
|
+
def nodes(**options)
|
381
|
+
Node.all(user: self, **options)
|
382
|
+
end
|
383
|
+
|
384
|
+
# Queries the API for a node belonging to this user by node id and returns
|
385
|
+
# a node instance if found.
|
386
|
+
#
|
387
|
+
# @param id [String] id of the node to find
|
388
|
+
#
|
389
|
+
# @raise [SynapsePayRest::Error] if node not found or other HTTP error
|
390
|
+
#
|
391
|
+
# @return [SynapsePayRest::BaseNode] subclass depends on node type
|
392
|
+
def find_node(id:)
|
393
|
+
Node.find(user: self, id: id)
|
394
|
+
end
|
395
|
+
|
396
|
+
# Creates an ACH-US node via account and routing numbers, belonging to this user.
|
397
|
+
#
|
398
|
+
# @param nickname [String] nickname for the node
|
399
|
+
# @param account_number [String]
|
400
|
+
# @param routing_number [String]
|
401
|
+
# @param account_type [String] 'PERSONAL' or 'BUSINESS'
|
402
|
+
# @param account_class [String] 'CHECKING' or 'SAVINGS'
|
403
|
+
# @param supp_id [String] (optional)
|
404
|
+
# @param gateway_restricted [Boolean] (optional)
|
405
|
+
#
|
406
|
+
# @raise [SynapsePayRest::Error]
|
407
|
+
#
|
408
|
+
# @return [SynapsePayRest::AchUsNode]
|
409
|
+
def create_ach_us_node(**options)
|
410
|
+
AchUsNode.create(user: self, **options)
|
411
|
+
end
|
412
|
+
|
413
|
+
# Creates an ACH-US node via bank login, belonging to this user.
|
414
|
+
#
|
415
|
+
# @param bank_name [String]
|
416
|
+
# @see https://synapsepay.com/api/v3/institutions/show valid bank_name options
|
417
|
+
# @param username [String] user's bank login username
|
418
|
+
# @param password [String] user's bank login password
|
419
|
+
#
|
420
|
+
# @raise [SynapsePayRest::Error]
|
421
|
+
#
|
422
|
+
# @return [Array<SynapsePayRest::AchUsNode>] may contain multiple nodes (checking and/or savings)
|
423
|
+
def create_ach_us_nodes_via_bank_login(**options)
|
424
|
+
AchUsNode.create_via_bank_login(user: self, **options)
|
425
|
+
end
|
426
|
+
|
427
|
+
# Creates an EFT-IND node.
|
428
|
+
#
|
429
|
+
# @param nickname [String] nickname for the node
|
430
|
+
# @param account_number [String]
|
431
|
+
# @param ifsc [String]
|
432
|
+
# @param supp_id [String] (optional)
|
433
|
+
# @param gateway_restricted [Boolean] (optional)
|
434
|
+
#
|
435
|
+
# @raise [SynapsePayRest::Error]
|
436
|
+
#
|
437
|
+
# @return [SynapsePayRest::EftIndNode]
|
438
|
+
def create_eft_ind_node(**options)
|
439
|
+
EftIndNode.create(user: self, **options)
|
440
|
+
end
|
441
|
+
|
442
|
+
# Creates an EFT-NP node.
|
443
|
+
#
|
444
|
+
# @param nickname [String] nickname for the node
|
445
|
+
# @param bank_name [String]
|
446
|
+
# @param account_number [String]
|
447
|
+
# @param supp_id [String] (optional)
|
448
|
+
# @param gateway_restricted [Boolean] (optional)
|
449
|
+
#
|
450
|
+
# @raise [SynapsePayRest::Error]
|
451
|
+
#
|
452
|
+
# @return [SynapsePayRest::EftNpNode]
|
453
|
+
def create_eft_np_node(**options)
|
454
|
+
EftNpNode.create(user: self, **options)
|
455
|
+
end
|
456
|
+
|
457
|
+
# Creates an IOU node.
|
458
|
+
#
|
459
|
+
# @param nickname [String] nickname for the node
|
460
|
+
# @param currency [String] e.g. 'USD'
|
461
|
+
# @param supp_id [String] (optional)
|
462
|
+
# @param gateway_restricted [Boolean] (optional)
|
463
|
+
#
|
464
|
+
# @raise [SynapsePayRest::Error]
|
465
|
+
#
|
466
|
+
# @return [SynapsePayRest::IouNode]
|
467
|
+
def create_iou_node(**options)
|
468
|
+
IouNode.create(user: self, **options)
|
469
|
+
end
|
470
|
+
|
471
|
+
# Creates a RESERVE-US node.
|
472
|
+
#
|
473
|
+
# @param nickname [String] nickname for the node
|
474
|
+
# @param supp_id [String] (optional)
|
475
|
+
# @param gateway_restricted [Boolean] (optional)
|
476
|
+
#
|
477
|
+
# @raise [SynapsePayRest::Error]
|
478
|
+
#
|
479
|
+
# @return [SynapsePayRest::IouNode]
|
480
|
+
def create_reserve_us_node(**options)
|
481
|
+
ReserveUsNode.create(user: self, **options)
|
482
|
+
end
|
483
|
+
|
484
|
+
# Creates a SYNAPSE-IND node.
|
485
|
+
#
|
486
|
+
# @param nickname [String] nickname for the node
|
487
|
+
# @param supp_id [String] (optional)
|
488
|
+
# @param gateway_restricted [Boolean] (optional)
|
489
|
+
#
|
490
|
+
# @raise [SynapsePayRest::Error]
|
491
|
+
#
|
492
|
+
# @return [SynapsePayRest::SynapseIndNode]
|
493
|
+
def create_synapse_ind_node(**options)
|
494
|
+
SynapseIndNode.create(user: self, **options)
|
495
|
+
end
|
496
|
+
|
497
|
+
# Creates a SYNAPSE-NP node.
|
498
|
+
#
|
499
|
+
# @param nickname [String] nickname for the node
|
500
|
+
# @param supp_id [String] (optional)
|
501
|
+
# @param gateway_restricted [Boolean] (optional)
|
502
|
+
#
|
503
|
+
# @raise [SynapsePayRest::Error]
|
504
|
+
#
|
505
|
+
# @return [SynapsePayRest::SynapseNpNode]
|
506
|
+
def create_synapse_np_node(**options)
|
507
|
+
SynapseNpNode.create(user: self, **options)
|
508
|
+
end
|
509
|
+
|
510
|
+
# Creates a SYNAPSE-US node.
|
511
|
+
#
|
512
|
+
# @param nickname [String] nickname for the node
|
513
|
+
# @param supp_id [String] (optional)
|
514
|
+
# @param gateway_restricted [Boolean] (optional)
|
515
|
+
#
|
516
|
+
# @raise [SynapsePayRest::Error]
|
517
|
+
#
|
518
|
+
# @return [SynapsePayRest::SynapseUsNode]
|
519
|
+
def create_synapse_us_node(**options)
|
520
|
+
SynapseUsNode.create(user: self, **options)
|
521
|
+
end
|
522
|
+
|
523
|
+
# Creates a WIRE-INT node.
|
524
|
+
#
|
525
|
+
# @param nickname [String] nickname for the node
|
526
|
+
# @param bank_name [String]
|
527
|
+
# @param account_number [String]
|
528
|
+
# @param swift [String]
|
529
|
+
# @param name_on_account [String]
|
530
|
+
# @param address [String]
|
531
|
+
# @param routing_number [String] (optional)
|
532
|
+
# @param correspondent_bank_name [String] (optional)
|
533
|
+
# @param correspondent_routing_number [String] (optional)
|
534
|
+
# @param correspondent_address [String] (optional)
|
535
|
+
# @param correspondent_swift [String] (optional)
|
536
|
+
# @param supp_id [String] (optional)
|
537
|
+
# @param gateway_restricted [Boolean] (optional)
|
538
|
+
#
|
539
|
+
# @raise [SynapsePayRest::Error]
|
540
|
+
#
|
541
|
+
# @return [SynapsePayRest::WireIntNode]
|
542
|
+
def create_wire_int_node(**options)
|
543
|
+
WireIntNode.create(user: self, **options)
|
544
|
+
end
|
545
|
+
|
546
|
+
# Creates a WIRE-US node.
|
547
|
+
#
|
548
|
+
# @param nickname [String] nickname for the node
|
549
|
+
# @param bank_name [String]
|
550
|
+
# @param account_number [String]
|
551
|
+
# @param routing_number [String]
|
552
|
+
# @param name_on_account [String]
|
553
|
+
# @param address [String]
|
554
|
+
# @param correspondent_routing_number [String] (optional)
|
555
|
+
# @param correspondent_bank_name [String] (optional)
|
556
|
+
# @param correspondent_address [String] (optional)
|
557
|
+
# @param supp_id [String] (optional)
|
558
|
+
# @param gateway_restricted [Boolean] (optional)
|
559
|
+
#
|
560
|
+
# @raise [SynapsePayRest::Error]
|
561
|
+
#
|
562
|
+
# @return [SynapsePayRest::WireUsNode]
|
563
|
+
def create_wire_us_node(**options)
|
564
|
+
WireUsNode.create(user: self, **options)
|
565
|
+
end
|
566
|
+
|
567
|
+
# Checks if two User instances have same id (different instances of same record).
|
568
|
+
def ==(other)
|
569
|
+
other.instance_of?(self.class) && !id.nil? && id == other.id
|
570
|
+
end
|
571
|
+
|
572
|
+
private
|
573
|
+
|
574
|
+
# Converts #update args into API payload structure.
|
575
|
+
def payload_for_update(**options)
|
576
|
+
payload = {
|
577
|
+
'refresh_token' => refresh_token,
|
578
|
+
'update' => {}
|
579
|
+
}
|
580
|
+
# must have one of these
|
581
|
+
payload['update']['login'] = options[:login] if options[:login]
|
582
|
+
payload['update']['remove_login'] = options[:remove_login] if options[:remove_login]
|
583
|
+
payload['update']['legal_name'] = options[:legal_name] if options[:legal_name]
|
584
|
+
payload['update']['phone_number'] = options[:phone_number] if options[:phone_number]
|
585
|
+
payload['update']['remove_phone_number'] = options[:remove_phone_number] if options[:remove_phone_number]
|
586
|
+
payload
|
587
|
+
end
|
588
|
+
|
589
|
+
def payload_for_refresh
|
590
|
+
{'refresh_token' => refresh_token}
|
591
|
+
end
|
592
|
+
end
|
593
|
+
end
|