slidepay 0.0.12 → 0.0.13
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +71 -0
- data/TODO.md +8 -6
- data/lib/slidepay.rb +5 -0
- data/lib/slidepay/ach/balance.rb +31 -0
- data/lib/slidepay/ach/resource.rb +17 -0
- data/lib/slidepay/ach/retrieval.rb +111 -0
- data/lib/slidepay/ach/settlement.rb +68 -0
- data/lib/slidepay/resources/api_resource.rb +18 -16
- data/lib/slidepay/resources/payment.rb +17 -0
- data/lib/slidepay/version.rb +1 -1
- data/scenarios/ach/balance.rb +18 -0
- data/scenarios/ach/fetch_retrieval.rb +19 -0
- data/scenarios/ach/retrieval_create_with_bank_account_info.rb +39 -0
- data/scenarios/ach/retrieval_create_with_id_fields.rb +25 -0
- data/scenarios/ach/retrieval_fetch_with_settlement_token_class_method.rb +17 -0
- data/scenarios/ach/retrieval_settlement_token_fetch.rb +19 -0
- data/scenarios/ach/settlement.rb +28 -0
- data/scenarios/create_simple_payment.rb +33 -0
- data/spec/ach/balance_spec.rb +38 -0
- data/spec/ach/resource_spec.rb +17 -0
- data/spec/ach/retrieval_spec.rb +170 -0
- data/spec/ach/settlement_spec.rb +121 -0
- data/spec/payment_spec.rb +26 -1
- data/spec/spec_helper.rb +524 -1
- metadata +22 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b65de9b4a73dbbcf304c9a88b93e9a03fcee70b4
|
4
|
+
data.tar.gz: cc564d6baeeec3e2ffbed1662ba04c5c82afcec1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7ca65bf127f9c5e96dfb5436ce8ec413e97c95b562ae9ebea81fb65a4e8bb53f1e4b357026387825e37997d3fa483dfba150d23579ae4b0e2b270a8f9b01cb92
|
7
|
+
data.tar.gz: 39dd5940961c5a0642e9a6c9f409d7a7eec33e9a468bccddab6aeec87461a4e0284c51d2da2a934a261dc3bb110cbf935404e8d688a6ab0f10167d7ac59b162b
|
data/README.md
CHANGED
@@ -253,6 +253,77 @@ payment_report.retrieve()
|
|
253
253
|
|
254
254
|
```
|
255
255
|
|
256
|
+
## ACH
|
257
|
+
|
258
|
+
SlidePay supports ACH retrievals, programmatic settlements via ACH, and checking the balance of unsettled funds for an account.
|
259
|
+
|
260
|
+
### Balance
|
261
|
+
|
262
|
+
To check the balance of a settlement ledger, just provide a location_id to the ```Balance.retrieve``` class method.
|
263
|
+
|
264
|
+
```ruby
|
265
|
+
location_id = 18
|
266
|
+
balance = SlidePay::ACH::Balance.retrieve(location_id)
|
267
|
+
```
|
268
|
+
|
269
|
+
### ACH Retrieval
|
270
|
+
|
271
|
+
To initiate an ACH retrieval from a bank account, supply a hash with bank account details to the
|
272
|
+
```Retrieval.create_with_bank_account``` class method, or create an instance of the Retrieval class and
|
273
|
+
use the instance method of the same name.
|
274
|
+
|
275
|
+
A retrieval can be initiated using either a stored bank account or explicit bank account details. A full
|
276
|
+
explanation of the differences can be found on the
|
277
|
+
[ACH retrieval documentation page](https://getcube.atlassian.net/wiki/display/CDP/Processing+Manual+ACH+Retrievals).
|
278
|
+
|
279
|
+
```ruby
|
280
|
+
retrieval_info = {
|
281
|
+
location_id: 12,
|
282
|
+
company_id: 10,
|
283
|
+
bank_account_id: 3,
|
284
|
+
user_master_id: 20,
|
285
|
+
retrieval_amount: 1.02
|
286
|
+
}
|
287
|
+
|
288
|
+
# class method
|
289
|
+
r = ACH::Retrieval.create_with_bank_account(retrieval_info)
|
290
|
+
|
291
|
+
# instance method - the retrieval instance will be populated with the
|
292
|
+
# retrieval response data from the server.
|
293
|
+
r = ACH::Retrieval.new()
|
294
|
+
r.create_with_bank_account(retrieval_info)
|
295
|
+
```
|
296
|
+
|
297
|
+
### ACH Settlement
|
298
|
+
|
299
|
+
SlidePay users can settle funds from an account ledger to one or more bank accounts programmatically
|
300
|
+
via ACH. Note: To do this, an account must have manual settlement enabled. Complete documentation for
|
301
|
+
ACH settlements lives on the [documentation page](https://getcube.atlassian.net/wiki/display/CDP/Processing+Manual+ACH+Settlements).
|
302
|
+
|
303
|
+
To initiate an ACH settlement to one or more bank accounts, supply a settlement
|
304
|
+
descriptor Hash to the ```Settlement.process``` method in the following format:
|
305
|
+
|
306
|
+
```ruby
|
307
|
+
settlement_descriptor = {
|
308
|
+
location_id: YOUR_LOCATION_ID,
|
309
|
+
company_id: YOUR_COMPANY_ID,
|
310
|
+
settlement_list:[
|
311
|
+
{ bank_account_id: FIRST_SETTLEMENT_BANK_ACCOUNT_ID, amount: 0.25 },
|
312
|
+
{ bank_account_id: SECOND_SETTLEMENT_BANK_ACCOUNT_ID, amount: 0.25 }
|
313
|
+
],
|
314
|
+
notes:'Really important notes about this settlement. '
|
315
|
+
}
|
316
|
+
```
|
317
|
+
|
318
|
+
The Settlement.process class method will return an array of [transaction processor responses](https://getcube.atlassian.net/wiki/display/CDP/processor_ach_txn_response)
|
319
|
+
corresponding to the list of settlements requested.
|
320
|
+
|
321
|
+
```ruby
|
322
|
+
|
323
|
+
# Process the settlement
|
324
|
+
settlment_result = SlidePay::ACH::Settlement.process(settlement_descriptor)
|
325
|
+
```
|
326
|
+
|
256
327
|
## Testing
|
257
328
|
|
258
329
|
### Basics
|
data/TODO.md
CHANGED
@@ -1,12 +1,14 @@
|
|
1
|
-
[
|
1
|
+
[x] Reports
|
2
2
|
---------------
|
3
|
-
[
|
4
|
-
[
|
3
|
+
[x] Account
|
4
|
+
[x] Payment
|
5
5
|
|
6
6
|
[ ] ACH
|
7
7
|
---------------
|
8
|
-
[
|
8
|
+
[x] Retrieval
|
9
9
|
[ ] Settlement
|
10
|
-
[
|
10
|
+
[x] Balance
|
11
11
|
-- Speculative
|
12
|
-
[
|
12
|
+
[x] Credit
|
13
|
+
-- Payments
|
14
|
+
[x] is_settled? method
|
data/lib/slidepay.rb
CHANGED
@@ -28,6 +28,11 @@ require "slidepay/reports/post_report"
|
|
28
28
|
require "slidepay/reports/account_report"
|
29
29
|
require "slidepay/reports/payment_report"
|
30
30
|
|
31
|
+
# ACH
|
32
|
+
require "slidepay/ach/resource"
|
33
|
+
require "slidepay/ach/retrieval"
|
34
|
+
require "slidepay/ach/balance"
|
35
|
+
require "slidepay/ach/settlement"
|
31
36
|
|
32
37
|
module SlidePay
|
33
38
|
class << self
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module SlidePay
|
2
|
+
module ACH
|
3
|
+
class Balance
|
4
|
+
|
5
|
+
class << self
|
6
|
+
|
7
|
+
def retrieve(location_id, options={})
|
8
|
+
token = options[:token]
|
9
|
+
api_key = options[:api_key]
|
10
|
+
endpoint = options[:endpoint]
|
11
|
+
|
12
|
+
path = "settlement/balance/#{location_id}"
|
13
|
+
|
14
|
+
response = SlidePay.post(path: path, data: {}.to_json, token: token, api_key: api_key, endpoint: endpoint)
|
15
|
+
|
16
|
+
if response.was_successful?
|
17
|
+
return response.data
|
18
|
+
else
|
19
|
+
error_text = response.error_text unless response.error == false
|
20
|
+
|
21
|
+
if error_text != nil
|
22
|
+
raise "Could not get settlement balance: #{error_text}"
|
23
|
+
else
|
24
|
+
raise "Could not get settlement balance."
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module SlidePay
|
2
|
+
module ACH
|
3
|
+
class Resource < ApiResource
|
4
|
+
def initialize(values_hash={})
|
5
|
+
super(values_hash)
|
6
|
+
end
|
7
|
+
|
8
|
+
def save
|
9
|
+
raise "ACH Resources cannot be saved"
|
10
|
+
end
|
11
|
+
|
12
|
+
def destroy
|
13
|
+
raise "ACH Resources cannot be destroyed"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
module SlidePay
|
2
|
+
module ACH
|
3
|
+
class Retrieval < Resource
|
4
|
+
attr_accessor :retrieval_bank_account
|
5
|
+
|
6
|
+
@url_root = "retrieval"
|
7
|
+
@id_attribute = "retrieval_id"
|
8
|
+
|
9
|
+
def initialize(values_hash={})
|
10
|
+
@url_root = "retrieval"
|
11
|
+
@id_attribute = "retrieval_id"
|
12
|
+
|
13
|
+
# if values_hash[:retrieval_bank_account] is present and a hash, then we should probably pull
|
14
|
+
# that out and set the instance attribute for bank account
|
15
|
+
# @retrieval_bank_account = values_hash[:retrieval_bank_account]
|
16
|
+
# values_hash.remove(:retrieval_bank_account)
|
17
|
+
|
18
|
+
super(values_hash)
|
19
|
+
end
|
20
|
+
|
21
|
+
# url should allow retrieving by settlement_transaction_token
|
22
|
+
def url
|
23
|
+
if is_new?
|
24
|
+
@url_root
|
25
|
+
elsif self.settlement_transaction_token != nil
|
26
|
+
"#{@url_root}?settlement_transaction_token=#{self['settlement_transaction_token']}"
|
27
|
+
else
|
28
|
+
"#{@url_root}/#{self.id()}"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def settlement_transaction_token
|
33
|
+
self['settlement_transaction_token']
|
34
|
+
end
|
35
|
+
|
36
|
+
def is_new?
|
37
|
+
if id() == nil && self.settlement_transaction_token == nil
|
38
|
+
true
|
39
|
+
else
|
40
|
+
false
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def create_with_bank_account(bank_account, options={})
|
45
|
+
options[:token] = options[:token] || @token
|
46
|
+
options[:api_key] = options[:api_key] || @api_key
|
47
|
+
options[:endpoint] = options[:endpoint] || @endpoint
|
48
|
+
|
49
|
+
r = self.class.create_with_bank_account(bank_account, options)
|
50
|
+
merge! r
|
51
|
+
end
|
52
|
+
|
53
|
+
def retrieve_by_settlement_token(settlement_transaction_token, options={})
|
54
|
+
options[:token] = options[:token] || @token
|
55
|
+
options[:api_key] = options[:api_key] || @api_key
|
56
|
+
options[:endpoint] = options[:endpoint] || @endpoint
|
57
|
+
|
58
|
+
r = self.class.retrieve_by_settlement_token(settlement_transaction_token, options)
|
59
|
+
merge! r
|
60
|
+
end
|
61
|
+
|
62
|
+
def is_ledgered?
|
63
|
+
return self['ledgered'] == 1
|
64
|
+
end
|
65
|
+
|
66
|
+
def ledger_after
|
67
|
+
return self['ledger_after']
|
68
|
+
end
|
69
|
+
|
70
|
+
def retrieval_amount
|
71
|
+
return self['retrieval_amount']
|
72
|
+
end
|
73
|
+
|
74
|
+
def provider_status
|
75
|
+
return self['provider_status']
|
76
|
+
end
|
77
|
+
|
78
|
+
class << self
|
79
|
+
def create_with_bank_account(bank_account, options={})
|
80
|
+
token = options[:token]
|
81
|
+
api_key = options[:api_key]
|
82
|
+
endpoint = options[:endpoint]
|
83
|
+
|
84
|
+
response = SlidePay.post(path: "#{@url_root}/manual", data: bank_account.to_json, token: token, api_key: api_key, endpoint: endpoint)
|
85
|
+
if response.was_successful?
|
86
|
+
r = self.new()
|
87
|
+
r.populate_from_response(response)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def retrieve_by_settlement_token(settlement_transaction_token, options={})
|
92
|
+
if ! settlement_transaction_token.is_a? String
|
93
|
+
raise "settlement_transaction_token must be a String"
|
94
|
+
end
|
95
|
+
|
96
|
+
token = options[:token]
|
97
|
+
api_key = options[:api_key]
|
98
|
+
endpoint = options[:endpoint]
|
99
|
+
|
100
|
+
url_suffix = "?settlement_transaction_token=#{settlement_transaction_token}"
|
101
|
+
|
102
|
+
response = SlidePay.get(path: "#{@url_root}#{url_suffix}", token: token, api_key: api_key, endpoint: endpoint)
|
103
|
+
if response.was_successful?
|
104
|
+
r = self.new()
|
105
|
+
r.populate_from_response(response)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module SlidePay
|
2
|
+
module ACH
|
3
|
+
class Settlement < Resource
|
4
|
+
@url_root = "settlement"
|
5
|
+
@id_attribute = "settlement_id"
|
6
|
+
|
7
|
+
def initialize(values_hash={})
|
8
|
+
@url_root = "settlement"
|
9
|
+
@id_attribute = "settlement_id"
|
10
|
+
|
11
|
+
super(values_hash)
|
12
|
+
end
|
13
|
+
|
14
|
+
class << self
|
15
|
+
|
16
|
+
def process(settlement_order, options={})
|
17
|
+
validate_settlement_order(settlement_order)
|
18
|
+
|
19
|
+
token = @token || options[:token]
|
20
|
+
api_key = @api_key || options[:api_key]
|
21
|
+
endpoint = @endpoint || options[:endpoint]
|
22
|
+
|
23
|
+
response = SlidePay.post(path: "settlement/manual", api_key: api_key, token: token, endpoint: endpoint, data: settlement_order.to_json)
|
24
|
+
|
25
|
+
if response.was_successful?
|
26
|
+
return response.data
|
27
|
+
elsif response.error_text
|
28
|
+
raise Exception.new(response.error_text)
|
29
|
+
elsif response.data["status_message"]
|
30
|
+
raise Exception.new(response.data["status_message"])
|
31
|
+
else
|
32
|
+
raise Exception.new("Settlement(s) could not be processed.")
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def validate_single_settlement_descriptor(descriptor)
|
39
|
+
puts "Descriptor: #{descriptor}\n\n"
|
40
|
+
if ! descriptor.is_a? Hash
|
41
|
+
raise "Cannot validate a settlement descriptor that is not a hash."
|
42
|
+
elsif ! descriptor.include?(:bank_account_id) && ! descriptor.include?("bank_account_id")
|
43
|
+
raise "A bank_account_id is required for each settlement being processed."
|
44
|
+
elsif ! descriptor.include?(:amount) && ! descriptor.include?("amount")
|
45
|
+
raise "An amount is required for each settlement being processed."
|
46
|
+
else
|
47
|
+
return true
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def validate_settlement_order(settlement_order)
|
52
|
+
raise "A settlement must be a Hash object." unless settlement_order.is_a? Hash
|
53
|
+
raise "company_id is required for a settlement." unless (settlement_order['company_id'] || settlement_order[:company_id])
|
54
|
+
raise "location_id is required for a settlement." unless (settlement_order['location_id'] || settlement_order[:location_id])
|
55
|
+
raise "settlement_list is required for a settlement." unless (settlement_order['settlement_list'] || settlement_order[:settlement_list])
|
56
|
+
|
57
|
+
descriptor_list = settlement_order['settlement_list'] || settlement_order[:settlement_list]
|
58
|
+
descriptor_list.each do |descriptor|
|
59
|
+
validate_single_settlement_descriptor(descriptor)
|
60
|
+
end
|
61
|
+
|
62
|
+
return true
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -3,32 +3,34 @@ module SlidePay
|
|
3
3
|
attr_accessor :url_root, :id_attribute, :token, :api_key, :endpoint
|
4
4
|
|
5
5
|
def initialize(values_hash={})
|
6
|
-
|
7
|
-
|
8
|
-
|
6
|
+
values_clone = values_hash.clone()
|
7
|
+
|
8
|
+
if values_clone[:url_root]
|
9
|
+
@url_root = values_clone[:url_root]
|
10
|
+
values_clone.delete(:url_root)
|
9
11
|
end
|
10
12
|
|
11
|
-
if
|
12
|
-
@id_attribute =
|
13
|
-
|
13
|
+
if values_clone[:id_attribute]
|
14
|
+
@id_attribute = values_clone[:id_attribute]
|
15
|
+
values_clone.delete(:id_attribute)
|
14
16
|
end
|
15
17
|
|
16
|
-
if
|
17
|
-
@token =
|
18
|
-
|
18
|
+
if values_clone[:token]
|
19
|
+
@token = values_clone[:token]
|
20
|
+
values_clone.delete(:token)
|
19
21
|
end
|
20
22
|
|
21
|
-
if
|
22
|
-
@api_key =
|
23
|
-
|
23
|
+
if values_clone[:api_key]
|
24
|
+
@api_key = values_clone[:api_key]
|
25
|
+
values_clone.delete(:api_key)
|
24
26
|
end
|
25
27
|
|
26
|
-
if
|
27
|
-
@endpoint =
|
28
|
-
|
28
|
+
if values_clone[:endpoint]
|
29
|
+
@endpoint = values_clone[:endpoint]
|
30
|
+
values_clone.delete(:endpoint)
|
29
31
|
end
|
30
32
|
|
31
|
-
merge!
|
33
|
+
merge! values_clone
|
32
34
|
end
|
33
35
|
|
34
36
|
def url
|
@@ -18,6 +18,23 @@ module SlidePay
|
|
18
18
|
refund(options_hash)
|
19
19
|
end
|
20
20
|
|
21
|
+
def is_settled?
|
22
|
+
raise "Cannot check settlement status for a payment with no ID." unless is_new? == false
|
23
|
+
|
24
|
+
if ! self['provider_capture_state'].eql? "Captured"
|
25
|
+
# puts "#{self['provider_capture_state']}. \"Captured\" has evaluated to false"
|
26
|
+
return false
|
27
|
+
elsif self['settlement_transaction_token'] == nil
|
28
|
+
# puts 'self[\settlement_transaction_token\] == nil has evaluated to false'
|
29
|
+
return false
|
30
|
+
elsif self['settlement_transaction_token'].is_a?(String) && self['settlement_transaction_token'].empty?
|
31
|
+
# puts 'self[\'settlement_transaction_token\'].kind_of? String && self[\'settlement_transaction_token\'].empty? has evaluated to false'
|
32
|
+
return false
|
33
|
+
else
|
34
|
+
return true
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
21
38
|
def process(options_hash={})
|
22
39
|
token = @token || options_hash[:token]
|
23
40
|
api_key = @api_key || options_hash[:api_key]
|
data/lib/slidepay/version.rb
CHANGED
@@ -0,0 +1,18 @@
|
|
1
|
+
$:.unshift(File.join(File.dirname(__FILE__), 'lib'))
|
2
|
+
$:.unshift(File.join(File.dirname(__FILE__), 'spec'))
|
3
|
+
|
4
|
+
require 'multi_json'
|
5
|
+
require 'slidepay'
|
6
|
+
require 'spec_helper'
|
7
|
+
|
8
|
+
include SlidePay
|
9
|
+
|
10
|
+
SlidePay.configure(development: true)
|
11
|
+
SlidePay.api_key = ENV["ach_api_key"]
|
12
|
+
|
13
|
+
RestClient.proxy = "http://127.0.0.1:8888"
|
14
|
+
|
15
|
+
location_id = 18
|
16
|
+
balance = SlidePay::ACH::Balance.retrieve(location_id)
|
17
|
+
|
18
|
+
puts "Balance for location_id #{location_id}: #{balance}"
|