synapse_pay_rest 0.0.15 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +7 -2
  3. data/Gemfile.lock +10 -6
  4. data/LICENSE +20 -0
  5. data/README.md +80 -22
  6. data/lib/synapse_pay_rest.rb +65 -21
  7. data/lib/synapse_pay_rest/api/nodes.rb +93 -19
  8. data/lib/synapse_pay_rest/api/transactions.rb +103 -0
  9. data/lib/synapse_pay_rest/api/users.rb +101 -41
  10. data/lib/synapse_pay_rest/client.rb +49 -0
  11. data/lib/synapse_pay_rest/error.rb +8 -2
  12. data/lib/synapse_pay_rest/http_client.rb +94 -27
  13. data/lib/synapse_pay_rest/models/node/ach_us_node.rb +111 -0
  14. data/lib/synapse_pay_rest/models/node/base_node.rb +192 -0
  15. data/lib/synapse_pay_rest/models/node/eft_ind_node.rb +19 -0
  16. data/lib/synapse_pay_rest/models/node/eft_node.rb +27 -0
  17. data/lib/synapse_pay_rest/models/node/eft_np_node.rb +19 -0
  18. data/lib/synapse_pay_rest/models/node/iou_node.rb +27 -0
  19. data/lib/synapse_pay_rest/models/node/node.rb +99 -0
  20. data/lib/synapse_pay_rest/models/node/reserve_us_node.rb +23 -0
  21. data/lib/synapse_pay_rest/models/node/synapse_ind_node.rb +22 -0
  22. data/lib/synapse_pay_rest/models/node/synapse_node.rb +25 -0
  23. data/lib/synapse_pay_rest/models/node/synapse_np_node.rb +22 -0
  24. data/lib/synapse_pay_rest/models/node/synapse_us_node.rb +23 -0
  25. data/lib/synapse_pay_rest/models/node/unverified_node.rb +73 -0
  26. data/lib/synapse_pay_rest/models/node/wire_int_node.rb +23 -0
  27. data/lib/synapse_pay_rest/models/node/wire_node.rb +38 -0
  28. data/lib/synapse_pay_rest/models/node/wire_us_node.rb +23 -0
  29. data/lib/synapse_pay_rest/models/transaction/transaction.rb +212 -0
  30. data/lib/synapse_pay_rest/models/user/base_document.rb +346 -0
  31. data/lib/synapse_pay_rest/models/user/document.rb +71 -0
  32. data/lib/synapse_pay_rest/models/user/physical_document.rb +29 -0
  33. data/lib/synapse_pay_rest/models/user/question.rb +45 -0
  34. data/lib/synapse_pay_rest/models/user/social_document.rb +7 -0
  35. data/lib/synapse_pay_rest/models/user/user.rb +593 -0
  36. data/lib/synapse_pay_rest/models/user/virtual_document.rb +77 -0
  37. data/lib/synapse_pay_rest/version.rb +2 -1
  38. data/samples.md +391 -219
  39. data/synapse_pay_rest.gemspec +13 -12
  40. metadata +78 -24
  41. 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,7 @@
1
+ module SynapsePayRest
2
+ # Represents social documents that can be added to a base document.
3
+ #
4
+ # @see https://docs.synapsepay.com/docs/user-resources#section-social-document-types
5
+ # social document types
6
+ class SocialDocument < Document; end
7
+ 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