buckaroo_client 0.0.1.pre
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +15 -0
- data/.rspec +2 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +98 -0
- data/Rakefile +2 -0
- data/buckaroo_client.gemspec +26 -0
- data/lib/buckaroo_client.rb +35 -0
- data/lib/buckaroo_client/gateway.rb +7 -0
- data/lib/buckaroo_client/gateway/batch.rb +31 -0
- data/lib/buckaroo_client/gateway/nvp.rb +84 -0
- data/lib/buckaroo_client/gateway/nvp/invoice_info_response.rb +16 -0
- data/lib/buckaroo_client/gateway/nvp/response.rb +56 -0
- data/lib/buckaroo_client/gateway/nvp/signature.rb +38 -0
- data/lib/buckaroo_client/service.rb +8 -0
- data/lib/buckaroo_client/service/credit_management.rb +71 -0
- data/lib/buckaroo_client/service/invoice_specification.rb +92 -0
- data/lib/buckaroo_client/service/pay_per_email.rb +40 -0
- data/lib/buckaroo_client/transaction.rb +39 -0
- data/lib/buckaroo_client/version.rb +3 -0
- data/spec/buckaroo_client/gateway/batch_spec.rb +54 -0
- data/spec/buckaroo_client/gateway/nvp_spec.rb +173 -0
- data/spec/buckaroo_client/service/credit_management_spec.rb +52 -0
- data/spec/buckaroo_client/service/invoice_specification_spec.rb +44 -0
- data/spec/buckaroo_client/service/pay_per_email_spec.rb +68 -0
- data/spec/buckaroo_client/transaction_spec.rb +73 -0
- data/spec/spec_helper.rb +89 -0
- metadata +148 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 2911056e32cd71be8234f88bf7dd089fe06adadf
|
4
|
+
data.tar.gz: cfbec2e5be8084042f248a16bd456aae797d187b
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: f5c6d596217c75f66e093138cfe01170f55f2d6a1080a7437a1db9e7856384548cd7726f6900ffe321d10fcec58dab96364889eb065f16ad1571979f14afbd6c
|
7
|
+
data.tar.gz: 1b1bd2230444f5a8154af5800c92a8a7dc19a27856332f9d280bbd3c3ca4d41581e402ef16ed1194ae768e9bf9ba45555eac5bc8557ff0f4d87eb84595f26561
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Floris Huetink
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
# BuckarooClient
|
2
|
+
|
3
|
+
Ruby support for Buckaroo Payment Engine 3.0
|
4
|
+
|
5
|
+
|
6
|
+
## Installation
|
7
|
+
|
8
|
+
Add this line to your application's Gemfile:
|
9
|
+
|
10
|
+
```ruby
|
11
|
+
gem 'buckaroo_client'
|
12
|
+
```
|
13
|
+
|
14
|
+
And then execute:
|
15
|
+
|
16
|
+
$ bundle
|
17
|
+
|
18
|
+
Or install it yourself as:
|
19
|
+
|
20
|
+
$ gem install buckaroo_client
|
21
|
+
|
22
|
+
|
23
|
+
## Usage
|
24
|
+
|
25
|
+
### Setup
|
26
|
+
|
27
|
+
Set the following `ENV` vars in your application:
|
28
|
+
|
29
|
+
* `BUCKAROO_CLIENT_WEBSITEKEY`: website key as generated by Buckaroo
|
30
|
+
* `BUCKAROO_CLIENT_SECRET`: shared secret to digitally sign API requests
|
31
|
+
* `BUCKAROO_CLIENT_ENVIRONMENT`: set this to `production` to create real
|
32
|
+
transactions. Defaults to `test`.
|
33
|
+
|
34
|
+
Or alternatively, configure using a block (e.g. in a Rails initializer script):
|
35
|
+
|
36
|
+
```ruby
|
37
|
+
BuckarooClient.configure do |c|
|
38
|
+
c.websitekey = 'yourwebsitekey'
|
39
|
+
c.secret = 'randomsharedsecretstring'
|
40
|
+
c.environment = 'production'
|
41
|
+
end
|
42
|
+
```
|
43
|
+
|
44
|
+
### Creating a transaction
|
45
|
+
|
46
|
+
Start by creating a base transaction:
|
47
|
+
|
48
|
+
```ruby
|
49
|
+
transaction = BuckarooClient.transaction(amount: 9.99, description: 'Payment')
|
50
|
+
```
|
51
|
+
|
52
|
+
After that, you must select a primary service:
|
53
|
+
|
54
|
+
```ruby
|
55
|
+
transaction.select_service(:pay_per_email) do |s|
|
56
|
+
s.customeremail = 'example@example.com'
|
57
|
+
# ... and some more values
|
58
|
+
end
|
59
|
+
```
|
60
|
+
|
61
|
+
Optionally select additional payment services as you please:
|
62
|
+
|
63
|
+
```ruby
|
64
|
+
transaction.select_additional_service(:invoice_specification) do |s|
|
65
|
+
s.add_invoice_line(description: 'Some Product', amount: 10.00)
|
66
|
+
s.add_total_line(description: 'Total', amount: 10.00)
|
67
|
+
end
|
68
|
+
transaction.select_additional_service(:credit_management) do |s|
|
69
|
+
s.invoice_date = Date.current
|
70
|
+
s.date_due = s.invoice_date.next_day(14)
|
71
|
+
end
|
72
|
+
```
|
73
|
+
|
74
|
+
### Sending data to Buckaroo Payment Engine
|
75
|
+
|
76
|
+
Use `BuckarooClient.gateway` to set up Buckaroo NVP Gateway transactions:
|
77
|
+
|
78
|
+
```ruby
|
79
|
+
BuckarooClient.gateway.transaction_request(transaction.gateway_attributes)
|
80
|
+
```
|
81
|
+
|
82
|
+
This will send a signed `POST` request to the Buckaroo gateway.
|
83
|
+
|
84
|
+
|
85
|
+
## Known limitations
|
86
|
+
|
87
|
+
* This gem currently only supports PayPerEmail transactions.
|
88
|
+
* Batch file creation is experimental and cannot handle invoices with
|
89
|
+
mixed numbers of invoice lines.
|
90
|
+
|
91
|
+
|
92
|
+
## Contributing
|
93
|
+
|
94
|
+
1. Fork it ( https://github.com/brightin/buckaroo_client/fork )
|
95
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
96
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
97
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
98
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'buckaroo_client/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "buckaroo_client"
|
8
|
+
spec.version = BuckarooClient::VERSION
|
9
|
+
spec.authors = ["Floris Huetink"]
|
10
|
+
spec.email = ["floris@brightin.nl"]
|
11
|
+
spec.summary = %q{Ruby support for Buckaroo Payment Engine 3.0}
|
12
|
+
spec.homepage = "https://github.com/brightin/buckaroo_client"
|
13
|
+
spec.license = "MIT"
|
14
|
+
|
15
|
+
spec.files = `git ls-files -z`.split("\x0")
|
16
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
17
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
18
|
+
spec.require_paths = ["lib"]
|
19
|
+
|
20
|
+
spec.add_dependency "addressable"
|
21
|
+
|
22
|
+
spec.add_development_dependency "bundler", "~> 1.7"
|
23
|
+
spec.add_development_dependency "dotenv", "~> 1.0"
|
24
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
25
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
26
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'buckaroo_client/gateway'
|
2
|
+
require 'buckaroo_client/service'
|
3
|
+
require 'buckaroo_client/transaction'
|
4
|
+
require 'buckaroo_client/version'
|
5
|
+
|
6
|
+
module BuckarooClient
|
7
|
+
DEFAULT_TRANSACTION_ATTRIBUTES = {
|
8
|
+
websitekey: ENV['BUCKAROO_CLIENT_WEBSITEKEY']
|
9
|
+
}
|
10
|
+
|
11
|
+
def self.gateway
|
12
|
+
Gateway::NVP
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.batch(attributes = {})
|
16
|
+
Gateway::Batch.new(attributes)
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.transaction(attributes = {})
|
20
|
+
Transaction.new(DEFAULT_TRANSACTION_ATTRIBUTES.merge(attributes))
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.service(name, attributes = {})
|
24
|
+
case name.to_s
|
25
|
+
when 'credit_management'
|
26
|
+
Service::CreditManagement.new
|
27
|
+
when 'invoice_specification'
|
28
|
+
Service::InvoiceSpecification.new
|
29
|
+
when 'pay_per_email'
|
30
|
+
Service::PayPerEmail.new(attributes)
|
31
|
+
else
|
32
|
+
raise ArgumentError.new("service '#{name}' does not exist")
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
require 'csv'
|
3
|
+
|
4
|
+
module BuckarooClient
|
5
|
+
module Gateway
|
6
|
+
class Batch
|
7
|
+
attr_reader :transactions
|
8
|
+
|
9
|
+
extend Forwardable
|
10
|
+
def_delegators :@transactions, :<<, :size, :each
|
11
|
+
|
12
|
+
def initialize(args = {})
|
13
|
+
@transactions = args.fetch(:transactions, [])
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_csv
|
17
|
+
table = CSV::Table.new([])
|
18
|
+
transactions.each { |t| table << transaction_to_csv(t) }
|
19
|
+
table.to_csv(col_sep: ';')
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def transaction_to_csv(transaction)
|
25
|
+
data = transaction.gateway_attributes
|
26
|
+
CSV::Row.new(data.keys, data.values)
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require 'addressable/uri'
|
2
|
+
require 'digest/sha1'
|
3
|
+
require 'buckaroo_client/gateway/nvp/response'
|
4
|
+
require 'buckaroo_client/gateway/nvp/signature'
|
5
|
+
|
6
|
+
module BuckarooClient
|
7
|
+
module Gateway
|
8
|
+
module NVP
|
9
|
+
extend Signature
|
10
|
+
|
11
|
+
def self.environment
|
12
|
+
ENV['BUCKAROO_CLIENT_ENVIRONMENT'] || 'test'
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.websitekey
|
16
|
+
ENV['BUCKAROO_CLIENT_WEBSITEKEY'] || raise("BUCKAROO_CLIENT_WEBSITEKEY not set")
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.url(action: nil)
|
20
|
+
path = case environment
|
21
|
+
when 'production'
|
22
|
+
'https://checkout.buckaroo.nl/nvp/'
|
23
|
+
else
|
24
|
+
'https://testcheckout.buckaroo.nl/nvp/'
|
25
|
+
end
|
26
|
+
path += "?op=#{action}" unless action.nil?
|
27
|
+
URI.parse(path)
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.transaction_request(buckaroo_variables, custom: {}, additional: {})
|
31
|
+
do_request('transactionrequest', buckaroo_variables, custom, additional)
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.transaction_status(buckaroo_variables, custom: {}, additional: {})
|
35
|
+
do_request('transactionstatus', buckaroo_variables, custom, additional)
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.invoice_info(buckaroo_variables, custom: {}, additional: {})
|
39
|
+
do_request('invoiceinfo', buckaroo_variables, custom, additional)
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.transaction_request_specification(buckaroo_variables, custom: {}, additional: {})
|
43
|
+
do_request('transactionrequestspecification', buckaroo_variables, custom, additional)
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def self.do_request(action, buckaroo_variables, custom = {}, additional = {})
|
49
|
+
nvp_data = prefixed_and_signed_request_data(buckaroo_variables, custom, additional)
|
50
|
+
Response.for_action(action, post_data(action, nvp_data))
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.post_data(action, data)
|
54
|
+
Net::HTTP.post_form(url(action: action), data)
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.prefixed_and_signed_request_data(buckaroo, custom, additional)
|
58
|
+
output = prefix_if_needed(data: buckaroo, prefix: 'brq_')
|
59
|
+
output.merge!(prefix_if_needed(data: custom, prefix: 'cust_'))
|
60
|
+
output.merge!(prefix_if_needed(data: additional, prefix: 'add_'))
|
61
|
+
output['brq_websitekey'] ||= websitekey
|
62
|
+
if output.key?('brq_service')
|
63
|
+
output['brq_payment_method'] = output.delete('brq_service')
|
64
|
+
end
|
65
|
+
sign!(output)
|
66
|
+
return output
|
67
|
+
end
|
68
|
+
|
69
|
+
def self.prefix_if_needed(prefix:, data:)
|
70
|
+
output = {}
|
71
|
+
data.each do |key, value|
|
72
|
+
prefixed_key = key.to_s.index(prefix) == 0 ? key : "#{prefix}#{key}"
|
73
|
+
output[prefixed_key] = value
|
74
|
+
end
|
75
|
+
return output
|
76
|
+
end
|
77
|
+
|
78
|
+
def self.sign!(input)
|
79
|
+
input['brq_signature'] = signature(input)
|
80
|
+
input
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'buckaroo_client/gateway/nvp/response'
|
2
|
+
|
3
|
+
module BuckarooClient
|
4
|
+
module Gateway
|
5
|
+
module NVP
|
6
|
+
class InvoiceInfoResponse < Response
|
7
|
+
|
8
|
+
def paid?
|
9
|
+
return nil unless response.key?('BRQ_INVOICE_1_PAID')
|
10
|
+
response['BRQ_INVOICE_1_PAID'].to_s.downcase == 'true'
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# Partly taken from:
|
2
|
+
# https://github.com/inventid/buckaroo/blob/develop/lib/buckaroo/response.rb
|
3
|
+
require 'addressable/uri'
|
4
|
+
require 'digest/sha1'
|
5
|
+
require 'buckaroo_client/gateway/nvp/signature'
|
6
|
+
|
7
|
+
module BuckarooClient
|
8
|
+
module Gateway
|
9
|
+
module NVP
|
10
|
+
class Response
|
11
|
+
|
12
|
+
include Signature
|
13
|
+
|
14
|
+
attr_reader :body, :response, :status_code, :success
|
15
|
+
|
16
|
+
def self.for_action(action, response)
|
17
|
+
klass = case action
|
18
|
+
when 'invoiceinfo'
|
19
|
+
require 'buckaroo_client/gateway/nvp/invoice_info_response'
|
20
|
+
InvoiceInfoResponse
|
21
|
+
else
|
22
|
+
Response
|
23
|
+
end
|
24
|
+
klass.new(response)
|
25
|
+
end
|
26
|
+
|
27
|
+
def initialize(response)
|
28
|
+
@body = response.body
|
29
|
+
@response = Hash[Addressable::URI.form_unencode(body)]
|
30
|
+
@status_code = @response['BRQ_STATUSCODE'].to_i
|
31
|
+
@success = !error_occurred?
|
32
|
+
end
|
33
|
+
|
34
|
+
alias_method :success?, :success
|
35
|
+
|
36
|
+
def verify!
|
37
|
+
verified? or raise "Response signature does not match expected value"
|
38
|
+
end
|
39
|
+
|
40
|
+
def verified?
|
41
|
+
input = response.dup
|
42
|
+
given_hash = input['BRQ_SIGNATURE']
|
43
|
+
input.delete('BRQ_SIGNATURE')
|
44
|
+
signature(input) == given_hash
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def error_occurred?
|
50
|
+
!verified? || status_code == 491 || status_code == 492
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'digest/sha1'
|
2
|
+
|
3
|
+
module BuckarooClient
|
4
|
+
module Gateway
|
5
|
+
module NVP
|
6
|
+
module Signature
|
7
|
+
def secret_key
|
8
|
+
ENV['BUCKAROO_CLIENT_SECRET'] || raise("BUCKAROO_CLIENT_SECRET not set")
|
9
|
+
end
|
10
|
+
|
11
|
+
def signature(input)
|
12
|
+
# Base logic and comments taken from github.com/inventid/buckaroo
|
13
|
+
#
|
14
|
+
# This might actually need some explanation why we are converting do lowercase here
|
15
|
+
# BuckarooClient specifies to sort these parameters, although the exact matter of sorting
|
16
|
+
# is quite ambigious. So after quite a while of debugging, I discovered that by
|
17
|
+
# sorting they do not use the ASCII based sorting Ruby uses. In fact, the sorting
|
18
|
+
# is specified to place symbols first (which ASCII does, except for the underscore (_)
|
19
|
+
# which is located between the capitals and lowercase letters (jeej ASCII!).
|
20
|
+
# So in this case, by converting everything to lowercase before comparing, we ensure
|
21
|
+
# that all symbols are in the table before the letters.
|
22
|
+
#
|
23
|
+
# Actual case where it went wrong: keys BRQ_TRANSACTIONS and BRQ_TRANSACTION_CANCELABLE
|
24
|
+
# Ruby would sort these in this exact order, whereas BuckarooClient would reverse them. And
|
25
|
+
# since for hashing the reversal generates a totally different sequence, that would
|
26
|
+
# break message validation.
|
27
|
+
#
|
28
|
+
# TLDR; Leave it with a downcase
|
29
|
+
sorted_data = input.sort_by { |key, _| key.to_s.downcase }
|
30
|
+
to_hash = ''
|
31
|
+
sorted_data.each { |key, value| to_hash << key.to_s+'='+value.to_s }
|
32
|
+
to_hash << secret_key
|
33
|
+
Digest::SHA1.hexdigest(to_hash)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|