killbill 3.1.11 → 3.1.12
Sign up to get free protection for your applications and to get access to all the features.
- 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)
|