vaulted_billing 1.0.2 → 1.1.0

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.
@@ -0,0 +1,372 @@
1
+ require 'multi_json'
2
+ require 'multi_xml'
3
+ require 'net/http'
4
+
5
+ module VaultedBilling
6
+ module Gateways
7
+ ##
8
+ # Interface to IPCommerce.
9
+ #
10
+ # == Example
11
+ #
12
+ # VaultedBilling::Gateways::IPCommerce.new(:username => 'identity-token', :service_key_store => XXX).tap do |ipc|
13
+ # customer = ipc.add_customer(Customer.new)
14
+ # credit_card = ipc.add_credit_card(customer, CreditCard.new)
15
+ # ipc.purchase(customer, credit_card, 10.00)
16
+ # end
17
+ #
18
+ class Ipcommerce
19
+ include VaultedBilling::Gateway
20
+
21
+ Companies = {
22
+ 2 => /^4\d{12}(\d{3})?$/, # Visa
23
+ 3 => /^(5[1-5]\d{4}|677189)\d{10}$/, # MasterCard
24
+ 4 => /^3[47]\d{13}$/, # American Express
25
+ 5 => /^3(0[0-5]|[68]\d)\d{11}$/, # Diners Club
26
+ 6 => /^(6011|65\d{2}|64[4-9]\d)\d{12}|(62\d{14})$/, # Discover
27
+ 7 => /^35(28|29|[3-8]\d)\d{12}$/ # JCB
28
+ }.freeze
29
+
30
+ class ServiceKeyStore
31
+ UnavailableKeyError = Class.new(VaultedBilling::CredentialError)
32
+
33
+ attr_reader :identity_token
34
+
35
+ def initialize(identity_token)
36
+ @identity_token = identity_token
37
+ @expires_at = nil
38
+ end
39
+
40
+ def key
41
+ renew! unless valid?
42
+ read_key || raise(UnavailableKeyError, 'A service key could not be retrieved for this session.')
43
+ end
44
+
45
+ def read_key
46
+ @key
47
+ end
48
+
49
+ def store_key(key)
50
+ @key = key
51
+ end
52
+
53
+ def valid?
54
+ @expires_at && (@expires_at > Time.now + 5.minutes)
55
+ end
56
+
57
+ def before_request(request)
58
+ request.delete "accept"
59
+ end
60
+
61
+ def renew!
62
+ @expires_at = Time.now + 30.minutes
63
+ @key = http.get.body.try(:[], 1...-1)
64
+ end
65
+
66
+ private
67
+ def http
68
+ @request ||= begin
69
+ urls = ["https://cws-01.cert.ipcommerce.com/REST/2.0.15/SvcInfo/token",
70
+ "https://cws-02.cert.ipcommerce.com/REST/2.0.15/SvcInfo/token"]
71
+ VaultedBilling::HTTP.new(self, urls, {
72
+ :headers => {'Content-Type' => 'application/json'},
73
+ :before_request => :before_request,
74
+ :basic_auth => [@identity_token, ""]
75
+ })
76
+ end
77
+ end
78
+ end
79
+
80
+ attr_reader :service_key_store
81
+
82
+ def initialize(options = {})
83
+ @identity_token = options[:username] || VaultedBilling.config.ipcommerce.username
84
+ @raw_options = options[:raw_options] || VaultedBilling.config.ipcommerce.raw_options
85
+ @test_mode = options.has_key?(:test) ? options[:test] : (VaultedBilling.config.ipcommerce.test_mode || VaultedBilling.config.test_mode)
86
+ @application_id = options[:application_id] || @raw_options["application_id"]
87
+ @service_id = options[:service_id] || @raw_options["service_id"]
88
+ @service_key_store = options[:service_key_store] || ServiceKeyStore.new(@identity_token)
89
+ end
90
+
91
+
92
+ ##
93
+ # A stub, since the IP Commerce only generates tokens during
94
+ # successful authorization transactions.
95
+ #
96
+ def add_customer(customer)
97
+ respond_with customer.to_vaulted_billing
98
+ end
99
+
100
+ ##
101
+ # A stub, since the IP Commerce only generates tokens during
102
+ # successful authorization transactions.
103
+ #
104
+ #--
105
+ # TODO: If necessary, this may be implemented by Authorizing the given card for $1.00, then voiding immediately.
106
+ #++
107
+ #
108
+ def add_customer_credit_card(customer, credit_card)
109
+ respond_with credit_card.to_vaulted_billing
110
+ end
111
+
112
+ def authorize(customer, credit_card, amount, options = {})
113
+ data = {
114
+ "__type" => "AuthorizeTransaction:http://schemas.ipcommerce.com/CWS/v2.0/Transactions/Rest",
115
+ :ApplicationProfileId => @application_id,
116
+ :MerchantProfileId => options[:merchant_profile_id],
117
+ :Transaction => {
118
+ :"__type" => "BankcardTransaction:http://schemas.ipcommerce.com/CWS/v2.0/Transactions/Bankcard",
119
+ :TransactionData => {
120
+ :Amount => "%.2f" % amount,
121
+ :CurrencyCode => 4,
122
+ :TransactionDateTime => Time.now.xmlschema,
123
+ :CustomerPresent => 0,
124
+ :EntryMode => 1,
125
+ :GoodsType => 0,
126
+ :IndustryType => 2,
127
+ :SignatureCaptured => false,
128
+ :OrderNumber => options[:order_id] || generate_order_number
129
+ },
130
+ :TenderData => {
131
+ :CardData => {
132
+ :CardholderName => nil,
133
+ :CardType => self.class.credit_card_type_id(credit_card.card_number),
134
+ :Expire => credit_card.expires_on.try(:strftime, "%m%y"),
135
+ :PAN => credit_card.card_number
136
+ }
137
+ }
138
+ }
139
+ }
140
+
141
+ response = http(options[:workflow_id] || @service_id).post(data)
142
+ transaction = new_transaction_from_response(response)
143
+ respond_with(transaction,
144
+ response,
145
+ :success => (transaction.code == 1))
146
+ end
147
+
148
+ def capture(transaction_id, amount, options = {})
149
+ data = {
150
+ :"__type" => "Capture:http://schemas.ipcommerce.com/CWS/v2.0/Transactions/Rest",
151
+ :ApplicationProfileId => @application_id,
152
+ :DifferenceData => {
153
+ :"__type" => "BankcardCapture:http://schemas.ipcommerce.com/CWS/v2.0/Transactions/Bankcard",
154
+ :TransactionId => transaction_id,
155
+ :Addendum => nil,
156
+ :Amount => "%.2f" % amount
157
+ }
158
+ }
159
+
160
+ response = http(options[:workflow_id] || @service_id, transaction_id).put(data)
161
+ transaction = new_transaction_from_response(response)
162
+ respond_with(transaction,
163
+ response,
164
+ :success => (transaction.code == 1))
165
+ end
166
+
167
+ def purchase(customer, credit_card, amount, options = {})
168
+ data = {
169
+ "__type" => "AuthorizeAndCaptureTransaction:http://schemas.ipcommerce.com/CWS/v2.0/Transactions/Rest",
170
+ :ApplicationProfileId => @application_id,
171
+ :MerchantProfileId => options[:merchant_profile_id],
172
+ :Transaction => {
173
+ :"__type" => "BankcardTransaction:http://schemas.ipcommerce.com/CWS/v2.0/Transactions/Bankcard",
174
+ :TransactionData => {
175
+ :Amount => "%.2f" % amount,
176
+ :CurrencyCode => 4,
177
+ :TransactionDateTime => Time.now.xmlschema,
178
+ :CustomerPresent => 0,
179
+ :EmployeeId => options[:employee_id],
180
+ :EntryMode => 1,
181
+ :GoodsType => 0,
182
+ :IndustryType => 2,
183
+ :OrderNumber => options[:order_id] || generate_order_number,
184
+ :SignatureCaptured => false
185
+ },
186
+ :TenderData => {
187
+ :CardData => {
188
+ :CardholderName => nil,
189
+ :CardType => self.class.credit_card_type_id(credit_card.card_number),
190
+ :Expire => credit_card.expires_on.try(:strftime, "%m%y"),
191
+ :PAN => credit_card.card_number
192
+ }
193
+ }
194
+ }
195
+ }
196
+ response = http(options[:workflow_id] || @service_id).post(data)
197
+ transaction = new_transaction_from_response(response)
198
+ respond_with(transaction,
199
+ response,
200
+ :success => (transaction.code == 1))
201
+ end
202
+
203
+ def refund(transaction_id, amount, options = {})
204
+ data = {
205
+ :"__type" => "ReturnById:http://schemas.ipcommerce.com/CWS/v2.0/Transactions/Rest",
206
+ :ApplicationProfileId => @application_id,
207
+ :MerchantProfileId => options[:merchant_profile_id],
208
+ :DifferenceData => {
209
+ :"__type" => "BankcardReturn:http://schemas.ipcommerce.com/CWS/v2.0/Transactions/Bankcard",
210
+ :TransactionId => transaction_id,
211
+ :Addendum => nil,
212
+ :Amount => "%.2f" % amount
213
+ }
214
+ }
215
+
216
+ response = http(options[:workflow_id] || @service_id).post(data)
217
+ transaction = new_transaction_from_response(response)
218
+ respond_with(transaction,
219
+ response,
220
+ :success => (transaction.code == 1))
221
+ end
222
+
223
+ ##
224
+ # A stub, since the IP Commerce only generates tokens during
225
+ # successful authorization transactions.
226
+ #
227
+ def remove_customer(customer)
228
+ respond_with customer.to_vaulted_billing
229
+ end
230
+
231
+ ##
232
+ # A stub, since the IP Commerce only generates tokens during
233
+ # successful authorization transactions.
234
+ #
235
+ def remove_customer_credit_card(customer, credit_card)
236
+ respond_with credit_card.to_vaulted_billing
237
+ end
238
+
239
+ ##
240
+ # A stub, since the IP Commerce only generates tokens during
241
+ # successful authorization transactions.
242
+ #
243
+ def update_customer(customer)
244
+ respond_with customer.to_vaulted_billing
245
+ end
246
+
247
+ ##
248
+ # A stub, since the IP Commerce only generates tokens during
249
+ # successful authorization transactions.
250
+ #
251
+ def update_customer_credit_card(customer, credit_card)
252
+ respond_with credit_card.to_vaulted_billing
253
+ end
254
+
255
+ def void(transaction_id, options = {})
256
+ data = {
257
+ :"__type" => "Undo:http://schemas.ipcommerce.com/CWS/v2.0/Transactions/Rest",
258
+ :ApplicationProfileId => @application_id,
259
+ :DifferenceData => {
260
+ :"__type" => "BankcardUndo:http://schemas.ipcommerce.com/CWS/v2.0/Transactions/Bankcard",
261
+ :TransactionId => transaction_id,
262
+ :Addendum => nil
263
+ }
264
+ }
265
+
266
+ response = http(options[:workflow_id] || @service_id, transaction_id).put(data)
267
+ transaction = new_transaction_from_response(response)
268
+ respond_with(transaction,
269
+ response,
270
+ :success => (transaction.code == 1))
271
+ end
272
+
273
+
274
+ private
275
+
276
+
277
+ ##
278
+ # Returns the name of the card company based on the given number, or
279
+ # nil if it is unrecognized.
280
+ #
281
+ # This was heavily lifted from ActiveMerchant.
282
+ #
283
+ def self.credit_card_type_id(number)
284
+ Companies.each do |company, pattern|
285
+ return company if number =~ pattern
286
+ end
287
+ return 1
288
+ end
289
+
290
+
291
+ def generate_order_number
292
+ (Time.now.to_f * 100000).to_i.to_s(36) + rand(60000000).to_s(36)
293
+ end
294
+
295
+ def http(*params)
296
+ urls = %W(
297
+ https://cws-01.cert.ipcommerce.com/REST/2.0.15/Txn/#{params.join('/')}
298
+ https://cws-02.cert.ipcommerce.com/REST/2.0.15/Txn/#{params.join('/')}
299
+ )
300
+ VaultedBilling::HTTP.new(self, urls, {
301
+ :headers => { 'Content-Type' => 'application/json' },
302
+ :before_request => :before_request,
303
+ :basic_auth => [@service_key_store.key, ""],
304
+ :on_success => :on_success
305
+ })
306
+ end
307
+
308
+ def before_request(request)
309
+ request.body = MultiJson.encode(request.body)
310
+ # request.delete "accept"
311
+ end
312
+
313
+ def on_success(response)
314
+ response.body = decode_body(response.body) || {}
315
+ response.success = [1, 2].include?(response.body['Status'])
316
+ end
317
+
318
+ def decode_body(string)
319
+ MultiJson.decode(string)
320
+ rescue MultiJson::DecodeError
321
+ parse_error(string)
322
+ end
323
+
324
+ def new_transaction_from_response(response)
325
+ if response.success?
326
+ Transaction.new({
327
+ :id => response.body['TransactionId'],
328
+ :avs_response => response.body['AVSResult'] == 1,
329
+ :cvv_response => response.body['CVResult'] == 1,
330
+ :authcode => response.body['ApprovalCode'],
331
+ :message => response.body['StatusMessage'],
332
+ :code => response.body['Status'],
333
+ :masked_card_number => response.body['MaskedPAN']
334
+ })
335
+ else
336
+ if errors = parse_validation_errors(response)
337
+ Transaction.new({
338
+ :message => errors.join("\n"),
339
+ :code => (response.body['ErrorResponse'] || {})['ErrorId']
340
+ })
341
+ else
342
+ Transaction.new({
343
+ :message => response.body ? (response.body['ErrorResponse'] || {})['Reason'] : nil,
344
+ :code => response.body ? (response.body['ErrorResponse'] || {})['ErrorId'] : nil
345
+ })
346
+ end
347
+ end
348
+ end
349
+
350
+ def parse_validation_errors(response)
351
+ errors = ChainableHash.new.merge(response.body || {})
352
+ if errors['ErrorResponse']['ValidationErrors'].present?
353
+ [errors['ErrorResponse']['ValidationErrors']['ValidationError']].flatten.collect { |e| e['RuleMessage'] }
354
+ end
355
+ end
356
+
357
+ def parse_error(response_body)
358
+ MultiXml.parse(response_body)
359
+ rescue MultiXml::ParseError
360
+ end
361
+
362
+ def respond_with(object, response = nil, options = {}, &block)
363
+ super(object, options, &block).tap do |o|
364
+ if response
365
+ o.raw_response = response.raw_response.try(:body)
366
+ o.connection_error = response.connection_error
367
+ end
368
+ end
369
+ end
370
+ end
371
+ end
372
+ end
@@ -13,16 +13,16 @@ module VaultedBilling
13
13
  #
14
14
  class NmiCustomerVault
15
15
  include VaultedBilling::Gateway
16
- include VaultedBilling::HttpsInterface
16
+ attr_accessor :use_test_uri
17
17
 
18
18
  def initialize(options = {})
19
- self.live_uri = self.test_uri = "https://secure.nmi.com/api/transact.php"
19
+ @live_uri = "https://secure.nmi.com/api/transact.php"
20
20
 
21
21
  options = HashWithIndifferentAccess.new(options)
22
22
  @username = options[:username] || VaultedBilling.config.nmi_customer_vault.username
23
23
  @password = options[:password] || VaultedBilling.config.nmi_customer_vault.password
24
24
  @raw_options = options[:raw_options] || VaultedBilling.config.nmi_customer_vault.raw_options
25
- self.use_test_uri = options.has_key?(:test) ? options[:test] : (VaultedBilling.config.nmi_customer_vault.test_mode || VaultedBilling.config.test_mode)
25
+ @use_test_uri = options.has_key?(:test) ? options[:test] : (VaultedBilling.config.nmi_customer_vault.test_mode || VaultedBilling.config.test_mode)
26
26
  end
27
27
 
28
28
  ##
@@ -53,79 +53,91 @@ module VaultedBilling
53
53
  end
54
54
 
55
55
  def add_customer_credit_card(customer, credit_card)
56
- response = post_data(storage_data('add_customer', customer.to_vaulted_billing, credit_card.to_vaulted_billing))
56
+ data = storage_data('add_customer', customer.to_vaulted_billing, credit_card.to_vaulted_billing)
57
+ response = http.post(data)
57
58
  respond_with(credit_card, response, :success => response.success?) do |c|
58
59
  c.vault_id = response.body['customer_vault_id']
59
60
  end
60
61
  end
61
62
 
62
63
  def update_customer_credit_card(customer, credit_card)
63
- response = post_data(storage_data('update_customer', customer.to_vaulted_billing, credit_card.to_vaulted_billing))
64
+ data = storage_data('update_customer', customer.to_vaulted_billing, credit_card.to_vaulted_billing)
65
+ response = http.post(data)
64
66
  respond_with(credit_card, response, :success => response.success?)
65
67
  end
66
68
 
67
69
  def remove_customer_credit_card(customer, credit_card)
68
- response = post_data(core_data.merge({
70
+ data = core_data.merge({
69
71
  :customer_vault => 'delete_customer',
70
72
  :customer_vault_id => credit_card.to_vaulted_billing.vault_id
71
- }).to_querystring)
73
+ }).to_querystring
74
+ response = http.post(data)
72
75
  respond_with(credit_card, response, :success => response.success?)
73
76
  end
74
77
 
75
- def purchase(customer, credit_card, amount)
76
- response = post_data(transaction_data('sale', {
78
+ def purchase(customer, credit_card, amount, options = {})
79
+ data = transaction_data('sale', {
77
80
  :customer_vault_id => credit_card.to_vaulted_billing.vault_id,
78
81
  :amount => amount
79
- }))
82
+ })
83
+ response = http.post(data)
80
84
  respond_with(new_transaction_from_response(response.body),
81
85
  response,
82
86
  :success => response.success?)
83
87
  end
84
88
 
85
- def authorize(customer, credit_card, amount)
86
- response = post_data(transaction_data('auth', {
89
+ def authorize(customer, credit_card, amount, options = {})
90
+ data = transaction_data('auth', {
87
91
  :customer_vault_id => credit_card.to_vaulted_billing.vault_id,
88
92
  :amount => amount
89
- }))
93
+ })
94
+ response = http.post(data)
90
95
  respond_with(new_transaction_from_response(response.body),
91
96
  response,
92
97
  :success => response.success?)
93
98
  end
94
99
 
95
- def capture(transaction_id, amount)
96
- response = post_data(transaction_data('capture', {
100
+ def capture(transaction_id, amount, options = {})
101
+ data = transaction_data('capture', {
97
102
  :transactionid => transaction_id,
98
103
  :amount => amount
99
- }))
104
+ })
105
+ response = http.post(data)
100
106
  respond_with(new_transaction_from_response(response.body),
101
107
  response,
102
108
  :success => response.success?)
103
109
  end
104
110
 
105
111
  def refund(transaction_id, amount, options = {})
106
- response = post_data(transaction_data('refund', {
112
+ data = transaction_data('refund', {
107
113
  :transactionid => transaction_id,
108
114
  :amount => amount
109
- }))
115
+ })
116
+ response = http.post(data)
110
117
  respond_with(new_transaction_from_response(response.body),
111
118
  response,
112
119
  :success => response.success?)
113
120
  end
114
121
 
115
- def void(transaction_id)
116
- response = post_data(transaction_data('void', {
122
+ def void(transaction_id, options = {})
123
+ data = transaction_data('void', {
117
124
  :transactionid => transaction_id
118
- }))
125
+ })
126
+ response = http.post(data)
119
127
  respond_with(new_transaction_from_response(response.body),
120
128
  response,
121
129
  :success => response.success?)
122
130
  end
123
131
 
132
+ def uri
133
+ @live_uri
134
+ end
135
+
124
136
 
125
137
  protected
126
138
 
127
139
 
128
- def after_post_on_exception(response, exception)
140
+ def on_error(response, exception)
129
141
  response.body = {
130
142
  'response' => '3',
131
143
  'responsetext' => 'A communication problem has occurred.',
@@ -134,14 +146,21 @@ module VaultedBilling
134
146
  response.success = false
135
147
  end
136
148
 
137
- def after_post(response)
138
- response.body = Hash.from_querystring(response.body)
149
+ def on_complete(response)
150
+ response.body = Hash.from_querystring(response.body.to_s) unless response.body.is_a?(Hash)
139
151
  response.success = response.body['response'] == '1'
140
152
  end
141
153
 
142
154
 
143
155
  private
144
156
 
157
+ def http
158
+ VaultedBilling::HTTP.new(self, uri, {
159
+ :headers => {'Content-Type' => 'text/xml'},
160
+ :on_complete => :on_complete,
161
+ :on_error => :on_error
162
+ })
163
+ end
145
164
 
146
165
  def core_data
147
166
  {
@@ -0,0 +1,157 @@
1
+ require 'uri'
2
+
3
+ module VaultedBilling
4
+ class HTTP
5
+ HTTP_ERRORS = [
6
+ Timeout::Error,
7
+ Errno::ETIMEDOUT,
8
+ Errno::EINVAL,
9
+ Errno::ECONNRESET,
10
+ Errno::ECONNREFUSED,
11
+ Errno::EHOSTUNREACH,
12
+ EOFError,
13
+ Net::HTTPBadResponse,
14
+ Net::HTTPHeaderSyntaxError,
15
+ Net::ProtocolError
16
+ ] unless defined?(HTTP_ERRORS)
17
+
18
+
19
+ class Response
20
+ attr_accessor :code
21
+ attr_accessor :message
22
+ attr_accessor :body
23
+ attr_accessor :success
24
+ attr_accessor :raw_response
25
+ attr_accessor :connection_error
26
+ alias :connection_error? :connection_error
27
+
28
+ def initialize(http_response)
29
+ if http_response
30
+ self.raw_response = http_response
31
+ self.code = http_response.code
32
+ self.message = http_response.message
33
+ self.body = http_response.body
34
+ self.success = ((http_response.code =~ /^2\d{2}/) == 0)
35
+ self.connection_error = false
36
+ end
37
+ end
38
+
39
+ alias :success? :success
40
+ alias :status_code :code
41
+ end
42
+
43
+ attr_reader :uri
44
+
45
+ def initialize(caller, uri, options = {})
46
+ @uri = [uri].flatten.compact.collect { |u| URI.parse(u.to_s).normalize }
47
+ @headers = options[:headers] || {}
48
+ @basic_auth = options[:basic_auth]
49
+ @content_type = options[:content_type]
50
+ @caller = caller
51
+ @before_request = options[:before_request]
52
+ @on_success = options[:on_success]
53
+ @on_error = options[:on_error]
54
+ @on_complete = options[:on_complete]
55
+ end
56
+
57
+ def post(body, options = {})
58
+ request(:post, uri.dup, body, options)
59
+ end
60
+
61
+ def get(options = {})
62
+ request(:get, uri.dup, nil, options)
63
+ end
64
+
65
+ def put(body, options = {})
66
+ request(:put, uri.dup, body, options)
67
+ end
68
+
69
+ private
70
+
71
+
72
+ def log(level, string)
73
+ if VaultedBilling.logger?
74
+ VaultedBilling.logger.send(level) { string }
75
+ end
76
+ end
77
+
78
+ def request(method, uris, body = nil, options = {})
79
+ uri = uris.shift || raise(ArgumentError, "URI is empty")
80
+
81
+ request = case method
82
+ when :get
83
+ Net::HTTP::Get
84
+ when :put
85
+ Net::HTTP::Put
86
+ when :post
87
+ Net::HTTP::Post
88
+ else
89
+ raise ArugmentError
90
+ end.new(uri.path)
91
+
92
+ request.initialize_http_header(@headers.merge(options[:headers] || {}).reverse_merge({
93
+ 'User-Agent' => user_agent_string
94
+ }))
95
+
96
+ request.body = body if body
97
+ set_basic_auth request, options[:basic_auth] || @basic_auth
98
+ set_content_type request, options[:content_type] || @content_type
99
+
100
+ response = Net::HTTP.new(uri.host, uri.port).tap do |https|
101
+ https.read_timeout = options[:read_timeout] || 60
102
+ https.open_timeout = options[:open_timeout] || 60
103
+ https.use_ssl = true
104
+ https.ca_file = VaultedBilling.config.ca_file
105
+ https.verify_mode = OpenSSL::SSL::VERIFY_PEER
106
+ end
107
+
108
+ run_callback(:before_request, options[:before_request] || @before_request, request)
109
+ http_response = run_request(request, response, options)
110
+
111
+ if http_response.connection_error && uris.present?
112
+ request(method, uris, body, options)
113
+ else
114
+ run_callback(:on_complete, options[:on_complete] || @on_complete, http_response)
115
+ http_response
116
+ end
117
+ end
118
+
119
+ def run_callback(type, callback, *payload)
120
+ case callback
121
+ when Proc
122
+ callback.call(*payload)
123
+ when String, Symbol
124
+ @caller.send(callback, *payload)
125
+ end
126
+ end
127
+
128
+ def run_request(request, response, options)
129
+ log :debug, "%s %s to %s" % [request.class.name.split('::').last, request.body.inspect, uri.to_s]
130
+
131
+ http_response = Response.new(response.request(request))
132
+ log :info, "Response code %s (HTTP %s), %s" % [http_response.message, http_response.code.presence || '0', http_response.body.inspect]
133
+ run_callback(:on_success, options[:on_success] || @on_success, http_response)
134
+ http_response
135
+ rescue *HTTP_ERRORS
136
+ log :info, "HTTP Error: %s - %s" % [$!.class.name, $!.message]
137
+ Response.new(nil).tap do |request_response|
138
+ request_response.success = false
139
+ request_response.message = "%s - %s" % [$!.class.name, $!.message]
140
+ request_response.connection_error = true
141
+ run_callback(:on_error, options[:on_error] || @on_error, request_response, $!)
142
+ end
143
+ end
144
+
145
+ def set_content_type(request, content)
146
+ request.set_content_type(content) if content
147
+ end
148
+
149
+ def set_basic_auth(request, auth)
150
+ request.basic_auth(auth.first, auth.last) if auth
151
+ end
152
+
153
+ def user_agent_string
154
+ "vaulted_billing/%s (Rubygems; Ruby %s %s)" % [VaultedBilling::Version, RUBY_VERSION, RUBY_PLATFORM]
155
+ end
156
+ end
157
+ end
@@ -1,3 +1,3 @@
1
1
  module VaultedBilling
2
- Version = '1.0.2'
2
+ Version = '1.1.0'
3
3
  end