killbill 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +23 -0
- data/.travis.yml +19 -0
- data/Gemfile +3 -0
- data/Jarfile +2 -0
- data/README.md +116 -0
- data/Rakefile +13 -0
- data/VERSION +1 -0
- data/killbill.gemspec +32 -0
- data/lib/killbill.rb +46 -0
- data/lib/killbill/notification.rb +14 -0
- data/lib/killbill/payment.rb +44 -0
- data/lib/killbill/plugin.rb +64 -0
- data/lib/killbill/rake_task.rb +185 -0
- data/spec/killbill/base_plugin_spec.rb +44 -0
- data/spec/killbill/killbill_integration_spec.rb +51 -0
- data/spec/killbill/notification_plugin_spec.rb +49 -0
- data/spec/killbill/payment_plugin_spec.rb +30 -0
- data/spec/spec_helper.rb +35 -0
- metadata +124 -0
data/.gitignore
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
coverage
|
6
|
+
InstalledFiles
|
7
|
+
lib/bundler/man
|
8
|
+
pkg
|
9
|
+
rdoc
|
10
|
+
spec/reports
|
11
|
+
test/tmp
|
12
|
+
test/version_tmp
|
13
|
+
tmp
|
14
|
+
build
|
15
|
+
# YARD artifacts
|
16
|
+
.yardoc
|
17
|
+
_yardoc
|
18
|
+
doc/
|
19
|
+
|
20
|
+
.jbundler
|
21
|
+
Jarfile.lock
|
22
|
+
Gemfile.lock
|
23
|
+
*.swp
|
data/.travis.yml
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
language: ruby
|
2
|
+
|
3
|
+
notifications:
|
4
|
+
email:
|
5
|
+
- killbilling-dev@googlegroups.com
|
6
|
+
|
7
|
+
rvm:
|
8
|
+
- jruby-18mode
|
9
|
+
- jruby-19mode
|
10
|
+
- jruby-head
|
11
|
+
|
12
|
+
jdk:
|
13
|
+
- openjdk7
|
14
|
+
- oraclejdk7
|
15
|
+
- openjdk6
|
16
|
+
|
17
|
+
matrix:
|
18
|
+
allow_failures:
|
19
|
+
- rvm: jruby-head
|
data/Gemfile
ADDED
data/Jarfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,116 @@
|
|
1
|
+
killbill-plugin-framework-ruby
|
2
|
+
==============================
|
3
|
+
|
4
|
+
Framework to write Killbill plugins in Ruby.
|
5
|
+
|
6
|
+
There are various types of plugins one can write for Killbill:
|
7
|
+
|
8
|
+
1. notifications plugins, which listen to external bus events and can react to it
|
9
|
+
2. payment plugins, which are used to issue payments against a payment gateway
|
10
|
+
|
11
|
+
How to write a Notification plugin
|
12
|
+
----------------------------------
|
13
|
+
|
14
|
+
require 'killbill'
|
15
|
+
|
16
|
+
class MyNotificationPlugin < Killbill::Plugin::Notification
|
17
|
+
# Overriding this method is optional, only if you need to do some initialization work
|
18
|
+
def start_plugin
|
19
|
+
puts "MyNotificationPlugin plugin starting"
|
20
|
+
super
|
21
|
+
puts "MyNotificationPlugin plugin started"
|
22
|
+
end
|
23
|
+
|
24
|
+
# Invoked each time an event is received
|
25
|
+
def on_event(event)
|
26
|
+
puts "Received Killbill event #{event}"
|
27
|
+
end
|
28
|
+
|
29
|
+
# Overriding this method is optional, only if you need to do some tear down work
|
30
|
+
def stop_plugin
|
31
|
+
puts "MyNotificationPlugin plugin stopping"
|
32
|
+
super
|
33
|
+
puts "MyNotificationPlugin plugin stopped"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
Make sure to create the corresponding killbill.properties file:
|
38
|
+
|
39
|
+
mainClass=MyNotificationPlugin
|
40
|
+
pluginType=NOTIFICATION
|
41
|
+
|
42
|
+
How to write a Payment plugin
|
43
|
+
-----------------------------
|
44
|
+
|
45
|
+
require 'killbill'
|
46
|
+
|
47
|
+
class MyPaymentPlugin < Killbill::Plugin::Payment
|
48
|
+
def start_plugin
|
49
|
+
puts "MyPaymentPlugin plugin starting"
|
50
|
+
super
|
51
|
+
puts "MyPaymentPlugin plugin started"
|
52
|
+
end
|
53
|
+
|
54
|
+
def charge(killbill_account_id, killbill_payment_id, amount_in_cents, options = {})
|
55
|
+
end
|
56
|
+
|
57
|
+
def refund(killbill_account_id, killbill_payment_id, amount_in_cents, options = {})
|
58
|
+
end
|
59
|
+
|
60
|
+
def get_payment_info(killbill_payment_id, options = {})
|
61
|
+
end
|
62
|
+
|
63
|
+
def add_payment_method(payment_method, options = {})
|
64
|
+
end
|
65
|
+
|
66
|
+
def delete_payment_method(external_payment_method_id, options = {})
|
67
|
+
end
|
68
|
+
|
69
|
+
def update_payment_method(payment_method, options = {})
|
70
|
+
end
|
71
|
+
|
72
|
+
def set_default_payment_method(payment_method, options = {})
|
73
|
+
end
|
74
|
+
|
75
|
+
def create_account(killbill_account, options = {})
|
76
|
+
end
|
77
|
+
|
78
|
+
# Overriding this method is optional, only if you need to do some tear down work
|
79
|
+
def stop_plugin
|
80
|
+
puts "MyPaymentPlugin plugin stopping"
|
81
|
+
super
|
82
|
+
puts "MyPaymentPlugin plugin stopped"
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
Make sure to create the corresponding killbill.properties file:
|
87
|
+
|
88
|
+
mainClass=MyPaymentPlugin
|
89
|
+
pluginType=PAYMENT
|
90
|
+
|
91
|
+
Rake tasks
|
92
|
+
----------
|
93
|
+
|
94
|
+
The killbill gem also ships helpful Rake tasks to package Killbill-ready plugins.
|
95
|
+
|
96
|
+
To access these tasks, add the following to your Rakefile:
|
97
|
+
|
98
|
+
# Install tasks to package the plugin for Killbill
|
99
|
+
require 'killbill/rake_task'
|
100
|
+
Killbill::PluginHelper.install_tasks
|
101
|
+
|
102
|
+
# (Optional) Install tasks to build and release your plugin gem
|
103
|
+
require 'bundler/setup'
|
104
|
+
Bundler::GemHelper.install_tasks
|
105
|
+
|
106
|
+
You can verify these tasks are available by running `rake -T`.
|
107
|
+
|
108
|
+
To build the artifacts into pkg/:
|
109
|
+
|
110
|
+
# Cleanup output directories
|
111
|
+
rake killbill:clean
|
112
|
+
# Build your plugin gem in the pkg/ directory
|
113
|
+
rake build
|
114
|
+
# Build the Killbill plugin in the pkg/ directory
|
115
|
+
# The <plugin_name>-<plugin-version>/ directory is used as a staging directory
|
116
|
+
rake killbill:package
|
data/Rakefile
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
#!/usr/bin/env rake
|
2
|
+
|
3
|
+
# Install tasks to build and release the plugin
|
4
|
+
require 'bundler/setup'
|
5
|
+
Bundler::GemHelper.install_tasks
|
6
|
+
|
7
|
+
# Install test tasks
|
8
|
+
require 'rspec/core/rake_task'
|
9
|
+
desc "Run RSpec"
|
10
|
+
RSpec::Core::RakeTask.new
|
11
|
+
|
12
|
+
# Run tests by default
|
13
|
+
task :default => :spec
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
1.0.0
|
data/killbill.gemspec
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
version = File.read(File.expand_path('../VERSION', __FILE__)).strip
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = 'killbill'
|
5
|
+
s.version = version
|
6
|
+
s.summary = 'Framework to write Killbill plugins in Ruby.'
|
7
|
+
s.description = 'Base classes to write plugins.'
|
8
|
+
|
9
|
+
s.required_ruby_version = '>= 1.9.3'
|
10
|
+
|
11
|
+
s.license = 'Apache License (2.0)'
|
12
|
+
|
13
|
+
s.author = 'Killbill core team'
|
14
|
+
s.email = 'killbilling-users@googlegroups.com'
|
15
|
+
s.homepage = 'http://www.killbilling.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_development_dependency 'jbundler', '~> 0.4.1'
|
26
|
+
s.add_development_dependency 'rake', '>= 0.8.7'
|
27
|
+
s.add_development_dependency 'rspec', '~> 2.12.0'
|
28
|
+
|
29
|
+
s.requirements << "jar 'com.ning.billing:killbill-api', '0.1.48'"
|
30
|
+
# For testing only
|
31
|
+
s.requirements << "jar 'com.ning.billing:killbill-util:tests', '0.1.48'"
|
32
|
+
end
|
data/lib/killbill.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
begin
|
2
|
+
require 'java'
|
3
|
+
rescue LoadError => e
|
4
|
+
warn 'You need JRuby to run Killbill plugins'
|
5
|
+
raise e
|
6
|
+
end
|
7
|
+
|
8
|
+
KILLBILL_APIS = %w(
|
9
|
+
com.ning.billing.account.api.AccountUserApi
|
10
|
+
com.ning.billing.analytics.api.sanity.AnalyticsSanityApi
|
11
|
+
com.ning.billing.analytics.api.user.AnalyticsUserApi
|
12
|
+
com.ning.billing.catalog.api.CatalogUserApi
|
13
|
+
com.ning.billing.entitlement.api.migration.EntitlementMigrationApi
|
14
|
+
com.ning.billing.entitlement.api.timeline.EntitlementTimelineApi
|
15
|
+
com.ning.billing.entitlement.api.transfer.EntitlementTransferApi
|
16
|
+
com.ning.billing.entitlement.api.user.EntitlementUserApi
|
17
|
+
com.ning.billing.invoice.api.InvoiceMigrationApi
|
18
|
+
com.ning.billing.invoice.api.InvoicePaymentApi
|
19
|
+
com.ning.billing.invoice.api.InvoiceUserApi
|
20
|
+
com.ning.billing.meter.api.MeterUserApi
|
21
|
+
com.ning.billing.overdue.OverdueUserApi
|
22
|
+
com.ning.billing.payment.api.PaymentApi
|
23
|
+
com.ning.billing.tenant.api.TenantUserApi
|
24
|
+
com.ning.billing.usage.api.UsageUserApi
|
25
|
+
com.ning.billing.util.api.AuditUserApi
|
26
|
+
com.ning.billing.util.api.CustomFieldUserApi
|
27
|
+
com.ning.billing.util.api.ExportUserApi
|
28
|
+
com.ning.billing.util.api.TagUserApi
|
29
|
+
)
|
30
|
+
|
31
|
+
begin
|
32
|
+
KILLBILL_APIS.each { |api| java_import api }
|
33
|
+
rescue NameError
|
34
|
+
# killbill-api should be provided by the JRuby OSGI bundle. We default to using JBundler for development purposes only
|
35
|
+
begin
|
36
|
+
require 'jbundler'
|
37
|
+
KILLBILL_APIS.each { |api| java_import api }
|
38
|
+
warn 'Using JBundler to load killbill-api. This should only happen in development mode!'
|
39
|
+
warn "Classpath (see .jbundler/classpath.rb):\n\t#{JBUNDLER_CLASSPATH.join("\n\t")}"
|
40
|
+
rescue LoadError => e
|
41
|
+
warn 'Unable to load killbill-api and couldn\'t find JBundler. For development purposes, make sure to run: `bundle install && jbundle install\' from the killbill gem source tree'
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
require 'killbill/notification'
|
46
|
+
require 'killbill/payment'
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'killbill/plugin'
|
2
|
+
|
3
|
+
module Killbill
|
4
|
+
module Plugin
|
5
|
+
class Payment < PluginBase
|
6
|
+
|
7
|
+
class OperationUnsupportedByGatewayError < NotImplementedError
|
8
|
+
end
|
9
|
+
|
10
|
+
def charge(external_account_key, killbill_payment_id, amount_in_cents, options = {})
|
11
|
+
raise OperationUnsupportedByGatewayError
|
12
|
+
end
|
13
|
+
|
14
|
+
def refund(external_account_key, killbill_payment_id, amount_in_cents, options = {})
|
15
|
+
raise OperationUnsupportedByGatewayError
|
16
|
+
end
|
17
|
+
|
18
|
+
def get_payment_info(killbill_payment_id, options = {})
|
19
|
+
raise OperationUnsupportedByGatewayError
|
20
|
+
end
|
21
|
+
|
22
|
+
def add_payment_method(external_account_key, payment_method, options = {})
|
23
|
+
raise OperationUnsupportedByGatewayError
|
24
|
+
end
|
25
|
+
|
26
|
+
def delete_payment_method(external_account_key, external_payment_method_id, options = {})
|
27
|
+
raise OperationUnsupportedByGatewayError
|
28
|
+
end
|
29
|
+
|
30
|
+
def update_payment_method(external_account_key, payment_method, options = {})
|
31
|
+
raise OperationUnsupportedByGatewayError
|
32
|
+
end
|
33
|
+
|
34
|
+
def set_default_payment_method(external_account_key, payment_method, options = {})
|
35
|
+
raise OperationUnsupportedByGatewayError
|
36
|
+
end
|
37
|
+
|
38
|
+
def create_account(killbill_account, options = {})
|
39
|
+
raise OperationUnsupportedByGatewayError
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module Killbill
|
2
|
+
# There are various types of plugins one can write for Killbill:
|
3
|
+
#
|
4
|
+
# 1) notifications plugins, which listen to external bus events and can react to it
|
5
|
+
# 2) payment plugins, which are used to issue payments against a payment gateway
|
6
|
+
module Plugin
|
7
|
+
class PluginBase
|
8
|
+
|
9
|
+
attr_reader :active
|
10
|
+
|
11
|
+
# Called by the Killbill lifecycle when initializing the plugin
|
12
|
+
def start_plugin
|
13
|
+
@active = true
|
14
|
+
end
|
15
|
+
|
16
|
+
# Called by the Killbill lifecycle when stopping the plugin
|
17
|
+
def stop_plugin
|
18
|
+
@active = false
|
19
|
+
end
|
20
|
+
|
21
|
+
attr_accessor :account_user_api,
|
22
|
+
:analytics_sanity_api,
|
23
|
+
:analytics_user_api,
|
24
|
+
:catalog_user_api,
|
25
|
+
:entitlement_migration_api,
|
26
|
+
:entitlement_timeline_api,
|
27
|
+
:entitlement_transfer_api,
|
28
|
+
:entitlement_user_api,
|
29
|
+
:invoice_migration_api,
|
30
|
+
:invoice_payment_api,
|
31
|
+
:invoice_user_api,
|
32
|
+
:meter_user_api,
|
33
|
+
:overdue_user_api,
|
34
|
+
:payment_api,
|
35
|
+
:tenant_user_api,
|
36
|
+
:usage_user_api,
|
37
|
+
:audit_user_api,
|
38
|
+
:custom_field_user_api,
|
39
|
+
:export_user_api,
|
40
|
+
:tag_user_api
|
41
|
+
|
42
|
+
# Called by the Killbill lifecycle when instantiating the plugin
|
43
|
+
def initialize(apis_map = {})
|
44
|
+
@active = false
|
45
|
+
|
46
|
+
apis_map.each do |api_name, api_instance|
|
47
|
+
begin
|
48
|
+
self.send("#{api_name}=", api_instance)
|
49
|
+
rescue NoMethodError
|
50
|
+
warn "Ignoring unsupported API: #{api_name}"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
class APINotAvailableError < NotImplementedError
|
56
|
+
end
|
57
|
+
|
58
|
+
def method_missing(m, *args, &block)
|
59
|
+
raise APINotAvailableError.new("API #{m} is not available") if m =~ /_api$/
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,185 @@
|
|
1
|
+
require 'bundler'
|
2
|
+
require 'logger'
|
3
|
+
require 'pathname'
|
4
|
+
require 'rake'
|
5
|
+
require 'rake/packagetask'
|
6
|
+
|
7
|
+
module Killbill
|
8
|
+
class PluginHelper
|
9
|
+
include Rake::DSL
|
10
|
+
|
11
|
+
class << self
|
12
|
+
def install_tasks(opts = {})
|
13
|
+
new(opts[:base_name] || Dir.pwd, # Path to the plugin root directory (where the gempec and/or Gemfile should be)
|
14
|
+
opts[:plugin_name], # Plugin name, e.g. 'klogger'
|
15
|
+
opts[:gem_name], # Gem file name, e.g. 'klogger-1.0.0.gem'
|
16
|
+
opts[:gemfile_name] || "Gemfile", # Gemfile name
|
17
|
+
opts[:gemfile_lock_name] || "Gemfile.lock") # Gemfile.lock name
|
18
|
+
.install
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def initialize(base_name, plugin_name, gem_name, gemfile_name, gemfile_lock_name)
|
23
|
+
@logger = Logger.new(STDOUT)
|
24
|
+
@logger.level = Logger::INFO
|
25
|
+
|
26
|
+
@base_name = base_name
|
27
|
+
@plugin_name = plugin_name
|
28
|
+
@gem_name = gem_name
|
29
|
+
@gemfile_name = gemfile_name
|
30
|
+
@gemfile_lock_name = gemfile_lock_name
|
31
|
+
|
32
|
+
# Plugin base directory
|
33
|
+
@base = Pathname.new(@base_name).expand_path
|
34
|
+
|
35
|
+
# Find the gemspec to determine name and version
|
36
|
+
@plugin_gemspec = find_plugin_gemspec
|
37
|
+
|
38
|
+
# Temporary build directory
|
39
|
+
# Don't use 'pkg' as it is used by Rake::PackageTask already: it will
|
40
|
+
# hard link all files from @package_dir to pkg to avoid tar'ing up symbolic links
|
41
|
+
@package_dir = Pathname.new(name).expand_path
|
42
|
+
|
43
|
+
# Staging area to install gem dependencies
|
44
|
+
# Note the Killbill friendly structure (which we will keep in the tarball)
|
45
|
+
@target_dir = @package_dir.join("#{version}/gems").expand_path
|
46
|
+
|
47
|
+
# Staging area to install the killbill.properties file
|
48
|
+
@killbill_properties_target_dir = @package_dir.join("#{version}").expand_path
|
49
|
+
end
|
50
|
+
|
51
|
+
def specs
|
52
|
+
# Rely on the Gemfile definition, if it exists, to get all dependencies
|
53
|
+
# (we assume the Gemfile includes the plugin gemspec, as it should).
|
54
|
+
# Otherwise, use only the plugin gemspec.
|
55
|
+
@specs ||= @gemfile_definition ? @gemfile_definition.specs : [@plugin_gemspec]
|
56
|
+
end
|
57
|
+
|
58
|
+
def install
|
59
|
+
namespace :killbill do
|
60
|
+
desc "Validate plugin tree"
|
61
|
+
task :validate => killbill_properties_file do
|
62
|
+
validate
|
63
|
+
end
|
64
|
+
|
65
|
+
# Build the .tar.gz and .zip packages
|
66
|
+
task :package => :stage
|
67
|
+
package_task = Rake::PackageTask.new(name, version) do |p|
|
68
|
+
p.need_tar_gz = true
|
69
|
+
p.need_zip = true
|
70
|
+
end
|
71
|
+
|
72
|
+
desc "Stage all dependencies"
|
73
|
+
task :stage => :validate do
|
74
|
+
stage_dependencies
|
75
|
+
stage_killbill_properties_file
|
76
|
+
|
77
|
+
# Small hack! Update the list of files to package (Rake::FileList is evaluated too early above)
|
78
|
+
package_task.package_files = Rake::FileList.new("#{@package_dir.basename}/**/*")
|
79
|
+
end
|
80
|
+
|
81
|
+
desc "List all dependencies"
|
82
|
+
task :dependency => :validate do
|
83
|
+
print_dependencies
|
84
|
+
end
|
85
|
+
|
86
|
+
desc "Delete #{@package_dir}"
|
87
|
+
task :clean => :clobber_package do
|
88
|
+
rm_rf @package_dir
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
private
|
94
|
+
|
95
|
+
def validate
|
96
|
+
@plugin_gem_file = find_plugin_gem
|
97
|
+
@gemfile_definition = find_gemfile
|
98
|
+
end
|
99
|
+
|
100
|
+
def print_dependencies
|
101
|
+
puts "Gems to be staged:"
|
102
|
+
specs.each { |spec| puts " #{spec.name} (#{spec.version})" }
|
103
|
+
end
|
104
|
+
|
105
|
+
def name
|
106
|
+
@plugin_gemspec.name
|
107
|
+
end
|
108
|
+
|
109
|
+
def version
|
110
|
+
@plugin_gemspec.version
|
111
|
+
end
|
112
|
+
|
113
|
+
# Parse the <plugin_name>.gemspec file
|
114
|
+
def find_plugin_gemspec
|
115
|
+
gemspecs = @plugin_name ? [File.join(@base, "#{@plugin_name}.gemspec")] : Dir[File.join(@base, "{,*}.gemspec")]
|
116
|
+
raise "Unable to find your plugin gemspec in #{@base}" unless gemspecs.size == 1
|
117
|
+
spec_path = gemspecs.first
|
118
|
+
@logger.debug "Parsing #{spec_path}"
|
119
|
+
Bundler.load_gemspec(spec_path)
|
120
|
+
end
|
121
|
+
|
122
|
+
def find_plugin_gem
|
123
|
+
if @gem_name
|
124
|
+
# Absolute path?
|
125
|
+
plugin_gem_file = Pathname.new(@gem_name).expand_path
|
126
|
+
# Relative path to the base?
|
127
|
+
plugin_gem_file = @base.join(@gem_name).expand_path unless plugin_gem_file.file?
|
128
|
+
raise "Unable to find your plugin gem in #{@base}. Did you build it (`rake build')?" unless plugin_gem_file.file?
|
129
|
+
else
|
130
|
+
plugin_gem_files = Dir[File.join(@base, "**/#{name}-#{version}.gem")]
|
131
|
+
@logger.debug "Gem candidates found: #{plugin_gem_files}"
|
132
|
+
raise "Unable to find your plugin gem in #{@base}. Did you build it? (`rake build')" unless plugin_gem_files.size >= 1
|
133
|
+
# Take the first one, assume the other ones are from build directories (e.g. pkg)
|
134
|
+
plugin_gem_file = plugin_gem_files.first
|
135
|
+
end
|
136
|
+
|
137
|
+
@logger.debug "Found #{plugin_gem_file}"
|
138
|
+
Pathname.new(plugin_gem_file).expand_path
|
139
|
+
end
|
140
|
+
|
141
|
+
# Parse the existing Gemfile and Gemfile.lock files
|
142
|
+
def find_gemfile
|
143
|
+
gemfile = @base.join(@gemfile_name).expand_path
|
144
|
+
# Don't make the Gemfile a requirement, a gemspec should be enough
|
145
|
+
return nil unless gemfile.file?
|
146
|
+
|
147
|
+
# Make sure the developer ran `bundle install' first. We could probably run
|
148
|
+
# Bundler::Installer::install(@target_dir, @definition, {})
|
149
|
+
# but it may be better to make sure all dependencies are resolved first,
|
150
|
+
# before attempting to build the plugin
|
151
|
+
gemfile_lock = @base.join(@gemfile_lock_name).expand_path
|
152
|
+
raise "Unable to find the Gemfile.lock at #{gemfile_lock} for your plugin. Please run `bundle install' first" unless gemfile_lock.file?
|
153
|
+
|
154
|
+
@logger.debug "Parsing #{gemfile} and #{gemfile_lock}"
|
155
|
+
Bundler::Definition.build(gemfile, gemfile_lock, nil)
|
156
|
+
end
|
157
|
+
|
158
|
+
def stage_dependencies
|
159
|
+
# Create the target directory
|
160
|
+
mkdir_p @target_dir.to_s
|
161
|
+
|
162
|
+
@logger.debug "Installing all gem dependencies to #{@target_dir}"
|
163
|
+
# We can't simply use Bundler::Installer unfortunately, because we can't tell it to copy the gems for cached ones
|
164
|
+
# (it will default to using Bundler::Source::Path references to the gemspecs on "install").
|
165
|
+
specs.each do |spec|
|
166
|
+
# For the plugin itself, install it manually (the cache path is likely to be wrong)
|
167
|
+
next if spec.name == name and spec.version == version
|
168
|
+
@logger.debug "Staging #{spec.name} (#{spec.version}) from #{spec.cache_file}"
|
169
|
+
Gem::Installer.new(spec.cache_file, {:force => true, :install_dir => @target_dir}).install
|
170
|
+
end
|
171
|
+
|
172
|
+
@logger.debug "Staging #{name} (#{version}) from #{@plugin_gem_file}"
|
173
|
+
Gem::Installer.new(@plugin_gem_file, {:force => true, :install_dir => @target_dir}).install
|
174
|
+
end
|
175
|
+
|
176
|
+
def stage_killbill_properties_file
|
177
|
+
@logger.debug "Staging #{killbill_properties_file} to #{@killbill_properties_target_dir}"
|
178
|
+
cp killbill_properties_file, @killbill_properties_target_dir
|
179
|
+
end
|
180
|
+
|
181
|
+
def killbill_properties_file
|
182
|
+
@base.join("killbill.properties").expand_path.to_s
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class LifecycleNotificationPlugin < Killbill::Plugin::PluginBase
|
4
|
+
attr_accessor :lifecycled
|
5
|
+
|
6
|
+
def start_plugin
|
7
|
+
@lifecycled = true
|
8
|
+
super
|
9
|
+
end
|
10
|
+
|
11
|
+
def stop_plugin
|
12
|
+
@lifecycled = true
|
13
|
+
super
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe Killbill::Plugin::PluginBase do
|
18
|
+
it 'should be able to register Killbill API instances' do
|
19
|
+
plugin = Killbill::Plugin::PluginBase.new(:account_user_api => MockAccountUserApi.new)
|
20
|
+
|
21
|
+
plugin.account_user_api.get_accounts(nil).size.should == 0
|
22
|
+
lambda { plugin.foobar_user_api.do_foo('with my bar') }.should raise_error Killbill::Plugin::PluginBase::APINotAvailableError
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'should be able to add custom code in the startup/shutdown sequence' do
|
26
|
+
plugin = LifecycleNotificationPlugin.new
|
27
|
+
|
28
|
+
plugin.lifecycled = false
|
29
|
+
plugin.lifecycled.should be_false
|
30
|
+
plugin.active.should be_false
|
31
|
+
|
32
|
+
plugin.start_plugin
|
33
|
+
plugin.lifecycled.should be_true
|
34
|
+
plugin.active.should be_true
|
35
|
+
|
36
|
+
plugin.lifecycled = false
|
37
|
+
plugin.lifecycled.should be_false
|
38
|
+
plugin.active.should be_true
|
39
|
+
|
40
|
+
plugin.stop_plugin
|
41
|
+
plugin.lifecycled.should be_true
|
42
|
+
plugin.active.should be_false
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
java_import 'java.util.UUID'
|
4
|
+
java_import 'org.joda.time.DateTimeZone'
|
5
|
+
java_import 'com.ning.billing.catalog.api.Currency'
|
6
|
+
java_import 'com.ning.billing.mock.api.MockBillCycleDay'
|
7
|
+
|
8
|
+
describe Killbill::Plugin do
|
9
|
+
before(:each) do
|
10
|
+
@account_user_api = MockAccountUserApi.new
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'should be able to access Killbill mock APIs' do
|
14
|
+
@account_user_api.createAccount(UUID.randomUUID,
|
15
|
+
'externalKey',
|
16
|
+
'email',
|
17
|
+
'name',
|
18
|
+
1,
|
19
|
+
Currency::USD,
|
20
|
+
MockBillCycleDay.new(12),
|
21
|
+
UUID.randomUUID,
|
22
|
+
DateTimeZone::UTC,
|
23
|
+
'locale',
|
24
|
+
'address1',
|
25
|
+
'address2',
|
26
|
+
'companyName',
|
27
|
+
'city',
|
28
|
+
'stateOrProvince',
|
29
|
+
'country',
|
30
|
+
'postalCode',
|
31
|
+
'phone')
|
32
|
+
account = @account_user_api.getAccountByKey('externalKey', nil)
|
33
|
+
account.external_key.should == 'externalKey'
|
34
|
+
account.email.should == 'email'
|
35
|
+
account.name.should == 'name'
|
36
|
+
account.first_name_length.should == 1
|
37
|
+
account.currency.should == Currency::USD
|
38
|
+
account.payment_method_id.should_not be_nil
|
39
|
+
account.time_zone.should == DateTimeZone::UTC
|
40
|
+
account.locale.should == 'locale'
|
41
|
+
account.address1.should == 'address1'
|
42
|
+
account.address2.should == 'address2'
|
43
|
+
account.company_name.should == 'companyName'
|
44
|
+
account.city.should == 'city'
|
45
|
+
account.state_or_province.should == 'stateOrProvince'
|
46
|
+
account.country.should == 'country'
|
47
|
+
account.postal_code.should == 'postalCode'
|
48
|
+
account.phone.should == 'phone'
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class DummyNotificationPlugin < Killbill::Plugin::Notification
|
4
|
+
end
|
5
|
+
|
6
|
+
class HoarderNotificationPlugin < Killbill::Plugin::Notification
|
7
|
+
attr_reader :events
|
8
|
+
|
9
|
+
def start_plugin
|
10
|
+
@events = []
|
11
|
+
super
|
12
|
+
end
|
13
|
+
|
14
|
+
def on_event(event)
|
15
|
+
@events << event
|
16
|
+
end
|
17
|
+
|
18
|
+
def stop_plugin
|
19
|
+
super
|
20
|
+
@events = []
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe Killbill::Plugin::Notification do
|
25
|
+
before(:each) do
|
26
|
+
@event = Hash.new(:account_id => SecureRandom.uuid)
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should not raise exceptions by default" do
|
30
|
+
plugin = DummyNotificationPlugin.new
|
31
|
+
lambda { plugin.on_event(@event) }.should_not raise_error
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should be able to receive all events" do
|
35
|
+
plugin = HoarderNotificationPlugin.new
|
36
|
+
|
37
|
+
plugin.start_plugin
|
38
|
+
plugin.events.size.should == 0
|
39
|
+
|
40
|
+
(1..100).each do |i|
|
41
|
+
plugin.on_event(@event)
|
42
|
+
plugin.events.size.should == i
|
43
|
+
plugin.events[-1].should == @event
|
44
|
+
end
|
45
|
+
|
46
|
+
plugin.stop_plugin
|
47
|
+
plugin.events.size.should == 0
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class DummyPaymentPlugin < Killbill::Plugin::Payment
|
4
|
+
end
|
5
|
+
|
6
|
+
describe Killbill::Plugin::Payment do
|
7
|
+
before(:each) do
|
8
|
+
@external_account_key = SecureRandom.uuid
|
9
|
+
@killbill_payment_id = SecureRandom.uuid
|
10
|
+
@amount_in_cents = rand(100000)
|
11
|
+
|
12
|
+
@payment_method = Hash.new(:credit_card => SecureRandom.uuid)
|
13
|
+
@external_payment_method_id = SecureRandom.uuid
|
14
|
+
|
15
|
+
@killbill_account = Hash.new(:name => SecureRandom.uuid)
|
16
|
+
|
17
|
+
@plugin = DummyPaymentPlugin.new
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should raise exceptions for unsupported operations" do
|
21
|
+
lambda { @plugin.charge(@external_account_key, @killbill_payment_id, @amount_in_cents) }.should raise_error Killbill::Plugin::Payment::OperationUnsupportedByGatewayError
|
22
|
+
lambda { @plugin.refund(@external_account_key, @killbill_payment_id, @amount_in_cents) }.should raise_error Killbill::Plugin::Payment::OperationUnsupportedByGatewayError
|
23
|
+
lambda { @plugin.get_payment_info(@killbill_payment_id) }.should raise_error Killbill::Plugin::Payment::OperationUnsupportedByGatewayError
|
24
|
+
lambda { @plugin.add_payment_method(@external_account_key, @payment_method) }.should raise_error Killbill::Plugin::Payment::OperationUnsupportedByGatewayError
|
25
|
+
lambda { @plugin.delete_payment_method(@external_account_key, @external_payment_method_id) }.should raise_error Killbill::Plugin::Payment::OperationUnsupportedByGatewayError
|
26
|
+
lambda { @plugin.update_payment_method(@external_account_key, @payment_method) }.should raise_error Killbill::Plugin::Payment::OperationUnsupportedByGatewayError
|
27
|
+
lambda { @plugin.set_default_payment_method(@external_account_key, @payment_method) }.should raise_error Killbill::Plugin::Payment::OperationUnsupportedByGatewayError
|
28
|
+
lambda { @plugin.create_account(@killbill_account) }.should raise_error Killbill::Plugin::Payment::OperationUnsupportedByGatewayError
|
29
|
+
end
|
30
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'bundler'
|
2
|
+
require 'killbill'
|
3
|
+
|
4
|
+
%w(
|
5
|
+
MockAccountUserApi
|
6
|
+
MockEntitlementUserApi
|
7
|
+
).each do |api|
|
8
|
+
begin
|
9
|
+
java_import "com.ning.billing.mock.api.#{api}"
|
10
|
+
rescue LoadError
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
require 'rspec'
|
15
|
+
|
16
|
+
RSpec.configure do |config|
|
17
|
+
config.color_enabled = true
|
18
|
+
config.tty = true
|
19
|
+
config.formatter = 'documentation'
|
20
|
+
end
|
21
|
+
|
22
|
+
begin
|
23
|
+
require 'securerandom'
|
24
|
+
SecureRandom.uuid
|
25
|
+
rescue LoadError, NoMethodError
|
26
|
+
# See http://jira.codehaus.org/browse/JRUBY-6176
|
27
|
+
module SecureRandom
|
28
|
+
def self.uuid
|
29
|
+
ary = self.random_bytes(16).unpack("NnnnnN")
|
30
|
+
ary[2] = (ary[2] & 0x0fff) | 0x4000
|
31
|
+
ary[3] = (ary[3] & 0x3fff) | 0x8000
|
32
|
+
"%08x-%04x-%04x-%04x-%04x%08x" % ary
|
33
|
+
end unless respond_to?(:uuid)
|
34
|
+
end
|
35
|
+
end
|
metadata
ADDED
@@ -0,0 +1,124 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: killbill
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease:
|
5
|
+
version: 1.0.0
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Killbill core team
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-01-29 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: jbundler
|
16
|
+
version_requirements: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - ~>
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: 0.4.1
|
21
|
+
none: false
|
22
|
+
requirement: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.4.1
|
27
|
+
none: false
|
28
|
+
prerelease: false
|
29
|
+
type: :development
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: rake
|
32
|
+
version_requirements: !ruby/object:Gem::Requirement
|
33
|
+
requirements:
|
34
|
+
- - ! '>='
|
35
|
+
- !ruby/object:Gem::Version
|
36
|
+
version: 0.8.7
|
37
|
+
none: false
|
38
|
+
requirement: !ruby/object:Gem::Requirement
|
39
|
+
requirements:
|
40
|
+
- - ! '>='
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: 0.8.7
|
43
|
+
none: false
|
44
|
+
prerelease: false
|
45
|
+
type: :development
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: rspec
|
48
|
+
version_requirements: !ruby/object:Gem::Requirement
|
49
|
+
requirements:
|
50
|
+
- - ~>
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: 2.12.0
|
53
|
+
none: false
|
54
|
+
requirement: !ruby/object:Gem::Requirement
|
55
|
+
requirements:
|
56
|
+
- - ~>
|
57
|
+
- !ruby/object:Gem::Version
|
58
|
+
version: 2.12.0
|
59
|
+
none: false
|
60
|
+
prerelease: false
|
61
|
+
type: :development
|
62
|
+
description: Base classes to write plugins.
|
63
|
+
email: killbilling-users@googlegroups.com
|
64
|
+
executables: []
|
65
|
+
extensions: []
|
66
|
+
extra_rdoc_files: []
|
67
|
+
files:
|
68
|
+
- .gitignore
|
69
|
+
- .travis.yml
|
70
|
+
- Gemfile
|
71
|
+
- Jarfile
|
72
|
+
- README.md
|
73
|
+
- Rakefile
|
74
|
+
- VERSION
|
75
|
+
- killbill.gemspec
|
76
|
+
- lib/killbill.rb
|
77
|
+
- lib/killbill/notification.rb
|
78
|
+
- lib/killbill/payment.rb
|
79
|
+
- lib/killbill/plugin.rb
|
80
|
+
- lib/killbill/rake_task.rb
|
81
|
+
- spec/killbill/base_plugin_spec.rb
|
82
|
+
- spec/killbill/killbill_integration_spec.rb
|
83
|
+
- spec/killbill/notification_plugin_spec.rb
|
84
|
+
- spec/killbill/payment_plugin_spec.rb
|
85
|
+
- spec/spec_helper.rb
|
86
|
+
homepage: http://www.killbilling.org
|
87
|
+
licenses:
|
88
|
+
- Apache License (2.0)
|
89
|
+
post_install_message:
|
90
|
+
rdoc_options:
|
91
|
+
- --exclude
|
92
|
+
- .
|
93
|
+
require_paths:
|
94
|
+
- lib
|
95
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
96
|
+
requirements:
|
97
|
+
- - ! '>='
|
98
|
+
- !ruby/object:Gem::Version
|
99
|
+
version: 1.9.3
|
100
|
+
none: false
|
101
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
102
|
+
requirements:
|
103
|
+
- - ! '>='
|
104
|
+
- !ruby/object:Gem::Version
|
105
|
+
segments:
|
106
|
+
- 0
|
107
|
+
hash: 2
|
108
|
+
version: !binary |-
|
109
|
+
MA==
|
110
|
+
none: false
|
111
|
+
requirements:
|
112
|
+
- jar 'com.ning.billing:killbill-api', '0.1.48'
|
113
|
+
- jar 'com.ning.billing:killbill-util:tests', '0.1.48'
|
114
|
+
rubyforge_project:
|
115
|
+
rubygems_version: 1.8.24
|
116
|
+
signing_key:
|
117
|
+
specification_version: 3
|
118
|
+
summary: Framework to write Killbill plugins in Ruby.
|
119
|
+
test_files:
|
120
|
+
- spec/killbill/base_plugin_spec.rb
|
121
|
+
- spec/killbill/killbill_integration_spec.rb
|
122
|
+
- spec/killbill/notification_plugin_spec.rb
|
123
|
+
- spec/killbill/payment_plugin_spec.rb
|
124
|
+
- spec/spec_helper.rb
|