versapay 0.0.1
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.
- data/.gitignore +5 -0
- data/.rvmrc +1 -0
- data/Gemfile +4 -0
- data/Rakefile +26 -0
- data/lib/versapay/debit_agreement.rb +48 -0
- data/lib/versapay/fund_sources.rb +14 -0
- data/lib/versapay/rails_helpers.rb +12 -0
- data/lib/versapay/transactions.rb +31 -0
- data/lib/versapay/version.rb +3 -0
- data/lib/versapay/webhooks.rb +99 -0
- data/lib/versapay.rb +59 -0
- data/test/basic_test.rb +9 -0
- data/test/debit_agreements_test.rb +68 -0
- data/test/fund_sources_test.rb +24 -0
- data/test/helper_test.rb +18 -0
- data/test/test_helper.rb +21 -0
- data/test/transactions.test.rb +49 -0
- data/test/webhook_test.rb +75 -0
- data/versapay.gemspec +28 -0
- metadata +179 -0
data/.rvmrc
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rvm ree
|
data/Gemfile
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
require 'rake'
|
3
|
+
require 'rake/testtask'
|
4
|
+
require 'rake/rdoctask'
|
5
|
+
|
6
|
+
|
7
|
+
desc 'Test the plugin'
|
8
|
+
Rake::TestTask.new(:test) do |t|
|
9
|
+
t.libs << 'lib'
|
10
|
+
t.libs << 'test'
|
11
|
+
t.pattern = 'test/**/*_test.rb'
|
12
|
+
t.verbose = true
|
13
|
+
end
|
14
|
+
|
15
|
+
desc 'Generate RDoc docs'
|
16
|
+
Rake::RDocTask.new(:rdoc) do |rdoc|
|
17
|
+
rdoc.rdoc_dir = 'rdoc'
|
18
|
+
rdoc.title = 'Versapay'
|
19
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
20
|
+
rdoc.rdoc_files.include('README')
|
21
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
task :default => :test
|
26
|
+
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'rest_client'
|
2
|
+
|
3
|
+
module Versapay
|
4
|
+
class DebitAgreement
|
5
|
+
|
6
|
+
def initialize(&block)
|
7
|
+
yield self if block_given?
|
8
|
+
end
|
9
|
+
|
10
|
+
def create(email, reference = nil, message = nil)
|
11
|
+
args = { "email" => email, "reference" => email, "message" => message }
|
12
|
+
Versapay::make_request(:post, "/api/debit_agreements.json", args)
|
13
|
+
end
|
14
|
+
|
15
|
+
def list_sent(page = nil)
|
16
|
+
args = page.nil? ? {} : { :page => page }
|
17
|
+
Versapay::make_request(:get, "/api/debit_agreements/sent.json", args)
|
18
|
+
end
|
19
|
+
|
20
|
+
def list_received(page = nil)
|
21
|
+
args = page.nil? ? {} : { :page => page }
|
22
|
+
Versapay::make_request(:get, "/api/debit_agreements/received.json", args)
|
23
|
+
end
|
24
|
+
|
25
|
+
def view(token)
|
26
|
+
Versapay::make_request(:get, "/api/debit_agreements/#{token}.json")
|
27
|
+
end
|
28
|
+
|
29
|
+
def approve(token, fund_token = nil)
|
30
|
+
args = fund_token.nil? ? {} : { :fund_token => fund_token }
|
31
|
+
Versapay::make_request(:post, "/api/debit_agreements/#{token}/approve.json", args)
|
32
|
+
end
|
33
|
+
|
34
|
+
def reject(reason)
|
35
|
+
ags = { "rejection_reason" => reason }
|
36
|
+
Versapay::make_request(:post, "/api/debit_agreements/#{token}/reject.json", args)
|
37
|
+
end
|
38
|
+
|
39
|
+
def cancel
|
40
|
+
Versapay::make_request(:post, "/api/debit_agreements/#{token}/reject.json", {})
|
41
|
+
end
|
42
|
+
|
43
|
+
def revoke
|
44
|
+
Versapay::make_request(:post, "/api/debit_agreements/#{token}/reject.json", {})
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module Versapay
|
2
|
+
module Helpers
|
3
|
+
# Provides a debit agreement link
|
4
|
+
def debit_agreement_link_to(anchor, message = "Debit agreement", opts = {})
|
5
|
+
link = "https://" + Versapay::site + "/authorize?api_token=#{Versapay.token}&message=#{html_escape(message).gsub(/ /, "+")}"
|
6
|
+
opts.each do |k, v|
|
7
|
+
link += "&#{k}=#{html_escape(v)}"
|
8
|
+
end
|
9
|
+
"<a href=\"#{link}\">#{anchor}</a>"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'rest_client'
|
2
|
+
|
3
|
+
module Versapay
|
4
|
+
class Transactions
|
5
|
+
|
6
|
+
def initialize(&block)
|
7
|
+
yield self if block_given?
|
8
|
+
end
|
9
|
+
|
10
|
+
def create
|
11
|
+
end
|
12
|
+
|
13
|
+
def list(page = nil)
|
14
|
+
args = page.nil? ? {} : { :page => page }
|
15
|
+
Versapay::make_request(:get, "/api/transactions/sent.json", args)
|
16
|
+
end
|
17
|
+
|
18
|
+
def view(token)
|
19
|
+
Versapay::make_request(:get, "/api/transactions/#{token}.json")
|
20
|
+
end
|
21
|
+
|
22
|
+
def approve(token)
|
23
|
+
Versapay::make_request(:post, "/api/transactions/#{token}/approve.json")
|
24
|
+
end
|
25
|
+
|
26
|
+
def cancel(token)
|
27
|
+
Versapay::make_request(:post, "/api/transactions/#{token}/cancel.json")
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
require 'openssl'
|
2
|
+
require 'erb'
|
3
|
+
require 'base64'
|
4
|
+
|
5
|
+
class ActionController::Base
|
6
|
+
def self.check_versapay_signatures(key, *args)
|
7
|
+
before_filter lambda { |controller| check_versapay_signature(key, controller)}, *args
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.check_versapay_signature(key, controller)
|
11
|
+
# Save the signature first
|
12
|
+
signature = controller.request.parameters["signature"]
|
13
|
+
|
14
|
+
# Only want the parameters passed from VP, and no sig
|
15
|
+
params = controller.request.parameters.dup
|
16
|
+
["action", "controller", "signature"].each { |k| params.delete(k) }
|
17
|
+
|
18
|
+
url = controller.request.url.gsub /\?.*/, ""
|
19
|
+
check = Versapay::WebhookSignature.hmac(controller.request.method, url, key, params)
|
20
|
+
unless logger.nil? then
|
21
|
+
logger.warn "============="
|
22
|
+
logger.warn controller.request.method
|
23
|
+
logger.warn url
|
24
|
+
logger.warn key
|
25
|
+
logger.warn params.inspect
|
26
|
+
end
|
27
|
+
|
28
|
+
if check == signature
|
29
|
+
true
|
30
|
+
else
|
31
|
+
raise Versapay::InvalidWebhookSignature
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
module Versapay
|
37
|
+
class WebhookSignature
|
38
|
+
include ERB::Util
|
39
|
+
|
40
|
+
attr_reader :signature
|
41
|
+
alias :to_s :signature
|
42
|
+
|
43
|
+
def self.hmac(*args)
|
44
|
+
new(*args).to_s
|
45
|
+
end
|
46
|
+
|
47
|
+
# @param [Symbol, String] method The HTTP method of the request (:get, :post)
|
48
|
+
# @param [String] url The URL that the request is going to
|
49
|
+
# @param [String] key The private key to be used for signing
|
50
|
+
# @param [Hash] attributes The hash of attributes that will be sent in the request
|
51
|
+
def initialize(method, url, key, attributes)
|
52
|
+
@method = method
|
53
|
+
@url = url
|
54
|
+
@key = key
|
55
|
+
@attributes = attributes
|
56
|
+
@signature = generate_signature
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
# HMAC of the request in the following format.
|
62
|
+
# Attributes are sorted by byte size.
|
63
|
+
# Finally, it is Base64'd and url encoded.
|
64
|
+
#
|
65
|
+
# POST\n
|
66
|
+
# https://secure.versapay.com/transactions\n
|
67
|
+
# account_typebusinesuser[first_name]Johnuser[last_name]doe
|
68
|
+
#
|
69
|
+
def generate_signature
|
70
|
+
hmac = OpenSSL::HMAC.digest(OpenSSL::Digest::SHA256.new, @key, hmac_string)
|
71
|
+
url_encode Base64.encode64(hmac)
|
72
|
+
end
|
73
|
+
|
74
|
+
def hmac_string
|
75
|
+
str = "#{@method.to_s.upcase}\n"
|
76
|
+
str << "#{@url}\n"
|
77
|
+
str << sorted_attribute_string
|
78
|
+
end
|
79
|
+
|
80
|
+
def sorted_attribute_string
|
81
|
+
return "" if @attributes.empty?
|
82
|
+
nested_keys(@attributes).join
|
83
|
+
end
|
84
|
+
|
85
|
+
# Nested keys flattens nested hashes like :to_query ie. transaction[type]
|
86
|
+
# This is useful to support request signing when, for example, requests are sent as XML
|
87
|
+
def nested_keys(hash, namespace=nil, result=[])
|
88
|
+
hash.stringify_keys.each do |key,value|
|
89
|
+
current_key = namespace ? "#{namespace}[#{key}]" : key
|
90
|
+
if value.is_a?(Hash)
|
91
|
+
nested_keys(value, current_key, result)
|
92
|
+
else
|
93
|
+
result << [current_key, value]
|
94
|
+
end
|
95
|
+
end
|
96
|
+
result.sort_by {|k,v| k}
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
data/lib/versapay.rb
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
require "versapay/version"
|
2
|
+
|
3
|
+
require "versapay/rails_helpers"
|
4
|
+
require "versapay/webhooks"
|
5
|
+
require "versapay/debit_agreement"
|
6
|
+
require "versapay/fund_sources"
|
7
|
+
require "versapay/transactions"
|
8
|
+
|
9
|
+
module Versapay
|
10
|
+
|
11
|
+
class InvalidInput < Exception; end
|
12
|
+
class InvalidWebhookSignature < Exception; end
|
13
|
+
class Unprocessable < Exception; end
|
14
|
+
class NotFound < Exception; end
|
15
|
+
|
16
|
+
class << self
|
17
|
+
attr_accessor :token, :key
|
18
|
+
end
|
19
|
+
|
20
|
+
@@token = "PleaseOverrideYourToken"
|
21
|
+
@@key = "PleaseOverriedYourKey"
|
22
|
+
|
23
|
+
def self.site
|
24
|
+
Rails.env.production? ? "secure.versapay.com" : "demo.versapay.com"
|
25
|
+
end
|
26
|
+
|
27
|
+
|
28
|
+
def self.make_request(method, url, args = {})
|
29
|
+
if method == :get then
|
30
|
+
RestClient.get("https://#{Versapay.token}:#{Versapay.key}@" + Versapay::site + url, {:params => args}) do |response, request, result, &block|
|
31
|
+
case response.code
|
32
|
+
when 200
|
33
|
+
return JSON.parse(response)
|
34
|
+
when 422
|
35
|
+
raise Versapay::Unprocessable
|
36
|
+
when 500
|
37
|
+
raise Versapay::NotFound
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
if method == :post then
|
43
|
+
RestClient.post("https://#{Versapay.token}:#{Versapay.key}@" + Versapay::site + url, args.to_json, :content_type => :json, :accept => :json) do |response, request, result, &block|
|
44
|
+
case response.code
|
45
|
+
when 200,201
|
46
|
+
return JSON.parse(response)
|
47
|
+
when 412
|
48
|
+
raise Versapay::InvalidInput #(JSON.parse(response))
|
49
|
+
when 422
|
50
|
+
raise Versapay::Unprocessable
|
51
|
+
when 500
|
52
|
+
raise Versapay::NotFound
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
|
58
|
+
end
|
59
|
+
end
|
data/test/basic_test.rb
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class DebitAgreementsTest < ActionView::TestCase
|
4
|
+
|
5
|
+
def setup
|
6
|
+
Versapay.key = "mykey"
|
7
|
+
Versapay.token = "mytoken"
|
8
|
+
#Versapay.key = "EDe3Uflbesam5PnfNFPO"
|
9
|
+
#Versapay.token = "1kkOYn_uA6KWNjejFka1"
|
10
|
+
|
11
|
+
@v = Versapay::DebitAgreement.new
|
12
|
+
FakeWeb.register_uri(:any, "https://demo.versapay.com", :body => "response for any HTTP method", :status => ["500", "bad test"])
|
13
|
+
end
|
14
|
+
|
15
|
+
test "creating a transaction with valid details" do
|
16
|
+
body = "{\"type\":\"debit_agreement\",\"state\":\"pending\",\"created_by_account\":\"wavepayrolltest\",\"token\":\"3HKJLAS2B12W\",\"message\":\"\",\"created_by_user\":\"1kkOYn_uA6KWNjejFka1\",\"reference\":\"test@example.com\",\"email\":\"test@example.com\",\"name\":\"\"}"
|
17
|
+
FakeWeb.register_uri(:post, "https://mytoken:mykey@demo.versapay.com/api/debit_agreements.json", :body => body)
|
18
|
+
results = @v.create("test@example.com")
|
19
|
+
assert_equal Hash, results.class
|
20
|
+
assert_equal "pending", results["state"]
|
21
|
+
end
|
22
|
+
|
23
|
+
test "creating a transaction with missing information" do
|
24
|
+
body = "{\"email\":\"invalid\"}"
|
25
|
+
FakeWeb.register_uri(:post, "https://mytoken:mykey@demo.versapay.com/api/debit_agreements.json", :body => body, :status => ["412", "Invalid input"])
|
26
|
+
assert_raises Versapay::InvalidInput do
|
27
|
+
results = @v.create("test")
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
test "viewing a transaction's details" do
|
32
|
+
body = "{\"type\":\"debit_agreement\",\"state\":\"approved\",\"created_by_account\":\"wavepayrolltest\",\"token\":\"83F2W2K2FJFV\",\"message\":\"blah\",\"created_by_user\":\"1kkOYn_uA6KWNjejFka1\",\"reference\":\"BAhpBg==--7e0a441bda1b04f5b985fa654d357071bb9b32d1\",\"email\":\"jimbob@smallpayroll.ca\",\"name\":\"Jim Bob\"}"
|
33
|
+
FakeWeb.register_uri(:get, "https://mytoken:mykey@demo.versapay.com/api/debit_agreements/83F2W2K2FJFV.json", :body => body)
|
34
|
+
results = @v.view("83F2W2K2FJFV")
|
35
|
+
|
36
|
+
assert_equal Hash, results.class
|
37
|
+
assert_equal "debit_agreement", results["type"]
|
38
|
+
end
|
39
|
+
|
40
|
+
test "viewing an invalid transaction's details" do
|
41
|
+
body = "Not found"
|
42
|
+
FakeWeb.register_uri(:get, "https://mytoken:mykey@demo.versapay.com/api/debit_agreements/83F2W2K2FXXX.json", :body => body, :status => ["500", "Not found"])
|
43
|
+
assert_raises Versapay::NotFound do
|
44
|
+
@v.view("83F2W2K2FXXX")
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
test "listing sent transactions when there are none" do
|
49
|
+
body = "[]"
|
50
|
+
FakeWeb.register_uri(:get, "https://mytoken:mykey@demo.versapay.com/api/debit_agreements/sent.json", :body => body)
|
51
|
+
results = @v.list_sent
|
52
|
+
assert_equal Array, results.class
|
53
|
+
assert_equal 0, results.size
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
test "listing sent transactions when there are some" do
|
58
|
+
body = "[{\"type\":\"debit_agreement\",\"state\":\"approved\",\"created_by_account\":\"wavepayrolltest\",\"token\":\"83F2W2K2FJFV\",\"message\":\"blah\",\"created_by_user\":\"1kkOYn_uA6KWNjejFka1\",\"reference\":\"BAhpBg==--7e0a441bda1b04f5b985fa654d357071bb9b32d1\",\"email\":\"jimbob@smallpayroll.ca\",\"name\":\"Jim Bob\"},{\"type\":\"debit_agreement\",\"state\":\"approved\",\"created_by_account\":\"wavepayrolltest\",\"token\":\"6DCKWXXF1A51\",\"message\":\"blah\",\"created_by_user\":\"1kkOYn_uA6KWNjejFka1\",\"reference\":\"BAhpBg==--7e0a441bda1b04f5b985fa654d357071bb9b32d1\",\"email\":\"jimbob@smallpayroll.ca\",\"name\":\"Jim Bob\"},{\"type\":\"debit_agreement\",\"state\":\"revoked\",\"created_by_account\":\"wavepayrolltest\",\"token\":\"7VBJRS7TKLIL\",\"message\":\"blah\",\"created_by_user\":\"1kkOYn_uA6KWNjejFka1\",\"reference\":\"BAhpBg==--7e0a441bda1b04f5b985fa654d357071bb9b32d1\",\"email\":\"fredsmith@smallpayroll.ca\",\"name\":\"Fred Smith\"},{\"type\":\"debit_agreement\",\"state\":\"revoked\",\"created_by_account\":\"wavepayrolltest\",\"token\":\"6YI3MTVMY918\",\"message\":\"blah\",\"created_by_user\":\"1kkOYn_uA6KWNjejFka1\",\"reference\":null,\"email\":\"fredsmith@smallpayroll.ca\",\"name\":\"Fred Smith\"},{\"type\":\"debit_agreement\",\"state\":\"revoked\",\"created_by_account\":\"wavepayrolltest\",\"token\":\"1AYAG5T762TJ\",\"message\":\"blah\",\"created_by_user\":\"1kkOYn_uA6KWNjejFka1\",\"reference\":null,\"email\":\"fredsmith@smallpayroll.ca\",\"name\":\"Fred Smith\"},{\"type\":\"debit_agreement\",\"state\":\"cancelled\",\"created_by_account\":\"wavepayrolltest\",\"token\":\"7IN4ALWCXKJG\",\"message\":\"blah\",\"created_by_user\":\"1kkOYn_uA6KWNjejFka1\",\"reference\":null,\"email\":\"fredsmith@smallpayroll.ca\",\"name\":\"Fred Smith\"},{\"type\":\"debit_agreement\",\"state\":\"revoked\",\"created_by_account\":\"wavepayrolltest\",\"token\":\"4RQ58W5B4M1W\",\"message\":\"blah\",\"created_by_user\":\"1kkOYn_uA6KWNjejFka1\",\"reference\":null,\"email\":\"fredsmith@smallpayroll.ca\",\"name\":\"Fred Smith\"}]"
|
59
|
+
FakeWeb.register_uri(:get, "https://mytoken:mykey@demo.versapay.com/api/debit_agreements/sent.json", :body => body)
|
60
|
+
results = @v.list_sent
|
61
|
+
assert_equal Array, results.class
|
62
|
+
assert_equal 7, results.size
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
########
|
68
|
+
# When testing a transaction involving a post, ensure the data is in json and the content-type is application/json
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class FundSourcesTest < ActionView::TestCase
|
4
|
+
|
5
|
+
def setup
|
6
|
+
Versapay.key = "mykey"
|
7
|
+
Versapay.token = "mytoken"
|
8
|
+
|
9
|
+
@v = Versapay::FundSources.new
|
10
|
+
FakeWeb.register_uri(:any, "https://demo.versapay.com", :body => "response for any HTTP method", :status => ["500", "bad test"])
|
11
|
+
end
|
12
|
+
|
13
|
+
test "viewing a list of fund sources" do
|
14
|
+
body = "[{\"type\":\"bank_account\",\"state\":\"verified\",\"token\":\"BA8SWGUV931J\",\"name\":\"THE TORONTO-DOMINION BANK (9012)\"},{\"type\":\"balance\",\"token\":\"VPB3IRX9AZYA\",\"name\":\"VersaPay Account\"}]"
|
15
|
+
FakeWeb.register_uri(:get, "https://mytoken:mykey@demo.versapay.com/api/funds.json", :body => body)
|
16
|
+
results = @v.list
|
17
|
+
|
18
|
+
assert_equal Array, results.class
|
19
|
+
assert_equal "verified", results[0]["state"]
|
20
|
+
assert_equal "VersaPay Account", results[1]["name"]
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
data/test/helper_test.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class HelperTest < ActionView::TestCase
|
4
|
+
|
5
|
+
tests Versapay::Helpers
|
6
|
+
|
7
|
+
|
8
|
+
test "creating a debit agreement link" do
|
9
|
+
expected = '<a href="https://demo.versapay.com/authorize?api_token=' + Versapay::token + '&message=Testing+123">test</a>'
|
10
|
+
assert_equal expected, debit_agreement_link_to("test", "Testing 123")
|
11
|
+
end
|
12
|
+
|
13
|
+
test "creating a debit agreement link with extra options" do
|
14
|
+
expected = '<a href="https://demo.versapay.com/authorize?api_token=' + Versapay::token + '&message=Testing+123&reference=abc123">test</a>'
|
15
|
+
assert_equal expected, debit_agreement_link_to("test", "Testing 123", {:reference => "abc123"})
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'rubygems'
|
3
|
+
require 'action_controller'
|
4
|
+
require 'active_support'
|
5
|
+
require 'active_support/test_case'
|
6
|
+
require 'action_view'
|
7
|
+
require 'action_view/test_case'
|
8
|
+
require 'fakeweb'
|
9
|
+
require 'awesome_print'
|
10
|
+
|
11
|
+
require 'versapay'
|
12
|
+
|
13
|
+
Versapay.token = "abc"
|
14
|
+
|
15
|
+
module Rails
|
16
|
+
class << self
|
17
|
+
def env
|
18
|
+
@_env ||= ActiveSupport::StringInquirer.new(ENV["RAILS_ENV"] || ENV["RACK_ENV"] || "test")
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class TransactionsTest < ActionView::TestCase
|
4
|
+
|
5
|
+
def setup
|
6
|
+
Versapay.key = "mykey"
|
7
|
+
Versapay.token = "mytoken"
|
8
|
+
@v = Versapay::DebitAgreement.new
|
9
|
+
FakeWeb.register_uri(:any, "https://demo.versapay.com", :body => "response for any HTTP method", :status => ["500", "bad test"])
|
10
|
+
end
|
11
|
+
|
12
|
+
test "viewing a transaction's details" do
|
13
|
+
body = "{\"type\":\"debit_agreement\",\"state\":\"approved\",\"created_by_account\":\"wavepayrolltest\",\"token\":\"83F2W2K2FJFV\",\"message\":\"blah\",\"created_by_user\":\"1kkOYn_uA6KWNjejFka1\",\"reference\":\"BAhpBg==--7e0a441bda1b04f5b985fa654d357071bb9b32d1\",\"email\":\"jimbob@smallpayroll.ca\",\"name\":\"Jim Bob\"}"
|
14
|
+
FakeWeb.register_uri(:get, "https://mytoken:mykey@demo.versapay.com/api/debit_agreements/83F2W2K2FJFV.json", :body => body)
|
15
|
+
results = @v.view("83F2W2K2FJFV")
|
16
|
+
|
17
|
+
assert_equal Hash, results.class
|
18
|
+
assert_equal "debit_agreement", results["type"]
|
19
|
+
end
|
20
|
+
|
21
|
+
test "viewing an invalid transaction's details" do
|
22
|
+
body = "Not found"
|
23
|
+
FakeWeb.register_uri(:get, "https://mytoken:mykey@demo.versapay.com/api/debit_agreements/83F2W2K2FXXX.json", :body => body, :status => ["500", "Not found"])
|
24
|
+
assert_raises Versapay::NotFound do
|
25
|
+
@v.view("83F2W2K2FXXX")
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
test "listing sent transactions when there are none" do
|
30
|
+
body = "[]"
|
31
|
+
FakeWeb.register_uri(:get, "https://mytoken:mykey@demo.versapay.com/api/debit_agreements/sent.json", :body => body)
|
32
|
+
results = @v.list_sent
|
33
|
+
assert_equal Array, results.class
|
34
|
+
assert_equal 0, results.size
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
test "listing sent transactions when there are some" do
|
39
|
+
body = "[{\"type\":\"debit_agreement\",\"state\":\"approved\",\"created_by_account\":\"wavepayrolltest\",\"token\":\"83F2W2K2FJFV\",\"message\":\"blah\",\"created_by_user\":\"1kkOYn_uA6KWNjejFka1\",\"reference\":\"BAhpBg==--7e0a441bda1b04f5b985fa654d357071bb9b32d1\",\"email\":\"jimbob@smallpayroll.ca\",\"name\":\"Jim Bob\"},{\"type\":\"debit_agreement\",\"state\":\"approved\",\"created_by_account\":\"wavepayrolltest\",\"token\":\"6DCKWXXF1A51\",\"message\":\"blah\",\"created_by_user\":\"1kkOYn_uA6KWNjejFka1\",\"reference\":\"BAhpBg==--7e0a441bda1b04f5b985fa654d357071bb9b32d1\",\"email\":\"jimbob@smallpayroll.ca\",\"name\":\"Jim Bob\"},{\"type\":\"debit_agreement\",\"state\":\"revoked\",\"created_by_account\":\"wavepayrolltest\",\"token\":\"7VBJRS7TKLIL\",\"message\":\"blah\",\"created_by_user\":\"1kkOYn_uA6KWNjejFka1\",\"reference\":\"BAhpBg==--7e0a441bda1b04f5b985fa654d357071bb9b32d1\",\"email\":\"fredsmith@smallpayroll.ca\",\"name\":\"Fred Smith\"},{\"type\":\"debit_agreement\",\"state\":\"revoked\",\"created_by_account\":\"wavepayrolltest\",\"token\":\"6YI3MTVMY918\",\"message\":\"blah\",\"created_by_user\":\"1kkOYn_uA6KWNjejFka1\",\"reference\":null,\"email\":\"fredsmith@smallpayroll.ca\",\"name\":\"Fred Smith\"},{\"type\":\"debit_agreement\",\"state\":\"revoked\",\"created_by_account\":\"wavepayrolltest\",\"token\":\"1AYAG5T762TJ\",\"message\":\"blah\",\"created_by_user\":\"1kkOYn_uA6KWNjejFka1\",\"reference\":null,\"email\":\"fredsmith@smallpayroll.ca\",\"name\":\"Fred Smith\"},{\"type\":\"debit_agreement\",\"state\":\"cancelled\",\"created_by_account\":\"wavepayrolltest\",\"token\":\"7IN4ALWCXKJG\",\"message\":\"blah\",\"created_by_user\":\"1kkOYn_uA6KWNjejFka1\",\"reference\":null,\"email\":\"fredsmith@smallpayroll.ca\",\"name\":\"Fred Smith\"},{\"type\":\"debit_agreement\",\"state\":\"revoked\",\"created_by_account\":\"wavepayrolltest\",\"token\":\"4RQ58W5B4M1W\",\"message\":\"blah\",\"created_by_user\":\"1kkOYn_uA6KWNjejFka1\",\"reference\":null,\"email\":\"fredsmith@smallpayroll.ca\",\"name\":\"Fred Smith\"}]"
|
40
|
+
FakeWeb.register_uri(:get, "https://mytoken:mykey@demo.versapay.com/api/debit_agreements/sent.json", :body => body)
|
41
|
+
results = @v.list_sent
|
42
|
+
assert_equal Array, results.class
|
43
|
+
assert_equal 7, results.size
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
########
|
49
|
+
# When testing a transaction involving a post, ensure the data is in json and the content-type is application/json
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class WebhookTest < ActionView::TestCase
|
4
|
+
|
5
|
+
test "signature generation" do
|
6
|
+
params = {
|
7
|
+
"created_by_user" => "699cMPe6BAyqvVsZA5mo",
|
8
|
+
"email" => "user@example.com",
|
9
|
+
"from_fund" => "THE TORONTO-DOMINION BANK",
|
10
|
+
"link_url" => "",
|
11
|
+
"message" => "",
|
12
|
+
"type" => "transaction",
|
13
|
+
"state" => "completed",
|
14
|
+
"to_account" => "Example user",
|
15
|
+
"amount_in_cents" => "500",
|
16
|
+
"token" => "5TH3ACC3AU21",
|
17
|
+
"from_account" => "First1 Last1",
|
18
|
+
"transaction_type" => "send_money",
|
19
|
+
"transaction_reference" => "",
|
20
|
+
}
|
21
|
+
|
22
|
+
method = :post
|
23
|
+
url = "http://example.com/versapay"
|
24
|
+
key = "v7aJHjbbxASKiwDW5wq6"
|
25
|
+
|
26
|
+
assert_equal "w17XQBan8r%2BdtdvGQpa5HRAF8fRXJwKV9yUo4KXLl8E%3D%0A", Versapay::WebhookSignature.hmac(method, url, key, params)
|
27
|
+
assert_equal "w17XQBan8r%2BdtdvGQpa5HRAF8fRXJwKV9yUo4KXLl8E%3D%0A", Versapay::WebhookSignature.new(method, url, key, params).signature
|
28
|
+
end
|
29
|
+
|
30
|
+
test "signature generation 2" do
|
31
|
+
params = {
|
32
|
+
"name" => "Fred Smith",
|
33
|
+
"reference" => "",
|
34
|
+
"token" => "7IN4ALWCXKJG",
|
35
|
+
"created_by_account" => "wavepayrolltest",
|
36
|
+
"type" => "debit_agreement",
|
37
|
+
"created_by_user" => "1kkOYn_uA6KWNjejFka1",
|
38
|
+
"message" => "blah",
|
39
|
+
"state" => "cancelled",
|
40
|
+
"email" => "fredsmith@smallpayroll.ca"
|
41
|
+
|
42
|
+
}
|
43
|
+
|
44
|
+
method = :post
|
45
|
+
url = "http://home.ertw.com:3434/webhooks/versapay"
|
46
|
+
key = "76pppukB4HHkWt1U_V4-"
|
47
|
+
assert_equal "qL2vrB4nva8E1xh%2FDe83yCvXBNpQqoRTOi0p4gVI9xo%3D%0A", Versapay::WebhookSignature.hmac(method, url, key, params)
|
48
|
+
end
|
49
|
+
|
50
|
+
class TestController < ActionController::Base
|
51
|
+
check_versapay_signatures "v7aJHjbbxASKiwDW5wq6"
|
52
|
+
|
53
|
+
def index
|
54
|
+
render :text => "success"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def setup
|
59
|
+
@controller = TestController.new
|
60
|
+
@request = ActionController::TestRequest.new
|
61
|
+
@response = ActionController::TestResponse.new
|
62
|
+
ActionController::Routing::Routes.add_named_route :foo, '/foo', :controller => 'webhook_test/test', :action => 'index'
|
63
|
+
end
|
64
|
+
|
65
|
+
test "a valid webhook signature" do
|
66
|
+
post :index, { "blah" => "aaa", "signature" => "1Sz%2BgPS%2FO8%2BjwyBm7XQbNJ5f2DxHc3Qliysa8BKJ7aE%3D%0A" }
|
67
|
+
assert_equal "success", @response.body
|
68
|
+
end
|
69
|
+
|
70
|
+
test "an invalid webhook signature" do
|
71
|
+
post :index, { "blah" => "aab", "signature" => "1Sz%2BgPS%2FO8%2BjwyBm7XQbNJ5f2DxHc3Qliysa8BKJ7aE%3D%0A" }
|
72
|
+
assert_not_equal "success", @response.body
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
data/versapay.gemspec
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "versapay/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "versapay"
|
7
|
+
s.version = Versapay::VERSION
|
8
|
+
s.authors = ["Sean Walberg"]
|
9
|
+
s.email = ["sean@ertw.com"]
|
10
|
+
s.homepage = ""
|
11
|
+
s.summary = %q{A gem to use the Versapay API}
|
12
|
+
s.description = %q{A gem to use the Versapay API}
|
13
|
+
|
14
|
+
s.rubyforge_project = "versapay"
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
|
21
|
+
# specify any dependencies here; for example:
|
22
|
+
s.add_development_dependency "rails", "2.3.11"
|
23
|
+
s.add_development_dependency "activesupport", "2.3.11"
|
24
|
+
s.add_development_dependency "fakeweb"
|
25
|
+
s.add_development_dependency "awesome_print"
|
26
|
+
s.add_runtime_dependency "json"
|
27
|
+
s.add_runtime_dependency "rest-client"
|
28
|
+
end
|
metadata
ADDED
@@ -0,0 +1,179 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: versapay
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 29
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 1
|
10
|
+
version: 0.0.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Sean Walberg
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-09-08 00:00:00 -05:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: rails
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - "="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 21
|
30
|
+
segments:
|
31
|
+
- 2
|
32
|
+
- 3
|
33
|
+
- 11
|
34
|
+
version: 2.3.11
|
35
|
+
type: :development
|
36
|
+
version_requirements: *id001
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
name: activesupport
|
39
|
+
prerelease: false
|
40
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - "="
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
hash: 21
|
46
|
+
segments:
|
47
|
+
- 2
|
48
|
+
- 3
|
49
|
+
- 11
|
50
|
+
version: 2.3.11
|
51
|
+
type: :development
|
52
|
+
version_requirements: *id002
|
53
|
+
- !ruby/object:Gem::Dependency
|
54
|
+
name: fakeweb
|
55
|
+
prerelease: false
|
56
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
hash: 3
|
62
|
+
segments:
|
63
|
+
- 0
|
64
|
+
version: "0"
|
65
|
+
type: :development
|
66
|
+
version_requirements: *id003
|
67
|
+
- !ruby/object:Gem::Dependency
|
68
|
+
name: awesome_print
|
69
|
+
prerelease: false
|
70
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
71
|
+
none: false
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
hash: 3
|
76
|
+
segments:
|
77
|
+
- 0
|
78
|
+
version: "0"
|
79
|
+
type: :development
|
80
|
+
version_requirements: *id004
|
81
|
+
- !ruby/object:Gem::Dependency
|
82
|
+
name: json
|
83
|
+
prerelease: false
|
84
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
85
|
+
none: false
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
hash: 3
|
90
|
+
segments:
|
91
|
+
- 0
|
92
|
+
version: "0"
|
93
|
+
type: :runtime
|
94
|
+
version_requirements: *id005
|
95
|
+
- !ruby/object:Gem::Dependency
|
96
|
+
name: rest-client
|
97
|
+
prerelease: false
|
98
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
99
|
+
none: false
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
hash: 3
|
104
|
+
segments:
|
105
|
+
- 0
|
106
|
+
version: "0"
|
107
|
+
type: :runtime
|
108
|
+
version_requirements: *id006
|
109
|
+
description: A gem to use the Versapay API
|
110
|
+
email:
|
111
|
+
- sean@ertw.com
|
112
|
+
executables: []
|
113
|
+
|
114
|
+
extensions: []
|
115
|
+
|
116
|
+
extra_rdoc_files: []
|
117
|
+
|
118
|
+
files:
|
119
|
+
- .gitignore
|
120
|
+
- .rvmrc
|
121
|
+
- Gemfile
|
122
|
+
- Rakefile
|
123
|
+
- lib/versapay.rb
|
124
|
+
- lib/versapay/debit_agreement.rb
|
125
|
+
- lib/versapay/fund_sources.rb
|
126
|
+
- lib/versapay/rails_helpers.rb
|
127
|
+
- lib/versapay/transactions.rb
|
128
|
+
- lib/versapay/version.rb
|
129
|
+
- lib/versapay/webhooks.rb
|
130
|
+
- test/basic_test.rb
|
131
|
+
- test/debit_agreements_test.rb
|
132
|
+
- test/fund_sources_test.rb
|
133
|
+
- test/helper_test.rb
|
134
|
+
- test/test_helper.rb
|
135
|
+
- test/transactions.test.rb
|
136
|
+
- test/webhook_test.rb
|
137
|
+
- versapay.gemspec
|
138
|
+
has_rdoc: true
|
139
|
+
homepage: ""
|
140
|
+
licenses: []
|
141
|
+
|
142
|
+
post_install_message:
|
143
|
+
rdoc_options: []
|
144
|
+
|
145
|
+
require_paths:
|
146
|
+
- lib
|
147
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
148
|
+
none: false
|
149
|
+
requirements:
|
150
|
+
- - ">="
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
hash: 3
|
153
|
+
segments:
|
154
|
+
- 0
|
155
|
+
version: "0"
|
156
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
157
|
+
none: false
|
158
|
+
requirements:
|
159
|
+
- - ">="
|
160
|
+
- !ruby/object:Gem::Version
|
161
|
+
hash: 3
|
162
|
+
segments:
|
163
|
+
- 0
|
164
|
+
version: "0"
|
165
|
+
requirements: []
|
166
|
+
|
167
|
+
rubyforge_project: versapay
|
168
|
+
rubygems_version: 1.6.2
|
169
|
+
signing_key:
|
170
|
+
specification_version: 3
|
171
|
+
summary: A gem to use the Versapay API
|
172
|
+
test_files:
|
173
|
+
- test/basic_test.rb
|
174
|
+
- test/debit_agreements_test.rb
|
175
|
+
- test/fund_sources_test.rb
|
176
|
+
- test/helper_test.rb
|
177
|
+
- test/test_helper.rb
|
178
|
+
- test/transactions.test.rb
|
179
|
+
- test/webhook_test.rb
|