killbill-cybersource 4.0.6 → 4.0.7
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 +4 -4
- data/.travis.yml +1 -1
- data/Gemfile.lock +2 -2
- data/NEWS +7 -0
- data/Rakefile +9 -0
- data/VERSION +1 -1
- data/lib/cybersource/api.rb +47 -17
- data/lib/cybersource/cyber_source_on_demand.rb +2 -0
- data/lib/cybersource/models/response.rb +22 -1
- data/pom.xml +1 -1
- data/spec/cybersource/base_plugin_spec.rb +27 -1
- data/spec/cybersource/remote/integration_spec.rb +66 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7471ac43901ed2efb3ebeaa6b6ed62a28a38286f
|
4
|
+
data.tar.gz: 4a697cfdb6b8fad42f07c13df1a8ae1d4ce1f808
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 05e6ea3dba262e59947a316d7b26e4fa31388762ae3f79e308c11798cdeff366f64439b05c7241288918d4e807d9719ff95619dae073d81c28784b4221dab5d0
|
7
|
+
data.tar.gz: 61e4584909179d697d4ba137f4928b3a5a1d1af3dc0eff91289832c8f058d3a2390f6b00d01236b64c883f450c86e7d9231d4b0b74f9d6ef88ec20d80f3908c3
|
data/.travis.yml
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
killbill-cybersource (4.0.
|
4
|
+
killbill-cybersource (4.0.7)
|
5
5
|
actionpack (~> 4.1.0)
|
6
6
|
actionview (~> 4.1.0)
|
7
7
|
activemerchant (~> 1.48.0)
|
@@ -80,7 +80,7 @@ GEM
|
|
80
80
|
jdbc-sqlite3 (3.8.11.2)
|
81
81
|
jruby-openssl (0.9.16-java)
|
82
82
|
json (1.8.3-java)
|
83
|
-
killbill (7.0.
|
83
|
+
killbill (7.0.6)
|
84
84
|
rack (>= 1.5.2)
|
85
85
|
sinatra (~> 1.3.4)
|
86
86
|
typhoeus (~> 0.6.9)
|
data/NEWS
CHANGED
data/Rakefile
CHANGED
@@ -20,6 +20,15 @@ namespace :test do
|
|
20
20
|
task.pattern = './spec/*/remote/*_spec.rb'
|
21
21
|
end
|
22
22
|
end
|
23
|
+
|
24
|
+
namespace :ci do
|
25
|
+
desc 'Run RSpec CI tests'
|
26
|
+
RSpec::Core::RakeTask.new do |task|
|
27
|
+
task.name = 'spec'
|
28
|
+
task.pattern = './spec/**/*_spec.rb'
|
29
|
+
task.rspec_opts = '--tag ~ci_skip'
|
30
|
+
end
|
31
|
+
end
|
23
32
|
end
|
24
33
|
|
25
34
|
# Install tasks to package the plugin for Killbill
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
4.0.
|
1
|
+
4.0.7
|
data/lib/cybersource/api.rb
CHANGED
@@ -2,6 +2,7 @@ module Killbill #:nodoc:
|
|
2
2
|
module Cybersource #:nodoc:
|
3
3
|
class PaymentPlugin < ::Killbill::Plugin::ActiveMerchant::PaymentPlugin
|
4
4
|
|
5
|
+
SEVEN_DAYS_AGO = (7 * 86400)
|
5
6
|
SIXTY_DAYS_AGO = (60 * 86400)
|
6
7
|
|
7
8
|
def initialize
|
@@ -121,7 +122,10 @@ module Killbill #:nodoc:
|
|
121
122
|
next unless transaction_info_plugin.status == :UNDEFINED
|
122
123
|
|
123
124
|
cybersource_response_id = find_value_from_properties(transaction_info_plugin.properties, 'cybersourceResponseId')
|
124
|
-
|
125
|
+
if cybersource_response_id.nil?
|
126
|
+
logger.warn("Unable to fix UNDEFINED kb_transaction_id='#{transaction_info_plugin.kb_transaction_payment_id}' (cybersource_response_id not specified)")
|
127
|
+
next
|
128
|
+
end
|
125
129
|
|
126
130
|
report_date = transaction_info_plugin.created_date
|
127
131
|
authorization = find_value_from_properties(transaction_info_plugin.properties, 'authorization')
|
@@ -142,23 +146,34 @@ module Killbill #:nodoc:
|
|
142
146
|
report = get_report(order_id, report_date, options, context)
|
143
147
|
end
|
144
148
|
|
145
|
-
# Report API not configured or skip_gw=true
|
149
|
+
# Report API not configured, connection problem or skip_gw=true
|
146
150
|
next if report.nil?
|
147
151
|
|
148
|
-
|
149
|
-
|
150
|
-
|
152
|
+
threshold = (Killbill::Plugin::ActiveMerchant::Utils.normalized(options, :cancel_threshold) || SEVEN_DAYS_AGO).to_i
|
153
|
+
should_cancel_payment = (now - report_date) >= threshold
|
154
|
+
if report.empty? && !should_cancel_payment
|
155
|
+
# We'll retry later
|
156
|
+
logger.info("Unable to fix UNDEFINED kb_transaction_id='#{transaction_info_plugin.kb_transaction_payment_id}' (not found in CyberSource)")
|
151
157
|
next
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
158
|
+
else
|
159
|
+
# Update our rows
|
160
|
+
response = CybersourceResponse.find_by(:id => cybersource_response_id)
|
161
|
+
if response.nil?
|
162
|
+
logger.warn("Unable to fix UNDEFINED kb_transaction_id='#{transaction_info_plugin.kb_transaction_payment_id}' (CyberSource response='#{cybersource_response_id}' not found)")
|
163
|
+
next
|
164
|
+
end
|
157
165
|
|
158
|
-
|
166
|
+
if should_cancel_payment
|
167
|
+
# At this point, it's safe to assume the payment never happened
|
168
|
+
logger.info("Canceling UNDEFINED kb_transaction_id='#{transaction_info_plugin.kb_transaction_payment_id}'")
|
169
|
+
response.cancel
|
170
|
+
else
|
171
|
+
logger.info("Fixing UNDEFINED kb_transaction_id='#{transaction_info_plugin.kb_transaction_payment_id}', success='#{report.response.success?}'")
|
172
|
+
response.update_and_create_transaction(report.response)
|
173
|
+
end
|
159
174
|
|
160
|
-
|
161
|
-
|
175
|
+
stale = true
|
176
|
+
end
|
162
177
|
end
|
163
178
|
|
164
179
|
# If we updated the state, re-fetch the latest data
|
@@ -258,8 +273,6 @@ module Killbill #:nodoc:
|
|
258
273
|
|
259
274
|
threshold = (Killbill::Plugin::ActiveMerchant::Utils.normalized(options, :auto_credit_threshold) || SIXTY_DAYS_AGO).to_i
|
260
275
|
|
261
|
-
# we might want a 'util' function to make the conversion joda DateTime to a ruby Time object
|
262
|
-
now = Time.parse(@clock.get_clock.get_utc_now.to_s)
|
263
276
|
(now - transaction.created_at) >= threshold
|
264
277
|
end
|
265
278
|
|
@@ -292,8 +305,20 @@ module Killbill #:nodoc:
|
|
292
305
|
get_single_transaction_report(report_api, merchant_reference_code, date)
|
293
306
|
end
|
294
307
|
|
295
|
-
def get_single_transaction_report(report_api, merchant_reference_code, date)
|
296
|
-
|
308
|
+
def get_single_transaction_report(report_api, merchant_reference_code, date, fuzzy_date=true)
|
309
|
+
if fuzzy_date
|
310
|
+
report = get_single_transaction_report(report_api, merchant_reference_code, date, false)
|
311
|
+
report = get_single_transaction_report(report_api, merchant_reference_code, date - 1.day, false) if report.nil? || report.empty?
|
312
|
+
report = get_single_transaction_report(report_api, merchant_reference_code, date + 1.day, false) if report.nil? || report.empty?
|
313
|
+
return report
|
314
|
+
end
|
315
|
+
|
316
|
+
begin
|
317
|
+
report_api.single_transaction_report(merchant_reference_code, date.strftime('%Y%m%d'))
|
318
|
+
rescue => e
|
319
|
+
logger.warn "Error retrieving report for merchant_reference_code='#{merchant_reference_code}', target_date='#{date}': #{e.message}\n#{e.backtrace.join("\n")}"
|
320
|
+
nil
|
321
|
+
end
|
297
322
|
end
|
298
323
|
|
299
324
|
def add_required_options(kb_account_id, properties, options, context)
|
@@ -364,6 +389,11 @@ module Killbill #:nodoc:
|
|
364
389
|
|
365
390
|
new_auth_response
|
366
391
|
end
|
392
|
+
|
393
|
+
def now
|
394
|
+
# We might want a 'util' function to make the conversion Joda DateTime to a Ruby Time object
|
395
|
+
Time.parse(@clock.get_clock.get_utc_now.to_s)
|
396
|
+
end
|
367
397
|
end
|
368
398
|
end
|
369
399
|
end
|
@@ -31,6 +31,8 @@ module Killbill #:nodoc:
|
|
31
31
|
|
32
32
|
data = URI.encode_www_form(params)
|
33
33
|
|
34
|
+
@logger.info "Retrieving report for merchant_reference_code='#{merchant_reference_code}', target_date='#{target_date}', merchant_id='#{@config[:merchantID]}'"
|
35
|
+
|
34
36
|
# Will raise ResponseError if the response code is > 300
|
35
37
|
CyberSourceOnDemandTransactionReport.new(ssl_post(endpoint, data, headers), @logger)
|
36
38
|
end
|
@@ -42,6 +42,27 @@ module Killbill #:nodoc:
|
|
42
42
|
}
|
43
43
|
end
|
44
44
|
|
45
|
+
def cancel
|
46
|
+
begin
|
47
|
+
error_details = JSON.parse(message)
|
48
|
+
original_message = nil
|
49
|
+
rescue
|
50
|
+
error_details = {}
|
51
|
+
original_message = message
|
52
|
+
end
|
53
|
+
error_details['original_message'] = original_message unless original_message.blank?
|
54
|
+
error_details['payment_plugin_status'] = 'CANCELED'
|
55
|
+
|
56
|
+
updated_attributes = {
|
57
|
+
:message => error_details.to_json,
|
58
|
+
:success => false,
|
59
|
+
:updated_at => Time.now.utc
|
60
|
+
}
|
61
|
+
|
62
|
+
# Update the response row
|
63
|
+
update!(updated_attributes)
|
64
|
+
end
|
65
|
+
|
45
66
|
def update_and_create_transaction(gw_response)
|
46
67
|
updated_attributes = {
|
47
68
|
:message => gw_response.message,
|
@@ -108,7 +129,7 @@ module Killbill #:nodoc:
|
|
108
129
|
|
109
130
|
def set_correct_status(t_info_plugin)
|
110
131
|
# Respect the existing status if the payment was successful, if overridden or if there is no error code
|
111
|
-
return if success || (message && message.strip.start_with?('{'))
|
132
|
+
return if success || (message && message.strip.start_with?('{')) || gateway_error_code.blank?
|
112
133
|
|
113
134
|
if CANCELED_ERROR_CODES.include?(gateway_error_code.to_i)
|
114
135
|
t_info_plugin.status = :CANCELED
|
data/pom.xml
CHANGED
@@ -25,7 +25,7 @@
|
|
25
25
|
<groupId>org.kill-bill.billing.plugin.ruby</groupId>
|
26
26
|
<artifactId>cybersource-plugin</artifactId>
|
27
27
|
<packaging>pom</packaging>
|
28
|
-
<version>4.0.
|
28
|
+
<version>4.0.7</version>
|
29
29
|
<name>cybersource-plugin</name>
|
30
30
|
<url>http://github.com/killbill/killbill-cybersource-plugin</url>
|
31
31
|
<description>Plugin for accessing Cybersource as a payment gateway</description>
|
@@ -61,7 +61,7 @@ describe Killbill::Cybersource::PaymentPlugin do
|
|
61
61
|
|
62
62
|
with_transaction(kb_payment_id, :AUTHORIZE, 61.days.ago, context) do
|
63
63
|
@plugin.should_credit?(kb_payment_id, context, {:disable_auto_credit => true}).should be_false
|
64
|
-
@plugin.should_credit?(kb_payment_id, context, {:auto_credit_threshold => 61 * 86400}).should be_false
|
64
|
+
@plugin.should_credit?(kb_payment_id, context, {:auto_credit_threshold => 61 * 86400 + 10}).should be_false
|
65
65
|
@plugin.should_credit?(kb_payment_id, context).should be_true
|
66
66
|
end
|
67
67
|
end
|
@@ -168,6 +168,32 @@ describe Killbill::Cybersource::PaymentPlugin do
|
|
168
168
|
payment_response.gateway_error.should == 'One or more fields contains invalid data'
|
169
169
|
payment_response.gateway_error_code.should == '102'
|
170
170
|
end
|
171
|
+
|
172
|
+
it 'cancels UNDEFINED transactions with a JSON message' do
|
173
|
+
response = Killbill::Cybersource::CybersourceResponse.create(:api_call => 'authorization',
|
174
|
+
:message => '{"exception_message":"Timeout","payment_plugin_status":"UNDEFINED"}',
|
175
|
+
:created_at => Time.now,
|
176
|
+
:updated_at => Time.now)
|
177
|
+
response.cancel
|
178
|
+
response.message.should == '{"exception_message":"Timeout","payment_plugin_status":"CANCELED"}'
|
179
|
+
end
|
180
|
+
|
181
|
+
it 'cancels UNDEFINED transactions with a plain test message' do
|
182
|
+
response = Killbill::Cybersource::CybersourceResponse.create(:api_call => 'authorization',
|
183
|
+
:message => 'Internal error',
|
184
|
+
:created_at => Time.now,
|
185
|
+
:updated_at => Time.now)
|
186
|
+
response.cancel
|
187
|
+
response.message.should == '{"original_message":"Internal error","payment_plugin_status":"CANCELED"}'
|
188
|
+
end
|
189
|
+
|
190
|
+
it 'cancels UNDEFINED transactions with no message' do
|
191
|
+
response = Killbill::Cybersource::CybersourceResponse.create(:api_call => 'authorization',
|
192
|
+
:created_at => Time.now,
|
193
|
+
:updated_at => Time.now)
|
194
|
+
response.cancel
|
195
|
+
response.message.should == '{"payment_plugin_status":"CANCELED"}'
|
196
|
+
end
|
171
197
|
end
|
172
198
|
|
173
199
|
private
|
@@ -61,6 +61,9 @@ describe Killbill::Cybersource::PaymentPlugin do
|
|
61
61
|
# Skip the rest of the test if the report API isn't configured to check for duplicates
|
62
62
|
break unless with_report_api && report_api.check_for_duplicates?
|
63
63
|
|
64
|
+
# The report API can be delayed
|
65
|
+
await { !@plugin.get_single_transaction_report(report_api, @kb_payment.transactions[0].id, Time.now.utc).empty? }
|
66
|
+
|
64
67
|
payment_response = @plugin.purchase_payment(@pm.kb_account_id, @kb_payment.id, @kb_payment.transactions[0].id, @pm.kb_payment_method_id, @amount, @currency, @properties, @call_context)
|
65
68
|
payment_response.amount.should == @amount
|
66
69
|
payment_response.status.should == :PROCESSED
|
@@ -203,6 +206,9 @@ describe Killbill::Cybersource::PaymentPlugin do
|
|
203
206
|
|
204
207
|
# Skip if the report API isn't configured
|
205
208
|
if with_report_api
|
209
|
+
# The report API can be delayed
|
210
|
+
await { !@plugin.get_single_transaction_report(report_api, @kb_payment.transactions[0].id, Time.now.utc).empty? }
|
211
|
+
|
206
212
|
# Fix it
|
207
213
|
transaction_info_plugins = @plugin.get_payment_info(@pm.kb_account_id, @kb_payment.id, @properties, @call_context)
|
208
214
|
transaction_info_plugins.size.should == 1
|
@@ -240,6 +246,50 @@ describe Killbill::Cybersource::PaymentPlugin do
|
|
240
246
|
new_response.message.should == (with_report_api ? 'Request was processed successfully.' : '{"payment_plugin_status":"UNDEFINED"}')
|
241
247
|
end
|
242
248
|
|
249
|
+
it 'should eventually cancel UNDEFINED payments' do
|
250
|
+
response = Killbill::Cybersource::CybersourceResponse.create(:api_call => 'authorization',
|
251
|
+
:kb_account_id => @pm.kb_account_id,
|
252
|
+
:kb_payment_id => @kb_payment.id,
|
253
|
+
:kb_payment_transaction_id => @kb_payment.transactions[0].id,
|
254
|
+
:kb_tenant_id => @call_context.tenant_id,
|
255
|
+
:message => '{"exception_message":"Timeout","payment_plugin_status":"UNDEFINED"}',
|
256
|
+
:created_at => Time.now,
|
257
|
+
:updated_at => Time.now)
|
258
|
+
|
259
|
+
# Set skip_gw=true, to avoid calling the report API
|
260
|
+
skip_gw = Killbill::Plugin::Model::PluginProperty.new
|
261
|
+
skip_gw.key = 'skip_gw'
|
262
|
+
skip_gw.value = 'true'
|
263
|
+
properties_with_cancel_threshold = @properties.clone
|
264
|
+
properties_with_cancel_threshold << skip_gw
|
265
|
+
transaction_info_plugins = @plugin.get_payment_info(@pm.kb_account_id, @kb_payment.id, properties_with_cancel_threshold, @call_context)
|
266
|
+
transaction_info_plugins.size.should == 1
|
267
|
+
transaction_info_plugins.first.status.should eq(:UNDEFINED)
|
268
|
+
|
269
|
+
# Call the reporting API (if configured) and verify the state still cannot be fixed
|
270
|
+
transaction_info_plugins = @plugin.get_payment_info(@pm.kb_account_id, @kb_payment.id, @properties, @call_context)
|
271
|
+
transaction_info_plugins.size.should == 1
|
272
|
+
transaction_info_plugins.first.status.should eq(:UNDEFINED)
|
273
|
+
|
274
|
+
# Transition to CANCEL won't work if the reporting API isn't configured
|
275
|
+
break unless with_report_api
|
276
|
+
|
277
|
+
# Force a transition to CANCEL
|
278
|
+
cancel_threshold = Killbill::Plugin::Model::PluginProperty.new
|
279
|
+
cancel_threshold.key = 'cancel_threshold'
|
280
|
+
cancel_threshold.value = '0'
|
281
|
+
properties_with_cancel_threshold = @properties.clone
|
282
|
+
properties_with_cancel_threshold << cancel_threshold
|
283
|
+
transaction_info_plugins = @plugin.get_payment_info(@pm.kb_account_id, @kb_payment.id, properties_with_cancel_threshold, @call_context)
|
284
|
+
transaction_info_plugins.size.should == 1
|
285
|
+
transaction_info_plugins.first.status.should eq(:CANCELED)
|
286
|
+
|
287
|
+
# Verify the state is sticky
|
288
|
+
transaction_info_plugins = @plugin.get_payment_info(@pm.kb_account_id, @kb_payment.id, @properties, @call_context)
|
289
|
+
transaction_info_plugins.size.should == 1
|
290
|
+
transaction_info_plugins.first.status.should eq(:CANCELED)
|
291
|
+
end
|
292
|
+
|
243
293
|
it 'should be able to charge and refund' do
|
244
294
|
payment_response = @plugin.purchase_payment(@pm.kb_account_id, @kb_payment.id, @kb_payment.transactions[0].id, @pm.kb_payment_method_id, @amount, @currency, @properties, @call_context)
|
245
295
|
check_response(payment_response, @amount, :PURCHASE, :PROCESSED, 'Successful transaction', '100')
|
@@ -277,6 +327,14 @@ describe Killbill::Cybersource::PaymentPlugin do
|
|
277
327
|
check_response(payment_response, nil, :VOID, :PROCESSED, 'Successful transaction', '100')
|
278
328
|
end
|
279
329
|
|
330
|
+
it 'should be able to auth and void in CAD', :ci_skip => true do
|
331
|
+
payment_response = @plugin.authorize_payment(@pm.kb_account_id, @kb_payment.id, @kb_payment.transactions[0].id, @pm.kb_payment_method_id, @amount, 'CAD', @properties, @call_context)
|
332
|
+
check_response(payment_response, @amount, :AUTHORIZE, :PROCESSED, 'Successful transaction', '100')
|
333
|
+
|
334
|
+
payment_response = @plugin.void_payment(@pm.kb_account_id, @kb_payment.id, @kb_payment.transactions[1].id, @pm.kb_payment_method_id, @properties, @call_context)
|
335
|
+
check_response(payment_response, nil, :VOID, :PROCESSED, 'Successful transaction', '100')
|
336
|
+
end
|
337
|
+
|
280
338
|
it 'should be able to auth, partial capture and void' do
|
281
339
|
payment_response = @plugin.authorize_payment(@pm.kb_account_id, @kb_payment.id, @kb_payment.transactions[0].id, @pm.kb_payment_method_id, @amount, @currency, @properties, @call_context)
|
282
340
|
check_response(payment_response, @amount, :AUTHORIZE, :PROCESSED, 'Successful transaction', '100')
|
@@ -507,4 +565,12 @@ describe Killbill::Cybersource::PaymentPlugin do
|
|
507
565
|
end
|
508
566
|
kb_payment
|
509
567
|
end
|
568
|
+
|
569
|
+
def await(timeout=15)
|
570
|
+
timeout.times do
|
571
|
+
return if block_given? && yield
|
572
|
+
sleep(1)
|
573
|
+
end
|
574
|
+
fail('Timeout')
|
575
|
+
end
|
510
576
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: killbill-cybersource
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 4.0.
|
4
|
+
version: 4.0.7
|
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-
|
11
|
+
date: 2016-06-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: killbill
|