killbill-stripe 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,38 +0,0 @@
1
- require 'logger'
2
-
3
- module Killbill::Stripe
4
- mattr_reader :logger
5
- mattr_reader :config
6
- mattr_reader :gateway
7
- mattr_reader :kb_apis
8
- mattr_reader :stripe_payment_description
9
- mattr_reader :initialized
10
- mattr_reader :test
11
-
12
- def self.initialize!(logger=Logger.new(STDOUT), conf_dir=File.expand_path('../../../', File.dirname(__FILE__)), kb_apis = nil)
13
- @@logger = logger
14
- @@kb_apis = kb_apis
15
-
16
- config_file = "#{conf_dir}/stripe.yml"
17
- @@config = Properties.new(config_file)
18
- @@config.parse!
19
- @@test = @@config[:stripe][:test]
20
-
21
- @@logger.log_level = Logger::DEBUG if (@@config[:logger] || {})[:debug]
22
-
23
- @@stripe_payment_description = @@config[:stripe][:payment_description]
24
-
25
- @@gateway = Killbill::Stripe::Gateway.from_config(@@config[:stripe])
26
-
27
- if defined?(JRUBY_VERSION)
28
- # See https://github.com/jruby/activerecord-jdbc-adapter/issues/302
29
- require 'jdbc/mysql'
30
- Jdbc::MySQL.load_driver(:require) if Jdbc::MySQL.respond_to?(:load_driver)
31
- end
32
-
33
- ActiveRecord::Base.establish_connection(@@config[:database])
34
- ActiveRecord::Base.logger = @@logger
35
-
36
- @@initialized = true
37
- end
38
- end
@@ -1,23 +0,0 @@
1
- module Killbill::Stripe
2
- class Properties
3
- def initialize(file = 'stripe.yml')
4
- @config_file = Pathname.new(file).expand_path
5
- end
6
-
7
- def parse!
8
- raise "#{@config_file} is not a valid file" unless @config_file.file?
9
- @config = YAML.load_file(@config_file.to_s)
10
- validate!
11
- end
12
-
13
- def [](key)
14
- @config[key]
15
- end
16
-
17
- private
18
-
19
- def validate!
20
- raise "Bad configuration for Stripe plugin. Config is #{@config.inspect}" if @config.blank? || !@config[:stripe] || !@config[:stripe][:api_secret_key]
21
- end
22
- end
23
- end
@@ -1,182 +0,0 @@
1
- module Killbill::Stripe
2
- class StripePaymentMethod < ActiveRecord::Base
3
- attr_accessible :kb_account_id,
4
- :kb_payment_method_id,
5
- :stripe_customer_id,
6
- :stripe_card_id_or_token,
7
- :cc_first_name,
8
- :cc_last_name,
9
- :cc_type,
10
- :cc_exp_month,
11
- :cc_exp_year,
12
- :cc_last_4,
13
- :address1,
14
- :address2,
15
- :city,
16
- :state,
17
- :zip,
18
- :country
19
-
20
- alias_attribute :external_payment_method_id, :stripe_card_id_or_token
21
-
22
- def self.from_kb_account_id(kb_account_id)
23
- find_all_by_kb_account_id_and_is_deleted(kb_account_id, false)
24
- end
25
-
26
- def self.stripe_customer_id_from_kb_account_id(kb_account_id)
27
- pms = from_kb_account_id(kb_account_id)
28
- return nil if pms.empty?
29
-
30
- stripe_customer_ids = Set.new
31
- pms.each { |pm| stripe_customer_ids << pm.stripe_customer_id }
32
- raise "No Stripe customer id found for account #{kb_account_id}" if stripe_customer_ids.empty?
33
- raise "Killbill account #{kb_account_id} mapping to multiple Stripe customers: #{stripe_customer_ids}" if stripe_customer_ids.size > 1
34
- stripe_customer_ids.first
35
- end
36
-
37
- def self.from_kb_payment_method_id(kb_payment_method_id)
38
- payment_methods = find_all_by_kb_payment_method_id_and_is_deleted(kb_payment_method_id, false)
39
- raise "No payment method found for payment method #{kb_payment_method_id}" if payment_methods.empty?
40
- raise "Killbill payment method mapping to multiple active Stripe tokens for payment method #{kb_payment_method_id}" if payment_methods.size > 1
41
- payment_methods[0]
42
- end
43
-
44
- def self.mark_as_deleted!(kb_payment_method_id)
45
- payment_method = from_kb_payment_method_id(kb_payment_method_id)
46
- payment_method.is_deleted = true
47
- payment_method.save!
48
- end
49
-
50
- # VisibleForTesting
51
- def self.search_query(search_key, offset = nil, limit = nil)
52
- t = self.arel_table
53
-
54
- # Exact match for kb_account_id, kb_payment_method_id, stripe_customer_id, stripe_card_id_or_token, cc_type, cc_exp_month,
55
- # cc_exp_year, cc_last_4, state and zip, partial match for the reset
56
- where_clause = t[:kb_account_id].eq(search_key)
57
- .or(t[:kb_payment_method_id].eq(search_key))
58
- .or(t[:stripe_customer_id].eq(search_key))
59
- .or(t[:stripe_card_id_or_token].eq(search_key))
60
- .or(t[:cc_type].eq(search_key))
61
- .or(t[:state].eq(search_key))
62
- .or(t[:zip].eq(search_key))
63
- .or(t[:cc_first_name].matches("%#{search_key}%"))
64
- .or(t[:cc_last_name].matches("%#{search_key}%"))
65
- .or(t[:address1].matches("%#{search_key}%"))
66
- .or(t[:address2].matches("%#{search_key}%"))
67
- .or(t[:city].matches("%#{search_key}%"))
68
- .or(t[:country].matches("%#{search_key}%"))
69
-
70
- # Coming from Kill Bill, search_key will always be a String. Check to see if it represents a numeric for numeric-only fields
71
- if search_key.is_a?(Numeric) or search_key.to_s =~ /\A[-+]?[0-9]*\.?[0-9]+\Z/
72
- where_clause = where_clause.or(t[:cc_exp_month].eq(search_key))
73
- .or(t[:cc_exp_year].eq(search_key))
74
- .or(t[:cc_last_4].eq(search_key))
75
- end
76
-
77
- # Remove garbage payment methods (added in the plugin but not reconcilied with Kill Bill yet)
78
- query = t.where(where_clause)
79
- .where(t[:kb_payment_method_id].not_eq(nil))
80
- .order(t[:id])
81
-
82
- if offset.blank? and limit.blank?
83
- # true is for count distinct
84
- query.project(t[:id].count(true))
85
- else
86
- query.skip(offset) unless offset.blank?
87
- query.take(limit) unless limit.blank?
88
- query.project(t[Arel.star])
89
- # Not chainable
90
- query.distinct
91
- end
92
- query
93
- end
94
-
95
- def self.search(search_key, offset = 0, limit = 100)
96
- pagination = Killbill::Plugin::Model::Pagination.new
97
- pagination.current_offset = offset
98
- pagination.total_nb_records = self.count_by_sql(self.search_query(search_key))
99
- pagination.max_nb_records = self.count
100
- pagination.next_offset = (!pagination.total_nb_records.nil? && offset + limit >= pagination.total_nb_records) ? nil : offset + limit
101
- # Reduce the limit if the specified value is larger than the number of records
102
- actual_limit = [pagination.max_nb_records, limit].min
103
- pagination.iterator = StreamyResultSet.new(actual_limit) do |offset,limit|
104
- self.find_by_sql(self.search_query(search_key, offset, limit))
105
- .map(&:to_payment_method_response)
106
- end
107
- pagination
108
- end
109
-
110
- def to_payment_method_response
111
- properties = []
112
- properties << create_pm_kv_info('token', external_payment_method_id)
113
- properties << create_pm_kv_info('ccName', cc_name)
114
- properties << create_pm_kv_info('ccType', cc_type)
115
- properties << create_pm_kv_info('ccExpirationMonth', cc_exp_month)
116
- properties << create_pm_kv_info('ccExpirationYear', cc_exp_year)
117
- properties << create_pm_kv_info('ccLast4', cc_last_4)
118
- properties << create_pm_kv_info('address1', address1)
119
- properties << create_pm_kv_info('address2', address2)
120
- properties << create_pm_kv_info('city', city)
121
- properties << create_pm_kv_info('state', state)
122
- properties << create_pm_kv_info('zip', zip)
123
- properties << create_pm_kv_info('country', country)
124
-
125
- pm_plugin = Killbill::Plugin::Model::PaymentMethodPlugin.new
126
- pm_plugin.kb_payment_method_id = kb_payment_method_id
127
- pm_plugin.external_payment_method_id = external_payment_method_id
128
- pm_plugin.is_default_payment_method = is_default
129
- pm_plugin.properties = properties
130
- pm_plugin.type = 'CreditCard'
131
- pm_plugin.cc_name = cc_name
132
- pm_plugin.cc_type = cc_type
133
- pm_plugin.cc_expiration_month = cc_exp_month
134
- pm_plugin.cc_expiration_year = cc_exp_year
135
- pm_plugin.cc_last4 = cc_last_4
136
- pm_plugin.address1 = address1
137
- pm_plugin.address2 = address2
138
- pm_plugin.city = city
139
- pm_plugin.state = state
140
- pm_plugin.zip = zip
141
- pm_plugin.country = country
142
-
143
- pm_plugin
144
- end
145
-
146
- def to_payment_method_info_response
147
- pm_info_plugin = Killbill::Plugin::Model::PaymentMethodInfoPlugin.new
148
- pm_info_plugin.account_id = kb_account_id
149
- pm_info_plugin.payment_method_id = kb_payment_method_id
150
- pm_info_plugin.is_default = is_default
151
- pm_info_plugin.external_payment_method_id = external_payment_method_id
152
- pm_info_plugin
153
- end
154
-
155
- def is_default
156
- # There is a concept of default credit card in Stripe but it's not exposed by the API
157
- # Return false to let Kill Bill knows it's authoritative on the matter
158
- false
159
- end
160
-
161
- def cc_name
162
- if cc_first_name and cc_last_name
163
- "#{cc_first_name} #{cc_last_name}"
164
- elsif cc_first_name
165
- cc_first_name
166
- elsif cc_last_name
167
- cc_last_name
168
- else
169
- nil
170
- end
171
- end
172
-
173
- private
174
-
175
- def create_pm_kv_info(key, value)
176
- prop = Killbill::Plugin::Model::PaymentMethodKVInfo.new
177
- prop.key = key
178
- prop.value = value
179
- prop
180
- end
181
- end
182
- end
@@ -1,242 +0,0 @@
1
- module Killbill::Stripe
2
- class StripeResponse < ActiveRecord::Base
3
- has_one :stripe_transaction
4
- attr_accessible :api_call,
5
- :kb_payment_id,
6
- :message,
7
- :authorization,
8
- :fraud_review,
9
- :test,
10
- :params_id,
11
- :params_object,
12
- :params_created,
13
- :params_livemode,
14
- :params_paid,
15
- :params_amount,
16
- :params_currency,
17
- :params_refunded,
18
- :params_card_id,
19
- :params_card_object,
20
- :params_card_last4,
21
- :params_card_type,
22
- :params_card_exp_month,
23
- :params_card_exp_year,
24
- :params_card_fingerprint,
25
- :params_card_customer,
26
- :params_card_country,
27
- :params_card_name,
28
- :params_card_address_line1,
29
- :params_card_address_line2,
30
- :params_card_address_city,
31
- :params_card_address_state,
32
- :params_card_address_zip,
33
- :params_card_address_country,
34
- :params_card_cvc_check,
35
- :params_card_address_line1_check,
36
- :params_card_address_zip_check,
37
- :params_captured,
38
- :params_refunds,
39
- :params_balance_transaction,
40
- :params_failure_message,
41
- :params_failure_code,
42
- :params_amount_refunded,
43
- :params_customer,
44
- :params_invoice,
45
- :params_description,
46
- :params_dispute,
47
- :params_metadata,
48
- :params_error_type,
49
- :params_error_message,
50
- :avs_result_code,
51
- :avs_result_message,
52
- :avs_result_street_match,
53
- :avs_result_postal_match,
54
- :cvv_result_code,
55
- :cvv_result_message,
56
- :success
57
-
58
- def stripe_txn_id
59
- params_id || authorization
60
- end
61
-
62
- def self.from_response(api_call, kb_payment_id, response)
63
- StripeResponse.new({
64
- :api_call => api_call,
65
- :kb_payment_id => kb_payment_id,
66
- :message => response.message,
67
- :authorization => response.authorization,
68
- :fraud_review => response.fraud_review?,
69
- :test => response.test?,
70
- :params_id => extract(response, "id"),
71
- :params_object => extract(response, "object"),
72
- :params_created => extract(response, "created"),
73
- :params_livemode => extract(response, "livemode"),
74
- :params_paid => extract(response, "paid"),
75
- :params_amount => extract(response, "amount"),
76
- :params_currency => extract(response, "currency"),
77
- :params_refunded => extract(response, "refunded"),
78
- :params_card_id => extract(response, "card", "id"),
79
- :params_card_object => extract(response, "card", "object"),
80
- :params_card_last4 => extract(response, "card", "last4"),
81
- :params_card_type => extract(response, "card", "type"),
82
- :params_card_exp_month => extract(response, "card", "exp_month"),
83
- :params_card_exp_year => extract(response, "card", "exp_year"),
84
- :params_card_fingerprint => extract(response, "card", "fingerprint"),
85
- :params_card_customer => extract(response, "card", "customer"),
86
- :params_card_country => extract(response, "card", "country"),
87
- :params_card_name => extract(response, "card", "name"),
88
- :params_card_address_line1 => extract(response, "card", "address_line1"),
89
- :params_card_address_line2 => extract(response, "card", "address_line2"),
90
- :params_card_address_city => extract(response, "card", "address_city"),
91
- :params_card_address_state => extract(response, "card", "address_state"),
92
- :params_card_address_zip => extract(response, "card", "address_zip"),
93
- :params_card_address_country => extract(response, "card", "address_country"),
94
- :params_card_cvc_check => extract(response, "card", "cvc_check"),
95
- :params_card_address_line1_check => extract(response, "card", "address_line1_check"),
96
- :params_card_address_zip_check => extract(response, "card", "address_zip_check"),
97
- :params_captured => extract(response, "captured"),
98
- :params_refunds => extract(response, "refunds"),
99
- :params_balance_transaction => extract(response, "balance_transaction"),
100
- :params_failure_message => extract(response, "failure_message"),
101
- :params_failure_code => extract(response, "failure_code"),
102
- :params_amount_refunded => extract(response, "amount_refunded"),
103
- :params_customer => extract(response, "customer"),
104
- :params_invoice => extract(response, "invoice"),
105
- :params_description => extract(response, "description"),
106
- :params_dispute => extract(response, "dispute"),
107
- :params_metadata => extract(response, "metadata"),
108
- :params_error_type => extract(response, "error", "type"),
109
- :params_error_message => extract(response, "error", "message"),
110
- :avs_result_code => response.avs_result.kind_of?(ActiveMerchant::Billing::AVSResult) ? response.avs_result.code : response.avs_result['code'],
111
- :avs_result_message => response.avs_result.kind_of?(ActiveMerchant::Billing::AVSResult) ? response.avs_result.message : response.avs_result['message'],
112
- :avs_result_street_match => response.avs_result.kind_of?(ActiveMerchant::Billing::AVSResult) ? response.avs_result.street_match : response.avs_result['street_match'],
113
- :avs_result_postal_match => response.avs_result.kind_of?(ActiveMerchant::Billing::AVSResult) ? response.avs_result.postal_match : response.avs_result['postal_match'],
114
- :cvv_result_code => response.cvv_result.kind_of?(ActiveMerchant::Billing::CVVResult) ? response.cvv_result.code : response.cvv_result['code'],
115
- :cvv_result_message => response.cvv_result.kind_of?(ActiveMerchant::Billing::CVVResult) ? response.cvv_result.message : response.cvv_result['message'],
116
- :success => response.success?
117
- })
118
- end
119
-
120
- def to_payment_response
121
- to_killbill_response :payment
122
- end
123
-
124
- def to_refund_response
125
- to_killbill_response :refund
126
- end
127
-
128
- # VisibleForTesting
129
- def self.search_query(api_call, search_key, offset = nil, limit = nil)
130
- t = self.arel_table
131
-
132
- # Exact matches only
133
- where_clause = t[:authorization].eq(search_key)
134
- .or(t[:params_id].eq(search_key))
135
- .or(t[:params_card_id].eq(search_key))
136
-
137
- # Only search successful payments and refunds
138
- where_clause = where_clause.and(t[:api_call].eq(api_call))
139
- .and(t[:success].eq(true))
140
-
141
- query = t.where(where_clause)
142
- .order(t[:id])
143
-
144
- if offset.blank? and limit.blank?
145
- # true is for count distinct
146
- query.project(t[:id].count(true))
147
- else
148
- query.skip(offset) unless offset.blank?
149
- query.take(limit) unless limit.blank?
150
- query.project(t[Arel.star])
151
- # Not chainable
152
- query.distinct
153
- end
154
- query
155
- end
156
-
157
- def self.search(search_key, offset = 0, limit = 100, type = :payment)
158
- api_call = type == :payment ? 'charge' : 'refund'
159
- pagination = Killbill::Plugin::Model::Pagination.new
160
- pagination.current_offset = offset
161
- pagination.total_nb_records = self.count_by_sql(self.search_query(api_call, search_key))
162
- pagination.max_nb_records = self.where(:api_call => api_call, :success => true).count
163
- pagination.next_offset = (!pagination.total_nb_records.nil? && offset + limit >= pagination.total_nb_records) ? nil : offset + limit
164
- # Reduce the limit if the specified value is larger than the number of records
165
- actual_limit = [pagination.max_nb_records, limit].min
166
- pagination.iterator = StreamyResultSet.new(actual_limit) do |offset,limit|
167
- self.find_by_sql(self.search_query(api_call, search_key, offset, limit))
168
- .map { |x| type == :payment ? x.to_payment_response : x.to_refund_response }
169
- end
170
- pagination
171
- end
172
-
173
- private
174
-
175
- def to_killbill_response(type)
176
- if stripe_transaction.nil?
177
- amount_in_cents = nil
178
- currency = nil
179
- created_date = created_at
180
- first_payment_reference_id = nil
181
- second_payment_reference_id = nil
182
- else
183
- amount_in_cents = stripe_transaction.amount_in_cents
184
- currency = stripe_transaction.currency
185
- created_date = stripe_transaction.created_at
186
- first_reference_id = params_balance_transaction
187
- second_reference_id = stripe_transaction.stripe_txn_id
188
- end
189
-
190
- unless params_created.blank?
191
- effective_date = DateTime.strptime(params_created.to_s, "%s") rescue nil
192
- end
193
- effective_date ||= created_date
194
- gateway_error = message || params_error_message
195
- gateway_error_code = params_error_type
196
-
197
- if type == :payment
198
- p_info_plugin = Killbill::Plugin::Model::PaymentInfoPlugin.new
199
- p_info_plugin.kb_payment_id = kb_payment_id
200
- p_info_plugin.amount = Money.new(amount_in_cents, currency).to_d if currency
201
- p_info_plugin.currency = currency
202
- p_info_plugin.created_date = created_date
203
- p_info_plugin.effective_date = effective_date
204
- p_info_plugin.status = (success ? :PROCESSED : :ERROR)
205
- p_info_plugin.gateway_error = gateway_error
206
- p_info_plugin.gateway_error_code = gateway_error_code
207
- p_info_plugin.first_payment_reference_id = first_reference_id
208
- p_info_plugin.second_payment_reference_id = second_reference_id
209
- p_info_plugin
210
- else
211
- r_info_plugin = Killbill::Plugin::Model::RefundInfoPlugin.new
212
- r_info_plugin.kb_payment_id = kb_payment_id
213
- r_info_plugin.amount = Money.new(amount_in_cents, currency).to_d if currency
214
- r_info_plugin.currency = currency
215
- r_info_plugin.created_date = created_date
216
- r_info_plugin.effective_date = effective_date
217
- r_info_plugin.status = (success ? :PROCESSED : :ERROR)
218
- r_info_plugin.gateway_error = gateway_error
219
- r_info_plugin.gateway_error_code = gateway_error_code
220
- r_info_plugin.first_refund_reference_id = first_reference_id
221
- r_info_plugin.second_refund_reference_id = second_reference_id
222
- r_info_plugin
223
- end
224
- end
225
-
226
- def self.extract(response, key1, key2=nil, key3=nil)
227
- return nil if response.nil? || response.params.nil?
228
- level1 = response.params[key1]
229
-
230
- if level1.nil? or (key2.nil? and key3.nil?)
231
- return level1
232
- end
233
- level2 = level1[key2]
234
-
235
- if level2.nil? or key3.nil?
236
- return level2
237
- else
238
- return level2[key3]
239
- end
240
- end
241
- end
242
- end