barzahlen 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: d22d4228515d62a086a459066ed7d35a5fa596fb
4
+ data.tar.gz: add4528a9bb87a690c17609c81831f384bc9a06c
5
+ SHA512:
6
+ metadata.gz: bf406f3231504cd7c6047a5df34e3f4253d1a79e4a730323cfd92718f54b7380a9fb8cc90624f37d3d28c2d74a3a727a12a347978a968e1401597caefae24854
7
+ data.tar.gz: 5f9ca948df4a0fdb4c032eaf11271b61456b0c89008d7bce56b2bac805b49ebb10f9f7ecf3bf33897bdbfecd3bb9e44a96ddc50a46ff61b4f1c81f80d87b57a3
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/README.md ADDED
@@ -0,0 +1,207 @@
1
+ # Barzahlen Ruby-SDK
2
+
3
+ The official Ruby-SDK for accessing the Barzalen Payment Service.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'barzahlen-sdk'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install barzahlen-sdk
18
+
19
+ ## Usage
20
+
21
+ ```Ruby
22
+ require 'barzahlen/api/online'
23
+ ```
24
+
25
+ ### Configuration
26
+
27
+ The configuration is a hash where :shop_id, :payment_key and :notification_key are required. For
28
+ using the sandbox you can set the :host to 'api-sandbox.barzahlen.de'.
29
+
30
+ ```Ruby
31
+ Barzahlen::Api::Online.config = {:shop_id => 'xxx',
32
+ :payment_key => 'xxx',
33
+ :notification_key => 'xxx',
34
+ :host => 'api-sandbox.barzahlen.de',}
35
+ ```
36
+
37
+ The credentials, which are necessary to handle payments with Barzahlen, can be received at
38
+ https://partner.barzahlen.de. After a successful registration a shop ID is assigned to you as
39
+ well as a payment and a notification key. Furthermore you can set your callback URL which is used
40
+ to send updates on payment and refund transactions.
41
+
42
+ ### Send requests
43
+
44
+ After you set the config you can create the transaction-api object with the method
45
+ Barzahlen::Api::Online.new_transaction_api.
46
+
47
+ If there are any errors, a RuntimeError is thrown.
48
+
49
+ #### Create transactions
50
+
51
+ ```Ruby
52
+ transaction = {
53
+ :customer_email => 'barzahlen@example.com',
54
+ :amount => '13.37',
55
+ :currency => 'EUR',
56
+ :order_id => '123', #optional
57
+ :customer_street_nr => 'Wallstr. 14a',
58
+ :customer_zipcode => '10179',
59
+ :customer_city => 'Berlin',
60
+ :customer_country => 'DE',
61
+ :customer_custom_var_0 => 'customer_custom_var_0', #optional
62
+ :customer_custom_var_1 => 'customer_custom_var_1', #optional
63
+ :customer_custom_var_2 => 'customer_custom_var_2', #optional
64
+ :language => 'de',
65
+ }
66
+ api_transaction = Barzahlen::Api::Online.new_transaction_api
67
+ response = api_transaction.create transaction
68
+ # Response contains :transaction_id, :payment_slip_link, :expiration_notice, :infotext1,
69
+ # :infotext2, :result, :hash
70
+ ```
71
+
72
+ #### Update order-id of a transaction
73
+
74
+ ```Ruby
75
+ transaction_id = 123
76
+ order_id = 456
77
+ api_transaction = Barzahlen::Api::Online.new_transaction_api
78
+ response = api_transaction.update_order_id transaction_id, order_id
79
+ # Response contains :transaction_id, :result, :hash
80
+ ```
81
+
82
+ #### Resend E-Mail for a transaction
83
+
84
+ ```Ruby
85
+ transaction_id = 123
86
+ language = 'de'
87
+ api_transaction = Barzahlen::Api::Online.new_transaction_api
88
+ response = api_transaction.resend_email transaction_id, language
89
+ # Response contains :transaction_id, :result, :hash
90
+ ```
91
+
92
+ #### Cancel a transaction
93
+
94
+ ```Ruby
95
+ transaction_id = 123
96
+ language = 'de'
97
+ api_transaction = Barzahlen::Api::Online.new_transaction_api
98
+ response = api_transaction.cancel transaction_id, language
99
+ # Response contains :transaction_id, :result, :hash
100
+ ```
101
+
102
+ #### Create a refund for a transaction
103
+
104
+ ```Ruby
105
+ transaction_id = 123
106
+ amount = '1.23'
107
+ currency = 'EUR'
108
+ language = 'de'
109
+ api_transaction = Barzahlen::Api::Online.new_transaction_api
110
+ response = api_transaction.refund transaction_id, amount, currency, language
111
+ # Response contains :origin_transaction_id, :refund_transaction_id, :result, :hash
112
+ ```
113
+
114
+ ### Handle notifications
115
+
116
+ After you set the config you can create the notification-handler object with the method
117
+ Barzahlen::Api::Online.new_notification_handler.
118
+
119
+ #### Handle a transaction-paid notification
120
+
121
+ ```Ruby
122
+ notification_handler = Barzahlen::Api::Online.new_notification_handler
123
+ notification = notification_handler.validate_request(request_parameters)
124
+ # Notification contains :state, :transaction_id, :shop_id, :customer_email, :amount, :currency,
125
+ # :notification_key and, if set while request, :order_id, :custom_var_0, :custom_var_1, :custom_var_2
126
+ if notification[:state] == Barzahlen::Api::Online::NotificationHandler::TRANSACTION_PAID
127
+ # ...
128
+ end
129
+ ```
130
+
131
+ #### Handle a transaction-expired notification
132
+
133
+ ```Ruby
134
+ notification_handler = Barzahlen::Api::Online.new_notification_handler
135
+ notification = notification_handler.validate_request(request_parameters)
136
+ # Notification contains :state, :transaction_id, :shop_id, :customer_email, :amount, :currency,
137
+ # :notification_key and, if set while request, :order_id, :custom_var_0, :custom_var_1, :custom_var_2
138
+ if notification[:state] == Barzahlen::Api::Online::NotificationHandler::TRANSACTION_EXPIRED
139
+ # ...
140
+ end
141
+ ```
142
+
143
+ #### Handle a refund-completed notification
144
+
145
+ ```Ruby
146
+ notification_handler = Barzahlen::Api::Online.new_notification_handler
147
+ notification = notification_handler.validate_request(request_parameters)
148
+ # Notification contains :state, :refund_transaction_id, :origin_transaction_id, :shop_id,
149
+ #:customer_email, :amount, :currency, :notification_key and, if set while request, :origin_order_id,
150
+ #:custom_var_0, :custom_var_1, :custom_var_2
151
+ if notification[:state] == Barzahlen::Api::Online::NotificationHandler::REFUND_COMPLETED
152
+ # ...
153
+ end
154
+ ```
155
+
156
+ #### Handle a refund-expired notification
157
+
158
+ ```Ruby
159
+ notification_handler = Barzahlen::Api::Online.new_notification_handler
160
+ notification = notification_handler.validate_request(request_parameters)
161
+ # Notification contains :state, :refund_transaction_id, :origin_transaction_id, :shop_id,
162
+ #:customer_email, :amount, :currency, :notification_key and, if set while request, :origin_order_id,
163
+ #:custom_var_0, :custom_var_1, :custom_var_2
164
+ if notification[:state] == Barzahlen::Api::Online::NotificationHandler::REFUND_EXPIRED
165
+ # ...
166
+ end
167
+ ```
168
+
169
+ ### Logging
170
+
171
+ If you want to log all requests to the API and the raw responses, you have to implement your own
172
+ http_client. As you can see this is not as hard is sounds.
173
+
174
+ ```Ruby
175
+ class LoggingClient < Barzahlen::Api::Online::Http
176
+ def request(url, parameters)
177
+ response = super
178
+
179
+ puts url
180
+ puts parameters
181
+ puts response
182
+
183
+ response
184
+ end
185
+ end
186
+
187
+ config = {:shop_id => '13446',
188
+ :payment_key => '1c7439cda8256d9ead6602087462f0e478e3f2f4',
189
+ :notification_key => '390447f4a799dcae93d62bcd9567c1795188154b',
190
+ :host => 'api-sandbox.barzahlen.de',}
191
+
192
+ http = LoggingClient.new
193
+ Barzahlen::Api::Online.api = Barzahlen::Api::Online::Api.new http, config
194
+
195
+ transaction_id = 123
196
+ order_id = 456
197
+ api_transaction = Barzahlen::Api::Online.new_transaction_api
198
+ response = api_transaction.update_order_id transaction_id, order_id
199
+ ```
200
+
201
+ ## Contributing
202
+
203
+ 1. Fork it ( http://github.com/barzahlen/Barzahlen-Ruby/fork )
204
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
205
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
206
+ 4. Push to the branch (`git push origin my-new-feature`)
207
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,3 @@
1
+ # coding: utf-8
2
+
3
+ require 'bundler/gem_tasks'
data/bin/test.rb ADDED
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env ruby_executable_hooks
2
+ # coding: utf-8
3
+
4
+ $:.unshift '/home/mathiashertlein/Development/sdk/ruby_sdk/lib'
5
+ require 'barzahlen/api/online'
6
+
7
+ class LoggingClient < Barzahlen::Api::Online::Http
8
+ def request(url, parameters)
9
+ response = super
10
+
11
+ puts url
12
+ puts parameters
13
+ puts response
14
+
15
+ response
16
+ end
17
+ end
18
+
19
+ config = {:shop_id => '13446',
20
+ :payment_key => '1c7439cda8256d9ead6602087462f0e478e3f2f4',
21
+ :notification_key => '390447f4a799dcae93d62bcd9567c1795188154b',
22
+ :host => 'api-sandbox.barzahlen.de',}
23
+
24
+ http = LoggingClient.new
25
+ Barzahlen::Api::Online.api = Barzahlen::Api::Online::Api.new http, config
26
+
27
+ transaction_id = 123
28
+ order_id = 456
29
+ Barzahlen::Api::Online.new_transaction_api
30
+ response = api_transaction.update_order_id transaction_id, order_id
@@ -0,0 +1,67 @@
1
+ # coding: utf-8
2
+
3
+ require 'openssl'
4
+ require 'net/http'
5
+
6
+ require 'barzahlen/api/online/hash_builder'
7
+ require 'barzahlen/api/online/response_parser'
8
+
9
+ module Barzahlen
10
+ module Api
11
+ module Online
12
+
13
+ # Is responsible for assemble requests and send them via https to the Barzahlen API. It can
14
+ # send requests or just build and return the hash.
15
+ class Api
16
+ def initialize(http, config)
17
+ @http = http
18
+ @config = config
19
+ end
20
+
21
+ # Assembles and sends Request to the Barzahlen API, and returns the parsed response
22
+ # path is the url path.
23
+ # key_name is the name of the config field, that contains the secret key.
24
+ # parameters_order is the order of the parameters, in which they are concatenated for
25
+ # hash building.
26
+ # parameters contains the parameters of the request.
27
+ def request(path, key_name, parameters_order, parameters)
28
+ request_url = build_url path
29
+ request_parameters = build_parameters key_name, parameters_order, parameters
30
+
31
+ response = @http.request request_url, request_parameters
32
+ parsed_response = ResponseParser.parse response.body
33
+
34
+ if response.code != '200'
35
+ raise RuntimeError, 'Barzahlen-API returned http-status: ' + response.code.to_s + ',' +
36
+ 'error-message: ' + parsed_response[:error_message] + ',' +
37
+ 'error-code: ' + parsed_response[:result]
38
+ end
39
+
40
+ parsed_response
41
+ end
42
+
43
+ # Calculates hash from passed parameters.
44
+ #
45
+ # key_name is the name of the config field, that contains the secret key.
46
+ # parameters_order is the order of the parameters, in which they are concatenated.
47
+ # parameters contains the parameters of the request.
48
+ def build_hash(key_name, parameters_order, parameters)
49
+ request_parameters = parameters.merge :shop_id => @config[:shop_id]
50
+
51
+ hash_builder = HashBuilder.new parameters_order, key_name
52
+ hash_builder.build_hash @config[key_name], request_parameters
53
+ end
54
+
55
+ private
56
+ def build_url(path)
57
+ URI('https://' + @config[:host] + '/' + path)
58
+ end
59
+
60
+ def build_parameters(key_name, parameters_order, parameters)
61
+ hash = build_hash key_name, parameters_order, parameters
62
+ parameters.merge :shop_id => @config[:shop_id], :hash => hash
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,43 @@
1
+ # coding: utf-8
2
+
3
+ require 'digest/sha2'
4
+
5
+ module Barzahlen
6
+ module Api
7
+ module Online
8
+
9
+ # Creates hashes from a set of parameters with th following algorithm:
10
+ # SHA512(param1;param2;param3;key)
11
+ class HashBuilder
12
+ def initialize(parameters_order, key_parameter_name)
13
+ @parameters_order = parameters_order
14
+ @key_parameter_name = key_parameter_name
15
+ end
16
+
17
+ # Calculates hash from passed parameters and key
18
+ def build_hash(key, parameters)
19
+ parameters_string = build_parameters_string key, parameters
20
+ Digest::SHA512.hexdigest parameters_string
21
+ end
22
+
23
+ private
24
+ def build_parameters_string(key, parameters)
25
+ parameters_string = ''
26
+
27
+ @parameters_order.each_with_index do |parameter_name, index|
28
+ if parameter_name == @key_parameter_name
29
+ parameter_value = key.to_s
30
+ else
31
+ parameter_value = parameters[parameter_name].to_s
32
+ end
33
+
34
+ parameters_string += parameter_value.to_s
35
+ parameters_string += ';' unless index == @parameters_order.size - 1
36
+ end
37
+
38
+ parameters_string
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,31 @@
1
+ # coding: utf-8
2
+
3
+ require 'openssl'
4
+ require 'net/http'
5
+
6
+ require 'barzahlen/api/online/hash_builder'
7
+ require 'barzahlen/api/online/response_parser'
8
+
9
+ module Barzahlen
10
+ module Api
11
+ module Online
12
+
13
+ # A wrapper class for Net::HTTP
14
+ # If you want to use your own http client, you must implement the request method and let it
15
+ # return an HTTPResponse compatible object
16
+ class Http
17
+ # sends a request with specified parameters to url, and returns an HTTPResponse object
18
+ def request(url, parameters)
19
+ http_client = Net::HTTP.new url.host, 443
20
+ http_client.use_ssl = true
21
+ http_client.verify_mode = OpenSSL::SSL::VERIFY_PEER
22
+
23
+ request = Net::HTTP::Post.new url.to_s
24
+ request.set_form_data parameters
25
+
26
+ http_client.request request
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,50 @@
1
+ # coding: utf-8
2
+
3
+ module Barzahlen
4
+ module Api
5
+ module Online
6
+
7
+ # Checks requests from Barzahlen-API
8
+ class NotificationHandler
9
+ TRANSACTION_PAID = 'paid'
10
+ TRANSACTION_EXPIRED = 'expired'
11
+ REFUND_COMPLETED = 'refund_completed'
12
+ REFUND_EXPIRED = 'refund_expired'
13
+
14
+ def initialize(api)
15
+ @api = api
16
+ end
17
+
18
+ # validates request and return the parameters
19
+ def validate_request(request_parameters)
20
+ parameters_order = case request_parameters[:state]
21
+ when TRANSACTION_PAID then [:state, :transaction_id, :shop_id, :customer_email,
22
+ :amount, :currency, :order_id, :custom_var_0,
23
+ :custom_var_1, :custom_var_2, :notification_key]
24
+ when TRANSACTION_EXPIRED then [:state, :transaction_id, :shop_id, :customer_email,
25
+ :amount, :currency, :order_id, :custom_var_0,
26
+ :custom_var_1, :custom_var_2, :notification_key]
27
+ when REFUND_COMPLETED then [:state, :refund_transaction_id, :origin_transaction_id,
28
+ :shop_id, :customer_email, :amount, :currency,
29
+ :origin_order_id, :custom_var_0, :custom_var_1,
30
+ :custom_var_2, :notification_key]
31
+ when REFUND_EXPIRED then [:state, :refund_transaction_id, :origin_transaction_id,
32
+ :shop_id, :customer_email, :amount, :currency,
33
+ :origin_order_id, :custom_var_0, :custom_var_1,
34
+ :custom_var_2, :notification_key]
35
+ else raise RuntimeError, "Unknown state '#{request_parameters[:state]}'"
36
+ end
37
+
38
+ expected_hash = @api.build_hash(:notification_key, parameters_order, request_parameters)
39
+ if request_parameters[:hash] != expected_hash
40
+ raise RuntimeError, 'Expected hash does not equal passed hash.' +
41
+ 'Passed: ' + request_parameters[:hash] + ', ' +
42
+ 'Expected: ' + expected_hash
43
+ end
44
+
45
+ request_parameters
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+
3
+ require 'rexml/document'
4
+
5
+ module Barzahlen
6
+ module Api
7
+ module Online
8
+
9
+ # Parses Barzahlen-API response
10
+ class ResponseParser
11
+
12
+ # Parses xml and returns hash
13
+ def self.parse(response_body)
14
+ response_parsed = {}
15
+
16
+ doc = REXML::Document.new response_body
17
+ doc.elements.each('/response/*') do |element|
18
+ response_parsed[element.name.gsub(/\-/, '_').to_sym] = element.text
19
+ end
20
+
21
+ response_parsed
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,57 @@
1
+ # coding: utf-8
2
+
3
+ module Barzahlen
4
+ module Api
5
+ module Online
6
+
7
+ # Manipulates transactions via Barzahlen-API
8
+ class Transaction
9
+ def initialize(api)
10
+ @api = api
11
+ end
12
+
13
+ # create a transaction
14
+ # necessary fields of the transaction are:
15
+ # :customer_email, :amount, :currency, :language, :order_id,
16
+ # :customer_street_nr, :customer_zipcode, :customer_city, :customer_country
17
+ # optional are:
18
+ # :custom_var_0, :custom_var_1, :custom_var_2
19
+ def create(transaction)
20
+ parameters_order = [:shop_id, :customer_email, :amount, :currency, :language, :order_id, :customer_street_nr,
21
+ :customer_zipcode, :customer_city, :customer_country, :custom_var_0, :custom_var_1,
22
+ :custom_var_2, :payment_key]
23
+ @api.request('v1/transactions/create', :payment_key, parameters_order, transaction)
24
+ end
25
+
26
+ # update order-id of a transaction
27
+ def update_order_id(transaction_id, order_id)
28
+ parameters_order = [:shop_id, :transaction_id, :order_id, :payment_key]
29
+ @api.request('v1/transactions/update', :payment_key, parameters_order,
30
+ {:transaction_id => transaction_id, :order_id => order_id})
31
+ end
32
+
33
+ # resend email for a transaction
34
+ def resend_email(transaction_id, language)
35
+ parameters_order = [:shop_id, :transaction_id, :language, :payment_key]
36
+ @api.request('v1/transactions/resend_email', :payment_key, parameters_order,
37
+ {:transaction_id => transaction_id, :language => language})
38
+ end
39
+
40
+ # cancels a transaction
41
+ def cancel(transaction_id, language)
42
+ parameters_order = [:shop_id, :transaction_id, :language, :payment_key]
43
+ @api.request('v1/transactions/cancel', :payment_key, parameters_order,
44
+ {:transaction_id => transaction_id, :language => language})
45
+ end
46
+
47
+ # create a refund for a transaction
48
+ def refund(transaction_id, amount, currency, language)
49
+ parameters_order = [:shop_id, :transaction_id, :amount, :currency, :language, :payment_key]
50
+ @api.request('v1/transactions/refund', :payment_key, parameters_order,
51
+ {:transaction_id => transaction_id, :amount => amount,
52
+ :currency => currency, :language => language})
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,9 @@
1
+ # coding: utf-8
2
+
3
+ module Barzahlen
4
+ module Api
5
+ module Online
6
+ VERSION = '1.0.0'
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,58 @@
1
+ # coding: utf-8
2
+
3
+ require 'net/http'
4
+ require 'barzahlen/api/online/http'
5
+ require 'barzahlen/api/online/api'
6
+
7
+ module Barzahlen
8
+ module Api
9
+ module Online
10
+
11
+ # Is responsible for initializing objects to access the Barzahlen-API and check
12
+ # notifications. For using it, you must set a config, in seldom cases an api object.
13
+ #
14
+ # For using the sandbox mode, you must pass the host api.barzahlen.de in the config.
15
+ #
16
+ # = Examples
17
+ # Barzahlen::Api::Online.config = {
18
+ # :shop_id => 'your shop id',
19
+ # :payment_key => 'your payment key',
20
+ # :notification_key => 'your notification key',
21
+ # }
22
+ # api_transaction = Barzahlen::Api::Online.new_transaction_api
23
+ class << self
24
+ HOST = 'api.barzahlen.de'
25
+
26
+ attr_writer :config
27
+ attr_writer :api
28
+
29
+ # Returns an object that represents the Barzahlen-API and all it's methods.
30
+ def new_transaction_api
31
+ autoload :Transaction, 'barzahlen/api/online/transaction'
32
+
33
+ initialize_api
34
+ Barzahlen::Api::Online::Transaction.new @api
35
+ end
36
+
37
+ # Returns an object that validates notification requests, which came from Barzahlen
38
+ def new_notification_handler
39
+ autoload :NotificationHandler, 'barzahlen/api/online/notification_handler'
40
+
41
+ initialize_api
42
+ Barzahlen::Api::Online::NotificationHandler.new @api
43
+ end
44
+
45
+ private
46
+ def initialize_api
47
+ raise ArgumentError, 'You must set a config or api' if @config == nil && @api == nil
48
+ @config[:host] = HOST unless @config.include? :host
49
+
50
+ if @api == nil && @config != nil
51
+ http = Barzahlen::Api::Online::Http.new
52
+ @api = Barzahlen::Api::Online::Api.new http, @config
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,3 @@
1
+ module RubySdk
2
+ VERSION = "0.0.1"
3
+ end
data/lib/ruby_sdk.rb ADDED
@@ -0,0 +1,5 @@
1
+ require "ruby_sdk/version"
2
+
3
+ module RubySdk
4
+ # Your code goes here...
5
+ end