killbill 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|