killbill-cybersource 1.0.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d96b3cde985847f615dfb6827472f74fa18286f9
4
- data.tar.gz: 7061ae8a789e1122e79109548133334695ff8f37
3
+ metadata.gz: bfa04a9b0d7a032fbd01ed8e985c0a77682c54f4
4
+ data.tar.gz: baabc471f68b9130025b82c0045438246178c6b9
5
5
  SHA512:
6
- metadata.gz: 7fbd21f7d2483df9594fddaf45249528162dc8f8ea374f94dda8ada1343edf12b1179d995d469426b2ea06e19f6c05ec61d6049249c77da5fac1be4fb27fcb4b
7
- data.tar.gz: a3db194b14bfcf50f66ba93dec2815f12048ad008013237f1f6a9a7ea578246af78cdaa9aacbe7eaa6bdacd7eb8d8e34386337bb1f2b6ed8567b4d0810457a95
6
+ metadata.gz: 50e85249fc100dd9006445ea729cdb7fce42e16d9b39504fcad05d3e206f5c2d7d174ec75c97eaec72218f83ce0227e70ccb33df3c7194886c9502661f6bdc01
7
+ data.tar.gz: 7265f9d257964c162e754f0153eeb84a40324a848f3e23c872fc9ef59e283a846717eb24f6f0bb9bad86657710814ee2bedf2c8157f494fb84bc6f750b0bf23e
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- killbill-cybersource (1.0.0)
4
+ killbill-cybersource (2.0.0)
5
5
  actionpack (~> 4.1.0)
6
6
  actionview (~> 4.1.0)
7
7
  activemerchant (~> 1.48.0)
@@ -9,7 +9,7 @@ PATH
9
9
  activerecord-bogacs (~> 0.3)
10
10
  activerecord-jdbc-adapter (~> 1.3)
11
11
  jruby-openssl (~> 0.9.6)
12
- killbill (~> 4.0.0)
12
+ killbill (~> 4.4.0)
13
13
  monetize (~> 1.1.0)
14
14
  money (~> 6.5.1)
15
15
  offsite_payments (~> 2.1.0)
@@ -19,13 +19,13 @@ PATH
19
19
  GEM
20
20
  remote: https://rubygems.org/
21
21
  specs:
22
- actionpack (4.1.10)
23
- actionview (= 4.1.10)
24
- activesupport (= 4.1.10)
22
+ actionpack (4.1.12)
23
+ actionview (= 4.1.12)
24
+ activesupport (= 4.1.12)
25
25
  rack (~> 1.5.2)
26
26
  rack-test (~> 0.6.2)
27
- actionview (4.1.10)
28
- activesupport (= 4.1.10)
27
+ actionview (4.1.12)
28
+ activesupport (= 4.1.12)
29
29
  builder (~> 3.1)
30
30
  erubis (~> 2.7.0)
31
31
  active_utils (3.0.0)
@@ -36,18 +36,18 @@ GEM
36
36
  builder (>= 2.1.2, < 4.0.0)
37
37
  i18n (>= 0.6.9)
38
38
  nokogiri (~> 1.4)
39
- activemodel (4.1.10)
40
- activesupport (= 4.1.10)
39
+ activemodel (4.1.12)
40
+ activesupport (= 4.1.12)
41
41
  builder (~> 3.1)
42
- activerecord (4.1.10)
43
- activemodel (= 4.1.10)
44
- activesupport (= 4.1.10)
42
+ activerecord (4.1.12)
43
+ activemodel (= 4.1.12)
44
+ activesupport (= 4.1.12)
45
45
  arel (~> 5.0.0)
46
46
  activerecord-bogacs (0.3.0)
47
47
  thread_safe (~> 0.3)
48
- activerecord-jdbc-adapter (1.3.16)
48
+ activerecord-jdbc-adapter (1.3.17)
49
49
  activerecord (>= 2.2)
50
- activesupport (4.1.10)
50
+ activesupport (4.1.12)
51
51
  i18n (~> 0.6, >= 0.6.9)
52
52
  json (~> 1.7, >= 1.7.7)
53
53
  minitest (~> 5.1)
@@ -57,24 +57,24 @@ GEM
57
57
  builder (3.2.2)
58
58
  diff-lcs (1.1.3)
59
59
  erubis (2.7.0)
60
- ethon (0.7.3)
60
+ ethon (0.7.4)
61
61
  ffi (>= 1.3.0)
62
- ffi (1.9.8-java)
62
+ ffi (1.9.10-java)
63
63
  i18n (0.7.0)
64
64
  jbundler (0.4.3)
65
65
  maven-tools (~> 0.32.1)
66
66
  ruby-maven (~> 3.0.4)
67
67
  jdbc-mariadb (1.1.8)
68
- jdbc-sqlite3 (3.8.7)
68
+ jdbc-sqlite3 (3.8.10.1)
69
69
  jruby-openssl (0.9.7-java)
70
- json (1.8.2-java)
71
- killbill (4.0.0)
70
+ json (1.8.3-java)
71
+ killbill (4.4.0)
72
72
  rack (>= 1.5.2)
73
73
  sinatra (~> 1.3.4)
74
74
  typhoeus (~> 0.6.9)
75
75
  tzinfo (~> 1.2.0)
76
76
  maven-tools (0.32.5)
77
- minitest (5.6.1)
77
+ minitest (5.7.0)
78
78
  monetize (1.1.0)
79
79
  money (~> 6.5.0)
80
80
  money (6.5.1)
@@ -88,7 +88,7 @@ GEM
88
88
  i18n (~> 0.5)
89
89
  money (>= 5.0.0, < 7.0.0)
90
90
  nokogiri (~> 1.4)
91
- rack (1.5.3)
91
+ rack (1.5.5)
92
92
  rack-protection (1.5.3)
93
93
  rack
94
94
  rack-test (0.6.3)
@@ -127,3 +127,6 @@ DEPENDENCIES
127
127
  killbill-cybersource!
128
128
  rake (>= 10.0.0)
129
129
  rspec (~> 2.12.0)
130
+
131
+ BUNDLED WITH
132
+ 1.10.5
data/Jarfile CHANGED
@@ -1,9 +1,10 @@
1
- jar 'org.kill-bill.billing:killbill-api', '0.14'
2
- jar 'org.kill-bill.billing.plugin:killbill-plugin-api-currency', '0.9'
3
- jar 'org.kill-bill.billing.plugin:killbill-plugin-api-invoice', '0.9'
4
- jar 'org.kill-bill.billing.plugin:killbill-plugin-api-notification', '0.9'
5
- jar 'org.kill-bill.billing.plugin:killbill-plugin-api-payment', '0.9'
6
- jar 'org.kill-bill.billing.plugin:killbill-plugin-api-routing', '0.9'
7
- jar 'org.kill-bill.billing:killbill-util:tests', '0.13.7'
1
+ jar 'org.kill-bill.billing:killbill-api', '0.18'
2
+ jar 'org.kill-bill.billing.plugin:killbill-plugin-api-currency', '0.11'
3
+ jar 'org.kill-bill.billing.plugin:killbill-plugin-api-invoice', '0.11'
4
+ jar 'org.kill-bill.billing.plugin:killbill-plugin-api-notification', '0.11'
5
+ jar 'org.kill-bill.billing.plugin:killbill-plugin-api-payment', '0.11'
6
+ jar 'org.kill-bill.billing.plugin:killbill-plugin-api-routing', '0.11'
7
+ jar 'org.kill-bill.billing.plugin:killbill-plugin-api-catalog', '0.11'
8
+ jar 'org.kill-bill.billing:killbill-util:tests', '0.14.0'
8
9
  jar 'org.mockito:mockito-all', '1.10.19'
9
10
  jar 'javax.servlet:javax.servlet-api', '3.1.0'
data/Jarfile.lock CHANGED
@@ -1,12 +1,15 @@
1
- org.kill-bill.billing:killbill-api:jar:0.14
1
+ org.bouncycastle:bcpkix-jdk15on:jar:1.50
2
+ org.bouncycastle:bcprov-jdk15on:jar:1.50
3
+ org.kill-bill.billing:killbill-api:jar:0.18
2
4
  com.fasterxml.jackson.core:jackson-annotations:jar:2.4.3
3
5
  joda-time:joda-time:jar:2.3
4
- org.kill-bill.billing.plugin:killbill-plugin-api-currency:jar:0.9
5
- org.kill-bill.billing.plugin:killbill-plugin-api-invoice:jar:0.9
6
- org.kill-bill.billing.plugin:killbill-plugin-api-notification:jar:0.9
7
- org.kill-bill.billing.plugin:killbill-plugin-api-payment:jar:0.9
8
- org.kill-bill.billing.plugin:killbill-plugin-api-routing:jar:0.9
9
- org.kill-bill.billing:killbill-util:jar:tests:0.13.7
6
+ org.kill-bill.billing.plugin:killbill-plugin-api-currency:jar:0.11
7
+ org.kill-bill.billing.plugin:killbill-plugin-api-invoice:jar:0.11
8
+ org.kill-bill.billing.plugin:killbill-plugin-api-notification:jar:0.11
9
+ org.kill-bill.billing.plugin:killbill-plugin-api-payment:jar:0.11
10
+ org.kill-bill.billing.plugin:killbill-plugin-api-routing:jar:0.11
11
+ org.kill-bill.billing.plugin:killbill-plugin-api-catalog:jar:0.11
12
+ org.kill-bill.billing:killbill-util:jar:tests:0.14.0
10
13
  com.fasterxml.jackson.core:jackson-databind:jar:2.4.3
11
14
  com.fasterxml.jackson.core:jackson-core:jar:2.4.3
12
15
  com.fasterxml.jackson.dataformat:jackson-dataformat-csv:jar:2.4.3
@@ -32,7 +35,7 @@ aopalliance:aopalliance:jar:1.0
32
35
  com.google.inject.extensions:guice-multibindings:jar:3.0
33
36
  org.jdbi:jdbi:jar:2.62
34
37
  org.joda:joda-money:jar:0.9
35
- org.kill-bill.billing:killbill-internal-api:jar:0.13.7
38
+ org.kill-bill.billing:killbill-internal-api:jar:0.14.0
36
39
  org.kill-bill.billing:killbill-platform-api:jar:0.2
37
40
  org.kill-bill.billing:killbill-platform-base:jar:0.2
38
41
  com.google.code.findbugs:annotations:jar:3.0.0
@@ -54,5 +57,3 @@ org.slf4j:slf4j-api:jar:1.7.12
54
57
  org.weakref:jmxutils:jar:1.12
55
58
  org.mockito:mockito-all:jar:1.10.19
56
59
  javax.servlet:javax.servlet-api:jar:3.1.0
57
- org.bouncycastle:bcpkix-jdk15on:jar:1.50
58
- org.bouncycastle:bcprov-jdk15on:jar:1.50
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.0.0
1
+ 2.0.0
@@ -22,7 +22,7 @@ Gem::Specification.new do |s|
22
22
 
23
23
  s.rdoc_options << '--exclude' << '.'
24
24
 
25
- s.add_dependency 'killbill', '~> 4.0.0'
25
+ s.add_dependency 'killbill', '~> 4.4.0'
26
26
 
27
27
  s.add_dependency 'sinatra', '~> 1.3.4'
28
28
  s.add_dependency 'thread_safe', '~> 0.3.4'
@@ -22,14 +22,6 @@ module Killbill #:nodoc:
22
22
  #
23
23
  end
24
24
 
25
- def start_plugin
26
- super
27
- gateway = lookup_gateway(:on_demand)
28
- @report_api = CyberSourceOnDemand.new(gateway, logger)
29
- rescue => e
30
- @report_api = nil
31
- end
32
-
33
25
  def authorize_payment(kb_account_id, kb_payment_id, kb_payment_transaction_id, kb_payment_method_id, amount, currency, properties, context)
34
26
  # Pass extra parameters for the gateway here
35
27
  options = {}
@@ -95,7 +87,64 @@ module Killbill #:nodoc:
95
87
  options = {}
96
88
 
97
89
  properties = merge_properties(properties, options)
98
- super(kb_account_id, kb_payment_id, properties, context)
90
+ transaction_info_plugins = super(kb_account_id, kb_payment_id, properties, context)
91
+
92
+ # Should never happen...
93
+ return [] if transaction_info_plugins.nil?
94
+
95
+ # Note: this won't handle the case where we don't have any record in the DB. While this should very rarely happen
96
+ # (see Killbill::Plugin::ActiveMerchant::Gateway), we could use the CyberSource Payment Batch Detail Report to fix it.
97
+
98
+ options = properties_to_hash(properties)
99
+
100
+ stale = false
101
+ transaction_info_plugins.each do |transaction_info_plugin|
102
+ # We only need to fix the UNDEFINED ones
103
+ next unless transaction_info_plugin.status == :UNDEFINED
104
+
105
+ cybersource_response_id = find_value_from_properties(transaction_info_plugin.properties, 'cybersourceResponseId')
106
+ next if cybersource_response_id.nil?
107
+
108
+ report_date = transaction_info_plugin.created_date
109
+ authorization = find_value_from_properties(transaction_info_plugin.properties, 'authorization')
110
+
111
+ order_id = Killbill::Plugin::ActiveMerchant::Utils.normalized(options, :order_id)
112
+ # authorization is very likely nil, as we didn't get an answer from the gateway in the first place
113
+ order_id ||= authorization.split(';')[0] unless authorization.nil?
114
+
115
+ # Retrieve the report from CyberSource
116
+ if order_id.nil?
117
+ # order_id undetermined - try the defaults (see PaymentPlugin#dispatch_to_gateways)
118
+ report = get_report(transaction_info_plugin.kb_transaction_payment_id, report_date, options, context)
119
+ if report.nil?
120
+ kb_transaction = get_kb_transaction(kb_payment_id, transaction_info_plugin.kb_transaction_payment_id, context.tenant_id)
121
+ report = get_report(kb_transaction.external_key, report_date, options, context)
122
+ end
123
+ else
124
+ report = get_report(order_id, report_date, options, context)
125
+ end
126
+
127
+ # Report API not configured or skip_gw=true
128
+ next if report.nil?
129
+
130
+ # Report not found
131
+ if report.empty?
132
+ logger.info("Unable to fix UNDEFINED transaction #{transaction_info_plugin.kb_transaction_payment_id} (not found in CyberSource)")
133
+ next
134
+ end
135
+
136
+ # Update our rows
137
+ response = CybersourceResponse.find_by(:id => cybersource_response_id)
138
+ next if response.nil?
139
+
140
+ logger.info("Fixing UNDEFINED transaction #{transaction_info_plugin.kb_transaction_payment_id}: success? = #{report.response.success?}")
141
+
142
+ response.update_and_create_transaction(report.response)
143
+ stale = true
144
+ end
145
+
146
+ # If we updated the state, re-fetch the latest data
147
+ stale ? super(kb_account_id, kb_payment_id, properties, context) : transaction_info_plugins
99
148
  end
100
149
 
101
150
  def search_payments(search_key, offset, limit, properties, context)
@@ -183,25 +232,25 @@ module Killbill #:nodoc:
183
232
  end
184
233
 
185
234
  # Make calls idempotent
186
- def before_gateway(gateway, kb_transaction, last_transaction, payment_source, amount_in_cents, currency, options)
235
+ def before_gateway(gateway, kb_transaction, last_transaction, payment_source, amount_in_cents, currency, options, context)
187
236
  super
188
- return if @report_api.nil? || options[:skip_gw]
189
237
 
190
238
  merchant_reference_code = options[:order_id]
191
- report = @report_api.single_transaction_report(merchant_reference_code, kb_transaction.created_date.strftime('%Y%m%d'))
192
-
193
- if !report.nil? &&
194
- !report['Report'].nil? &&
195
- !report['Report']['Requests'].nil? &&
196
- !report['Report']['Requests']['Request'].nil? &&
197
- report['Report']['Requests']['Request']['MerchantReferenceNumber'] == merchant_reference_code
198
- logger.info "Skipping gateway call for existing transaction #{kb_transaction.id}, merchant reference code #{merchant_reference_code}"
199
- options[:skip_gw] = true
200
- end
239
+ report = get_report(merchant_reference_code, kb_transaction.created_date, options, context)
240
+ return nil if report.nil? || report.empty?
241
+
242
+ logger.info "Skipping gateway call for existing transaction #{kb_transaction.id}, merchant reference code #{merchant_reference_code}"
243
+ options[:skip_gw] = true
201
244
  rescue => e
202
245
  logger.warn "Error checking for duplicate payment: #{e.message}"
203
246
  end
204
247
 
248
+ def get_report(merchant_reference_code, date, options, context)
249
+ report_api = get_report_api(context.tenant_id)
250
+ return nil if report_api.nil? || options[:skip_gw]
251
+ report_api.single_transaction_report(merchant_reference_code, date.strftime('%Y%m%d'))
252
+ end
253
+
205
254
  def add_required_options(kb_account_id, properties, options, context)
206
255
  if options[:email].nil?
207
256
  email = find_value_from_properties(properties, 'email')
@@ -213,6 +262,13 @@ module Killbill #:nodoc:
213
262
  options[:email] = email
214
263
  end
215
264
  end
265
+
266
+ def get_report_api(kb_tenant_id)
267
+ gateway = lookup_gateway(:on_demand, kb_tenant_id)
268
+ CyberSourceOnDemand.new(gateway, logger)
269
+ rescue
270
+ nil
271
+ end
216
272
  end
217
273
  end
218
274
  end
@@ -1,5 +1,6 @@
1
1
  module Killbill #:nodoc:
2
2
  module Cybersource #:nodoc:
3
+ # See http://apps.cybersource.com/library/documentation/dev_guides/Reporting_Developers_Guide/reporting_dg.pdf
3
4
  class CyberSourceOnDemand
4
5
 
5
6
  @@live_url = 'https://ebc.cybersource.com/ebc/Query'
@@ -7,17 +8,17 @@ module Killbill #:nodoc:
7
8
 
8
9
  def initialize(gateway, logger)
9
10
  @gateway = gateway
10
- @logger = logger
11
+ @logger = logger
11
12
  end
12
13
 
13
14
  def single_transaction_report(merchant_reference_code, target_date)
14
15
  params = {
15
- :merchantID => @gateway.config[:merchantID],
16
+ :merchantID => @gateway.config[:merchantID],
16
17
  :merchantReferenceNumber => merchant_reference_code,
17
- :targetDate => target_date,
18
- :type => 'transaction',
19
- :subtype => 'transactionDetail',
20
- :versionNumber => '1.7',
18
+ :targetDate => target_date,
19
+ :type => 'transaction',
20
+ :subtype => 'transactionDetail',
21
+ :versionNumber => '1.7',
21
22
  }
22
23
 
23
24
  headers = {
@@ -25,21 +26,118 @@ module Killbill #:nodoc:
25
26
  'Authorization' => 'Basic ' + Base64.encode64("#{@gateway.config[:username]}:#{@gateway.config[:password]}").chomp
26
27
  }
27
28
 
28
- data = URI.encode_www_form(params)
29
+ data = URI.encode_www_form(params)
29
30
  endpoint = @gateway.test? ? @@test_url : @@live_url
30
31
 
31
32
  # Will raise ResponseError if the response code is > 300
32
- parse(@gateway.ssl_post(endpoint, data, headers))
33
+ CyberSourceOnDemandTransactionReport.new(@gateway.ssl_post(endpoint, data, headers), @logger)
33
34
  end
34
35
 
35
- private
36
+ class CyberSourceOnDemandTransactionReport
36
37
 
37
- def parse(body)
38
- # Thanks ActiveSupport!
39
- Hash.from_xml(body)
40
- rescue # Parser error - request failed
41
- @logger.warn "Error checking for duplicate payment, CyberSource response: #{body}"
42
- nil
38
+ attr_reader :response
39
+
40
+ def initialize(xml_report, logger)
41
+ @logger = logger
42
+ @hash_report = parse_xml(xml_report)
43
+ parse
44
+ end
45
+
46
+ def success?
47
+ @response.success?
48
+ end
49
+
50
+ def empty?
51
+ @response.params['merchantReferenceCode'].nil?
52
+ end
53
+
54
+ private
55
+
56
+ def parse
57
+ report = parse_report
58
+ request = parse_request(report)
59
+ payment_data = !request.nil? ? request['PaymentData'] : nil
60
+ profile = !request.nil? && !request['ProfileList'].nil? ? request['ProfileList']['Profile'] : nil
61
+
62
+ test = parse_test(report)
63
+ success, message = parse_success_message(request)
64
+ merchant_reference_code = extract(request, 'MerchantReferenceNumber')
65
+ request_id = extract(request, 'RequestID') || extract(payment_data, 'PaymentRequestID')
66
+ request_token = nil
67
+
68
+ # ActiveMerchant specific
69
+ authorization = "#{merchant_reference_code};#{request_id};#{request_token}"
70
+
71
+ # See CybersourceResponse
72
+ params = {
73
+ 'merchantReferenceCode' => merchant_reference_code,
74
+ 'requestID' => request_id,
75
+ 'decision' => extract(profile, 'ProfileDecision'),
76
+ 'reasonCode' => nil,
77
+ 'requestToken' => request_token,
78
+ 'currency' => extract(payment_data, 'CurrencyCode'),
79
+ 'amount' => extract(payment_data, 'Amount'),
80
+ 'authorizationCode' => extract(payment_data, 'AuthorizationCode'),
81
+ 'avsCode' => extract(payment_data, 'AVSResultMapped'),
82
+ 'avsCodeRaw' => extract(payment_data, 'AVSResult'),
83
+ 'cvCode' => nil,
84
+ 'authorizedDateTime' => nil,
85
+ 'processorResponse' => nil,
86
+ 'reconciliationID' => extract(request, 'TransactionReferenceNumber'),
87
+ 'subscriptionID' => extract(request, 'SubscriptionID')
88
+ }
89
+
90
+ @response = ::ActiveMerchant::Billing::Response.new(success,
91
+ message,
92
+ params,
93
+ :test => test,
94
+ :authorization => authorization,
95
+ :avs_result => {:code => params['avsCode']},
96
+ :cvv_result => params['cvCode'])
97
+ end
98
+
99
+ def parse_report
100
+ !@hash_report.nil? ? @hash_report['Report'] : nil
101
+ end
102
+
103
+ def parse_test(report)
104
+ !report.nil? && !report['xmlns'].nil? && report['xmlns'].starts_with?('https://ebctest.cybersource.com')
105
+ end
106
+
107
+ def parse_request(report)
108
+ # Assume the report contains a single request
109
+ !report.nil? && !report['Requests'].nil? ? report['Requests']['Request'] : nil
110
+ end
111
+
112
+ # Note: for now, we only look at the response from CyberSource.
113
+ # It would be nice to take into account the processor response too.
114
+ def parse_success_message(request)
115
+ success = false
116
+ msg = nil
117
+ return [success, msg] if request.nil? || request['ApplicationReplies'].nil? || request['ApplicationReplies']['ApplicationReply'].nil? || request['ApplicationReplies']['ApplicationReply'].empty?
118
+
119
+ application_replies = request['ApplicationReplies']['ApplicationReply'].is_a?(Hash) ? [request['ApplicationReplies']['ApplicationReply']] : request['ApplicationReplies']['ApplicationReply']
120
+
121
+ success = true
122
+ application_replies.each do |application_reply|
123
+ success &&= (application_reply['RCode'].to_s == '1')
124
+ # Last message by convention
125
+ msg = application_reply['RMsg']
126
+ end
127
+ [success, msg]
128
+ end
129
+
130
+ def extract(hash, key)
131
+ !hash.nil? ? hash[key] : nil
132
+ end
133
+
134
+ def parse_xml(body)
135
+ # Thanks ActiveSupport!
136
+ Hash.from_xml(body)
137
+ rescue # Parser error - request failed
138
+ @logger.warn "Error checking for duplicate payment, CyberSource response: #{!body.nil? && body.respond_to?(:message) ? body.message : body}"
139
+ nil
140
+ end
43
141
  end
44
142
  end
45
143
  end
@@ -0,0 +1,28 @@
1
+ module ActiveMerchant
2
+ module Billing
3
+ class CyberSourceGateway
4
+
5
+ # See https://github.com/killbill/killbill-cybersource-plugin/issues/4
6
+ def commit(request, options)
7
+ request = build_request(request, options)
8
+ begin
9
+ raw_response = ssl_post(test? ? self.test_url : self.live_url, request)
10
+ rescue ResponseError => e
11
+ raw_response = e.response.body
12
+ end
13
+ response = parse(raw_response)
14
+
15
+ success = response[:decision] == 'ACCEPT'
16
+ message = @@response_codes[('r' + response[:reasonCode]).to_sym] rescue response[:message]
17
+ authorization = success ? [options[:order_id], response[:requestID], response[:requestToken]].compact.join(";") : nil
18
+
19
+ Response.new(success, message, response,
20
+ :test => test?,
21
+ :authorization => authorization,
22
+ :avs_result => {:code => response[:avsCode]},
23
+ :cvv_result => response[:cvCode]
24
+ )
25
+ end
26
+ end
27
+ end
28
+ end
@@ -15,26 +15,72 @@ module Killbill #:nodoc:
15
15
  payment_processor_account_id,
16
16
  kb_tenant_id,
17
17
  response,
18
- {
19
- :params_merchant_reference_code => extract(response, 'merchantReferenceCode'),
20
- :params_request_id => extract(response, 'requestID'),
21
- :params_decision => extract(response, 'decision'),
22
- :params_reason_code => extract(response, 'reasonCode'),
23
- :params_request_token => extract(response, 'requestToken'),
24
- :params_currency => extract(response, 'currency'),
25
- :params_amount => extract(response, 'amount'),
26
- :params_authorization_code => extract(response, 'authorizationCode'),
27
- :params_avs_code => extract(response, 'avsCode'),
28
- :params_avs_code_raw => extract(response, 'avsCodeRaw'),
29
- :params_cv_code => extract(response, 'cvCode'),
30
- :params_authorized_date_time => extract(response, 'authorizedDateTime'),
31
- :params_processor_response => extract(response, 'processorResponse'),
32
- :params_reconciliation_id => extract(response, 'reconciliationID'),
33
- :params_subscription_id => extract(response, 'subscriptionID'),
34
- }.merge!(extra_params),
18
+ cybersource_response_params(response).merge!(extra_params),
35
19
  model)
36
20
  end
37
21
 
22
+ def self.cybersource_response_params(response)
23
+ {
24
+ :params_merchant_reference_code => extract(response, 'merchantReferenceCode'),
25
+ :params_request_id => extract(response, 'requestID'),
26
+ :params_decision => extract(response, 'decision'),
27
+ :params_reason_code => extract(response, 'reasonCode'),
28
+ :params_request_token => extract(response, 'requestToken'),
29
+ :params_currency => extract(response, 'currency'),
30
+ :params_amount => extract(response, 'amount'),
31
+ :params_authorization_code => extract(response, 'authorizationCode'),
32
+ :params_avs_code => extract(response, 'avsCode'),
33
+ :params_avs_code_raw => extract(response, 'avsCodeRaw'),
34
+ :params_cv_code => extract(response, 'cvCode'),
35
+ :params_authorized_date_time => extract(response, 'authorizedDateTime'),
36
+ :params_processor_response => extract(response, 'processorResponse'),
37
+ :params_reconciliation_id => extract(response, 'reconciliationID'),
38
+ :params_subscription_id => extract(response, 'subscriptionID'),
39
+ }
40
+ end
41
+
42
+ def update_and_create_transaction(gw_response)
43
+ updated_attributes = {
44
+ :message => gw_response.message,
45
+ :authorization => gw_response.authorization,
46
+ :fraud_review => gw_response.fraud_review?,
47
+ :test => gw_response.test?,
48
+ :avs_result_code => gw_response.avs_result.kind_of?(::ActiveMerchant::Billing::AVSResult) ? gw_response.avs_result.code : gw_response.avs_result['code'],
49
+ :avs_result_message => gw_response.avs_result.kind_of?(::ActiveMerchant::Billing::AVSResult) ? gw_response.avs_result.message : gw_response.avs_result['message'],
50
+ :avs_result_street_match => gw_response.avs_result.kind_of?(::ActiveMerchant::Billing::AVSResult) ? gw_response.avs_result.street_match : gw_response.avs_result['street_match'],
51
+ :avs_result_postal_match => gw_response.avs_result.kind_of?(::ActiveMerchant::Billing::AVSResult) ? gw_response.avs_result.postal_match : gw_response.avs_result['postal_match'],
52
+ :cvv_result_code => gw_response.cvv_result.kind_of?(::ActiveMerchant::Billing::CVVResult) ? gw_response.cvv_result.code : gw_response.cvv_result['code'],
53
+ :cvv_result_message => gw_response.cvv_result.kind_of?(::ActiveMerchant::Billing::CVVResult) ? gw_response.cvv_result.message : gw_response.cvv_result['message'],
54
+ :success => gw_response.success?,
55
+ :updated_at => Time.now.utc
56
+ }.merge(CybersourceResponse.cybersource_response_params(gw_response))
57
+
58
+ # Keep original values as much as possible
59
+ updated_attributes.delete_if { |k, v| v.blank? }
60
+
61
+ # Update the response row
62
+ update!(updated_attributes)
63
+
64
+ # Create the transaction row if needed (cannot have been created before or the state wouldn't have been UNDEFINED)
65
+ if gw_response.success?
66
+ amount = gw_response.params['amount']
67
+ currency = gw_response.params['currency']
68
+ amount_in_cents = amount.nil? ? nil : ::Monetize.from_numeric(amount.to_f, currency).cents.to_i
69
+ build_cybersource_transaction(:kb_account_id => kb_account_id,
70
+ :kb_tenant_id => kb_tenant_id,
71
+ :amount_in_cents => amount_in_cents,
72
+ :currency => currency,
73
+ :api_call => api_call,
74
+ :kb_payment_id => kb_payment_id,
75
+ :kb_payment_transaction_id => kb_payment_transaction_id,
76
+ :transaction_type => transaction_type,
77
+ :payment_processor_account_id => payment_processor_account_id,
78
+ :txn_id => txn_id,
79
+ :created_at => updated_at,
80
+ :updated_at => updated_at).save!
81
+ end
82
+ end
83
+
38
84
  def first_reference_id
39
85
  params_request_id
40
86
  end
@@ -46,6 +92,14 @@ module Killbill #:nodoc:
46
92
  def gateway_error_code
47
93
  params_reason_code
48
94
  end
95
+
96
+ def to_transaction_info_plugin(transaction=nil)
97
+ t_info_plugin = super(transaction)
98
+
99
+ t_info_plugin.properties << create_plugin_property('cybersourceResponseId', id)
100
+
101
+ t_info_plugin
102
+ end
49
103
  end
50
104
  end
51
105
  end
data/lib/cybersource.rb CHANGED
@@ -18,6 +18,8 @@ require 'yaml'
18
18
  require 'killbill'
19
19
  require 'killbill/helpers/active_merchant'
20
20
 
21
+ require 'cybersource/ext/active_merchant/active_merchant.rb'
22
+
21
23
  require 'cybersource/cyber_source_on_demand'
22
24
  require 'cybersource/api'
23
25
  require 'cybersource/private_api'
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>1.0.0</version>
28
+ <version>2.0.0</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>
@@ -0,0 +1,276 @@
1
+ require 'spec_helper'
2
+
3
+ describe Killbill::Cybersource::CyberSourceOnDemand do
4
+
5
+ it 'parses a transaction detail report with a single ApplicationReply correctly' do
6
+ xml_report = <<eos
7
+ <?xml version="1.0" encoding="UTF-8"?>
8
+ <!DOCTYPE Report SYSTEM "https://ebctest.cybersource.com/ebctest/reports/dtd/tdr_1_3.dtd">
9
+ <Report xmlns="https://ebctest.cybersource.com/ebctest/reports/dtd/tdr_1_3.dtd"
10
+ Name="Transaction Detail"
11
+ Version="1.3"
12
+ MerchantID="testMerchant"
13
+ ReportStartDate="2008-09-10 21:46:41.765-08:00"
14
+ ReportEndDate="2008-09-10 21:46:41.765-08:00">
15
+ <Requests>
16
+ <Request MerchantReferenceNumber="33038191"
17
+ RequestDate="2008-09-10T14:00:08-08:00"
18
+ RequestID="2210804330010167904567"
19
+ SubscriptionID=""
20
+ Source="SCMP API"
21
+ User="merchant123"
22
+ TransactionReferenceNumber="0001094522"
23
+ PredecessorRequestID="7904567221330010160804">
24
+ <BillTo>
25
+ <FirstName>JANE</FirstName>
26
+ <LastName>Smith</LastName>
27
+ <Address1>1295 Charleston Rd</Address1>
28
+ <Address2>Suite 2</Address2>
29
+ <City>Mountain View</City>
30
+ <State>CA</State>
31
+ <Zip>06513</Zip>
32
+ <Email>null@cybersource.com</Email>
33
+ <Country>US</Country>
34
+ </BillTo>
35
+ <ShipTo>
36
+ <FirstName>JANE</FirstName>
37
+ <LastName>SMITH</LastName>
38
+ <Address1>1295 Charleston Rd</Address1>
39
+ <Address2>Suite 2</Address2>
40
+ <City>Mountain View</City>
41
+ <State>CA</State>
42
+ <Zip>94043</Zip>
43
+ <Country>US</Country>
44
+ </ShipTo>
45
+ <PaymentMethod>
46
+ <Card>
47
+ <AccountSuffix>1111</AccountSuffix>
48
+ <ExpirationMonth>11</ExpirationMonth>
49
+ <ExpirationYear>2011</ExpirationYear>
50
+ <CardType>Visa</CardType>
51
+ </Card>
52
+ </PaymentMethod>
53
+ <LineItems>
54
+ <LineItem Number="0">
55
+ <FulfillmentType/>
56
+ <Quantity>1</Quantity>
57
+ <UnitPrice>1.56</UnitPrice>
58
+ <TaxAmount>0.25</TaxAmount>
59
+ <MerchantProductSKU>testdl</MerchantProductSKU>
60
+ <ProductName>PName1</ProductName>
61
+ <ProductCode>electronic_software</ProductCode>
62
+ </LineItem>
63
+ </LineItems>
64
+ <ApplicationReplies>
65
+ <ApplicationReply Name="ics_bill">
66
+ <RCode>1</RCode>
67
+ <RFlag>SOK</RFlag>
68
+ <RMsg>Request was processed successfully.</RMsg>
69
+ </ApplicationReply>
70
+ </ApplicationReplies>
71
+ <PaymentData>
72
+ <PaymentProcessor>vital</PaymentProcessor>
73
+ <Amount>1.81</Amount>
74
+ <CurrencyCode>eur</CurrencyCode>
75
+ <TotalTaxAmount>0.25</TotalTaxAmount>
76
+ <EventType>TRANSMITTED</EventType>
77
+ </PaymentData>
78
+ </Request>
79
+ </Requests>
80
+ </Report>
81
+ eos
82
+ report = Killbill::Cybersource::CyberSourceOnDemand::CyberSourceOnDemandTransactionReport.new(xml_report, Logger.new(STDOUT))
83
+ response = report.response
84
+ response.success?.should be_true
85
+ response.message.should == 'Request was processed successfully.'
86
+ response.params['merchantReferenceCode'].should == '33038191'
87
+ response.params['requestID'].should == '2210804330010167904567'
88
+ response.params['decision'].should be_nil
89
+ response.params['reasonCode'].should be_nil
90
+ response.params['requestToken'].should be_nil
91
+ response.params['currency'].should == 'eur'
92
+ response.params['amount'].should == '1.81'
93
+ response.params['authorizationCode'].should be_nil
94
+ response.params['avsCode'].should be_nil
95
+ response.params['avsCodeRaw'].should be_nil
96
+ response.params['cvCode'].should be_nil
97
+ response.params['authorizedDateTime'].should be_nil
98
+ response.params['processorResponse'].should be_nil
99
+ response.params['reconciliationID'].should == '0001094522'
100
+ response.params['subscriptionID'].should == ''
101
+ end
102
+
103
+ it 'parses a transaction detail report with multiple ApplicationReplies correctly' do
104
+ xml_report = <<eos
105
+ <?xml version="1.0" encoding="UTF-8"?>
106
+ <!DOCTYPE Report SYSTEM "https://ebctest.cybersource.com/ebctest/reports/dtd/tdr_1_6.dtd">
107
+ <Report xmlns="https://ebctest.cybersource.com/ebctest/reports/dtd/tdr_1_6.dtd"
108
+ Name="Transaction Detail"
109
+ Version="1.6"
110
+ MerchantID="ok_go"
111
+ ReportStartDate="2009-05-26T18:30:00-08:00"
112
+ ReportEndDate="2009-05-27T18:30:00-08:00">
113
+ <Requests>
114
+ <Request RequestID="2434465504100167904567"
115
+ RequestDate="2009-05-27T17:49:10+05:30"
116
+ MerchantReferenceNumber="1234"
117
+ Source="SCMP API"
118
+ User=""
119
+ SubscriptionID=""
120
+ TransactionReferenceNumber="00013791KV8BZF3P">
121
+ <BillTo>
122
+ <FirstName>sample</FirstName>
123
+ <LastName>merchant</LastName>
124
+ <Address1>11 Lico Ave</Address1>
125
+ <City>Big City</City>
126
+ <State>CA</State>
127
+ <Zip>99999</Zip>
128
+ <Email>smerchant@example.com</Email>
129
+ <Country>US</Country>
130
+ <Phone/>
131
+ </BillTo>
132
+ <ShipTo>
133
+ <City>xyz</City>
134
+ <Zip>95117</Zip>
135
+ </ShipTo>
136
+ <PaymentMethod>
137
+ <Card>
138
+ <AccountSuffix>7392</AccountSuffix>
139
+ <ExpirationMonth>12</ExpirationMonth>
140
+ <ExpirationYear>2009</ExpirationYear>
141
+ <CardType>Visa</CardType>
142
+ </Card>
143
+ </PaymentMethod>
144
+ <LineItems>
145
+ <LineItem Number="0">
146
+ <FulfillmentType>P</FulfillmentType>
147
+ <Quantity>1</Quantity>
148
+ <UnitPrice>2.00</UnitPrice>
149
+ <TaxAmount>0.00</TaxAmount>
150
+ <ProductCode>default</ProductCode>
151
+ </LineItem>
152
+ </LineItems>
153
+ <ApplicationReplies>
154
+ <ApplicationReply Name="ics_auth">
155
+ <RCode>1</RCode>
156
+ <RFlag>SOK</RFlag>
157
+ <RMsg>Request was processed successfully.</RMsg>
158
+ </ApplicationReply>
159
+ <ApplicationReply Name="ics_decision">
160
+ <RCode>0</RCode>
161
+ <RFlag>DREVIEW</RFlag>
162
+ <RMsg>Decision is REVIEW.</RMsg>
163
+ </ApplicationReply>
164
+ <ApplicationReply Name="ics_decision_early">
165
+ <RCode>1</RCode>
166
+ <RFlag/>
167
+ </ApplicationReply>
168
+ <ApplicationReply Name="ics_score">
169
+ <RCode>1</RCode>
170
+ <RFlag>DSCORE</RFlag>
171
+ <RMsg>Score exceeds threshold. Score = 84</RMsg>
172
+ </ApplicationReply>
173
+ </ApplicationReplies>
174
+ <PaymentData>
175
+ <PaymentRequestID>2434465504100167904567</PaymentRequestID>
176
+ <PaymentProcessor>smartpay</PaymentProcessor>
177
+ <Amount>2.00</Amount>
178
+ <CurrencyCode>USD</CurrencyCode>
179
+ <TotalTaxAmount>0.00</TotalTaxAmount>
180
+ <AuthorizationType>O</AuthorizationType>
181
+ <AuthorizationCode>888888</AuthorizationCode>
182
+ <AVSResult>I1</AVSResult>
183
+ <AVSResultMapped>X</AVSResultMapped>
184
+ <GrandTotal>2.00</GrandTotal>
185
+ <ACHVerificationResult>100</ACHVerificationResult>
186
+ </PaymentData>
187
+ <MerchantDefinedData>
188
+ <field1 name="mdd1">ca</field1>
189
+ </MerchantDefinedData>
190
+ <RiskData>
191
+ <Factors>C,Y,Z</Factors>
192
+ <HostSeverity>1</HostSeverity>
193
+ <Score>84</Score>
194
+ <TimeLocal>2009-05-27T10:49:10</TimeLocal>
195
+ <AppliedThreshold>20</AppliedThreshold>
196
+ <AppliedTimeHedge>normal</AppliedTimeHedge>
197
+ <AppliedVelocityHedge>high</AppliedVelocityHedge>
198
+ <AppliedHostHedge>normal</AppliedHostHedge>
199
+ <AppliedCategoryGift>n</AppliedCategoryGift>
200
+ <AppliedCategoryTime/>
201
+ <AppliedAVS>X</AppliedAVS>
202
+ <BinAccountType>CN</BinAccountType>
203
+ <BinScheme>Visa Credit</BinScheme>
204
+ <BinIssuer>Sample issuer</BinIssuer>
205
+ <BinCountry>us</BinCountry>
206
+ <InfoCodes>
207
+ <InfoCode>
208
+ <CodeType>address</CodeType>
209
+ <CodeValue>MM-C,MM-Z</CodeValue>
210
+ </InfoCode>
211
+ <InfoCode>
212
+ <CodeType>velocity</CodeType>
213
+ <CodeValue>VEL-CC</CodeValue>
214
+ </InfoCode>
215
+ </InfoCodes>
216
+ </RiskData>
217
+ <ProfileList>
218
+ <Profile Name="Default Profile">
219
+ <ProfileMode>Active</ProfileMode>
220
+ <ProfileDecision>ACCEPT</ProfileDecision>
221
+ <RuleList>
222
+ <Rule>
223
+ <RuleName>sample rule name</RuleName>
224
+ <RuleDecision>IGNORE</RuleDecision>
225
+ </Rule>
226
+ </RuleList>
227
+ </Profile>
228
+ </ProfileList>
229
+ <TravelData>
230
+ <TripInfo>
231
+ <CompleteRoute>AB-CD:EF-GH</CompleteRoute>
232
+ <JourneyType>round trip</JourneyType>
233
+ <DepartureDateTime>sample date &amp; time</DepartureDateTime>
234
+ </TripInfo>
235
+ <PassengerInfo>
236
+ <Passenger Number="0">
237
+ <PassengerFirstName>jane</PassengerFirstName>
238
+ <PassengerLastName>doe</PassengerLastName>
239
+ <PassengerID>Sing-001</PassengerID>
240
+ </Passenger>
241
+ <Passenger Number="1">
242
+ <PassengerFirstName>john</PassengerFirstName>
243
+ <PassengerLastName>doe</PassengerLastName>
244
+ <PassengerID>sing-002</PassengerID>
245
+ <PassengerStatus>Adult</PassengerStatus>
246
+ <PassengerType>Gold</PassengerType>
247
+ <PassengerPhone>9995551212</PassengerPhone>
248
+ <PassengerEmail>jdoe@example.com</PassengerEmail>
249
+ </Passenger>
250
+ </PassengerInfo>
251
+ </TravelData>
252
+ </Request>
253
+ </Requests>
254
+ </Report>
255
+ eos
256
+ report = Killbill::Cybersource::CyberSourceOnDemand::CyberSourceOnDemandTransactionReport.new(xml_report, Logger.new(STDOUT))
257
+ response = report.response
258
+ response.success?.should be_false
259
+ response.message.should == 'Score exceeds threshold. Score = 84'
260
+ response.params['merchantReferenceCode'].should == '1234'
261
+ response.params['requestID'].should == '2434465504100167904567'
262
+ response.params['decision'].should == 'ACCEPT'
263
+ response.params['reasonCode'].should be_nil
264
+ response.params['requestToken'].should be_nil
265
+ response.params['currency'].should == 'USD'
266
+ response.params['amount'].should == '2.00'
267
+ response.params['authorizationCode'].should == '888888'
268
+ response.params['avsCode'].should == 'X'
269
+ response.params['avsCodeRaw'].should == 'I1'
270
+ response.params['cvCode'].should be_nil
271
+ response.params['authorizedDateTime'].should be_nil
272
+ response.params['processorResponse'].should be_nil
273
+ response.params['reconciliationID'].should == '00013791KV8BZF3P'
274
+ response.params['subscriptionID'].should == ''
275
+ end
276
+ end
@@ -81,6 +81,62 @@ describe Killbill::Cybersource::PaymentPlugin do
81
81
  transactions[1].txn_id.should be_nil
82
82
  end
83
83
 
84
+ it 'should be able to fix UNDEFINED payments' do
85
+ 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)
86
+ payment_response.status.should eq(:PROCESSED), payment_response.gateway_error
87
+
88
+ # Force a transition to :UNDEFINED
89
+ Killbill::Cybersource::CybersourceTransaction.last.delete
90
+ response = Killbill::Cybersource::CybersourceResponse.last
91
+ response.update(:message => {:payment_plugin_status => 'UNDEFINED'}.to_json)
92
+
93
+ skip_gw = Killbill::Plugin::Model::PluginProperty.new
94
+ skip_gw.key = 'skip_gw'
95
+ skip_gw.value = 'true'
96
+ properties_with_skip_gw = @properties.clone
97
+ properties_with_skip_gw << skip_gw
98
+
99
+ # Set skip_gw=true, to avoid calling the report API
100
+ transaction_info_plugins = @plugin.get_payment_info(@pm.kb_account_id, @kb_payment.id, properties_with_skip_gw, @call_context)
101
+ transaction_info_plugins.size.should == 1
102
+ transaction_info_plugins.first.status.should eq(:UNDEFINED)
103
+
104
+ # Fix it
105
+ transaction_info_plugins = @plugin.get_payment_info(@pm.kb_account_id, @kb_payment.id, @properties, @call_context)
106
+ transaction_info_plugins.size.should == 1
107
+ transaction_info_plugins.first.status.should eq(:PROCESSED)
108
+
109
+ # Set skip_gw=true, to check the local state
110
+ transaction_info_plugins = @plugin.get_payment_info(@pm.kb_account_id, @kb_payment.id, properties_with_skip_gw, @call_context)
111
+ transaction_info_plugins.size.should == 1
112
+ transaction_info_plugins.first.status.should eq(:PROCESSED)
113
+
114
+ # Compare the state of the old and new response
115
+ new_response = Killbill::Cybersource::CybersourceResponse.last
116
+ new_response.id.should == response.id
117
+ new_response.api_call.should == 'purchase'
118
+ new_response.kb_tenant_id.should == @call_context.tenant_id
119
+ new_response.kb_account_id.should == @pm.kb_account_id
120
+ new_response.kb_payment_id.should == @kb_payment.id
121
+ new_response.kb_payment_transaction_id.should == @kb_payment.transactions[0].id
122
+ new_response.transaction_type.should == 'PURCHASE'
123
+ new_response.payment_processor_account_id.should == 'default'
124
+ # The report API doesn't give us the token
125
+ new_response.authorization.split(';')[0..1].should == response.authorization.split(';')[0..1]
126
+ new_response.test.should be_true
127
+ new_response.params_merchant_reference_code.should == response.params_merchant_reference_code
128
+ new_response.params_decision.should == response.params_decision
129
+ new_response.params_request_token.should == response.params_request_token
130
+ new_response.params_currency.should == response.params_currency
131
+ new_response.params_amount.should == response.params_amount
132
+ new_response.params_authorization_code.should == response.params_authorization_code
133
+ new_response.params_avs_code.should == response.params_avs_code
134
+ new_response.params_avs_code_raw.should == response.params_avs_code_raw
135
+ new_response.params_reconciliation_id.should == response.params_reconciliation_id
136
+ new_response.success.should be_true
137
+ new_response.message.should == 'Request was processed successfully.'
138
+ end
139
+
84
140
  it 'should be able to charge and refund' do
85
141
  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)
86
142
  payment_response.status.should eq(:PROCESSED), payment_response.gateway_error
@@ -156,4 +212,14 @@ describe Killbill::Cybersource::PaymentPlugin do
156
212
  payment_response.amount.should == @amount
157
213
  payment_response.transaction_type.should == :CREDIT
158
214
  end
215
+
216
+ # See https://github.com/killbill/killbill-cybersource-plugin/issues/4
217
+ it 'handles errors gracefully' do
218
+ properties_with_no_expiration_year = build_pm_properties
219
+ cc_exp_year = properties_with_no_expiration_year.find { |prop| prop.key == 'ccExpirationYear' }
220
+ cc_exp_year.value = nil
221
+
222
+ payment_response = @plugin.purchase_payment(@pm.kb_account_id, @kb_payment.id, @kb_payment.transactions[0].id, SecureRandom.uuid, @amount, @currency, properties_with_no_expiration_year, @call_context)
223
+ payment_response.status.should eq(:ERROR), payment_response.gateway_error
224
+ end
159
225
  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: 1.0.0
4
+ version: 2.0.0
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: 2015-05-08 00:00:00.000000000 Z
11
+ date: 2015-07-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: killbill
@@ -16,12 +16,12 @@ dependencies:
16
16
  requirements:
17
17
  - - ~>
18
18
  - !ruby/object:Gem::Version
19
- version: 4.0.0
19
+ version: 4.4.0
20
20
  requirement: !ruby/object:Gem::Requirement
21
21
  requirements:
22
22
  - - ~>
23
23
  - !ruby/object:Gem::Version
24
- version: 4.0.0
24
+ version: 4.4.0
25
25
  prerelease: false
26
26
  type: :runtime
27
27
  - !ruby/object:Gem::Dependency
@@ -290,6 +290,7 @@ files:
290
290
  - lib/cybersource/api.rb
291
291
  - lib/cybersource/application.rb
292
292
  - lib/cybersource/cyber_source_on_demand.rb
293
+ - lib/cybersource/ext/active_merchant/active_merchant.rb
293
294
  - lib/cybersource/models/payment_method.rb
294
295
  - lib/cybersource/models/response.rb
295
296
  - lib/cybersource/models/transaction.rb
@@ -298,6 +299,7 @@ files:
298
299
  - pom.xml
299
300
  - release.sh
300
301
  - spec/cybersource/base_plugin_spec.rb
302
+ - spec/cybersource/cyber_source_on_demand_spec.rb
301
303
  - spec/cybersource/remote/integration_spec.rb
302
304
  - spec/spec_helper.rb
303
305
  homepage: http://killbill.io
@@ -322,8 +324,12 @@ required_rubygems_version: !ruby/object:Gem::Requirement
322
324
  version: '0'
323
325
  requirements: []
324
326
  rubyforge_project:
325
- rubygems_version: 2.1.9
327
+ rubygems_version: 2.4.6
326
328
  signing_key:
327
329
  specification_version: 4
328
330
  summary: Plugin to use Cybersource as a gateway.
329
- test_files: []
331
+ test_files:
332
+ - spec/cybersource/base_plugin_spec.rb
333
+ - spec/cybersource/cyber_source_on_demand_spec.rb
334
+ - spec/cybersource/remote/integration_spec.rb
335
+ - spec/spec_helper.rb