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.
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