killbill-cybersource 0.0.2 → 0.0.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 +4 -4
- data/Gemfile +0 -1
- data/NEWS +7 -0
- data/VERSION +1 -1
- data/db/ddl.sql +3 -3
- data/db/schema.rb +2 -2
- data/killbill-cybersource.gemspec +1 -1
- data/lib/cybersource/api.rb +28 -0
- data/lib/cybersource/cyber_source_on_demand.rb +46 -0
- data/lib/cybersource.rb +3 -0
- data/pom.xml +1 -1
- data/spec/cybersource/remote/integration_spec.rb +88 -30
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1afabdf22905f7fad77d239026e4a3182831b7d5
|
4
|
+
data.tar.gz: eee1798b1e2bf264a2bd96023076755f24aa8929
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ecc764a9caa40931a26495c1d4122b71c80a0467ae97c42ab25b3d83b4b04427409792e63d6c2e0148ca3e042b7d6dffc19113eaeff9aefc928b315b13ff2a63
|
7
|
+
data.tar.gz: 7a251ccb11d31b1a127ec6de804a8c4c22b672c23b9a09cb358ea5c4200ad2c64bcb477b947d565402e869ee24dcd8a99ddfe878cbc95d8ccdf825ee6ab0d576
|
data/Gemfile
CHANGED
data/NEWS
CHANGED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.4
|
data/db/ddl.sql
CHANGED
@@ -37,9 +37,9 @@ CREATE TABLE `cybersource_transactions` (
|
|
37
37
|
`kb_payment_id` varchar(255) NOT NULL,
|
38
38
|
`kb_payment_transaction_id` varchar(255) NOT NULL,
|
39
39
|
`transaction_type` varchar(255) NOT NULL,
|
40
|
-
`txn_id` varchar(255)
|
41
|
-
`amount_in_cents` int(11)
|
42
|
-
`currency` varchar(255)
|
40
|
+
`txn_id` varchar(255),
|
41
|
+
`amount_in_cents` int(11),
|
42
|
+
`currency` varchar(255),
|
43
43
|
`created_at` datetime NOT NULL,
|
44
44
|
`updated_at` datetime NOT NULL,
|
45
45
|
`kb_account_id` varchar(255) NOT NULL,
|
data/db/schema.rb
CHANGED
@@ -39,8 +39,8 @@ ActiveRecord::Schema.define(:version => 20140410153635) do
|
|
39
39
|
t.string "kb_payment_transaction_id", :null => false
|
40
40
|
t.string "transaction_type", :null => false
|
41
41
|
t.string "txn_id" # cybersource transaction id
|
42
|
-
t.integer "amount_in_cents"
|
43
|
-
t.string "currency"
|
42
|
+
t.integer "amount_in_cents"
|
43
|
+
t.string "currency"
|
44
44
|
t.datetime "created_at", :null => false
|
45
45
|
t.datetime "updated_at", :null => false
|
46
46
|
t.string "kb_account_id", :null => false
|
@@ -22,7 +22,7 @@ Gem::Specification.new do |s|
|
|
22
22
|
|
23
23
|
s.rdoc_options << '--exclude' << '.'
|
24
24
|
|
25
|
-
s.add_dependency 'killbill', '~> 3.1.
|
25
|
+
s.add_dependency 'killbill', '~> 3.1.11'
|
26
26
|
s.add_dependency 'activemerchant', '~> 1.42.9'
|
27
27
|
s.add_dependency 'activerecord', '~> 4.1.0'
|
28
28
|
s.add_dependency 'actionpack', '~> 4.1.0'
|
data/lib/cybersource/api.rb
CHANGED
@@ -14,6 +14,14 @@ module Killbill #:nodoc:
|
|
14
14
|
::Killbill::Cybersource::CybersourceResponse)
|
15
15
|
end
|
16
16
|
|
17
|
+
def start_plugin
|
18
|
+
super
|
19
|
+
gateway = lookup_gateway(:on_demand)
|
20
|
+
@report_api = CyberSourceOnDemand.new(gateway, logger)
|
21
|
+
rescue => e
|
22
|
+
@report_api = nil
|
23
|
+
end
|
24
|
+
|
17
25
|
def authorize_payment(kb_account_id, kb_payment_id, kb_payment_transaction_id, kb_payment_method_id, amount, currency, properties, context)
|
18
26
|
# Pass extra parameters for the gateway here
|
19
27
|
options = {}
|
@@ -153,6 +161,26 @@ module Killbill #:nodoc:
|
|
153
161
|
# gw_notification.entity =
|
154
162
|
end
|
155
163
|
end
|
164
|
+
|
165
|
+
# Make calls idempotent
|
166
|
+
def before_gateway(gateway, kb_transaction, last_transaction, payment_source, amount_in_cents, currency, options)
|
167
|
+
super
|
168
|
+
return if @report_api.nil?
|
169
|
+
|
170
|
+
merchant_reference_code = options[:order_id]
|
171
|
+
report = @report_api.single_transaction_report(merchant_reference_code, kb_transaction.created_date.strftime('%Y%m%d'))
|
172
|
+
|
173
|
+
if !report.nil? &&
|
174
|
+
!report['Report'].nil? &&
|
175
|
+
!report['Report']['Requests'].nil? &&
|
176
|
+
!report['Report']['Requests']['Request'].nil? &&
|
177
|
+
report['Report']['Requests']['Request']['MerchantReferenceNumber'] == merchant_reference_code
|
178
|
+
logger.info "Skipping gateway call for existing transaction #{kb_transaction.id}, merchant reference code #{merchant_reference_code}"
|
179
|
+
options[:skip_gw] = true
|
180
|
+
end
|
181
|
+
rescue => e
|
182
|
+
logger.warn "Error checking for duplicate payment: #{e.message}"
|
183
|
+
end
|
156
184
|
end
|
157
185
|
end
|
158
186
|
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Killbill #:nodoc:
|
2
|
+
module Cybersource #:nodoc:
|
3
|
+
class CyberSourceOnDemand
|
4
|
+
|
5
|
+
@@live_url = 'https://ebc.cybersource.com/ebc/Query'
|
6
|
+
@@test_url = 'https://ebctest.cybersource.com/ebctest/Query'
|
7
|
+
|
8
|
+
def initialize(gateway, logger)
|
9
|
+
@gateway = gateway
|
10
|
+
@logger = logger
|
11
|
+
end
|
12
|
+
|
13
|
+
def single_transaction_report(merchant_reference_code, target_date)
|
14
|
+
params = {
|
15
|
+
:merchantID => @gateway.config[:merchantID],
|
16
|
+
:merchantReferenceNumber => merchant_reference_code,
|
17
|
+
:targetDate => target_date,
|
18
|
+
:type => 'transaction',
|
19
|
+
:subtype => 'transactionDetail',
|
20
|
+
:versionNumber => '1.7',
|
21
|
+
}
|
22
|
+
|
23
|
+
headers = {
|
24
|
+
# Don't use symbols or it will confuse Net/HTTP
|
25
|
+
'Authorization' => 'Basic ' + Base64.encode64("#{@gateway.config[:username]}:#{@gateway.config[:password]}").chomp
|
26
|
+
}
|
27
|
+
|
28
|
+
data = URI.encode_www_form(params)
|
29
|
+
endpoint = @gateway.test? ? @@test_url : @@live_url
|
30
|
+
|
31
|
+
# Will raise ResponseError if the response code is > 300
|
32
|
+
parse(@gateway.ssl_post(endpoint, data, headers))
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
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
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
data/lib/cybersource.rb
CHANGED
@@ -3,17 +3,20 @@ require 'active_record'
|
|
3
3
|
require 'action_view'
|
4
4
|
require 'active_merchant'
|
5
5
|
require 'active_support'
|
6
|
+
require 'base64'
|
6
7
|
require 'bigdecimal'
|
7
8
|
require 'money'
|
8
9
|
require 'monetize'
|
9
10
|
require 'pathname'
|
10
11
|
require 'sinatra'
|
11
12
|
require 'singleton'
|
13
|
+
require 'uri'
|
12
14
|
require 'yaml'
|
13
15
|
|
14
16
|
require 'killbill'
|
15
17
|
require 'killbill/helpers/active_merchant'
|
16
18
|
|
19
|
+
require 'cybersource/cyber_source_on_demand'
|
17
20
|
require 'cybersource/api'
|
18
21
|
require 'cybersource/private_api'
|
19
22
|
|
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>0.0.
|
28
|
+
<version>0.0.4</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>
|
@@ -10,10 +10,11 @@ describe Killbill::Cybersource::PaymentPlugin do
|
|
10
10
|
@plugin = Killbill::Cybersource::PaymentPlugin.new
|
11
11
|
|
12
12
|
@account_api = ::Killbill::Plugin::ActiveMerchant::RSpec::FakeJavaUserAccountApi.new
|
13
|
-
|
13
|
+
@payment_api = ::Killbill::Plugin::ActiveMerchant::RSpec::FakeJavaPaymentApi.new
|
14
|
+
svcs = {:account_user_api => @account_api, :payment_api => @payment_api}
|
14
15
|
@plugin.kb_apis = Killbill::Plugin::KillbillApi.new('cybersource', svcs)
|
15
16
|
|
16
|
-
@call_context = Killbill::Plugin::Model::CallContext.new
|
17
|
+
@call_context = ::Killbill::Plugin::Model::CallContext.new
|
17
18
|
@call_context.tenant_id = '00000011-0022-0033-0044-000000000055'
|
18
19
|
@call_context = @call_context.to_ruby(@call_context)
|
19
20
|
|
@@ -21,72 +22,129 @@ describe Killbill::Cybersource::PaymentPlugin do
|
|
21
22
|
@plugin.logger.level = Logger::INFO
|
22
23
|
@plugin.conf_dir = File.expand_path(File.dirname(__FILE__) + '../../../../')
|
23
24
|
@plugin.start_plugin
|
25
|
+
|
26
|
+
@pm = create_payment_method(::Killbill::Cybersource::CybersourcePaymentMethod, nil, @call_context.tenant_id)
|
27
|
+
@amount = BigDecimal.new('100')
|
28
|
+
@currency = 'USD'
|
29
|
+
@properties = []
|
30
|
+
|
31
|
+
kb_payment_id = SecureRandom.uuid
|
32
|
+
1.upto(6) do
|
33
|
+
@kb_payment = @payment_api.add_payment(kb_payment_id)
|
34
|
+
end
|
24
35
|
end
|
25
36
|
|
26
37
|
after(:each) do
|
27
38
|
@plugin.stop_plugin
|
28
39
|
end
|
29
40
|
|
30
|
-
it 'should be able to charge a Credit Card directly' do
|
31
|
-
|
32
|
-
|
33
|
-
|
41
|
+
it 'should be able to charge a Credit Card directly and calls should be idempotent' do
|
42
|
+
# We created the payment method, hence the rows
|
43
|
+
Killbill::Cybersource::CybersourceResponse.all.size.should == 1
|
44
|
+
Killbill::Cybersource::CybersourceTransaction.all.size.should == 0
|
45
|
+
|
46
|
+
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)
|
47
|
+
payment_response.amount.should == @amount
|
48
|
+
payment_response.status.should == :PROCESSED
|
49
|
+
payment_response.transaction_type.should == :PURCHASE
|
34
50
|
|
35
|
-
|
36
|
-
|
51
|
+
responses = Killbill::Cybersource::CybersourceResponse.all
|
52
|
+
responses.size.should == 2
|
53
|
+
responses[0].api_call.should == 'add_payment_method'
|
54
|
+
responses[0].message.should == 'Successful transaction'
|
55
|
+
responses[1].api_call.should == 'purchase'
|
56
|
+
responses[1].message.should == 'Successful transaction'
|
57
|
+
transactions = Killbill::Cybersource::CybersourceTransaction.all
|
58
|
+
transactions.size.should == 1
|
59
|
+
transactions[0].api_call.should == 'purchase'
|
60
|
+
|
61
|
+
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)
|
62
|
+
payment_response.amount.should == @amount
|
37
63
|
payment_response.status.should == :PROCESSED
|
38
64
|
payment_response.transaction_type.should == :PURCHASE
|
65
|
+
|
66
|
+
responses = Killbill::Cybersource::CybersourceResponse.all
|
67
|
+
responses.size.should == 3
|
68
|
+
responses[0].api_call.should == 'add_payment_method'
|
69
|
+
responses[0].message.should == 'Successful transaction'
|
70
|
+
responses[1].api_call.should == 'purchase'
|
71
|
+
responses[1].message.should == 'Successful transaction'
|
72
|
+
responses[2].api_call.should == 'purchase'
|
73
|
+
responses[2].message.should == 'Skipped Gateway call'
|
74
|
+
transactions = Killbill::Cybersource::CybersourceTransaction.all
|
75
|
+
transactions.size.should == 2
|
76
|
+
transactions[0].api_call.should == 'purchase'
|
77
|
+
transactions[0].txn_id.should_not be_nil
|
78
|
+
transactions[1].api_call.should == 'purchase'
|
79
|
+
transactions[1].txn_id.should be_nil
|
39
80
|
end
|
40
81
|
|
41
82
|
it 'should be able to charge and refund' do
|
42
|
-
|
43
|
-
amount
|
44
|
-
currency = 'USD'
|
45
|
-
kb_payment_id = SecureRandom.uuid
|
46
|
-
kb_payment_transaction_id = SecureRandom.uuid
|
47
|
-
|
48
|
-
payment_response = @plugin.purchase_payment pm.kb_account_id, kb_payment_id, kb_payment_transaction_id, pm.kb_payment_method_id, amount, currency, [], @call_context
|
49
|
-
payment_response.amount.should == amount
|
83
|
+
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)
|
84
|
+
payment_response.amount.should == @amount
|
50
85
|
payment_response.status.should == :PROCESSED
|
51
86
|
payment_response.transaction_type.should == :PURCHASE
|
52
87
|
|
53
88
|
# Try a full refund
|
54
|
-
refund_response = @plugin.refund_payment
|
55
|
-
refund_response.amount.should == amount
|
89
|
+
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)
|
90
|
+
refund_response.amount.should == @amount
|
56
91
|
refund_response.status.should == :PROCESSED
|
57
92
|
refund_response.transaction_type.should == :REFUND
|
58
93
|
end
|
59
94
|
|
60
95
|
it 'should be able to auth, capture and refund' do
|
61
|
-
|
62
|
-
amount
|
63
|
-
currency = 'USD'
|
64
|
-
kb_payment_id = SecureRandom.uuid
|
65
|
-
|
66
|
-
payment_response = @plugin.authorize_payment pm.kb_account_id, kb_payment_id, SecureRandom.uuid, pm.kb_payment_method_id, amount, currency, [], @call_context
|
67
|
-
payment_response.amount.should == amount
|
96
|
+
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)
|
97
|
+
payment_response.amount.should == @amount
|
68
98
|
payment_response.status.should == :PROCESSED
|
69
99
|
payment_response.transaction_type.should == :AUTHORIZE
|
70
100
|
|
71
101
|
# Try multiple partial captures
|
72
|
-
partial_capture_amount = BigDecimal.new(
|
73
|
-
1.upto(3) do
|
74
|
-
payment_response = @plugin.capture_payment
|
102
|
+
partial_capture_amount = BigDecimal.new('10')
|
103
|
+
1.upto(3) do |i|
|
104
|
+
payment_response = @plugin.capture_payment(@pm.kb_account_id, @kb_payment.id, @kb_payment.transactions[i].id, @pm.kb_payment_method_id, partial_capture_amount, @currency, @properties, @call_context)
|
75
105
|
payment_response.amount.should == partial_capture_amount
|
76
106
|
payment_response.status.should == :PROCESSED
|
77
107
|
payment_response.transaction_type.should == :CAPTURE
|
78
108
|
end
|
79
109
|
|
80
110
|
# Try a partial refund
|
81
|
-
refund_response = @plugin.refund_payment
|
111
|
+
refund_response = @plugin.refund_payment(@pm.kb_account_id, @kb_payment.id, @kb_payment.transactions[4].id, @pm.kb_payment_method_id, partial_capture_amount, @currency, @properties, @call_context)
|
82
112
|
refund_response.amount.should == partial_capture_amount
|
83
113
|
refund_response.status.should == :PROCESSED
|
84
114
|
refund_response.transaction_type.should == :REFUND
|
85
115
|
|
86
116
|
# Try to capture again
|
87
|
-
payment_response = @plugin.capture_payment
|
117
|
+
payment_response = @plugin.capture_payment(@pm.kb_account_id, @kb_payment.id, @kb_payment.transactions[5].id, @pm.kb_payment_method_id, partial_capture_amount, @currency, @properties, @call_context)
|
118
|
+
payment_response.amount.should == partial_capture_amount
|
119
|
+
payment_response.status.should == :PROCESSED
|
120
|
+
payment_response.transaction_type.should == :CAPTURE
|
121
|
+
end
|
122
|
+
|
123
|
+
it 'should be able to auth and void' do
|
124
|
+
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)
|
125
|
+
payment_response.amount.should == @amount
|
126
|
+
payment_response.status.should == :PROCESSED
|
127
|
+
payment_response.transaction_type.should == :AUTHORIZE
|
128
|
+
|
129
|
+
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)
|
130
|
+
payment_response.status.should == :PROCESSED
|
131
|
+
payment_response.transaction_type.should == :VOID
|
132
|
+
end
|
133
|
+
|
134
|
+
it 'should be able to auth, partial capture and void' do
|
135
|
+
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)
|
136
|
+
payment_response.amount.should == @amount
|
137
|
+
payment_response.status.should == :PROCESSED
|
138
|
+
payment_response.transaction_type.should == :AUTHORIZE
|
139
|
+
|
140
|
+
partial_capture_amount = BigDecimal.new('10')
|
141
|
+
payment_response = @plugin.capture_payment(@pm.kb_account_id, @kb_payment.id, @kb_payment.transactions[1].id, @pm.kb_payment_method_id, partial_capture_amount, @currency, @properties, @call_context)
|
88
142
|
payment_response.amount.should == partial_capture_amount
|
89
143
|
payment_response.status.should == :PROCESSED
|
90
144
|
payment_response.transaction_type.should == :CAPTURE
|
145
|
+
|
146
|
+
payment_response = @plugin.void_payment(@pm.kb_account_id, @kb_payment.id, @kb_payment.transactions[2].id, @pm.kb_payment_method_id, @properties, @call_context)
|
147
|
+
payment_response.status.should == :PROCESSED
|
148
|
+
payment_response.transaction_type.should == :VOID
|
91
149
|
end
|
92
150
|
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: 0.0.
|
4
|
+
version: 0.0.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: 2014-
|
11
|
+
date: 2014-10-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: 3.1.
|
19
|
+
version: 3.1.11
|
20
20
|
requirement: !ruby/object:Gem::Requirement
|
21
21
|
requirements:
|
22
22
|
- - ~>
|
23
23
|
- !ruby/object:Gem::Version
|
24
|
-
version: 3.1.
|
24
|
+
version: 3.1.11
|
25
25
|
prerelease: false
|
26
26
|
type: :runtime
|
27
27
|
- !ruby/object:Gem::Dependency
|
@@ -244,6 +244,7 @@ files:
|
|
244
244
|
- lib/cybersource.rb
|
245
245
|
- lib/cybersource/api.rb
|
246
246
|
- lib/cybersource/application.rb
|
247
|
+
- lib/cybersource/cyber_source_on_demand.rb
|
247
248
|
- lib/cybersource/models/payment_method.rb
|
248
249
|
- lib/cybersource/models/response.rb
|
249
250
|
- lib/cybersource/models/transaction.rb
|