killbill-paypal-express 4.1.3 → 4.1.4

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6d005c1bdd9b05742e9898d5fef94c1f72c0eb81
4
- data.tar.gz: 683c9b2837efe00ff16d65f62c964b9e46020540
3
+ metadata.gz: 9fb4b33361ba8a20052ea37ef62c5108a0ee7395
4
+ data.tar.gz: 2b398f445e49529f890d60365c7389a6c943b81a
5
5
  SHA512:
6
- metadata.gz: b8c9c42cb7e253e38338107f86efdd8dbc27d762c2590dd7bc5f9718de8e9b1acf3b82c186930d1dd8f85fe1698aec7d7ede4ced070a3a0ea2d37730399f35fb
7
- data.tar.gz: 03da6bfa63eaaa3631f48a40c07c99fff32ce0e58ff97a28b67a2bf2166bee83ca0546ab599e1cc91a0e6b8c0ef706631e5c35c5ae2e877b15dce200f8baf447
6
+ metadata.gz: c1ec6393cfb317f04f0ecde7e1bcbd0df40a2274345aafea0b67a2a9aafcd045658838e1debc7e09f4cd817dd3d9944892ea03a737aafe00c2aea3e28e9d3ca9
7
+ data.tar.gz: 83d24ce07bf944636c4b991fe4e8bc59632907c3c82a6d58c2bc8a50fea771fe65412cfffccab77bf119359edb0dbc741fb54265c876516f613973fff374f989
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- killbill-paypal-express (4.1.3)
4
+ killbill-paypal-express (4.1.4)
5
5
  actionpack (~> 4.1.0)
6
6
  actionview (~> 4.1.0)
7
7
  activemerchant (~> 1.53.0)
@@ -83,7 +83,7 @@ GEM
83
83
  jdbc-sqlite3 (3.8.11.2)
84
84
  jruby-openssl (0.9.16-java)
85
85
  json (1.8.3-java)
86
- killbill (7.0.3)
86
+ killbill (7.0.5)
87
87
  rack (>= 1.5.2)
88
88
  sinatra (~> 1.3.4)
89
89
  typhoeus (~> 0.6.9)
data/NEWS CHANGED
@@ -1,3 +1,7 @@
1
+ 4.1.4
2
+ Cancel expired payments
3
+ Always store the PayPal Payer Id
4
+
1
5
  4.1.3
2
6
  Add support for auth-only HPP flow
3
7
  Add support for multiple merchant accounts
data/VERSION CHANGED
@@ -1 +1 @@
1
- 4.1.3
1
+ 4.1.4
@@ -2,6 +2,8 @@ module Killbill #:nodoc:
2
2
  module PaypalExpress #:nodoc:
3
3
  class PaymentPlugin < ::Killbill::Plugin::ActiveMerchant::PaymentPlugin
4
4
 
5
+ THREE_HOURS_AGO = (3*3600)
6
+
5
7
  def initialize
6
8
  gateway_builder = Proc.new do |config|
7
9
  ::ActiveMerchant::Billing::PaypalExpressGateway.application_id = config[:button_source] || 'killbill_SP'
@@ -83,15 +85,29 @@ module Killbill #:nodoc:
83
85
 
84
86
  def get_payment_info(kb_account_id, kb_payment_id, properties, context)
85
87
  t_info_plugins = super(kb_account_id, kb_payment_id, properties, context)
88
+ # Should never happen...
89
+ return [] if t_info_plugins.nil?
86
90
 
87
91
  # Completed purchases/authorizations will have two rows in the responses table (one for api_call 'build_form_descriptor', one for api_call 'purchase/authorize')
88
92
  # Other transaction types don't support the :PENDING state
89
93
  target_transaction_types = [:PURCHASE, :AUTHORIZE]
90
- is_transaction_pending = t_info_plugins.find { |t_info_plugin| target_transaction_types.include?(t_info_plugin.transaction_type) && t_info_plugin.status != :PENDING }.nil?
94
+ only_pending_transaction = t_info_plugins.find { |t_info_plugin| target_transaction_types.include?(t_info_plugin.transaction_type) && t_info_plugin.status != :PENDING }.nil?
91
95
  t_info_plugins_without_pending = t_info_plugins.reject { |t_info_plugin| target_transaction_types.include?(t_info_plugin.transaction_type) && t_info_plugin.status == :PENDING }
92
96
 
93
- # If only pending transactions exist, return the pending one. Otherwise, only return those non-pending transaction.
94
- is_transaction_pending ? t_info_plugins: t_info_plugins_without_pending
97
+ # If its token has expired, cancel the payment and update the response row.
98
+ if only_pending_transaction
99
+ return t_info_plugins unless token_expired(t_info_plugins.last)
100
+ begin
101
+ cancel_pending_transaction(t_info_plugins.last).nil?
102
+ logger.info("Cancel pending kb_payment_id='#{t_info_plugins.last.kb_payment_id}', kb_payment_transaction_id='#{t_info_plugins.last.kb_transaction_payment_id}'")
103
+ super(kb_account_id, kb_payment_id, properties, context)
104
+ rescue => e
105
+ logger.warn("Unexpected exception while canceling pending kb_payment_id='#{t_info_plugins.last.kb_payment_id}', kb_payment_transaction_id='#{t_info_plugins.last.kb_transaction_payment_id}': #{e.message}\n#{e.backtrace.join("\n")}")
106
+ t_info_plugins
107
+ end
108
+ else
109
+ t_info_plugins_without_pending
110
+ end
95
111
  end
96
112
 
97
113
  def search_payments(search_key, offset, limit, properties, context)
@@ -188,9 +204,11 @@ module Killbill #:nodoc:
188
204
  create_pending_payment = ::Killbill::Plugin::ActiveMerchant::Utils.normalized(options, :create_pending_payment)
189
205
  if create_pending_payment
190
206
  payment_processor_account_id = ::Killbill::Plugin::ActiveMerchant::Utils.normalized(options, :payment_processor_account_id)
207
+ token_expiration_period = ::Killbill::Plugin::ActiveMerchant::Utils.normalized(options, :token_expiration_period)
191
208
  custom_props = hash_to_properties(:from_hpp => true,
192
209
  :token => response.token,
193
- :payment_processor_account_id => payment_processor_account_id)
210
+ :payment_processor_account_id => payment_processor_account_id,
211
+ :token_expiration_period => token_expiration_period)
194
212
  payment_external_key = ::Killbill::Plugin::ActiveMerchant::Utils.normalized(options, :payment_external_key)
195
213
  transaction_external_key = ::Killbill::Plugin::ActiveMerchant::Utils.normalized(options, :transaction_external_key)
196
214
 
@@ -321,14 +339,16 @@ module Killbill #:nodoc:
321
339
  end
322
340
 
323
341
  def authorize_or_purchase_payment(kb_account_id, kb_payment_id, kb_payment_transaction_id, kb_payment_method_id, amount, currency, properties, context, is_authorize = false)
324
- payment_processor_account_id = find_value_from_properties(properties, 'payment_processor_account_id')
342
+ properties_hash = properties_to_hash properties
343
+ payment_processor_account_id = ::Killbill::Plugin::ActiveMerchant::Utils.normalized(properties_hash, :payment_processor_account_id)
325
344
  transaction_type = is_authorize ? :AUTHORIZE : :PURCHASE
326
345
  api_call_type = is_authorize ? :authorize : :purchase
327
346
 
328
347
  # Callback from the plugin itself (HPP flow)
329
- if find_value_from_properties(properties, 'from_hpp') == 'true'
330
- token = find_value_from_properties(properties, 'token')
331
-
348
+ if ::Killbill::Plugin::ActiveMerchant::Utils.normalized(properties_hash, :from_hpp)
349
+ token = ::Killbill::Plugin::ActiveMerchant::Utils.normalized(properties_hash, :token)
350
+ message = {:payment_plugin_status => :PENDING,
351
+ :token_expiration_period => ::Killbill::Plugin::ActiveMerchant::Utils.normalized(properties_hash, :token_expiration_period) || THREE_HOURS_AGO.to_s}
332
352
  response = @response_model.create(:api_call => :build_form_descriptor,
333
353
  :kb_account_id => kb_account_id,
334
354
  :kb_payment_id => kb_payment_id,
@@ -340,7 +360,7 @@ module Killbill #:nodoc:
340
360
  :success => true,
341
361
  :created_at => Time.now.utc,
342
362
  :updated_at => Time.now.utc,
343
- :message => { :payment_plugin_status => :PENDING }.to_json)
363
+ :message => message.to_json)
344
364
  transaction = response.to_transaction_info_plugin(nil)
345
365
  transaction.amount = amount
346
366
  transaction.currency = currency
@@ -364,7 +384,7 @@ module Killbill #:nodoc:
364
384
  end
365
385
  else
366
386
  # One-off payment
367
- options[:token] = find_value_from_properties(properties, 'token') || find_last_token(kb_account_id, context.tenant_id)
387
+ options[:token] = ::Killbill::Plugin::ActiveMerchant::Utils.normalized(properties_hash, :token) || find_last_token(kb_account_id, context.tenant_id)
368
388
  if is_authorize
369
389
  gateway_call_proc = Proc.new do |gateway, linked_transaction, payment_source, amount_in_cents, options|
370
390
  gateway.authorize(amount_in_cents, options)
@@ -381,7 +401,7 @@ module Killbill #:nodoc:
381
401
  options[:payment_processor_account_id] = payment_processor_account_id
382
402
 
383
403
  # Populate the Payer id if missing
384
- options[:payer_id] = find_value_from_properties(properties, 'payer_id')
404
+ options[:payer_id] = ::Killbill::Plugin::ActiveMerchant::Utils.normalized(properties_hash, :payer_id)
385
405
  begin
386
406
  options[:payer_id] ||= find_payer_id(options[:token],
387
407
  kb_account_id,
@@ -405,13 +425,30 @@ module Killbill #:nodoc:
405
425
  end
406
426
 
407
427
  properties = merge_properties(properties, options)
408
- dispatch_to_gateways(api_call_type, kb_account_id, kb_payment_id, kb_payment_transaction_id, kb_payment_method_id, amount, currency, properties, context, gateway_call_proc)
428
+ dispatch_to_gateways(api_call_type, kb_account_id, kb_payment_id, kb_payment_transaction_id, kb_payment_method_id, amount, currency, properties, context, gateway_call_proc, nil, {:payer_id => options[:payer_id]})
409
429
  end
410
430
  end
411
431
 
412
432
  def find_payment_processor_id_from_initial_call(kb_account_id, kb_tenant_id, token)
413
433
  @response_model.initial_payment_account_processor_id kb_account_id, kb_tenant_id, token
414
434
  end
435
+
436
+ def token_expired(transaction_plugin_info)
437
+ paypal_response_id = find_value_from_properties(transaction_plugin_info.properties, 'paypalExpressResponseId')
438
+ response = PaypalExpressResponse.find_by(:id => paypal_response_id)
439
+ begin
440
+ message_details = JSON.parse response.message
441
+ expiration_period = (message_details['token_expiration_period'] || THREE_HOURS_AGO).to_i
442
+ rescue
443
+ expiration_period = THREE_HOURS_AGO.to_i
444
+ end
445
+ now = Time.parse(@clock.get_clock.get_utc_now.to_s)
446
+ (now - transaction_plugin_info.created_date) >= expiration_period
447
+ end
448
+
449
+ def cancel_pending_transaction(transaction_plugin_info)
450
+ @response_model.cancel_pending_payment transaction_plugin_info
451
+ end
415
452
  end
416
453
  end
417
454
  end
@@ -77,6 +77,14 @@ module Killbill #:nodoc:
77
77
  response.nil? ? nil : response.payment_processor_account_id
78
78
  end
79
79
 
80
+ def self.cancel_pending_payment(transaction_plugin_info)
81
+ where( :api_call => 'build_form_descriptor',
82
+ :kb_payment_id => transaction_plugin_info.kb_payment_id,
83
+ :kb_payment_transaction_id => transaction_plugin_info.kb_transaction_payment_id).update_all( :success => false,
84
+ :updated_at => Time.now.utc,
85
+ :message => { :payment_plugin_status => :CANCELED, :exception_message => 'Token expired. Payment Canceled by Janitor.' }.to_json)
86
+ end
87
+
80
88
  def to_transaction_info_plugin(transaction=nil)
81
89
  t_info_plugin = super(transaction)
82
90
 
@@ -119,6 +127,7 @@ module Killbill #:nodoc:
119
127
  t_info_plugin.properties << create_plugin_property('paymentInfoShipDiscount', payment_info_shipdiscount)
120
128
  t_info_plugin.properties << create_plugin_property('paymentInfoInsuranceAmount', payment_info_insuranceamount)
121
129
  t_info_plugin.properties << create_plugin_property('paymentInfoSubject', payment_info_subject)
130
+ t_info_plugin.properties << create_plugin_property('paypalExpressResponseId', id)
122
131
 
123
132
  t_info_plugin
124
133
  end
data/pom.xml CHANGED
@@ -26,7 +26,7 @@
26
26
  <groupId>org.kill-bill.billing.plugin.ruby</groupId>
27
27
  <artifactId>paypal-express-plugin</artifactId>
28
28
  <packaging>pom</packaging>
29
- <version>4.1.3</version>
29
+ <version>4.1.4</version>
30
30
  <name>paypal-express-plugin</name>
31
31
  <url>http://github.com/killbill/killbill-paypal-express-plugin</url>
32
32
  <description>Plugin for accessing Paypal Express Checkout as a payment gateway</description>
@@ -18,6 +18,8 @@ shared_examples 'baid_spec_common' do
18
18
  payment_response.status.should eq(:PROCESSED), payment_response.gateway_error
19
19
  payment_response.amount.should == @amount
20
20
  payment_response.transaction_type.should == :PURCHASE
21
+ payer_id = find_value_from_properties(payment_response.properties, 'payerId')
22
+ payer_id.should_not be_nil
21
23
 
22
24
  # Verify GET API
23
25
  payment_infos = @plugin.get_payment_info(@pm.kb_account_id, @kb_payment.id, [], @call_context)
@@ -30,6 +32,7 @@ shared_examples 'baid_spec_common' do
30
32
  payment_infos[0].gateway_error.should == 'Success'
31
33
  payment_infos[0].gateway_error_code.should be_nil
32
34
  find_value_from_properties(payment_infos[0].properties, 'payment_processor_account_id').should == @payment_processor_account_id
35
+ find_value_from_properties(payment_infos[0].properties, 'payerId').should == payer_id
33
36
 
34
37
  # Try a full refund
35
38
  refund_response = @plugin.refund_payment(@pm.kb_account_id, @kb_payment.id, @kb_payment.transactions[1].id, @pm.kb_payment_method_id, @amount, @currency, @properties, @call_context)
@@ -63,6 +66,8 @@ shared_examples 'baid_spec_common' do
63
66
  payment_response.status.should eq(:PROCESSED), payment_response.gateway_error
64
67
  payment_response.amount.should == @amount
65
68
  payment_response.transaction_type.should == :AUTHORIZE
69
+ payer_id = find_value_from_properties(payment_response.properties, 'payerId')
70
+ payer_id.should_not be_nil
66
71
 
67
72
  # Verify GET API
68
73
  payment_infos = @plugin.get_payment_info(@pm.kb_account_id, @kb_payment.id, [], @call_context)
@@ -75,6 +80,7 @@ shared_examples 'baid_spec_common' do
75
80
  payment_infos[0].gateway_error.should == 'Success'
76
81
  payment_infos[0].gateway_error_code.should be_nil
77
82
  find_value_from_properties(payment_infos[0].properties, 'payment_processor_account_id').should == @payment_processor_account_id
83
+ find_value_from_properties(payment_infos[0].properties, 'payerId').should == payer_id
78
84
 
79
85
  # Try multiple partial captures
80
86
  partial_capture_amount = BigDecimal.new('10')
@@ -91,7 +91,8 @@ shared_examples 'hpp_spec_common' do
91
91
  payment_infos[0].amount.should be_nil
92
92
  payment_infos[0].currency.should be_nil
93
93
  payment_infos[0].status.should == :PENDING
94
- payment_infos[0].gateway_error.should == '{"payment_plugin_status":"PENDING"}'
94
+ payment_infos[0].gateway_error.should == {:payment_plugin_status => 'PENDING',
95
+ :token_expiration_period => @plugin.class.const_get(:THREE_HOURS_AGO).to_s}.to_json
95
96
  payment_infos[0].gateway_error_code.should be_nil
96
97
 
97
98
  properties = []
@@ -155,7 +156,8 @@ shared_examples 'hpp_spec_common' do
155
156
  payment_infos[0].amount.should be_nil
156
157
  payment_infos[0].currency.should be_nil
157
158
  payment_infos[0].status.should == :PENDING
158
- payment_infos[0].gateway_error.should == '{"payment_plugin_status":"PENDING"}'
159
+ payment_infos[0].gateway_error.should == {:payment_plugin_status => 'PENDING',
160
+ :token_expiration_period => @plugin.class.const_get(:THREE_HOURS_AGO).to_s}.to_json
159
161
  payment_infos[0].gateway_error_code.should be_nil
160
162
  find_value_from_properties(payment_infos[0].properties, :payment_processor_account_id).should == @payment_processor_account_id
161
163
  else
@@ -293,6 +295,80 @@ shared_examples 'hpp_spec_common' do
293
295
  token = validate_form_property(form, 'token')
294
296
  @plugin.send(:find_payment_processor_id_from_initial_call, @pm.kb_account_id, @call_context.tenant_id, token).should == @payment_processor_account_id
295
297
  end
298
+
299
+ it 'should cancel the pending payment if the token expires' do
300
+ ::Killbill::PaypalExpress::PaypalExpressTransaction.count.should == 0
301
+ ::Killbill::PaypalExpress::PaypalExpressResponse.count.should == 0
302
+
303
+ expiration_period = 5
304
+ payment_external_key = SecureRandom.uuid
305
+ properties = @plugin.hash_to_properties(
306
+ :transaction_external_key => payment_external_key,
307
+ :create_pending_payment => true,
308
+ :token_expiration_period => expiration_period
309
+ )
310
+
311
+ form = @plugin.build_form_descriptor(@pm.kb_account_id, @form_fields, properties, @call_context)
312
+ kb_payment_id = validate_form_property(form, 'kb_payment_id')
313
+ payment_infos = @plugin.get_payment_info(@pm.kb_account_id, kb_payment_id, properties, @call_context)
314
+ payment_infos.size.should == 1
315
+ payment_infos[0].kb_payment_id.should == kb_payment_id
316
+ payment_infos[0].amount.should be_nil
317
+ payment_infos[0].currency.should be_nil
318
+ payment_infos[0].status.should == :PENDING
319
+ payment_infos[0].gateway_error.should == {:payment_plugin_status => 'PENDING',
320
+ :token_expiration_period => expiration_period.to_s}.to_json
321
+ payment_infos[0].gateway_error_code.should be_nil
322
+
323
+ sleep payment_infos[0].created_date + expiration_period - Time.parse(@plugin.clock.get_clock.get_utc_now.to_s) + 1
324
+
325
+ payment_infos = @plugin.get_payment_info(@pm.kb_account_id, kb_payment_id, properties, @call_context)
326
+ # Make sure no extra response is created
327
+ payment_infos.size.should == 1
328
+ payment_infos[0].kb_payment_id.should == kb_payment_id
329
+ payment_infos[0].amount.should be_nil
330
+ payment_infos[0].currency.should be_nil
331
+ payment_infos[0].status.should == :CANCELED
332
+ payment_infos[0].gateway_error.should == 'Token expired. Payment Canceled by Janitor.'
333
+ payment_infos[0].gateway_error_code.should be_nil
334
+ end
335
+
336
+ it 'should cancel the pending payment if the token expires without passing property' do
337
+ ::Killbill::PaypalExpress::PaypalExpressTransaction.count.should == 0
338
+ ::Killbill::PaypalExpress::PaypalExpressResponse.count.should == 0
339
+
340
+ expiration_period = 5
341
+ @plugin.class.const_set(:THREE_HOURS_AGO, expiration_period)
342
+ payment_external_key = SecureRandom.uuid
343
+ properties = @plugin.hash_to_properties(
344
+ :transaction_external_key => payment_external_key,
345
+ :create_pending_payment => true
346
+ )
347
+
348
+ form = @plugin.build_form_descriptor(@pm.kb_account_id, @form_fields, properties, @call_context)
349
+ kb_payment_id = validate_form_property(form, 'kb_payment_id')
350
+ payment_infos = @plugin.get_payment_info(@pm.kb_account_id, kb_payment_id, properties, @call_context)
351
+ payment_infos.size.should == 1
352
+ payment_infos[0].kb_payment_id.should == kb_payment_id
353
+ payment_infos[0].amount.should be_nil
354
+ payment_infos[0].currency.should be_nil
355
+ payment_infos[0].status.should == :PENDING
356
+ payment_infos[0].gateway_error.should == {:payment_plugin_status => 'PENDING',
357
+ :token_expiration_period => expiration_period.to_s}.to_json
358
+ payment_infos[0].gateway_error_code.should be_nil
359
+
360
+ sleep payment_infos[0].created_date + expiration_period - Time.parse(@plugin.clock.get_clock.get_utc_now.to_s) + 1
361
+
362
+ payment_infos = @plugin.get_payment_info(@pm.kb_account_id, kb_payment_id, properties, @call_context)
363
+ # Make sure no extra response is created
364
+ payment_infos.size.should == 1
365
+ payment_infos[0].kb_payment_id.should == kb_payment_id
366
+ payment_infos[0].amount.should be_nil
367
+ payment_infos[0].currency.should be_nil
368
+ payment_infos[0].status.should == :CANCELED
369
+ payment_infos[0].gateway_error.should == 'Token expired. Payment Canceled by Janitor.'
370
+ payment_infos[0].gateway_error_code.should be_nil
371
+ end
296
372
  end
297
373
 
298
374
  describe Killbill::PaypalExpress::PaymentPlugin do
@@ -50,6 +50,8 @@ module Killbill
50
50
  payment_response.status.should eq(:PROCESSED), payment_response.gateway_error
51
51
  payment_response.amount.should == @amount
52
52
  payment_response.transaction_type.should == :PURCHASE
53
+ payer_id = find_value_from_properties(payment_response.properties, 'payerId')
54
+ payer_id.should_not be_nil
53
55
 
54
56
  # Verify GET API
55
57
  payment_infos = @plugin.get_payment_info(@pm.kb_account_id, kb_payment_id, [], @call_context)
@@ -61,6 +63,7 @@ module Killbill
61
63
  payment_infos[0].status.should == :PROCESSED
62
64
  payment_infos[0].gateway_error.should == 'Success'
63
65
  payment_infos[0].gateway_error_code.should be_nil
66
+ find_value_from_properties(payment_infos[0].properties, 'payerId').should == payer_id
64
67
 
65
68
  # Try a full refund
66
69
  refund_response = @plugin.refund_payment(@pm.kb_account_id, kb_payment_id, SecureRandom.uuid, @pm.kb_payment_method_id, @amount, @currency, [], @call_context)
@@ -93,6 +96,8 @@ module Killbill
93
96
  payment_response.status.should eq(:PROCESSED), payment_response.gateway_error
94
97
  payment_response.amount.should == @amount
95
98
  payment_response.transaction_type.should == :AUTHORIZE
99
+ payer_id = find_value_from_properties(payment_response.properties, 'payerId')
100
+ payer_id.should_not be_nil
96
101
 
97
102
  # Verify GET AUTHORIZED PAYMENT
98
103
  payment_infos = @plugin.get_payment_info(@pm.kb_account_id, kb_payment_id, properties, @call_context)
@@ -104,6 +109,7 @@ module Killbill
104
109
  payment_infos[0].status.should == :PROCESSED
105
110
  payment_infos[0].gateway_error.should == 'Success'
106
111
  payment_infos[0].gateway_error_code.should be_nil
112
+ find_value_from_properties(payment_infos[0].properties, 'payerId').should == payer_id
107
113
  find_value_from_properties(payment_infos[0].properties, 'paymentInfoPaymentStatus').should == 'Pending'
108
114
  find_value_from_properties(payment_infos[0].properties, 'paymentInfoPendingReason').should == 'authorization'
109
115
  find_value_from_properties(payment_infos[0].properties, 'payment_processor_account_id').should == payment_processor_account_id
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: killbill-paypal-express
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.1.3
4
+ version: 4.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kill Bill core team
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-05-16 00:00:00.000000000 Z
11
+ date: 2016-05-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: killbill