killbill-securenet 0.1.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.
@@ -0,0 +1,80 @@
1
+ # -- encoding : utf-8 --
2
+
3
+ set :views, File.expand_path(File.dirname(__FILE__) + '/views')
4
+
5
+ include Killbill::Plugin::ActiveMerchant::Sinatra
6
+
7
+ configure do
8
+ # Usage: rackup -Ilib -E test
9
+ if development? or test?
10
+ # Make sure the plugin is initialized
11
+ plugin = ::Killbill::Securenet::PaymentPlugin.new
12
+ plugin.logger = Logger.new(STDOUT)
13
+ plugin.logger.level = Logger::INFO
14
+ plugin.conf_dir = File.dirname(File.dirname(__FILE__)) + '/..'
15
+ plugin.start_plugin
16
+ end
17
+ end
18
+
19
+ helpers do
20
+ def plugin(session = {})
21
+ ::Killbill::Securenet::PrivatePaymentPlugin.new(session)
22
+ end
23
+ end
24
+
25
+ # curl -v http://127.0.0.1:9292/plugins/killbill-securenet/form
26
+ get '/plugins/killbill-securenet/form', :provides => 'html' do
27
+ order_id = request.GET['order_id']
28
+ account_id = request.GET['account_id']
29
+ options = {
30
+ :amount => request.GET['amount'],
31
+ :currency => request.GET['currency'],
32
+ :test => request.GET['test'],
33
+ :credential2 => request.GET['credential2'],
34
+ :credential3 => request.GET['credential3'],
35
+ :credential4 => request.GET['credential4'],
36
+ :country => request.GET['country'],
37
+ :account_name => request.GET['account_name'],
38
+ :transaction_type => request.GET['transaction_type'],
39
+ :authcode => request.GET['authcode'],
40
+ :notify_url => request.GET['notify_url'],
41
+ :return_url => request.GET['return_url'],
42
+ :redirect_param => request.GET['redirect_param'],
43
+ :forward_url => request.GET['forward_url']
44
+ }
45
+
46
+ @form = plugin(session).payment_form_for(order_id, account_id, :securenet, options) do |service|
47
+ # Add your custom hidden tags here, e.g.
48
+ #service.token = config[:securenet][:token]
49
+ submit_tag 'Submit'
50
+ end
51
+
52
+ erb :form
53
+ end
54
+
55
+ # curl -v http://127.0.0.1:9292/plugins/killbill-securenet/1.0/pms/1
56
+ get '/plugins/killbill-securenet/1.0/pms/:id', :provides => 'json' do
57
+ if pm = ::Killbill::Securenet::SecurenetPaymentMethod.find_by_id(params[:id].to_i)
58
+ pm.to_json
59
+ else
60
+ status 404
61
+ end
62
+ end
63
+
64
+ # curl -v http://127.0.0.1:9292/plugins/killbill-securenet/1.0/transactions/1
65
+ get '/plugins/killbill-securenet/1.0/transactions/:id', :provides => 'json' do
66
+ if transaction = ::Killbill::Securenet::SecurenetTransaction.find_by_id(params[:id].to_i)
67
+ transaction.to_json
68
+ else
69
+ status 404
70
+ end
71
+ end
72
+
73
+ # curl -v http://127.0.0.1:9292/plugins/killbill-securenet/1.0/responses/1
74
+ get '/plugins/killbill-securenet/1.0/responses/:id', :provides => 'json' do
75
+ if transaction = ::Killbill::Securenet::SecurenetResponse.find_by_id(params[:id].to_i)
76
+ transaction.to_json
77
+ else
78
+ status 404
79
+ end
80
+ end
@@ -0,0 +1,23 @@
1
+ module Killbill #:nodoc:
2
+ module Securenet #:nodoc:
3
+ class SecurenetPaymentMethod < ::Killbill::Plugin::ActiveMerchant::ActiveRecord::PaymentMethod
4
+
5
+ self.table_name = 'securenet_payment_methods'
6
+
7
+ def self.from_response(kb_account_id, kb_payment_method_id, kb_tenant_id, cc_or_token, response, options, extra_params = {}, model = ::Killbill::Securenet::SecurenetPaymentMethod)
8
+ super(kb_account_id,
9
+ kb_payment_method_id,
10
+ kb_tenant_id,
11
+ cc_or_token,
12
+ response,
13
+ options,
14
+ {
15
+ # Pass custom key/values here
16
+ #:params_id => extract(response, 'id'),
17
+ #:params_card_id => extract(response, 'card', 'id')
18
+ }.merge!(extra_params),
19
+ model)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,27 @@
1
+ module Killbill #:nodoc:
2
+ module Securenet #:nodoc:
3
+ class SecurenetResponse < ::Killbill::Plugin::ActiveMerchant::ActiveRecord::Response
4
+
5
+ self.table_name = 'securenet_responses'
6
+
7
+ has_one :securenet_transaction
8
+
9
+ def self.from_response(api_call, kb_account_id, kb_payment_id, kb_payment_transaction_id, transaction_type, payment_processor_account_id, kb_tenant_id, response, extra_params = {}, model = ::Killbill::Securenet::SecurenetResponse)
10
+ super(api_call,
11
+ kb_account_id,
12
+ kb_payment_id,
13
+ kb_payment_transaction_id,
14
+ transaction_type,
15
+ payment_processor_account_id,
16
+ kb_tenant_id,
17
+ response,
18
+ {
19
+ # Pass custom key/values here
20
+ #:params_id => extract(response, 'id'),
21
+ #:params_card_id => extract(response, 'card', 'id')
22
+ }.merge!(extra_params),
23
+ model)
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,11 @@
1
+ module Killbill #:nodoc:
2
+ module Securenet #:nodoc:
3
+ class SecurenetTransaction < ::Killbill::Plugin::ActiveMerchant::ActiveRecord::Transaction
4
+
5
+ self.table_name = 'securenet_transactions'
6
+
7
+ belongs_to :securenet_response
8
+
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,13 @@
1
+ module Killbill #:nodoc:
2
+ module Securenet #:nodoc:
3
+ class PrivatePaymentPlugin < ::Killbill::Plugin::ActiveMerchant::PrivatePaymentPlugin
4
+ def initialize(session = {})
5
+ super(:securenet,
6
+ ::Killbill::Securenet::SecurenetPaymentMethod,
7
+ ::Killbill::Securenet::SecurenetTransaction,
8
+ ::Killbill::Securenet::SecurenetResponse,
9
+ session)
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,8 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <body>
4
+
5
+ <%= @form %>
6
+
7
+ </body>
8
+ </html>
data/pom.xml ADDED
@@ -0,0 +1,44 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!--
3
+ ~ Copyright 2014-2015 The Billing Project, LLC
4
+ ~
5
+ ~ The Billing Project licenses this file to you under the Apache License, version 2.0
6
+ ~ (the "License"); you may not use this file except in compliance with the
7
+ ~ License. You may obtain a copy of the License at:
8
+ ~
9
+ ~ http://www.apache.org/licenses/LICENSE-2.0
10
+ ~
11
+ ~ Unless required by applicable law or agreed to in writing, software
12
+ ~ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13
+ ~ WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14
+ ~ License for the specific language governing permissions and limitations
15
+ ~ under the License.
16
+ -->
17
+
18
+ <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
19
+ <parent>
20
+ <groupId>org.sonatype.oss</groupId>
21
+ <artifactId>oss-parent</artifactId>
22
+ <version>5</version>
23
+ </parent>
24
+ <modelVersion>4.0.0</modelVersion>
25
+ <groupId>org.kill-bill.billing.plugin.ruby</groupId>
26
+ <artifactId>securenet-plugin</artifactId>
27
+ <packaging>pom</packaging>
28
+ <version>0.1.0</version>
29
+ <name>securenet-plugin</name>
30
+ <url>http://github.com/killbill/killbill-securenet-plugin</url>
31
+ <description>Plugin for accessing SecureNet as a payment gateway</description>
32
+ <licenses>
33
+ <license>
34
+ <name>Apache License 2.0</name>
35
+ <url>http://www.apache.org/licenses/LICENSE-2.0.html</url>
36
+ <distribution>repo</distribution>
37
+ </license>
38
+ </licenses>
39
+ <scm>
40
+ <connection>scm:git:git://github.com/killbill/killbill-securenet-plugin.git</connection>
41
+ <url>https://github.com/killbill/killbill-securenet-plugin/</url>
42
+ <developerConnection>scm:git:git@github.com:killbill/killbill-securenet-plugin.git</developerConnection>
43
+ </scm>
44
+ </project>
data/release.sh ADDED
@@ -0,0 +1,61 @@
1
+ set -e
2
+
3
+ BUNDLE=${BUNDLE-"bundle exec"}
4
+ MVN=${MVN-"mvn"}
5
+
6
+ if [ 'GNU' != "$(tar --help | grep GNU | head -1 | awk '{print $1}')" ]; then
7
+ echo 'Unable to release: make sure to use GNU tar'
8
+ exit 1
9
+ fi
10
+
11
+ if $(ruby -e'require "java"'); then
12
+ # Good
13
+ echo 'Detected JRuby'
14
+ else
15
+ echo 'Unable to release: make sure to use JRuby'
16
+ exit 1
17
+ fi
18
+
19
+ VERSION=`grep -E '<version>([0-9]+\.[0-9]+\.[0-9]+)</version>' pom.xml | sed 's/[\t \n]*<version>\(.*\)<\/version>[\t \n]*/\1/'`
20
+ if [[ -z "$NO_RELEASE" && "$VERSION" != "$(cat $PWD/VERSION)" ]]; then
21
+ echo 'Unable to release: make sure the versions in pom.xml and VERSION match'
22
+ exit 1
23
+ fi
24
+
25
+ echo 'Cleaning up'
26
+ $BUNDLE rake killbill:clean
27
+
28
+ echo 'Building gem'
29
+ $BUNDLE rake build
30
+
31
+ if [[ -z "$NO_RELEASE" ]]; then
32
+ echo 'Pushing the gem to Rubygems'
33
+ $BUNDLE rake release
34
+ fi
35
+
36
+ echo 'Building artifact'
37
+ $BUNDLE rake killbill:package
38
+
39
+ ARTIFACT="$PWD/pkg/killbill-securenet-$VERSION.tar.gz"
40
+ echo "Pushing $ARTIFACT to Maven Central"
41
+
42
+ if [[ -z "$NO_RELEASE" ]]; then
43
+ GOAL=gpg:sign-and-deploy-file
44
+ REPOSITORY_ID=ossrh-releases
45
+ URL=https://oss.sonatype.org/service/local/staging/deploy/maven2/
46
+ else
47
+ GOAL=deploy:deploy-file
48
+ REPOSITORY_ID=sonatype-nexus-snapshots
49
+ URL=https://oss.sonatype.org/content/repositories/snapshots/
50
+ VERSION="$VERSION-SNAPSHOT"
51
+ fi
52
+
53
+ $MVN $GOAL \
54
+ -DgroupId=org.kill-bill.billing.plugin.ruby \
55
+ -DartifactId=securenet-plugin \
56
+ -Dversion=$VERSION \
57
+ -Dpackaging=tar.gz \
58
+ -DrepositoryId=$REPOSITORY_ID \
59
+ -Durl=$URL \
60
+ -Dfile=$ARTIFACT \
61
+ -DpomFile=pom.xml
data/securenet.yml ADDED
@@ -0,0 +1,29 @@
1
+ :securenet:
2
+ :login: <%= ENV['LOGIN'] %>
3
+ :password: <%= ENV['PASSWORD'] %>
4
+ :test: true
5
+
6
+ :database:
7
+ # SQLite (development)
8
+ :adapter: sqlite3
9
+ :database: test.db
10
+ # For MySQL
11
+ # :adapter: mysql
12
+ # :username: 'killbill'
13
+ # :password: 'killbill'
14
+ # :database: 'killbill' # or set the URL :
15
+ # #:url: jdbc:mysql://127.0.0.1:3306/killbill
16
+ # :driver: org.mariadb.jdbc.Driver # as in KB
17
+ # :pool: 30 # AR's default is max 5 connections
18
+ # In Kill Bill
19
+ # :adapter: mysql
20
+ # :jndi: 'killbill/osgi/jdbc'
21
+ # :pool: false # false-pool (JNDI pool's max)
22
+ # # uncomment if pool does not support JDBC4 :
23
+ # #:connection_alive_sql: 'select 1'
24
+ # # MySQL adapter #configure_connection defaults :
25
+ # # @@SESSION.sql_auto_is_null = 0,
26
+ # # @@SESSION.wait_timeout = 2147483,
27
+ # # @@SESSION.sql_mode = 'STRICT_ALL_TABLES'
28
+ # # ... can be disabled (on AR-JDBC 1.4) using :
29
+ # :configure_connection: false
@@ -0,0 +1,30 @@
1
+ require 'spec_helper'
2
+
3
+ describe Killbill::Securenet::PaymentPlugin do
4
+
5
+ include ::Killbill::Plugin::ActiveMerchant::RSpec
6
+
7
+ before(:each) do
8
+ Dir.mktmpdir do |dir|
9
+ file = File.new(File.join(dir, 'securenet.yml'), 'w+')
10
+ file.write(<<-eos)
11
+ :securenet:
12
+ :test: true
13
+ # As defined by spec_helper.rb
14
+ :database:
15
+ :adapter: 'sqlite3'
16
+ :database: 'test.db'
17
+ eos
18
+ file.close
19
+
20
+ @plugin = build_plugin(::Killbill::Securenet::PaymentPlugin, 'securenet', File.dirname(file))
21
+
22
+ # Start the plugin here - since the config file will be deleted
23
+ @plugin.start_plugin
24
+ end
25
+ end
26
+
27
+ it 'should start and stop correctly' do
28
+ @plugin.stop_plugin
29
+ end
30
+ end
@@ -0,0 +1,132 @@
1
+ require 'spec_helper'
2
+
3
+ ActiveMerchant::Billing::Base.mode = :test
4
+
5
+ describe Killbill::Securenet::PaymentPlugin do
6
+
7
+ include ::Killbill::Plugin::ActiveMerchant::RSpec
8
+
9
+ before(:each) do
10
+ ::Killbill::Securenet::SecurenetPaymentMethod.delete_all
11
+ ::Killbill::Securenet::SecurenetResponse.delete_all
12
+ ::Killbill::Securenet::SecurenetTransaction.delete_all
13
+
14
+ @plugin = build_plugin(::Killbill::Securenet::PaymentPlugin, 'securenet')
15
+ @plugin.start_plugin
16
+
17
+ @call_context = build_call_context
18
+
19
+ @properties = []
20
+ @pm = create_payment_method(::Killbill::Securenet::SecurenetPaymentMethod, nil, @call_context.tenant_id, [build_property('skip_gw', 'true')])
21
+ @amount = BigDecimal.new('100')
22
+ @currency = 'USD'
23
+
24
+ kb_payment_id = SecureRandom.uuid
25
+ 1.upto(6) do
26
+ @kb_payment = @plugin.kb_apis.proxied_services[:payment_api].add_payment(kb_payment_id)
27
+ end
28
+ end
29
+
30
+ after(:each) do
31
+ @plugin.stop_plugin
32
+ end
33
+
34
+ it 'should be able to charge a Credit Card directly' do
35
+ properties = build_pm_properties
36
+
37
+ # We created the payment method, hence the rows
38
+ Killbill::Securenet::SecurenetResponse.all.size.should == 1
39
+ Killbill::Securenet::SecurenetTransaction.all.size.should == 0
40
+
41
+ payment_response = @plugin.purchase_payment(@pm.kb_account_id, @kb_payment.id, @kb_payment.transactions[0].id, @pm.kb_payment_method_id, @amount, @currency, properties, @call_context)
42
+ payment_response.status.should eq(:PROCESSED), payment_response.gateway_error
43
+ payment_response.amount.should == @amount
44
+ payment_response.transaction_type.should == :PURCHASE
45
+
46
+ responses = Killbill::Securenet::SecurenetResponse.all
47
+ responses.size.should == 2
48
+ responses[0].api_call.should == 'add_payment_method'
49
+ responses[0].message.should == 'Skipped Gateway call'
50
+ responses[1].api_call.should == 'purchase'
51
+ responses[1].message.should == 'Approved'
52
+ transactions = Killbill::Securenet::SecurenetTransaction.all
53
+ transactions.size.should == 1
54
+ transactions[0].api_call.should == 'purchase'
55
+ end
56
+
57
+ =begin
58
+ it 'should be able to charge and refund' do
59
+ payment_response = @plugin.purchase_payment(@pm.kb_account_id, @kb_payment.id, @kb_payment.transactions[0].id, @pm.kb_payment_method_id, @amount, @currency, @properties, @call_context)
60
+ payment_response.status.should eq(:PROCESSED), payment_response.gateway_error
61
+ payment_response.amount.should == @amount
62
+ payment_response.transaction_type.should == :PURCHASE
63
+
64
+ # TODO Close the batch
65
+
66
+ # Try a full refund
67
+ refund_response = @plugin.refund_payment(@pm.kb_account_id, @kb_payment.id, @kb_payment.transactions[1].id, @pm.kb_payment_method_id, @amount, @currency, @properties, @call_context)
68
+ refund_response.status.should eq(:PROCESSED), refund_response.gateway_error
69
+ refund_response.amount.should == @amount
70
+ refund_response.transaction_type.should == :REFUND
71
+ end
72
+ =end
73
+
74
+ =begin
75
+ # No support for partial captures?
76
+ it 'should be able to auth, capture and refund' do
77
+ payment_response = @plugin.authorize_payment(@pm.kb_account_id, @kb_payment.id, @kb_payment.transactions[0].id, @pm.kb_payment_method_id, @amount, @currency, @properties, @call_context)
78
+ payment_response.status.should eq(:PROCESSED), payment_response.gateway_error
79
+ payment_response.amount.should == @amount
80
+ payment_response.transaction_type.should == :AUTHORIZE
81
+
82
+ # Try multiple partial captures
83
+ partial_capture_amount = BigDecimal.new('10')
84
+ 1.upto(3) do |i|
85
+ payment_response = @plugin.capture_payment(@pm.kb_account_id, @kb_payment.id, @kb_payment.transactions[i].id, @pm.kb_payment_method_id, partial_capture_amount, @currency, @properties, @call_context)
86
+ payment_response.status.should eq(:PROCESSED), payment_response.gateway_error
87
+ payment_response.amount.should == partial_capture_amount
88
+ payment_response.transaction_type.should == :CAPTURE
89
+ end
90
+
91
+ # Try a partial refund
92
+ refund_response = @plugin.refund_payment(@pm.kb_account_id, @kb_payment.id, @kb_payment.transactions[4].id, @pm.kb_payment_method_id, partial_capture_amount, @currency, @properties, @call_context)
93
+ refund_response.status.should eq(:PROCESSED), refund_response.gateway_error
94
+ refund_response.amount.should == partial_capture_amount
95
+ refund_response.transaction_type.should == :REFUND
96
+
97
+ # Try to capture again
98
+ payment_response = @plugin.capture_payment(@pm.kb_account_id, @kb_payment.id, @kb_payment.transactions[5].id, @pm.kb_payment_method_id, partial_capture_amount, @currency, @properties, @call_context)
99
+ payment_response.status.should eq(:PROCESSED), payment_response.gateway_error
100
+ payment_response.amount.should == partial_capture_amount
101
+ payment_response.transaction_type.should == :CAPTURE
102
+ end
103
+ =end
104
+
105
+ it 'should be able to auth and void' do
106
+ payment_response = @plugin.authorize_payment(@pm.kb_account_id, @kb_payment.id, @kb_payment.transactions[0].id, @pm.kb_payment_method_id, @amount, @currency, @properties, @call_context)
107
+ payment_response.status.should eq(:PROCESSED), payment_response.gateway_error
108
+ payment_response.amount.should == @amount
109
+ payment_response.transaction_type.should == :AUTHORIZE
110
+
111
+ payment_response = @plugin.void_payment(@pm.kb_account_id, @kb_payment.id, @kb_payment.transactions[1].id, @pm.kb_payment_method_id, @properties, @call_context)
112
+ payment_response.status.should eq(:PROCESSED), payment_response.gateway_error
113
+ payment_response.transaction_type.should == :VOID
114
+ end
115
+
116
+ it 'should be able to auth, partial capture and void' do
117
+ payment_response = @plugin.authorize_payment(@pm.kb_account_id, @kb_payment.id, @kb_payment.transactions[0].id, @pm.kb_payment_method_id, @amount, @currency, @properties, @call_context)
118
+ payment_response.status.should eq(:PROCESSED), payment_response.gateway_error
119
+ payment_response.amount.should == @amount
120
+ payment_response.transaction_type.should == :AUTHORIZE
121
+
122
+ partial_capture_amount = BigDecimal.new('10')
123
+ payment_response = @plugin.capture_payment(@pm.kb_account_id, @kb_payment.id, @kb_payment.transactions[1].id, @pm.kb_payment_method_id, partial_capture_amount, @currency, @properties, @call_context)
124
+ payment_response.status.should eq(:PROCESSED), payment_response.gateway_error
125
+ payment_response.amount.should == partial_capture_amount
126
+ payment_response.transaction_type.should == :CAPTURE
127
+
128
+ payment_response = @plugin.void_payment(@pm.kb_account_id, @kb_payment.id, @kb_payment.transactions[2].id, @pm.kb_payment_method_id, @properties, @call_context)
129
+ payment_response.status.should eq(:PROCESSED), payment_response.gateway_error
130
+ payment_response.transaction_type.should == :VOID
131
+ end
132
+ end