killbill 3.1.11 → 3.1.12
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/Jarfile +6 -6
- data/NEWS +6 -0
- data/VERSION +1 -1
- data/generators/active_merchant/templates/Jarfile.rb +7 -7
- data/generators/active_merchant/templates/db/ddl.sql.rb +2 -0
- data/generators/active_merchant/templates/db/schema.rb +2 -0
- data/generators/active_merchant/templates/lib/application.rb +1 -5
- data/generators/active_merchant/templates/lib/models/response.rb +2 -1
- data/generators/active_merchant/templates/lib/plugin.rb +1 -0
- data/generators/active_merchant/templates/lib/private_api.rb +7 -0
- data/generators/active_merchant/templates/plugin.gemspec.rb +3 -2
- data/generators/active_merchant/templates/spec/integration_spec.rb +103 -2
- data/killbill.gemspec +4 -1
- data/lib/killbill/gen/api/dry_run_arguments.rb +100 -0
- data/lib/killbill/gen/api/invoice_user_api.rb +5 -5
- data/lib/killbill/gen/api/killbill_api.rb +19 -18
- data/lib/killbill/gen/api/payment_options.rb +17 -5
- data/lib/killbill/gen/api/require_gen.rb +5 -0
- data/lib/killbill/gen/api/rolled_up_unit.rb +63 -0
- data/lib/killbill/gen/api/rolled_up_usage.rb +31 -35
- data/lib/killbill/gen/api/static_catalog.rb +8 -8
- data/lib/killbill/gen/api/subscription_usage_record.rb +75 -0
- data/lib/killbill/gen/api/unit_usage_record.rb +74 -0
- data/lib/killbill/gen/api/usage_record.rb +66 -0
- data/lib/killbill/gen/api/usage_user_api.rb +22 -56
- data/lib/killbill/gen/plugin-api/currency_plugin_api.rb +19 -18
- data/lib/killbill/gen/plugin-api/ext_bus_event.rb +19 -18
- data/lib/killbill/gen/plugin-api/gateway_notification.rb +19 -18
- data/lib/killbill/gen/plugin-api/hosted_payment_page_form_descriptor.rb +19 -18
- data/lib/killbill/gen/plugin-api/notification_plugin_api.rb +19 -18
- data/lib/killbill/gen/plugin-api/payment_method_info_plugin.rb +19 -18
- data/lib/killbill/gen/plugin-api/payment_plugin_api.rb +19 -18
- data/lib/killbill/gen/plugin-api/payment_plugin_api_exception.rb +19 -18
- data/lib/killbill/gen/plugin-api/payment_transaction_info_plugin.rb +19 -18
- data/lib/killbill/gen/plugin-api/require_gen.rb +19 -18
- data/lib/killbill/helpers/active_merchant/active_record/models/payment_method.rb +4 -0
- data/lib/killbill/helpers/active_merchant/active_record/models/response.rb +65 -34
- data/lib/killbill/helpers/active_merchant/active_record/models/streamy_result_set.rb +14 -2
- data/lib/killbill/helpers/active_merchant/active_record/models/transaction.rb +9 -5
- data/lib/killbill/helpers/active_merchant/killbill_spec_helper.rb +28 -23
- data/lib/killbill/helpers/active_merchant/payment_plugin.rb +66 -36
- data/lib/killbill/helpers/active_merchant/private_payment_plugin.rb +18 -31
- data/lib/killbill/helpers/active_merchant/sinatra.rb +7 -2
- data/lib/killbill/helpers/active_merchant/utils.rb +12 -0
- data/spec/killbill/helpers/killbill_spec_helper_spec.rb +24 -0
- data/spec/killbill/helpers/payment_method_spec.rb +5 -0
- data/spec/killbill/helpers/payment_plugin_spec.rb +33 -2
- data/spec/killbill/helpers/private_payment_plugin_spec.rb +83 -0
- data/spec/killbill/helpers/response_spec.rb +47 -24
- data/spec/killbill/helpers/streamy_result_set_spec.rb +38 -0
- data/spec/killbill/helpers/test_schema.rb +2 -0
- metadata +57 -8
- data/lib/killbill/gen/api/payment_attempt.rb +0 -117
- data/lib/killbill/gen/api/payment_method_kv_info.rb +0 -71
- data/lib/killbill/gen/plugin-api/billing_address.rb +0 -85
- data/lib/killbill/gen/plugin-api/customer.rb +0 -73
@@ -1,21 +1,22 @@
|
|
1
|
-
|
2
|
-
#
|
3
|
-
# Copyright 2010-2013 Ning, Inc.
|
4
|
-
# Copyright 2014
|
5
|
-
#
|
6
|
-
#
|
7
|
-
#
|
8
|
-
#
|
9
|
-
#
|
10
|
-
#
|
11
|
-
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
17
|
-
#
|
18
|
-
|
1
|
+
#############################################################################################
|
2
|
+
# #
|
3
|
+
# Copyright 2010-2013 Ning, Inc. #
|
4
|
+
# Copyright 2014 Groupon, Inc. #
|
5
|
+
# Copyright 2014 The Billing Project, LLC #
|
6
|
+
# #
|
7
|
+
# The Billing Project licenses this file to you under the Apache License, version 2.0 #
|
8
|
+
# (the "License"); you may not use this file except in compliance with the #
|
9
|
+
# License. You may obtain a copy of the License at: #
|
10
|
+
# #
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0 #
|
12
|
+
# #
|
13
|
+
# Unless required by applicable law or agreed to in writing, software #
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT #
|
15
|
+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the #
|
16
|
+
# License for the specific language governing permissions and limitations #
|
17
|
+
# under the License. #
|
18
|
+
# #
|
19
|
+
#############################################################################################
|
19
20
|
|
20
21
|
|
21
22
|
#
|
@@ -1,21 +1,22 @@
|
|
1
|
-
|
2
|
-
#
|
3
|
-
# Copyright 2010-2013 Ning, Inc.
|
4
|
-
# Copyright 2014
|
5
|
-
#
|
6
|
-
#
|
7
|
-
#
|
8
|
-
#
|
9
|
-
#
|
10
|
-
#
|
11
|
-
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
17
|
-
#
|
18
|
-
|
1
|
+
#############################################################################################
|
2
|
+
# #
|
3
|
+
# Copyright 2010-2013 Ning, Inc. #
|
4
|
+
# Copyright 2014 Groupon, Inc. #
|
5
|
+
# Copyright 2014 The Billing Project, LLC #
|
6
|
+
# #
|
7
|
+
# The Billing Project licenses this file to you under the Apache License, version 2.0 #
|
8
|
+
# (the "License"); you may not use this file except in compliance with the #
|
9
|
+
# License. You may obtain a copy of the License at: #
|
10
|
+
# #
|
11
|
+
# http://www.apache.org/licenses/LICENSE-2.0 #
|
12
|
+
# #
|
13
|
+
# Unless required by applicable law or agreed to in writing, software #
|
14
|
+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT #
|
15
|
+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the #
|
16
|
+
# License for the specific language governing permissions and limitations #
|
17
|
+
# under the License. #
|
18
|
+
# #
|
19
|
+
#############################################################################################
|
19
20
|
|
20
21
|
|
21
22
|
#
|
@@ -48,6 +48,10 @@ module Killbill
|
|
48
48
|
end
|
49
49
|
end
|
50
50
|
|
51
|
+
def self.from_kb_account_id_and_token(token, kb_account_id, kb_tenant_id)
|
52
|
+
from_kb_account_id(kb_account_id, kb_tenant_id).where("token = #{@@quotes_cache[token]}")
|
53
|
+
end
|
54
|
+
|
51
55
|
def self.from_kb_payment_method_id(kb_payment_method_id, kb_tenant_id)
|
52
56
|
if kb_tenant_id.nil?
|
53
57
|
payment_methods = where("kb_payment_method_id = #{@@quotes_cache[kb_payment_method_id]} AND kb_tenant_id is NULL AND is_deleted = #{@@quotes_cache[false]}")
|
@@ -14,40 +14,43 @@ module Killbill
|
|
14
14
|
|
15
15
|
self.abstract_class = true
|
16
16
|
|
17
|
-
|
17
|
+
@@quotes_cache = build_quotes_cache
|
18
|
+
|
19
|
+
def self.from_response(api_call, kb_account_id, kb_payment_id, kb_payment_transaction_id, transaction_type, payment_processor_account_id, kb_tenant_id, response, extra_params = {}, model = Response)
|
18
20
|
# Under high load, Rails sometimes fails to set timestamps. Unclear why...
|
19
21
|
current_time = Time.now.utc
|
20
22
|
model.new({
|
21
|
-
:api_call
|
22
|
-
:kb_account_id
|
23
|
-
:kb_payment_id
|
24
|
-
:kb_payment_transaction_id
|
25
|
-
:transaction_type
|
26
|
-
:
|
27
|
-
:
|
28
|
-
:
|
29
|
-
:
|
30
|
-
:
|
31
|
-
:
|
32
|
-
:
|
33
|
-
:
|
34
|
-
:
|
35
|
-
:
|
36
|
-
:
|
37
|
-
:
|
38
|
-
:
|
39
|
-
:
|
23
|
+
:api_call => api_call,
|
24
|
+
:kb_account_id => kb_account_id,
|
25
|
+
:kb_payment_id => kb_payment_id,
|
26
|
+
:kb_payment_transaction_id => kb_payment_transaction_id,
|
27
|
+
:transaction_type => transaction_type,
|
28
|
+
:payment_processor_account_id => payment_processor_account_id,
|
29
|
+
:kb_tenant_id => kb_tenant_id,
|
30
|
+
:message => response.message,
|
31
|
+
:authorization => response.authorization,
|
32
|
+
:fraud_review => response.fraud_review?,
|
33
|
+
:test => response.test?,
|
34
|
+
:avs_result_code => response.avs_result.kind_of?(::ActiveMerchant::Billing::AVSResult) ? response.avs_result.code : response.avs_result['code'],
|
35
|
+
:avs_result_message => response.avs_result.kind_of?(::ActiveMerchant::Billing::AVSResult) ? response.avs_result.message : response.avs_result['message'],
|
36
|
+
:avs_result_street_match => response.avs_result.kind_of?(::ActiveMerchant::Billing::AVSResult) ? response.avs_result.street_match : response.avs_result['street_match'],
|
37
|
+
:avs_result_postal_match => response.avs_result.kind_of?(::ActiveMerchant::Billing::AVSResult) ? response.avs_result.postal_match : response.avs_result['postal_match'],
|
38
|
+
:cvv_result_code => response.cvv_result.kind_of?(::ActiveMerchant::Billing::CVVResult) ? response.cvv_result.code : response.cvv_result['code'],
|
39
|
+
:cvv_result_message => response.cvv_result.kind_of?(::ActiveMerchant::Billing::CVVResult) ? response.cvv_result.message : response.cvv_result['message'],
|
40
|
+
:success => response.success?,
|
41
|
+
:created_at => current_time,
|
42
|
+
:updated_at => current_time
|
40
43
|
}.merge!(extra_params))
|
41
44
|
end
|
42
45
|
|
43
|
-
def self.create_response_and_transaction(identifier, transaction_model, api_call, kb_account_id, kb_payment_id, kb_payment_transaction_id, transaction_type, kb_tenant_id, gw_response, amount_in_cents, currency, extra_params = {}, model = Response)
|
46
|
+
def self.create_response_and_transaction(identifier, transaction_model, api_call, kb_account_id, kb_payment_id, kb_payment_transaction_id, transaction_type, payment_processor_account_id, kb_tenant_id, gw_response, amount_in_cents, currency, extra_params = {}, model = Response)
|
44
47
|
response, transaction, exception = nil
|
45
48
|
|
46
49
|
# Rails wraps all create/save calls in a transaction. To speed things up, create a single transaction for both rows.
|
47
50
|
# This has a small gotcha in the unhappy path though (see below).
|
48
51
|
transaction do
|
49
52
|
# Save the response to our logs
|
50
|
-
response = from_response(api_call, kb_account_id, kb_payment_id, kb_payment_transaction_id, transaction_type, kb_tenant_id, gw_response, extra_params, model)
|
53
|
+
response = from_response(api_call, kb_account_id, kb_payment_id, kb_payment_transaction_id, transaction_type, payment_processor_account_id, kb_tenant_id, gw_response, extra_params, model)
|
51
54
|
response.save!(shared_activerecord_options)
|
52
55
|
|
53
56
|
transaction = nil
|
@@ -58,19 +61,20 @@ module Killbill
|
|
58
61
|
begin
|
59
62
|
# Originally, we used response.send("build_#{identifier}_transaction"), but the ActiveRecord magic was adding
|
60
63
|
# about 20% overhead - instead, we now construct the transaction record manually
|
61
|
-
transaction = transaction_model.new(:kb_account_id
|
62
|
-
:kb_tenant_id
|
63
|
-
:amount_in_cents
|
64
|
-
:currency
|
65
|
-
:api_call
|
66
|
-
:kb_payment_id
|
67
|
-
:kb_payment_transaction_id
|
68
|
-
:transaction_type
|
69
|
-
:
|
70
|
-
|
64
|
+
transaction = transaction_model.new(:kb_account_id => kb_account_id,
|
65
|
+
:kb_tenant_id => kb_tenant_id,
|
66
|
+
:amount_in_cents => amount_in_cents,
|
67
|
+
:currency => currency,
|
68
|
+
:api_call => api_call,
|
69
|
+
:kb_payment_id => kb_payment_id,
|
70
|
+
:kb_payment_transaction_id => kb_payment_transaction_id,
|
71
|
+
:transaction_type => transaction_type,
|
72
|
+
:payment_processor_account_id => payment_processor_account_id,
|
73
|
+
:txn_id => txn_id,
|
74
|
+
"#{identifier}_response_id" => response.id,
|
71
75
|
# See Response#from_response
|
72
|
-
:created_at
|
73
|
-
:updated_at
|
76
|
+
:created_at => response.created_at,
|
77
|
+
:updated_at => response.updated_at)
|
74
78
|
transaction.save!(shared_activerecord_options)
|
75
79
|
rescue => e
|
76
80
|
exception = e
|
@@ -109,6 +113,7 @@ module Killbill
|
|
109
113
|
t_info_plugin.second_payment_reference_id = second_reference_id
|
110
114
|
|
111
115
|
properties = []
|
116
|
+
properties << create_plugin_property('payment_processor_account_id', payment_processor_account_id)
|
112
117
|
properties << create_plugin_property('message', message)
|
113
118
|
properties << create_plugin_property('authorization', authorization)
|
114
119
|
properties << create_plugin_property('fraudReview', fraud_review)
|
@@ -217,6 +222,32 @@ module Killbill
|
|
217
222
|
prop.value = value
|
218
223
|
prop
|
219
224
|
end
|
225
|
+
|
226
|
+
class << self
|
227
|
+
[:kb_payment_id, :kb_payment_transaction_id].each do |attribute|
|
228
|
+
define_method("responses_from_#{attribute.to_s}") do |transaction_type, attribute_value, kb_tenant_id, how_many = :multiple|
|
229
|
+
if kb_tenant_id.nil?
|
230
|
+
if transaction_type.nil?
|
231
|
+
transactions = where("kb_tenant_id is NULL AND #{attribute.to_s} = ?", attribute_value).order(:created_at)
|
232
|
+
else
|
233
|
+
transactions = where("transaction_type = #{@@quotes_cache[transaction_type]} AND kb_tenant_id is NULL AND #{attribute.to_s} = #{@@quotes_cache[attribute_value]}").order(:created_at)
|
234
|
+
end
|
235
|
+
else
|
236
|
+
if transaction_type.nil?
|
237
|
+
transactions = where("kb_tenant_id = #{@@quotes_cache[kb_tenant_id]} AND #{attribute.to_s} = #{@@quotes_cache[attribute_value]}").order(:created_at)
|
238
|
+
else
|
239
|
+
transactions = where("transaction_type = #{@@quotes_cache[transaction_type]} AND kb_tenant_id = #{@@quotes_cache[kb_tenant_id]} AND #{attribute.to_s} = #{@@quotes_cache[attribute_value]}").order(:created_at)
|
240
|
+
end
|
241
|
+
end
|
242
|
+
if how_many == :single
|
243
|
+
raise "Kill Bill #{attribute} = #{attribute_value} mapping to multiple plugin transactions" if transactions.size > 1
|
244
|
+
transactions[0]
|
245
|
+
else
|
246
|
+
transactions
|
247
|
+
end
|
248
|
+
end
|
249
|
+
end
|
250
|
+
end
|
220
251
|
end
|
221
252
|
end
|
222
253
|
end
|
@@ -21,15 +21,27 @@ module Killbill
|
|
21
21
|
# Optimization: bail out if no more results
|
22
22
|
break if result.nil? || result.empty?
|
23
23
|
end if @batch > 0
|
24
|
+
ensure
|
24
25
|
# Make sure to return DB connections to the Pool
|
25
|
-
|
26
|
+
close_connection
|
26
27
|
end
|
27
28
|
|
28
29
|
def to_a
|
29
30
|
super.to_a.flatten
|
30
31
|
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def close_connection
|
36
|
+
pool = ::ActiveRecord::Base.connection_pool
|
37
|
+
return unless pool.active_connection?
|
38
|
+
|
39
|
+
connection = ::ActiveRecord::Base.connection
|
40
|
+
pool.remove(connection)
|
41
|
+
connection.disconnect!
|
42
|
+
end
|
31
43
|
end
|
32
44
|
end
|
33
45
|
end
|
34
46
|
end
|
35
|
-
end
|
47
|
+
end
|
@@ -41,11 +41,15 @@ module Killbill
|
|
41
41
|
transaction_from_kb_payment_transaction_id(nil, kb_payment_transaction_id, kb_tenant_id, :single)
|
42
42
|
end
|
43
43
|
|
44
|
-
def find_candidate_transaction_for_refund(kb_payment_id, kb_tenant_id, amount_in_cents)
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
44
|
+
def find_candidate_transaction_for_refund(kb_payment_id, kb_tenant_id, amount_in_cents, transaction_type = nil)
|
45
|
+
if transaction_type.nil?
|
46
|
+
begin
|
47
|
+
do_find_candidate_transaction_for_refund(:authorize, kb_payment_id, kb_tenant_id, amount_in_cents)
|
48
|
+
rescue
|
49
|
+
do_find_candidate_transaction_for_refund(:purchase, kb_payment_id, kb_tenant_id, amount_in_cents)
|
50
|
+
end
|
51
|
+
else
|
52
|
+
do_find_candidate_transaction_for_refund(transaction_type, kb_payment_id, kb_tenant_id, amount_in_cents)
|
49
53
|
end
|
50
54
|
end
|
51
55
|
|
@@ -3,7 +3,7 @@ module Killbill
|
|
3
3
|
module ActiveMerchant
|
4
4
|
module RSpec
|
5
5
|
|
6
|
-
def create_payment_method(payment_method_model=::Killbill::Plugin::ActiveMerchant::ActiveRecord::PaymentMethod, kb_account_id=nil, kb_tenant_id=nil)
|
6
|
+
def create_payment_method(payment_method_model=::Killbill::Plugin::ActiveMerchant::ActiveRecord::PaymentMethod, kb_account_id=nil, kb_tenant_id=nil, properties = [], options = {})
|
7
7
|
kb_payment_method_id = SecureRandom.uuid
|
8
8
|
|
9
9
|
if kb_account_id.nil?
|
@@ -13,20 +13,20 @@ module Killbill
|
|
13
13
|
create_kb_account kb_account_id
|
14
14
|
end
|
15
15
|
|
16
|
-
context
|
17
|
-
account
|
16
|
+
context = @plugin.kb_apis.create_context(kb_tenant_id)
|
17
|
+
account = @plugin.kb_apis.account_user_api.get_account_by_id(kb_account_id, context)
|
18
18
|
|
19
19
|
# The rest is pure Ruby
|
20
|
-
context
|
20
|
+
context = context.to_ruby(context)
|
21
21
|
|
22
22
|
# Generate a token
|
23
|
-
|
23
|
+
pm_properties = build_pm_properties(account, options)
|
24
24
|
|
25
25
|
info = Killbill::Plugin::Model::PaymentMethodPlugin.new
|
26
|
-
info.properties =
|
27
|
-
payment_method = @plugin.add_payment_method(kb_account_id, kb_payment_method_id, info, true,
|
26
|
+
info.properties = pm_properties
|
27
|
+
payment_method = @plugin.add_payment_method(kb_account_id, kb_payment_method_id, info, true, properties, context)
|
28
28
|
|
29
|
-
pm = payment_method_model.from_kb_payment_method_id
|
29
|
+
pm = payment_method_model.from_kb_payment_method_id(kb_payment_method_id, context.tenant_id)
|
30
30
|
pm.should == payment_method
|
31
31
|
pm.kb_account_id.should == kb_account_id
|
32
32
|
pm.kb_payment_method_id.should == kb_payment_method_id
|
@@ -34,21 +34,21 @@ module Killbill
|
|
34
34
|
pm
|
35
35
|
end
|
36
36
|
|
37
|
-
def build_pm_properties(account = nil)
|
38
|
-
cc_number = '4242424242424242'
|
39
|
-
cc_first_name = 'John'
|
40
|
-
cc_last_name = 'Doe'
|
41
|
-
cc_type = 'Visa'
|
42
|
-
cc_exp_month = 12
|
43
|
-
cc_exp_year = 2017
|
44
|
-
cc_last_4 = 4242
|
45
|
-
address1 = '5, oakriu road'
|
46
|
-
address2 = 'apt. 298'
|
47
|
-
city = 'Gdio Foia'
|
48
|
-
state = 'FL'
|
49
|
-
zip = 49302
|
50
|
-
country = 'US'
|
51
|
-
cc_verification_value = 1234
|
37
|
+
def build_pm_properties(account = nil, overrides = {})
|
38
|
+
cc_number = (overrides.delete(:cc_number) || '4242424242424242')
|
39
|
+
cc_first_name = (overrides.delete(:cc_first_name) || 'John')
|
40
|
+
cc_last_name = (overrides.delete(:cc_last_name) || 'Doe')
|
41
|
+
cc_type = (overrides.delete(:cc_type) || 'Visa')
|
42
|
+
cc_exp_month = (overrides.delete(:cc_exp_month) || 12)
|
43
|
+
cc_exp_year = (overrides.delete(:cc_exp_year) || 2017)
|
44
|
+
cc_last_4 = (overrides.delete(:cc_last_4) || 4242)
|
45
|
+
address1 = (overrides.delete(:address1) || '5, oakriu road')
|
46
|
+
address2 = (overrides.delete(:address2) || 'apt. 298')
|
47
|
+
city = (overrides.delete(:city) || 'Gdio Foia')
|
48
|
+
state = (overrides.delete(:state) || 'FL')
|
49
|
+
zip = (overrides.delete(:zip) || 49302)
|
50
|
+
country = (overrides.delete(:country) || 'US')
|
51
|
+
cc_verification_value = (overrides.delete(:cc_verification_value) || 1234)
|
52
52
|
|
53
53
|
properties = []
|
54
54
|
properties << create_pm_kv_info('ccNumber', cc_number)
|
@@ -66,6 +66,11 @@ module Killbill
|
|
66
66
|
properties << create_pm_kv_info('zip', zip)
|
67
67
|
properties << create_pm_kv_info('country', country)
|
68
68
|
properties << create_pm_kv_info('ccVerificationValue', cc_verification_value)
|
69
|
+
|
70
|
+
overrides.each do |key, value|
|
71
|
+
properties << create_pm_kv_info(key, value)
|
72
|
+
end
|
73
|
+
|
69
74
|
properties
|
70
75
|
end
|
71
76
|
|
@@ -1,9 +1,11 @@
|
|
1
1
|
module Killbill
|
2
2
|
module Plugin
|
3
3
|
module ActiveMerchant
|
4
|
+
require 'active_merchant'
|
4
5
|
require 'active_record'
|
5
6
|
require 'monetize'
|
6
7
|
require 'money'
|
8
|
+
require 'offsite_payments'
|
7
9
|
|
8
10
|
class PaymentPlugin < ::Killbill::Plugin::Payment
|
9
11
|
|
@@ -44,7 +46,7 @@ module Killbill
|
|
44
46
|
end
|
45
47
|
|
46
48
|
def authorize_payment(kb_account_id, kb_payment_id, kb_payment_transaction_id, kb_payment_method_id, amount, currency, properties, context)
|
47
|
-
gateway_call_proc = Proc.new do |gateway, payment_source, amount_in_cents, options|
|
49
|
+
gateway_call_proc = Proc.new do |gateway, linked_transaction, payment_source, amount_in_cents, options|
|
48
50
|
gateway.authorize(amount_in_cents, payment_source, options)
|
49
51
|
end
|
50
52
|
|
@@ -52,18 +54,22 @@ module Killbill
|
|
52
54
|
end
|
53
55
|
|
54
56
|
def capture_payment(kb_account_id, kb_payment_id, kb_payment_transaction_id, kb_payment_method_id, amount, currency, properties, context)
|
55
|
-
gateway_call_proc = Proc.new do |gateway, payment_source, amount_in_cents, options|
|
57
|
+
gateway_call_proc = Proc.new do |gateway, linked_transaction, payment_source, amount_in_cents, options|
|
58
|
+
gateway.capture(amount_in_cents, linked_transaction.txn_id, options)
|
59
|
+
end
|
60
|
+
|
61
|
+
linked_transaction_proc = Proc.new do |amount_in_cents, options|
|
56
62
|
# TODO We use the last transaction at the moment, is it good enough?
|
57
63
|
last_authorization = @transaction_model.authorizations_from_kb_payment_id(kb_payment_id, context.tenant_id).last
|
58
64
|
raise "Unable to retrieve last authorization for operation=capture, kb_payment_id=#{kb_payment_id}, kb_payment_transaction_id=#{kb_payment_transaction_id}, kb_payment_method_id=#{kb_payment_method_id}" if last_authorization.nil?
|
59
|
-
|
65
|
+
last_authorization
|
60
66
|
end
|
61
67
|
|
62
|
-
dispatch_to_gateways(:capture, kb_account_id, kb_payment_id, kb_payment_transaction_id, kb_payment_method_id, amount, currency, properties, context, gateway_call_proc)
|
68
|
+
dispatch_to_gateways(:capture, kb_account_id, kb_payment_id, kb_payment_transaction_id, kb_payment_method_id, amount, currency, properties, context, gateway_call_proc, linked_transaction_proc)
|
63
69
|
end
|
64
70
|
|
65
71
|
def purchase_payment(kb_account_id, kb_payment_id, kb_payment_transaction_id, kb_payment_method_id, amount, currency, properties, context)
|
66
|
-
gateway_call_proc = Proc.new do |gateway, payment_source, amount_in_cents, options|
|
72
|
+
gateway_call_proc = Proc.new do |gateway, linked_transaction, payment_source, amount_in_cents, options|
|
67
73
|
gateway.purchase(amount_in_cents, payment_source, options)
|
68
74
|
end
|
69
75
|
|
@@ -71,31 +77,41 @@ module Killbill
|
|
71
77
|
end
|
72
78
|
|
73
79
|
def void_payment(kb_account_id, kb_payment_id, kb_payment_transaction_id, kb_payment_method_id, properties, context)
|
74
|
-
gateway_call_proc = Proc.new do |gateway, payment_source, amount_in_cents, options|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
+
gateway_call_proc = Proc.new do |gateway, linked_transaction, payment_source, amount_in_cents, options|
|
81
|
+
authorization = linked_transaction.txn_id
|
82
|
+
|
83
|
+
# Go to the gateway - while some gateways implementations are smart and have void support 'auth_reversal' and 'void' (e.g. Litle),
|
84
|
+
# others (e.g. CyberSource) implement different methods
|
85
|
+
linked_transaction.transaction_type == 'AUTHORIZE' && gateway.respond_to?(:auth_reversal) ? gateway.auth_reversal(linked_transaction.amount_in_cents, authorization, options) : gateway.void(authorization, options)
|
86
|
+
end
|
87
|
+
|
88
|
+
linked_transaction_proc = Proc.new do |amount_in_cents, options|
|
89
|
+
linked_transaction_type = find_value_from_properties(properties, :linked_transaction_type)
|
90
|
+
if linked_transaction_type.nil?
|
91
|
+
# Default behavior to search for the last transaction
|
92
|
+
# If an authorization is being voided, we're performing an 'auth_reversal', otherwise,
|
93
|
+
# we're voiding an unsettled capture or purchase (which often needs to happen within 24 hours).
|
94
|
+
last_transaction = @transaction_model.purchases_from_kb_payment_id(kb_payment_id, context.tenant_id).last
|
80
95
|
if last_transaction.nil?
|
81
|
-
last_transaction = @transaction_model.
|
96
|
+
last_transaction = @transaction_model.captures_from_kb_payment_id(kb_payment_id, context.tenant_id).last
|
82
97
|
if last_transaction.nil?
|
83
|
-
|
98
|
+
last_transaction = @transaction_model.authorizations_from_kb_payment_id(kb_payment_id, context.tenant_id).last
|
99
|
+
if last_transaction.nil?
|
100
|
+
raise ArgumentError.new("Kill Bill payment #{kb_payment_id} has no auth, capture or purchase, thus cannot be voided")
|
101
|
+
end
|
84
102
|
end
|
85
103
|
end
|
104
|
+
else
|
105
|
+
last_transaction = @transaction_model.send("#{linked_transaction_type.to_s}s_from_kb_payment_id", kb_payment_id, context.tenant_id).last
|
86
106
|
end
|
87
|
-
|
88
|
-
|
89
|
-
# Go to the gateway - while some gateways implementations are smart and have void support 'auth_reversal' and 'void' (e.g. Litle),
|
90
|
-
# others (e.g. CyberSource) implement different methods
|
91
|
-
last_transaction.transaction_type == 'AUTHORIZE' && gateway.respond_to?(:auth_reversal) ? gateway.auth_reversal(last_transaction.amount_in_cents, authorization, options) : gateway.void(authorization, options)
|
107
|
+
last_transaction
|
92
108
|
end
|
93
109
|
|
94
|
-
dispatch_to_gateways(:void, kb_account_id, kb_payment_id, kb_payment_transaction_id, kb_payment_method_id, nil, nil, properties, context, gateway_call_proc)
|
110
|
+
dispatch_to_gateways(:void, kb_account_id, kb_payment_id, kb_payment_transaction_id, kb_payment_method_id, nil, nil, properties, context, gateway_call_proc, linked_transaction_proc)
|
95
111
|
end
|
96
112
|
|
97
113
|
def credit_payment(kb_account_id, kb_payment_id, kb_payment_transaction_id, kb_payment_method_id, amount, currency, properties, context)
|
98
|
-
gateway_call_proc = Proc.new do |gateway, payment_source, amount_in_cents, options|
|
114
|
+
gateway_call_proc = Proc.new do |gateway, linked_transaction, payment_source, amount_in_cents, options|
|
99
115
|
gateway.credit(amount_in_cents, payment_source, options)
|
100
116
|
end
|
101
117
|
|
@@ -103,13 +119,18 @@ module Killbill
|
|
103
119
|
end
|
104
120
|
|
105
121
|
def refund_payment(kb_account_id, kb_payment_id, kb_payment_transaction_id, kb_payment_method_id, amount, currency, properties, context)
|
106
|
-
gateway_call_proc = Proc.new do |gateway, payment_source, amount_in_cents, options|
|
107
|
-
|
108
|
-
raise "Unable to retrieve transaction to refund for operation=capture, kb_payment_id=#{kb_payment_id}, kb_payment_transaction_id=#{kb_payment_transaction_id}, kb_payment_method_id=#{kb_payment_method_id}" if transaction.nil?
|
109
|
-
gateway.refund(amount_in_cents, transaction.txn_id, options)
|
122
|
+
gateway_call_proc = Proc.new do |gateway, linked_transaction, payment_source, amount_in_cents, options|
|
123
|
+
gateway.refund(amount_in_cents, linked_transaction.txn_id, options)
|
110
124
|
end
|
111
125
|
|
112
|
-
|
126
|
+
linked_transaction_proc = Proc.new do |amount_in_cents, options|
|
127
|
+
linked_transaction_type = find_value_from_properties(properties, :linked_transaction_type)
|
128
|
+
transaction = @transaction_model.find_candidate_transaction_for_refund(kb_payment_id, context.tenant_id, amount_in_cents, linked_transaction_type)
|
129
|
+
raise "Unable to retrieve transaction to refund for operation=refund, kb_payment_id=#{kb_payment_id}, kb_payment_transaction_id=#{kb_payment_transaction_id}, kb_payment_method_id=#{kb_payment_method_id}" if transaction.nil?
|
130
|
+
transaction
|
131
|
+
end
|
132
|
+
|
133
|
+
dispatch_to_gateways(:refund, kb_account_id, kb_payment_id, kb_payment_transaction_id, kb_payment_method_id, amount, currency, properties, context, gateway_call_proc, linked_transaction_proc)
|
113
134
|
end
|
114
135
|
|
115
136
|
def get_payment_info(kb_account_id, kb_payment_id, properties, context)
|
@@ -134,9 +155,10 @@ module Killbill
|
|
134
155
|
payment_source = get_payment_source(nil, all_properties, options, context)
|
135
156
|
|
136
157
|
# Go to the gateway
|
137
|
-
|
138
|
-
|
139
|
-
|
158
|
+
payment_processor_account_id = options[:payment_processor_account_id] || :default
|
159
|
+
gateway = lookup_gateway(payment_processor_account_id)
|
160
|
+
gw_response = gateway.store(payment_source, options)
|
161
|
+
response, transaction = save_response_and_transaction(gw_response, :add_payment_method, kb_account_id, context.tenant_id, payment_processor_account_id)
|
140
162
|
|
141
163
|
if response.success
|
142
164
|
# If we have skipped the call to the gateway, we still need to store the payment method
|
@@ -161,13 +183,14 @@ module Killbill
|
|
161
183
|
pm = @payment_method_model.from_kb_payment_method_id(kb_payment_method_id, context.tenant_id)
|
162
184
|
|
163
185
|
# Delete the card
|
164
|
-
|
186
|
+
payment_processor_account_id = options[:payment_processor_account_id] || :default
|
187
|
+
gateway = lookup_gateway(payment_processor_account_id)
|
165
188
|
if options[:customer_id]
|
166
189
|
gw_response = gateway.unstore(options[:customer_id], pm.token, options)
|
167
190
|
else
|
168
191
|
gw_response = gateway.unstore(pm.token, options)
|
169
192
|
end
|
170
|
-
response, transaction = save_response_and_transaction
|
193
|
+
response, transaction = save_response_and_transaction(gw_response, :delete_payment_method, kb_account_id, context.tenant_id, payment_processor_account_id)
|
171
194
|
|
172
195
|
if response.success
|
173
196
|
@payment_method_model.mark_as_deleted! kb_payment_method_id, context.tenant_id
|
@@ -329,7 +352,7 @@ module Killbill
|
|
329
352
|
# TODO Split settlements is partially implemented. Left to be done:
|
330
353
|
# * payment_source should probably be retrieved per gateway
|
331
354
|
# * amount per gateway should be retrieved from the options
|
332
|
-
def dispatch_to_gateways(operation, kb_account_id, kb_payment_id, kb_payment_transaction_id, kb_payment_method_id, amount, currency, properties, context, gateway_call_proc)
|
355
|
+
def dispatch_to_gateways(operation, kb_account_id, kb_payment_id, kb_payment_transaction_id, kb_payment_method_id, amount, currency, properties, context, gateway_call_proc, linked_transaction_proc=nil)
|
333
356
|
kb_transaction = get_kb_transaction(kb_payment_id, kb_payment_transaction_id, context.tenant_id)
|
334
357
|
amount_in_cents = amount.nil? ? nil : to_cents(amount, currency)
|
335
358
|
|
@@ -350,6 +373,13 @@ module Killbill
|
|
350
373
|
# Retrieve the previous transaction for the same operation and payment id - this is useful to detect dups for example
|
351
374
|
last_transaction = @transaction_model.send("#{operation.to_s}s_from_kb_payment_id", kb_payment_id, context.tenant_id).last
|
352
375
|
|
376
|
+
# Retrieve the linked transaction (authorization to capture, purchase to refund, etc.)
|
377
|
+
linked_transaction = nil
|
378
|
+
unless linked_transaction_proc.nil?
|
379
|
+
linked_transaction = linked_transaction_proc.call(amount_in_cents, options)
|
380
|
+
options[:payment_processor_account_id] ||= linked_transaction.payment_processor_account_id
|
381
|
+
end
|
382
|
+
|
353
383
|
# Filter before all gateways call
|
354
384
|
before_gateways(kb_transaction, last_transaction, payment_source, amount_in_cents, currency, options)
|
355
385
|
|
@@ -366,8 +396,8 @@ module Killbill
|
|
366
396
|
before_gateway(gateway, kb_transaction, last_transaction, payment_source, amount_in_cents, currency, options)
|
367
397
|
|
368
398
|
# Perform the operation in the gateway
|
369
|
-
gw_response = gateway_call_proc.call(gateway, payment_source, amount_in_cents, options)
|
370
|
-
response, transaction = save_response_and_transaction(gw_response, operation, kb_account_id, context.tenant_id, kb_payment_id, kb_payment_transaction_id, operation.upcase, amount_in_cents, currency)
|
399
|
+
gw_response = gateway_call_proc.call(gateway, linked_transaction, payment_source, amount_in_cents, options)
|
400
|
+
response, transaction = save_response_and_transaction(gw_response, operation, kb_account_id, context.tenant_id, payment_processor_account_id, kb_payment_id, kb_payment_transaction_id, operation.upcase, amount_in_cents, currency)
|
371
401
|
|
372
402
|
# Filter after each gateway call
|
373
403
|
after_gateway(response, transaction, gw_response)
|
@@ -460,10 +490,10 @@ module Killbill
|
|
460
490
|
account.currency
|
461
491
|
end
|
462
492
|
|
463
|
-
def save_response_and_transaction(gw_response, api_call, kb_account_id, kb_tenant_id, kb_payment_id=nil, kb_payment_transaction_id=nil, transaction_type=nil, amount_in_cents=0, currency=nil)
|
493
|
+
def save_response_and_transaction(gw_response, api_call, kb_account_id, kb_tenant_id, payment_processor_account_id, kb_payment_id=nil, kb_payment_transaction_id=nil, transaction_type=nil, amount_in_cents=0, currency=nil)
|
464
494
|
@logger.warn "Unsuccessful #{api_call}: #{gw_response.message}" unless gw_response.success?
|
465
495
|
|
466
|
-
response, transaction = @response_model.create_response_and_transaction(@identifier, @transaction_model, api_call, kb_account_id, kb_payment_id, kb_payment_transaction_id, transaction_type, kb_tenant_id, gw_response, amount_in_cents, currency, {}, @response_model)
|
496
|
+
response, transaction = @response_model.create_response_and_transaction(@identifier, @transaction_model, api_call, kb_account_id, kb_payment_id, kb_payment_transaction_id, transaction_type, payment_processor_account_id, kb_tenant_id, gw_response, amount_in_cents, currency, {}, @response_model)
|
467
497
|
|
468
498
|
@logger.debug "Recorded transaction: #{transaction.inspect}" unless transaction.nil?
|
469
499
|
|
@@ -506,7 +536,7 @@ module Killbill
|
|
506
536
|
end
|
507
537
|
|
508
538
|
def get_active_merchant_module
|
509
|
-
::
|
539
|
+
::OffsitePayments.integration(@identifier.to_s.camelize)
|
510
540
|
end
|
511
541
|
|
512
542
|
def merge_transaction_info_plugins(payment_processor_account_ids, responses, transactions)
|