killbill-bitpay 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,75 @@
1
+ CREATE TABLE `bitpay_payment_methods` (
2
+ `id` int(11) NOT NULL AUTO_INCREMENT,
3
+ `kb_payment_method_id` varchar(255) DEFAULT NULL,
4
+ `token` varchar(255) DEFAULT NULL,
5
+ `cc_first_name` varchar(255) DEFAULT NULL,
6
+ `cc_last_name` varchar(255) DEFAULT NULL,
7
+ `cc_type` varchar(255) DEFAULT NULL,
8
+ `cc_exp_month` varchar(255) DEFAULT NULL,
9
+ `cc_exp_year` varchar(255) DEFAULT NULL,
10
+ `cc_number` varchar(255) DEFAULT NULL,
11
+ `cc_last_4` varchar(255) DEFAULT NULL,
12
+ `cc_start_month` varchar(255) DEFAULT NULL,
13
+ `cc_start_year` varchar(255) DEFAULT NULL,
14
+ `cc_issue_number` varchar(255) DEFAULT NULL,
15
+ `cc_verification_value` varchar(255) DEFAULT NULL,
16
+ `cc_track_data` varchar(255) DEFAULT NULL,
17
+ `address1` varchar(255) DEFAULT NULL,
18
+ `address2` varchar(255) DEFAULT NULL,
19
+ `city` varchar(255) DEFAULT NULL,
20
+ `state` varchar(255) DEFAULT NULL,
21
+ `zip` varchar(255) DEFAULT NULL,
22
+ `country` varchar(255) DEFAULT NULL,
23
+ `is_deleted` tinyint(1) NOT NULL DEFAULT '0',
24
+ `created_at` datetime NOT NULL,
25
+ `updated_at` datetime NOT NULL,
26
+ `kb_account_id` varchar(255) DEFAULT NULL,
27
+ `kb_tenant_id` varchar(255) DEFAULT NULL,
28
+ PRIMARY KEY (`id`),
29
+ KEY `index_bitpay_payment_methods_on_kb_account_id` (`kb_account_id`),
30
+ KEY `index_bitpay_payment_methods_on_kb_payment_method_id` (`kb_payment_method_id`)
31
+ ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
32
+
33
+ CREATE TABLE `bitpay_transactions` (
34
+ `id` int(11) NOT NULL AUTO_INCREMENT,
35
+ `bitpay_response_id` int(11) NOT NULL,
36
+ `api_call` varchar(255) NOT NULL,
37
+ `kb_payment_id` varchar(255) NOT NULL,
38
+ `kb_payment_transaction_id` varchar(255) NOT NULL,
39
+ `transaction_type` varchar(255) NOT NULL,
40
+ `payment_processor_account_id` varchar(255) DEFAULT NULL,
41
+ `txn_id` varchar(255) DEFAULT NULL,
42
+ `amount_in_cents` int(11) DEFAULT NULL,
43
+ `currency` varchar(255) DEFAULT NULL,
44
+ `created_at` datetime NOT NULL,
45
+ `updated_at` datetime NOT NULL,
46
+ `kb_account_id` varchar(255) NOT NULL,
47
+ `kb_tenant_id` varchar(255) NOT NULL,
48
+ PRIMARY KEY (`id`),
49
+ KEY `index_bitpay_transactions_on_kb_payment_id` (`kb_payment_id`)
50
+ ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
51
+
52
+ CREATE TABLE `bitpay_responses` (
53
+ `id` int(11) NOT NULL AUTO_INCREMENT,
54
+ `api_call` varchar(255) NOT NULL,
55
+ `kb_payment_id` varchar(255) DEFAULT NULL,
56
+ `kb_payment_transaction_id` varchar(255) DEFAULT NULL,
57
+ `transaction_type` varchar(255) DEFAULT NULL,
58
+ `payment_processor_account_id` varchar(255) DEFAULT NULL,
59
+ `message` varchar(255) DEFAULT NULL,
60
+ `authorization` varchar(255) DEFAULT NULL,
61
+ `fraud_review` tinyint(1) DEFAULT NULL,
62
+ `test` tinyint(1) DEFAULT NULL,
63
+ `avs_result_code` varchar(255) DEFAULT NULL,
64
+ `avs_result_message` varchar(255) DEFAULT NULL,
65
+ `avs_result_street_match` varchar(255) DEFAULT NULL,
66
+ `avs_result_postal_match` varchar(255) DEFAULT NULL,
67
+ `cvv_result_code` varchar(255) DEFAULT NULL,
68
+ `cvv_result_message` varchar(255) DEFAULT NULL,
69
+ `success` tinyint(1) DEFAULT NULL,
70
+ `created_at` datetime NOT NULL,
71
+ `updated_at` datetime NOT NULL,
72
+ `kb_account_id` varchar(255) DEFAULT NULL,
73
+ `kb_tenant_id` varchar(255) DEFAULT NULL,
74
+ PRIMARY KEY (`id`)
75
+ ) ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_bin;
@@ -0,0 +1,76 @@
1
+ require 'active_record'
2
+
3
+ ActiveRecord::Schema.define(:version => 20140410153635) do
4
+ create_table "bitpay_payment_methods", :force => true do |t|
5
+ t.string "kb_payment_method_id" # NULL before Kill Bill knows about it
6
+ t.string "token" # bitpay id
7
+ t.string "cc_first_name"
8
+ t.string "cc_last_name"
9
+ t.string "cc_type"
10
+ t.string "cc_exp_month"
11
+ t.string "cc_exp_year"
12
+ t.string "cc_number"
13
+ t.string "cc_last_4"
14
+ t.string "cc_start_month"
15
+ t.string "cc_start_year"
16
+ t.string "cc_issue_number"
17
+ t.string "cc_verification_value"
18
+ t.string "cc_track_data"
19
+ t.string "address1"
20
+ t.string "address2"
21
+ t.string "city"
22
+ t.string "state"
23
+ t.string "zip"
24
+ t.string "country"
25
+ t.boolean "is_deleted", :null => false, :default => false
26
+ t.datetime "created_at", :null => false
27
+ t.datetime "updated_at", :null => false
28
+ t.string "kb_account_id"
29
+ t.string "kb_tenant_id"
30
+ end
31
+
32
+ add_index(:bitpay_payment_methods, :kb_account_id)
33
+ add_index(:bitpay_payment_methods, :kb_payment_method_id)
34
+
35
+ create_table "bitpay_transactions", :force => true do |t|
36
+ t.integer "bitpay_response_id", :null => false
37
+ t.string "api_call", :null => false
38
+ t.string "kb_payment_id", :null => false
39
+ t.string "kb_payment_transaction_id", :null => false
40
+ t.string "transaction_type", :null => false
41
+ t.string "payment_processor_account_id"
42
+ t.string "txn_id" # bitpay transaction id
43
+ # Both null for void
44
+ t.integer "amount_in_cents"
45
+ t.string "currency"
46
+ t.datetime "created_at", :null => false
47
+ t.datetime "updated_at", :null => false
48
+ t.string "kb_account_id", :null => false
49
+ t.string "kb_tenant_id", :null => false
50
+ end
51
+
52
+ add_index(:bitpay_transactions, :kb_payment_id)
53
+
54
+ create_table "bitpay_responses", :force => true do |t|
55
+ t.string "api_call", :null => false
56
+ t.string "kb_payment_id"
57
+ t.string "kb_payment_transaction_id"
58
+ t.string "transaction_type"
59
+ t.string "payment_processor_account_id"
60
+ t.string "message"
61
+ t.string "authorization"
62
+ t.boolean "fraud_review"
63
+ t.boolean "test"
64
+ t.string "avs_result_code"
65
+ t.string "avs_result_message"
66
+ t.string "avs_result_street_match"
67
+ t.string "avs_result_postal_match"
68
+ t.string "cvv_result_code"
69
+ t.string "cvv_result_message"
70
+ t.boolean "success"
71
+ t.datetime "created_at", :null => false
72
+ t.datetime "updated_at", :null => false
73
+ t.string "kb_account_id"
74
+ t.string "kb_tenant_id"
75
+ end
76
+ end
@@ -0,0 +1,49 @@
1
+ version = File.read(File.expand_path('../VERSION', __FILE__)).strip
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = 'killbill-bitpay'
5
+ s.version = version
6
+ s.summary = 'Plugin to use Bitpay as a gateway.'
7
+ s.description = 'Kill Bill payment plugin for Bitpay.'
8
+
9
+ s.required_ruby_version = '>= 1.9.3'
10
+
11
+ s.license = 'Apache License (2.0)'
12
+
13
+ s.author = 'Kill Bill core team'
14
+ s.email = 'killbilling-users@googlegroups.com'
15
+ s.homepage = 'http://kill-bill.org'
16
+
17
+ s.files = `git ls-files`.split("\n")
18
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
+ s.bindir = 'bin'
20
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
21
+ s.require_paths = ['lib']
22
+
23
+ s.rdoc_options << '--exclude' << '.'
24
+
25
+ s.add_dependency 'killbill', '~> 3.2.0'
26
+ s.add_dependency 'activemerchant', '~> 1.44.1'
27
+ s.add_dependency 'offsite_payments', '~> 2.0.1'
28
+ s.add_dependency 'activerecord', '~> 4.1.0'
29
+ s.add_dependency 'actionpack', '~> 4.1.0'
30
+ s.add_dependency 'actionview', '~> 4.1.0'
31
+ s.add_dependency 'activesupport', '~> 4.1.0'
32
+ s.add_dependency 'money', '~> 6.1.1'
33
+ s.add_dependency 'monetize', '~> 0.3.0'
34
+ s.add_dependency 'sinatra', '~> 1.3.4'
35
+ if defined?(JRUBY_VERSION)
36
+ s.add_dependency 'activerecord-jdbcmysql-adapter', '~> 1.3.7'
37
+ # Required to avoid errors like java.lang.NoClassDefFoundError: org/bouncycastle/asn1/DERBoolean
38
+ s.add_dependency 'jruby-openssl', '~> 0.9.4'
39
+ end
40
+
41
+ s.add_development_dependency 'jbundler', '~> 0.4.1'
42
+ s.add_development_dependency 'rake', '>= 10.0.0'
43
+ s.add_development_dependency 'rspec', '~> 2.12.0'
44
+ if defined?(JRUBY_VERSION)
45
+ s.add_development_dependency 'activerecord-jdbcsqlite3-adapter', '~> 1.3.7'
46
+ else
47
+ s.add_development_dependency 'sqlite3', '~> 1.3.7'
48
+ end
49
+ end
@@ -0,0 +1,3 @@
1
+ mainClass=Killbill::Bitpay::PaymentPlugin
2
+ require=bitpay
3
+ pluginType=PAYMENT
@@ -0,0 +1,25 @@
1
+ require 'openssl'
2
+ require 'action_controller'
3
+ require 'active_record'
4
+ require 'action_view'
5
+ require 'active_merchant'
6
+ require 'active_support'
7
+ require 'bigdecimal'
8
+ require 'money'
9
+ require 'monetize'
10
+ require 'offsite_payments'
11
+ require 'pathname'
12
+ require 'sinatra'
13
+ require 'singleton'
14
+ require 'yaml'
15
+
16
+ require 'killbill'
17
+ require 'killbill/helpers/active_merchant'
18
+
19
+ require 'bitpay/api'
20
+ require 'bitpay/private_api'
21
+
22
+ require 'bitpay/models/payment_method'
23
+ require 'bitpay/models/response'
24
+ require 'bitpay/models/transaction'
25
+
@@ -0,0 +1,228 @@
1
+ module Killbill #:nodoc:
2
+ module Bitpay #:nodoc:
3
+ class PaymentPlugin < ::Killbill::Plugin::ActiveMerchant::PaymentPlugin
4
+
5
+ def initialize
6
+ gateway_builder = Proc.new do |config|
7
+ nil
8
+ end
9
+
10
+ super(gateway_builder,
11
+ :bitpay,
12
+ ::Killbill::Bitpay::BitpayPaymentMethod,
13
+ ::Killbill::Bitpay::BitpayTransaction,
14
+ ::Killbill::Bitpay::BitpayResponse)
15
+ end
16
+
17
+ def authorize_payment(kb_account_id, kb_payment_id, kb_payment_transaction_id, kb_payment_method_id, amount, currency, properties, context)
18
+ # Pass extra parameters for the gateway here
19
+ options = {}
20
+
21
+ properties = merge_properties(properties, options)
22
+ super(kb_account_id, kb_payment_id, kb_payment_transaction_id, kb_payment_method_id, amount, currency, properties, context)
23
+ end
24
+
25
+ def capture_payment(kb_account_id, kb_payment_id, kb_payment_transaction_id, kb_payment_method_id, amount, currency, properties, context)
26
+ # Pass extra parameters for the gateway here
27
+ options = {}
28
+
29
+ properties = merge_properties(properties, options)
30
+ super(kb_account_id, kb_payment_id, kb_payment_transaction_id, kb_payment_method_id, amount, currency, properties, context)
31
+ end
32
+
33
+ def purchase_payment(kb_account_id, kb_payment_id, kb_payment_transaction_id, kb_payment_method_id, amount, currency, properties, context)
34
+ kb_tenant_id = context.tenant_id
35
+ kb_payment_transaction = get_kb_transaction(kb_payment_id, kb_payment_transaction_id, kb_tenant_id)
36
+ payment_transaction_external_key = kb_payment_transaction.external_key
37
+
38
+ response = @response_model.where("transaction_type = 'PURCHASE' AND kb_tenant_id = '#{kb_tenant_id}' AND authorization = '#{payment_transaction_external_key}'")
39
+ .order(:created_at)[0]
40
+
41
+ transaction = response.create_bitpay_transaction(:kb_account_id => kb_account_id,
42
+ :kb_tenant_id => kb_tenant_id,
43
+ :amount_in_cents => amount.nil? ? nil : to_cents(amount, currency),
44
+ :currency => currency,
45
+ :api_call => :purchase,
46
+ :kb_payment_id => kb_payment_id,
47
+ :kb_payment_transaction_id => kb_payment_transaction_id,
48
+ :transaction_type => response.transaction_type,
49
+ :payment_processor_account_id => response.payment_processor_account_id,
50
+ :txn_id => response.txn_id,
51
+ :bitpay_response_id => response.id)
52
+
53
+ response.to_transaction_info_plugin(transaction)
54
+ end
55
+
56
+ def void_payment(kb_account_id, kb_payment_id, kb_payment_transaction_id, kb_payment_method_id, properties, context)
57
+ # Pass extra parameters for the gateway here
58
+ options = {}
59
+
60
+ properties = merge_properties(properties, options)
61
+ super(kb_account_id, kb_payment_id, kb_payment_transaction_id, kb_payment_method_id, properties, context)
62
+ end
63
+
64
+ def credit_payment(kb_account_id, kb_payment_id, kb_payment_transaction_id, kb_payment_method_id, amount, currency, properties, context)
65
+ # Pass extra parameters for the gateway here
66
+ options = {}
67
+
68
+ properties = merge_properties(properties, options)
69
+ super(kb_account_id, kb_payment_id, kb_payment_transaction_id, kb_payment_method_id, amount, currency, properties, context)
70
+ end
71
+
72
+ def refund_payment(kb_account_id, kb_payment_id, kb_payment_transaction_id, kb_payment_method_id, amount, currency, properties, context)
73
+ # Pass extra parameters for the gateway here
74
+ options = {}
75
+
76
+ properties = merge_properties(properties, options)
77
+ super(kb_account_id, kb_payment_id, kb_payment_transaction_id, kb_payment_method_id, amount, currency, properties, context)
78
+ end
79
+
80
+ def get_payment_info(kb_account_id, kb_payment_id, properties, context)
81
+ # Pass extra parameters for the gateway here
82
+ options = {}
83
+
84
+ properties = merge_properties(properties, options)
85
+ super(kb_account_id, kb_payment_id, properties, context)
86
+ end
87
+
88
+ def search_payments(search_key, offset, limit, properties, context)
89
+ # Pass extra parameters for the gateway here
90
+ options = {}
91
+
92
+ properties = merge_properties(properties, options)
93
+ super(search_key, offset, limit, properties, context)
94
+ end
95
+
96
+ def add_payment_method(kb_account_id, kb_payment_method_id, payment_method_props, set_default, properties, context)
97
+ # Pass extra parameters for the gateway here
98
+ options = {}
99
+
100
+ properties = merge_properties(properties, options)
101
+ super(kb_account_id, kb_payment_method_id, payment_method_props, set_default, properties, context)
102
+ end
103
+
104
+ def delete_payment_method(kb_account_id, kb_payment_method_id, properties, context)
105
+ # Pass extra parameters for the gateway here
106
+ options = {}
107
+
108
+ properties = merge_properties(properties, options)
109
+ super(kb_account_id, kb_payment_method_id, properties, context)
110
+ end
111
+
112
+ def get_payment_method_detail(kb_account_id, kb_payment_method_id, properties, context)
113
+ # Pass extra parameters for the gateway here
114
+ options = {}
115
+
116
+ properties = merge_properties(properties, options)
117
+ super(kb_account_id, kb_payment_method_id, properties, context)
118
+ end
119
+
120
+ def set_default_payment_method(kb_account_id, kb_payment_method_id, properties, context)
121
+ # TODO
122
+ end
123
+
124
+ def get_payment_methods(kb_account_id, refresh_from_gateway, properties, context)
125
+ # Pass extra parameters for the gateway here
126
+ options = {}
127
+
128
+ properties = merge_properties(properties, options)
129
+ super(kb_account_id, refresh_from_gateway, properties, context)
130
+ end
131
+
132
+ def search_payment_methods(search_key, offset, limit, properties, context)
133
+ # Pass extra parameters for the gateway here
134
+ options = {}
135
+
136
+ properties = merge_properties(properties, options)
137
+ super(search_key, offset, limit, properties, context)
138
+ end
139
+
140
+ def reset_payment_methods(kb_account_id, payment_methods, properties, context)
141
+ super
142
+ end
143
+
144
+ def build_form_descriptor(kb_account_id, descriptor_fields, properties, context)
145
+ # Pass extra parameters for the gateway here
146
+ options = {}
147
+ properties = merge_properties(properties, options)
148
+
149
+ # Add the BitPay API key to generate the invoice id
150
+ options = {
151
+ :account_id => config[:bitpay][:api_key],
152
+ # Overload the order_id (passed as posData) (TODO fix OffsitePayments implementation)
153
+ :order_id => "#{kb_account_id};#{context.tenant_id}"
154
+ }
155
+ descriptor_fields = merge_properties(descriptor_fields, options)
156
+
157
+ super(kb_account_id, descriptor_fields, properties, context)
158
+ end
159
+
160
+ def process_notification(notification, properties, context)
161
+ # Add the BitPay API key to retrieve the invoice
162
+ options = {
163
+ :credential1 => config[:bitpay][:api_key]
164
+ }
165
+ properties = merge_properties(properties, options)
166
+
167
+ super(notification, properties, context) do |gw_notification, service|
168
+ is_success = nil
169
+ if service.status == 'Completed'
170
+ is_success = true
171
+ elsif service.status == 'Failed'
172
+ is_success = false
173
+ end
174
+
175
+ if is_success.nil?
176
+ # Either the invoice was never paid (expired) or hasn't been confirmed yet
177
+ logger.info "Ignoring BitPay IPN #{service.raw}"
178
+ else
179
+ # See above (parsed from posData)
180
+ kb_account_id, kb_tenant_id = service.item_id.nil? ? nil : service.item_id.split(';')
181
+ amount = service.params['price']
182
+ currency = service.currency
183
+ payment_external_key = service.transaction_id
184
+ payment_transaction_external_key = service.transaction_id
185
+
186
+ payment = record_payment(kb_account_id, kb_tenant_id, amount, currency, is_success, payment_external_key, payment_transaction_external_key)
187
+ gw_notification.kb_payment_id = payment.id unless payment.nil?
188
+ end
189
+ end
190
+ end
191
+
192
+ def record_payment(kb_account_id, kb_tenant_id, amount, currency, is_success, payment_external_key, payment_transaction_external_key, kb_payment_method_id=nil)
193
+ if kb_account_id.nil? || kb_tenant_id.nil? || amount.nil? || currency.nil? || payment_transaction_external_key.nil?
194
+ @logger.warn "Invalid notification: kb_account_id=#{kb_account_id}, kb_tenant_id=#{kb_tenant_id}, amount=#{amount}, currency=#{currency}, payment_external_key=#{payment_external_key}, payment_transaction_external_key=#{payment_transaction_external_key}, kb_payment_method_id=#{kb_payment_method_id}"
195
+ return nil
196
+ else
197
+ @logger.info "Recording payment: kb_account_id=#{kb_account_id}, amount=#{amount}, currency=#{currency}, payment_external_key=#{payment_external_key}, payment_transaction_external_key=#{payment_transaction_external_key}, kb_payment_method_id=#{kb_payment_method_id}"
198
+ end
199
+
200
+ @response_model.create(:api_call => :purchase,
201
+ :kb_account_id => kb_account_id,
202
+ :transaction_type => :PURCHASE,
203
+ :authorization => payment_transaction_external_key,
204
+ :kb_tenant_id => kb_tenant_id,
205
+ :success => is_success)
206
+
207
+ context = @kb_apis.create_context(kb_tenant_id)
208
+ kb_account = @kb_apis.account_user_api.get_account_by_id(kb_account_id, context)
209
+ kb_payment_method_id = kb_payment_method_id.nil? ? kb_account.payment_method_id : kb_payment_method_id
210
+ kb_payment_id = nil
211
+ properties = {}
212
+ @kb_apis.payment_api.create_purchase(kb_account,
213
+ kb_payment_method_id,
214
+ kb_payment_id,
215
+ amount,
216
+ currency,
217
+ payment_external_key,
218
+ payment_transaction_external_key,
219
+ properties,
220
+ context)
221
+ end
222
+
223
+ def get_active_merchant_module
224
+ ::OffsitePayments.integration(:bit_pay)
225
+ end
226
+ end
227
+ end
228
+ end