supercharged 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/MIT-LICENSE +20 -0
- data/README.md +96 -0
- data/Rakefile +13 -0
- data/app/assets/javascripts/charge_form.js.coffee.erb +43 -0
- data/app/assets/javascripts/supercharged.js +1 -0
- data/app/controllers/supercharged/charges_controller.rb +19 -0
- data/app/controllers/supercharged/gateway_notifications_controller.rb +53 -0
- data/app/helpers/supercharged/charges_helper.rb +45 -0
- data/app/models/charge.rb +2 -0
- data/app/models/gateway_notification.rb +55 -0
- data/app/models/payment_state_transition.rb +3 -0
- data/app/models/supercharged/charge/base.rb +45 -0
- data/config/routes.rb +17 -0
- data/lib/supercharged/activemerchant.rb +4 -0
- data/lib/supercharged/version.rb +3 -0
- data/lib/supercharged.rb +9 -0
- data/lib/tasks/supercharged_tasks.rake +4 -0
- data/test/fake_app.rb +94 -0
- data/test/supercharged/controllers/gateway_notifications_controller_test.rb +62 -0
- data/test/supercharged/models/charge_test.rb +25 -0
- data/test/supercharged/models/geneway_notification_test.rb +11 -0
- data/test/test_helper.rb +22 -0
- metadata +203 -0
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2012 Alexander Balashov
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,96 @@
|
|
1
|
+
# Supercharged
|
2
|
+
|
3
|
+
[](https://travis-ci.org/divineforest/supercharged)
|
4
|
+
[](https://codeclimate.com/github/divineforest/supercharged)
|
5
|
+
|
6
|
+
Complete MVC solution to accept charges from users on your Ruby on Rails site
|
7
|
+
|
8
|
+
## Installation
|
9
|
+
|
10
|
+
Add to Gemfile:
|
11
|
+
|
12
|
+
```ruby
|
13
|
+
gem 'supercharged'
|
14
|
+
```
|
15
|
+
|
16
|
+
run
|
17
|
+
|
18
|
+
rails g supercharged:migrations
|
19
|
+
rails db:migrate
|
20
|
+
rails g supercharged:views
|
21
|
+
|
22
|
+
In your application.js manifest:
|
23
|
+
|
24
|
+
```
|
25
|
+
//= require supercharged
|
26
|
+
```
|
27
|
+
|
28
|
+
Create config/initializers/supercharged.rb
|
29
|
+
|
30
|
+
```ruby
|
31
|
+
ActiveMerchant::Billing::Base.integration_mode = Rails.env.production? ? :production : :test
|
32
|
+
```
|
33
|
+
|
34
|
+
Add
|
35
|
+
|
36
|
+
```ruby
|
37
|
+
supercharged
|
38
|
+
```
|
39
|
+
|
40
|
+
to your routes.rb somewhere in draw block.
|
41
|
+
|
42
|
+
# Using
|
43
|
+
|
44
|
+
Create view in app/views/supercharged/charges/new.html.haml
|
45
|
+
|
46
|
+
```haml
|
47
|
+
= charge_form_for(:paypal, account: 'yourpaypalaccountid', html: {}) do |service|
|
48
|
+
- service.description 'Write here description that will be shown in paymennt form'
|
49
|
+
= charge_form_amount_field(service)
|
50
|
+
= submit_tag 'Pay now'
|
51
|
+
```
|
52
|
+
|
53
|
+
# Customization
|
54
|
+
|
55
|
+
## Controller
|
56
|
+
|
57
|
+
Create controller in app/controllers/charges_controller.rb and inherit from Supercharged::ChargesController.
|
58
|
+
Then add what you need or change existing methods with 'super'.
|
59
|
+
|
60
|
+
```ruby
|
61
|
+
class ChargesController < Supercharged::ChargesController
|
62
|
+
before_filter :authenticate_user! # this is Devise's authenticate method
|
63
|
+
end
|
64
|
+
```
|
65
|
+
|
66
|
+
If you create your own controllers then you need to customize routing method:
|
67
|
+
|
68
|
+
```
|
69
|
+
supercharged controllers: {charges: :charges, gateway_notifications: :gateway_notifications}
|
70
|
+
```
|
71
|
+
|
72
|
+
## Model
|
73
|
+
|
74
|
+
Create model in app/models/charge.rb and inherit from Supercharged::Charge::Base
|
75
|
+
|
76
|
+
```ruby
|
77
|
+
class Charge < Supercharged::Charge::Base
|
78
|
+
# your custom code here
|
79
|
+
|
80
|
+
def approve(real_amount)
|
81
|
+
transaction do
|
82
|
+
# update user balance with your own update_balance method or other things you want to do after charged approved
|
83
|
+
user.update_balance(real_amount)
|
84
|
+
super
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
```
|
89
|
+
|
90
|
+
# Contributing
|
91
|
+
|
92
|
+
The example app is at https://github.com/divineforest/supercharged-example-app
|
93
|
+
|
94
|
+
# License
|
95
|
+
|
96
|
+
Supercharged is released under the MIT License
|
data/Rakefile
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
class ChargeForm
|
2
|
+
constructor: (selector, options = {}) ->
|
3
|
+
@form = $(selector)
|
4
|
+
return if @form.length == 0
|
5
|
+
|
6
|
+
@amount_input = @form.find("[role='charge-amount']")
|
7
|
+
@id_input = @form.find("[value='<%= Supercharged::ChargesHelper::FAKE_ORDER_ID %>']")
|
8
|
+
|
9
|
+
@form.submit =>
|
10
|
+
@start_payment()
|
11
|
+
|
12
|
+
unless parseInt(@id_input.val())
|
13
|
+
alert("Error: undefined charge id")
|
14
|
+
return false
|
15
|
+
|
16
|
+
start_payment: ->
|
17
|
+
@create_internal_transaction(
|
18
|
+
success: (charge) =>
|
19
|
+
@prepare_gateway_form(charge)
|
20
|
+
)
|
21
|
+
|
22
|
+
create_internal_transaction: (options) ->
|
23
|
+
charge_attributes = @get_charge_attributes()
|
24
|
+
$.ajax(
|
25
|
+
url: "/charges.json",
|
26
|
+
type: "POST",
|
27
|
+
async: false,
|
28
|
+
data: { charge: charge_attributes},
|
29
|
+
success: (response, a) ->
|
30
|
+
options.success(response)
|
31
|
+
)
|
32
|
+
|
33
|
+
get_charge_attributes: ->
|
34
|
+
{
|
35
|
+
amount: @amount_input.val()
|
36
|
+
}
|
37
|
+
|
38
|
+
prepare_gateway_form: (charge) ->
|
39
|
+
@id_input.val(charge.id)
|
40
|
+
|
41
|
+
$ ->
|
42
|
+
window.widgets ||= {}
|
43
|
+
window.widgets.charge_form = new ChargeForm("[role='gateway-charge-form']")
|
@@ -0,0 +1 @@
|
|
1
|
+
//= require_directory .
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class Supercharged::ChargesController < ApplicationController
|
2
|
+
|
3
|
+
def new
|
4
|
+
end
|
5
|
+
|
6
|
+
def create
|
7
|
+
@charge = Charge.new(charge_params)
|
8
|
+
@charge.user = current_user
|
9
|
+
@charge.save!
|
10
|
+
render json: @charge.as_json(only: [:id])
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def charge_params
|
16
|
+
params.require(:charge).permit(:amount)
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
class Supercharged::GatewayNotificationsController < ApplicationController
|
2
|
+
skip_before_filter :verify_authenticity_token
|
3
|
+
|
4
|
+
def create
|
5
|
+
persistent_logger.info("Notification for #{params[:gateway]}")
|
6
|
+
persistent_logger.info("params = #{params.inspect}")
|
7
|
+
|
8
|
+
@notification = GatewayNotification.create!(params: params, gateway: params[:gateway], raw_post: request.raw_post)
|
9
|
+
@notification.logger = persistent_logger
|
10
|
+
|
11
|
+
error = if !@notification.complete?
|
12
|
+
"not_completed"
|
13
|
+
elsif !@notification.acknowledge
|
14
|
+
"acknowledge_failed"
|
15
|
+
elsif !@notification.charge
|
16
|
+
"charge not found"
|
17
|
+
end
|
18
|
+
|
19
|
+
if error
|
20
|
+
persistent_logger.error("Error: #{error.inspect}")
|
21
|
+
if @notification.charge
|
22
|
+
@notification.charge.failed!
|
23
|
+
@notification.charge.update_attribute(:error, error)
|
24
|
+
end
|
25
|
+
head :bad_request
|
26
|
+
else
|
27
|
+
persistent_logger.info("Success")
|
28
|
+
@notification.approve
|
29
|
+
if @notification.need_response?
|
30
|
+
persistent_logger.info("Need need_response: #{@notification.success_response.inspect}")
|
31
|
+
render text: @notification.success_response
|
32
|
+
else
|
33
|
+
persistent_logger.info("Redirecting")
|
34
|
+
redirect_to root_url
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def success
|
40
|
+
redirect_to root_url, notice: "Success"
|
41
|
+
end
|
42
|
+
|
43
|
+
def fail
|
44
|
+
redirect_to root_url, error: "Fail"
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def persistent_logger
|
50
|
+
@persistent_logger ||= Logger.new("log/supercharged/gateway_notifications.log")
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Supercharged::ChargesHelper
|
2
|
+
# No order id while generating form. It will be added later via JS
|
3
|
+
# JS finds order input by this fake id because id and name will be integration specific
|
4
|
+
FAKE_ORDER_ID = "[payment_order_id]"
|
5
|
+
|
6
|
+
DEFAULT_AMOUNT_FIELD_OPTIONS = {
|
7
|
+
role: "charge-amount",
|
8
|
+
required: true,
|
9
|
+
min: 0
|
10
|
+
}
|
11
|
+
|
12
|
+
def charge_form_for(service_name, options = {}, &block)
|
13
|
+
raise ArgumentError, "Missing block" unless block_given?
|
14
|
+
|
15
|
+
default_options = {service: service_name}
|
16
|
+
options.merge!(default_options)
|
17
|
+
|
18
|
+
options = with_default_html_options(options)
|
19
|
+
|
20
|
+
account = options.delete(:account)
|
21
|
+
notify_url = gateways_result_url(service_name)
|
22
|
+
|
23
|
+
payment_service_for(FAKE_ORDER_ID, account, options) do |service|
|
24
|
+
service.notify_url notify_url
|
25
|
+
block.call(service)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def charge_form_amount_field(service, options = {})
|
30
|
+
amount_field_name = service.mappings[:amount] || raise("Undefined amount field mapping")
|
31
|
+
|
32
|
+
options = options.merge(DEFAULT_AMOUNT_FIELD_OPTIONS)
|
33
|
+
|
34
|
+
number_field_tag amount_field_name, nil, options
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def with_default_html_options(options)
|
40
|
+
options[:html] ||= {}
|
41
|
+
options[:html].merge!(role: "gateway-charge-form")
|
42
|
+
options
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'active_merchant'
|
2
|
+
|
3
|
+
class GatewayNotification < ActiveRecord::Base
|
4
|
+
class EmptyChargeIdError < ArgumentError;end
|
5
|
+
|
6
|
+
belongs_to :charge
|
7
|
+
|
8
|
+
serialize :params
|
9
|
+
|
10
|
+
before_create :set_charge_id
|
11
|
+
|
12
|
+
attr_accessor :raw_post, :logger
|
13
|
+
|
14
|
+
def acknowledge
|
15
|
+
adapter.acknowledge
|
16
|
+
end
|
17
|
+
|
18
|
+
def complete?
|
19
|
+
adapter.complete?
|
20
|
+
end
|
21
|
+
|
22
|
+
def need_response?
|
23
|
+
adapter.respond_to?(:success_response)
|
24
|
+
end
|
25
|
+
|
26
|
+
def success_response
|
27
|
+
adapter.respond_to?(:success_response) ? adapter.success_response : "OK"
|
28
|
+
end
|
29
|
+
|
30
|
+
def approve
|
31
|
+
logger.info "real amount = #{real_amount}"
|
32
|
+
charge.approve(real_amount) unless charge.ok?
|
33
|
+
end
|
34
|
+
|
35
|
+
def real_amount
|
36
|
+
params[service.mappings[:amount]]
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def adapter
|
42
|
+
@adapter ||= "ActiveMerchant::Billing::Integrations::#{gateway.classify}::Notification".classify.constantize.new(raw_post)
|
43
|
+
rescue NameError
|
44
|
+
raise "Unknown integration '#{gateway}'"
|
45
|
+
end
|
46
|
+
|
47
|
+
def service
|
48
|
+
"ActiveMerchant::Billing::Integrations::#{gateway.classify}::Helper".classify.constantize
|
49
|
+
end
|
50
|
+
|
51
|
+
def set_charge_id
|
52
|
+
self.charge_id = adapter.item_id || raise(EmptyChargeIdError)
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Supercharged
|
2
|
+
module Charge
|
3
|
+
class Base < ActiveRecord::Base
|
4
|
+
include ActiveModel::ForbiddenAttributesProtection
|
5
|
+
|
6
|
+
self.table_name = "charges"
|
7
|
+
self.abstract_class = true
|
8
|
+
|
9
|
+
belongs_to :user
|
10
|
+
has_many :gateway_input_notifications
|
11
|
+
|
12
|
+
validates :amount, presence: true
|
13
|
+
|
14
|
+
scope :latest, order("created_at DESC")
|
15
|
+
|
16
|
+
state_machine :state, initial: :new do
|
17
|
+
# store_audit_trail
|
18
|
+
|
19
|
+
state :new
|
20
|
+
state :rejected
|
21
|
+
state :ok
|
22
|
+
state :error
|
23
|
+
|
24
|
+
event :set_ok do
|
25
|
+
transition [:new, :error] => :ok
|
26
|
+
end
|
27
|
+
|
28
|
+
event :failed do
|
29
|
+
transition [:new] => :error
|
30
|
+
end
|
31
|
+
|
32
|
+
event :reject do
|
33
|
+
transition [:new, :error] => :rejected
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# require implicit amount from gateway, not from user
|
38
|
+
def approve(real_amount)
|
39
|
+
self.real_amount = real_amount
|
40
|
+
set_ok!
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
data/config/routes.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
module ActionDispatch::Routing
|
2
|
+
class Mapper
|
3
|
+
|
4
|
+
def supercharged(options = {})
|
5
|
+
controllers = {
|
6
|
+
charges: "supercharged/charges",
|
7
|
+
gateway_notifications: "supercharged/gateway_notifications"
|
8
|
+
}
|
9
|
+
controllers.merge!(options[:controllers]) if options[:controllers]
|
10
|
+
|
11
|
+
resources :charges, only: [:new, :create], controller: controllers[:charges]
|
12
|
+
|
13
|
+
match "gateways/:gateway/result" => "#{controllers[:gateway_notifications]}#create", as: :gateways_result
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
data/lib/supercharged.rb
ADDED
data/test/fake_app.rb
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
require 'action_controller/railtie'
|
3
|
+
|
4
|
+
# database
|
5
|
+
if ENV['TRAVIS'].present?
|
6
|
+
ActiveRecord::Base.configurations = {'test' => {:adapter => 'postgresql', :database => 'supercharged_test', :username => "postgres"}}
|
7
|
+
else
|
8
|
+
ActiveRecord::Base.configurations = {'test' => {:adapter => 'sqlite3', :database => ':memory:'}}
|
9
|
+
end
|
10
|
+
ActiveRecord::Base.establish_connection('test')
|
11
|
+
|
12
|
+
# config
|
13
|
+
app = Class.new(Rails::Application)
|
14
|
+
app.config.secret_token = "3b7cd727ee24e8444053437c36cc66c4"
|
15
|
+
app.config.session_store :cookie_store, :key => "_fakeapp_session"
|
16
|
+
app.config.active_support.deprecation = :log
|
17
|
+
app.initialize!
|
18
|
+
|
19
|
+
Rails.application.routes.draw do
|
20
|
+
supercharged
|
21
|
+
|
22
|
+
root to: "empty#index"
|
23
|
+
end
|
24
|
+
|
25
|
+
# migrations
|
26
|
+
ActiveRecord::Base.silence do
|
27
|
+
ActiveRecord::Migration.verbose = false
|
28
|
+
ActiveRecord::Schema.define :version => 0 do
|
29
|
+
|
30
|
+
create_table "gateway_notifications", :force => true do |t|
|
31
|
+
t.text "params"
|
32
|
+
t.string "status"
|
33
|
+
t.string "external_transaction_id"
|
34
|
+
t.integer "charge_id"
|
35
|
+
t.datetime "created_at", :null => false
|
36
|
+
t.datetime "updated_at", :null => false
|
37
|
+
t.string "gateway"
|
38
|
+
end
|
39
|
+
|
40
|
+
add_index "gateway_notifications", ["charge_id"], :name => "index_gateway_input_notifications_on_charge_id"
|
41
|
+
|
42
|
+
create_table "charges_state_transitions", :force => true do |t|
|
43
|
+
t.integer "charges_id"
|
44
|
+
t.string "charges_type"
|
45
|
+
t.string "event"
|
46
|
+
t.string "from"
|
47
|
+
t.string "to"
|
48
|
+
t.datetime "created_at"
|
49
|
+
end
|
50
|
+
|
51
|
+
add_index "charges_state_transitions", ["charges_id"], :name => "index_charges_state_transitions_on_charges_id"
|
52
|
+
|
53
|
+
create_table "charges", :force => true do |t|
|
54
|
+
t.integer "amount", :null => false
|
55
|
+
t.integer "user_id", :null => false
|
56
|
+
t.string "external_transaction_id"
|
57
|
+
t.text "params"
|
58
|
+
t.string "state", :default => "new", :null => false
|
59
|
+
t.datetime "created_at", :null => false
|
60
|
+
t.datetime "updated_at", :null => false
|
61
|
+
t.integer "user_transaction_id"
|
62
|
+
t.string "error"
|
63
|
+
t.integer "approved_by"
|
64
|
+
t.text "reject_reason"
|
65
|
+
t.decimal "real_amount"
|
66
|
+
end
|
67
|
+
|
68
|
+
add_index "charges", ["approved_by"], :name => "index_charges_on_approved_by"
|
69
|
+
add_index "charges", ["state"], :name => "index_charges_on_state"
|
70
|
+
add_index "charges", ["user_id"], :name => "index_charges_on_user_id"
|
71
|
+
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
class ApplicationController < ActionController::Base
|
76
|
+
protect_from_forgery
|
77
|
+
|
78
|
+
helper_method :current_user
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
def current_user
|
83
|
+
raise "stub me"
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
class EmptyController < ApplicationController
|
88
|
+
def index
|
89
|
+
render empty: true
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# helpers
|
94
|
+
Object.const_set(:ApplicationHelper, Module.new)
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
describe Supercharged::GatewayNotificationsController do
|
4
|
+
describe "create action" do
|
5
|
+
let(:charge) { Charge.create!({user_id: 1, amount: 10}, without_protection: true) }
|
6
|
+
|
7
|
+
context "authorized" do
|
8
|
+
let(:fake_user) { stub(id: 1) }
|
9
|
+
|
10
|
+
before do
|
11
|
+
GatewayNotification.any_instance.stubs(:current_user).returns(fake_user)
|
12
|
+
end
|
13
|
+
|
14
|
+
context "correct conditions" do
|
15
|
+
it "works with good notification" do
|
16
|
+
adapter = stub(item_id: 1, "complete?" => true, acknowledge: true, charge: charge)
|
17
|
+
|
18
|
+
GatewayNotification.any_instance.stubs(:adapter).returns(adapter)
|
19
|
+
|
20
|
+
post :create, gateway: "webmoney"
|
21
|
+
|
22
|
+
charge.reload
|
23
|
+
charge.state_name.must_equal :ok
|
24
|
+
|
25
|
+
assert_response :redirect, "/"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
context "bad conditions" do
|
30
|
+
|
31
|
+
it "completed = false" do
|
32
|
+
adapter = stub(item_id: 1, "complete?" => false, acknowledge: true, charge: charge)
|
33
|
+
|
34
|
+
GatewayNotification.any_instance.stubs(:adapter).returns(adapter)
|
35
|
+
|
36
|
+
post :create, gateway: "webmoney"
|
37
|
+
|
38
|
+
charge.reload
|
39
|
+
charge.state_name.must_equal :error
|
40
|
+
charge.error.must_equal "not_completed"
|
41
|
+
|
42
|
+
assert_response :bad_request
|
43
|
+
end
|
44
|
+
|
45
|
+
it "acknowledge = false" do
|
46
|
+
adapter = stub(item_id: 1, "complete?" => true, acknowledge: false, charge: charge)
|
47
|
+
|
48
|
+
GatewayNotification.any_instance.stubs(:adapter).returns(adapter)
|
49
|
+
|
50
|
+
post :create, gateway: "webmoney"
|
51
|
+
|
52
|
+
charge.reload
|
53
|
+
charge.state_name.must_equal :error
|
54
|
+
charge.error.must_equal "acknowledge_failed"
|
55
|
+
|
56
|
+
assert_response :bad_request
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
describe Charge do
|
4
|
+
describe "validations" do
|
5
|
+
subject { Charge.new }
|
6
|
+
it "amount is required" do
|
7
|
+
subject.valid?
|
8
|
+
|
9
|
+
subject.errors[:amount][0].must_equal "can't be blank"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
describe "states" do
|
14
|
+
subject { Charge.create!({user_id: 1, amount: 10}, without_protection: true) }
|
15
|
+
|
16
|
+
describe "#approve" do
|
17
|
+
it "changes state and real_amount" do
|
18
|
+
subject.approve(5)
|
19
|
+
|
20
|
+
subject.real_amount.must_equal 5
|
21
|
+
subject.state_name.must_equal :ok
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
describe GatewayNotification do
|
4
|
+
describe "create" do
|
5
|
+
it "raise EmptyChargeId if charge_id = nil" do
|
6
|
+
->{
|
7
|
+
GatewayNotification.create!(gateway: "webmoney")
|
8
|
+
}.must_raise GatewayNotification::EmptyChargeIdError
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
ENV["RAILS_ENV"] = "test"
|
2
|
+
|
3
|
+
require "minitest/autorun"
|
4
|
+
require "minitest/spec"
|
5
|
+
require "minitest/pride"
|
6
|
+
require "minitest-spec-context"
|
7
|
+
require "mocha/setup"
|
8
|
+
|
9
|
+
require 'rails'
|
10
|
+
require 'active_record'
|
11
|
+
require 'action_view'
|
12
|
+
# require 'action_controller'
|
13
|
+
|
14
|
+
require "rails/test_help"
|
15
|
+
require "minitest/rails"
|
16
|
+
|
17
|
+
require 'state_machine'
|
18
|
+
require 'state_machine-audit_trail'
|
19
|
+
require 'strong_parameters'
|
20
|
+
|
21
|
+
require "supercharged"
|
22
|
+
require "fake_app"
|
metadata
ADDED
@@ -0,0 +1,203 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: supercharged
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 1
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
version: 1.0.0
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- divineforest
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2013-05-07 00:00:00 +04:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
type: :runtime
|
22
|
+
version_requirements: &id001 !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
segments:
|
27
|
+
- 3
|
28
|
+
- 1
|
29
|
+
version: "3.1"
|
30
|
+
name: rails
|
31
|
+
requirement: *id001
|
32
|
+
prerelease: false
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
type: :runtime
|
35
|
+
version_requirements: &id002 !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ">="
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
segments:
|
40
|
+
- 0
|
41
|
+
version: "0"
|
42
|
+
name: state_machine
|
43
|
+
requirement: *id002
|
44
|
+
prerelease: false
|
45
|
+
- !ruby/object:Gem::Dependency
|
46
|
+
type: :runtime
|
47
|
+
version_requirements: &id003 !ruby/object:Gem::Requirement
|
48
|
+
requirements:
|
49
|
+
- - ">="
|
50
|
+
- !ruby/object:Gem::Version
|
51
|
+
segments:
|
52
|
+
- 0
|
53
|
+
version: "0"
|
54
|
+
name: state_machine-audit_trail
|
55
|
+
requirement: *id003
|
56
|
+
prerelease: false
|
57
|
+
- !ruby/object:Gem::Dependency
|
58
|
+
type: :runtime
|
59
|
+
version_requirements: &id004 !ruby/object:Gem::Requirement
|
60
|
+
requirements:
|
61
|
+
- - ">="
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
segments:
|
64
|
+
- 0
|
65
|
+
version: "0"
|
66
|
+
name: activemerchant
|
67
|
+
requirement: *id004
|
68
|
+
prerelease: false
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
type: :runtime
|
71
|
+
version_requirements: &id005 !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
segments:
|
76
|
+
- 0
|
77
|
+
version: "0"
|
78
|
+
name: strong_parameters
|
79
|
+
requirement: *id005
|
80
|
+
prerelease: false
|
81
|
+
- !ruby/object:Gem::Dependency
|
82
|
+
type: :development
|
83
|
+
version_requirements: &id006 !ruby/object:Gem::Requirement
|
84
|
+
requirements:
|
85
|
+
- - ">="
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
segments:
|
88
|
+
- 0
|
89
|
+
version: "0"
|
90
|
+
name: sqlite3
|
91
|
+
requirement: *id006
|
92
|
+
prerelease: false
|
93
|
+
- !ruby/object:Gem::Dependency
|
94
|
+
type: :development
|
95
|
+
version_requirements: &id007 !ruby/object:Gem::Requirement
|
96
|
+
requirements:
|
97
|
+
- - ~>
|
98
|
+
- !ruby/object:Gem::Version
|
99
|
+
segments:
|
100
|
+
- 3
|
101
|
+
- 0
|
102
|
+
version: "3.0"
|
103
|
+
name: minitest
|
104
|
+
requirement: *id007
|
105
|
+
prerelease: false
|
106
|
+
- !ruby/object:Gem::Dependency
|
107
|
+
type: :development
|
108
|
+
version_requirements: &id008 !ruby/object:Gem::Requirement
|
109
|
+
requirements:
|
110
|
+
- - ">="
|
111
|
+
- !ruby/object:Gem::Version
|
112
|
+
segments:
|
113
|
+
- 0
|
114
|
+
version: "0"
|
115
|
+
name: minitest-spec-context
|
116
|
+
requirement: *id008
|
117
|
+
prerelease: false
|
118
|
+
- !ruby/object:Gem::Dependency
|
119
|
+
type: :development
|
120
|
+
version_requirements: &id009 !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
segments:
|
125
|
+
- 0
|
126
|
+
version: "0"
|
127
|
+
name: minitest-rails
|
128
|
+
requirement: *id009
|
129
|
+
prerelease: false
|
130
|
+
- !ruby/object:Gem::Dependency
|
131
|
+
type: :development
|
132
|
+
version_requirements: &id010 !ruby/object:Gem::Requirement
|
133
|
+
requirements:
|
134
|
+
- - ">="
|
135
|
+
- !ruby/object:Gem::Version
|
136
|
+
segments:
|
137
|
+
- 0
|
138
|
+
version: "0"
|
139
|
+
name: mocha
|
140
|
+
requirement: *id010
|
141
|
+
prerelease: false
|
142
|
+
description:
|
143
|
+
email:
|
144
|
+
executables: []
|
145
|
+
|
146
|
+
extensions: []
|
147
|
+
|
148
|
+
extra_rdoc_files: []
|
149
|
+
|
150
|
+
files:
|
151
|
+
- app/assets/javascripts/charge_form.js.coffee.erb
|
152
|
+
- app/assets/javascripts/supercharged.js
|
153
|
+
- app/controllers/supercharged/charges_controller.rb
|
154
|
+
- app/controllers/supercharged/gateway_notifications_controller.rb
|
155
|
+
- app/helpers/supercharged/charges_helper.rb
|
156
|
+
- app/models/charge.rb
|
157
|
+
- app/models/gateway_notification.rb
|
158
|
+
- app/models/payment_state_transition.rb
|
159
|
+
- app/models/supercharged/charge/base.rb
|
160
|
+
- config/routes.rb
|
161
|
+
- lib/supercharged/activemerchant.rb
|
162
|
+
- lib/supercharged/version.rb
|
163
|
+
- lib/supercharged.rb
|
164
|
+
- lib/tasks/supercharged_tasks.rake
|
165
|
+
- MIT-LICENSE
|
166
|
+
- Rakefile
|
167
|
+
- README.md
|
168
|
+
has_rdoc: true
|
169
|
+
homepage:
|
170
|
+
licenses: []
|
171
|
+
|
172
|
+
post_install_message:
|
173
|
+
rdoc_options: []
|
174
|
+
|
175
|
+
require_paths:
|
176
|
+
- lib
|
177
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
178
|
+
requirements:
|
179
|
+
- - ">="
|
180
|
+
- !ruby/object:Gem::Version
|
181
|
+
segments:
|
182
|
+
- 0
|
183
|
+
version: "0"
|
184
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
185
|
+
requirements:
|
186
|
+
- - ">="
|
187
|
+
- !ruby/object:Gem::Version
|
188
|
+
segments:
|
189
|
+
- 0
|
190
|
+
version: "0"
|
191
|
+
requirements: []
|
192
|
+
|
193
|
+
rubyforge_project:
|
194
|
+
rubygems_version: 1.3.6
|
195
|
+
signing_key:
|
196
|
+
specification_version: 3
|
197
|
+
summary: MVC solution for charges in rails
|
198
|
+
test_files:
|
199
|
+
- test/fake_app.rb
|
200
|
+
- test/supercharged/controllers/gateway_notifications_controller_test.rb
|
201
|
+
- test/supercharged/models/charge_test.rb
|
202
|
+
- test/supercharged/models/geneway_notification_test.rb
|
203
|
+
- test/test_helper.rb
|