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 ADDED
@@ -0,0 +1,5 @@
1
+ *.swp
2
+ *.gem
3
+ .bundle
4
+ Gemfile.lock
5
+ pkg/*
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm ree
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in versapay.gemspec
4
+ gemspec
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,14 @@
1
+ require 'rest_client'
2
+
3
+ module Versapay
4
+ class FundSources
5
+
6
+ def initialize(&block)
7
+ yield self if block_given?
8
+ end
9
+
10
+ def list
11
+ Versapay::make_request(:get, "/api/funds.json")
12
+ end
13
+ end
14
+ 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,3 @@
1
+ module Versapay
2
+ VERSION = "0.0.1"
3
+ 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
@@ -0,0 +1,9 @@
1
+ require 'test_helper'
2
+
3
+ class BasicTest < ActiveSupport::TestCase
4
+
5
+ test "foo" do
6
+ assert_equal "0.0.1", Versapay::VERSION
7
+ end
8
+
9
+ end
@@ -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
+
@@ -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
@@ -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