supercharged 1.0.0 → 2.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/README.md +26 -1
- data/app/assets/javascripts/charge_form.js.coffee.erb +33 -19
- data/app/controllers/supercharged/charges_controller.rb +36 -5
- data/app/controllers/supercharged/gateway_notifications_controller.rb +8 -0
- data/app/helpers/supercharged/charges_helper.rb +12 -8
- data/app/models/gateway_response.rb +17 -0
- data/app/models/supercharged/charge/base.rb +72 -1
- data/config/routes.rb +6 -1
- data/lib/generators/supercharged/migrations_generator.rb +27 -0
- data/lib/generators/supercharged/templates/install_migration.rb +64 -0
- data/lib/supercharged/helpers.rb +26 -0
- data/lib/supercharged/version.rb +1 -1
- data/lib/supercharged.rb +1 -0
- data/test/fake_app.rb +29 -6
- data/test/supercharged/controllers/charges_controller_test.rb +35 -0
- data/test/supercharged/controllers/gateway_notifications_controller_test.rb +12 -3
- data/test/supercharged/helpers/charges_helper_test.rb +12 -0
- data/test/supercharged/models/charge_test.rb +16 -0
- data/test/supercharged/models/geneway_notification_test.rb +10 -0
- data/test/test_helper.rb +0 -1
- metadata +180 -139
- data/lib/tasks/supercharged_tasks.rake +0 -4
data/README.md
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# Supercharged
|
2
2
|
|
3
3
|
[](https://travis-ci.org/divineforest/supercharged)
|
4
|
-
[](https://codeclimate.com/github/divineforest/supercharged)
|
5
5
|
|
6
6
|
Complete MVC solution to accept charges from users on your Ruby on Rails site
|
7
7
|
|
@@ -25,6 +25,13 @@ In your application.js manifest:
|
|
25
25
|
//= require supercharged
|
26
26
|
```
|
27
27
|
|
28
|
+
And then in some assets file:
|
29
|
+
|
30
|
+
```
|
31
|
+
$ ->
|
32
|
+
new SuperchargedForm("[role='gateway-charge-form']")
|
33
|
+
```
|
34
|
+
|
28
35
|
Create config/initializers/supercharged.rb
|
29
36
|
|
30
37
|
```ruby
|
@@ -84,9 +91,27 @@ class Charge < Supercharged::Charge::Base
|
|
84
91
|
super
|
85
92
|
end
|
86
93
|
end
|
94
|
+
|
95
|
+
def min_amount
|
96
|
+
# specify min value for amount field here
|
97
|
+
# default is 1
|
98
|
+
42
|
99
|
+
end
|
87
100
|
end
|
88
101
|
```
|
89
102
|
|
103
|
+
## Form
|
104
|
+
|
105
|
+
To display validation, supercharged form class has `onValidationError` callback:
|
106
|
+
|
107
|
+
```
|
108
|
+
$ ->
|
109
|
+
new SuperchargedForm("[role='gateway-charge-form']", {
|
110
|
+
onValidationError: (errors)->
|
111
|
+
console.log "supercharged validation errors: ", errors
|
112
|
+
})
|
113
|
+
```
|
114
|
+
|
90
115
|
# Contributing
|
91
116
|
|
92
117
|
The example app is at https://github.com/divineforest/supercharged-example-app
|
@@ -1,43 +1,57 @@
|
|
1
|
-
class
|
2
|
-
constructor: (selector, options = {}) ->
|
1
|
+
class @SuperchargedForm
|
2
|
+
constructor: (selector, @options = {}) ->
|
3
3
|
@form = $(selector)
|
4
4
|
return if @form.length == 0
|
5
5
|
|
6
6
|
@amount_input = @form.find("[role='charge-amount']")
|
7
|
-
@
|
7
|
+
@gateway_name_input = @form.find("[role='charge-gateway-name']")
|
8
|
+
@id_input = if @form.find("[role='charge-id']").length then @form.find("[role='charge-id']") else @form.find("[value='<%= Supercharged::ChargesHelper::FAKE_ORDER_ID %>']")
|
8
9
|
|
9
10
|
@form.submit =>
|
10
|
-
@
|
11
|
+
valid = @startPayment()
|
12
|
+
|
13
|
+
unless valid
|
14
|
+
return false
|
11
15
|
|
12
16
|
unless parseInt(@id_input.val())
|
13
17
|
alert("Error: undefined charge id")
|
14
18
|
return false
|
15
19
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
+
startPayment: ->
|
21
|
+
valid = true
|
22
|
+
@createInternalTransaction(
|
23
|
+
success: (charge)=>
|
24
|
+
@prepareGatewayForm(charge)
|
25
|
+
validationError: (errors)=>
|
26
|
+
valid = false
|
27
|
+
|
28
|
+
handler = @options.onValidationError
|
29
|
+
handler.call(this, errors) if handler?
|
20
30
|
)
|
21
31
|
|
22
|
-
|
23
|
-
|
32
|
+
valid
|
33
|
+
|
34
|
+
createInternalTransaction: (options) ->
|
35
|
+
charge_attributes = @getChargeAttributes()
|
24
36
|
$.ajax(
|
25
37
|
url: "/charges.json",
|
26
38
|
type: "POST",
|
27
39
|
async: false,
|
28
|
-
data: {
|
29
|
-
success: (response
|
40
|
+
data: {charge: charge_attributes},
|
41
|
+
success: (response) ->
|
30
42
|
options.success(response)
|
43
|
+
error: (jqXHR, status, errorThrown)=>
|
44
|
+
return unless jqXHR.status == 422
|
45
|
+
|
46
|
+
response = $.parseJSON(jqXHR.responseText)
|
47
|
+
options.validationError(response.errors)
|
31
48
|
)
|
32
49
|
|
33
|
-
|
50
|
+
getChargeAttributes: ->
|
34
51
|
{
|
35
|
-
amount: @amount_input.val()
|
52
|
+
amount: @amount_input.val(),
|
53
|
+
gateway_name: @gateway_name_input.val()
|
36
54
|
}
|
37
55
|
|
38
|
-
|
56
|
+
prepareGatewayForm: (charge) ->
|
39
57
|
@id_input.val(charge.id)
|
40
|
-
|
41
|
-
$ ->
|
42
|
-
window.widgets ||= {}
|
43
|
-
window.widgets.charge_form = new ChargeForm("[role='gateway-charge-form']")
|
@@ -4,16 +4,47 @@ class Supercharged::ChargesController < ApplicationController
|
|
4
4
|
end
|
5
5
|
|
6
6
|
def create
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
7
|
+
charge = Charge.new(charge_params)
|
8
|
+
charge.user = current_user
|
9
|
+
|
10
|
+
if charge.save
|
11
|
+
render json: charge.as_json(only: [:id])
|
12
|
+
else
|
13
|
+
render json: { errors: charge.errors }, status: :unprocessable_entity
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# For example for PayPal Express, which requires getting token before purchase action.
|
18
|
+
def setup_purchase
|
19
|
+
charge = Charge.find(params[:charge_id])
|
20
|
+
|
21
|
+
token = charge.setup_purchase(
|
22
|
+
ip: request.remote_ip,
|
23
|
+
return_url: complete_charges_url,
|
24
|
+
cancel_return_url: new_charge_url
|
25
|
+
)
|
26
|
+
|
27
|
+
redirect_to charge.gateway.redirect_url_for(token)
|
28
|
+
end
|
29
|
+
|
30
|
+
def complete
|
31
|
+
params.require(:token)
|
32
|
+
|
33
|
+
@charge = Charge.with_token(params[:token])
|
34
|
+
|
35
|
+
if @charge
|
36
|
+
@charge.complete(params)
|
37
|
+
|
38
|
+
redirect_to root_url
|
39
|
+
else
|
40
|
+
head :not_found
|
41
|
+
end
|
11
42
|
end
|
12
43
|
|
13
44
|
private
|
14
45
|
|
15
46
|
def charge_params
|
16
|
-
params.require(:charge).permit(:amount)
|
47
|
+
params.require(:charge).permit(:amount, :gateway_name)
|
17
48
|
end
|
18
49
|
|
19
50
|
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
class Supercharged::GatewayNotificationsController < ApplicationController
|
2
2
|
skip_before_filter :verify_authenticity_token
|
3
|
+
before_filter :check_any_params, only: :create
|
3
4
|
|
4
5
|
def create
|
5
6
|
persistent_logger.info("Notification for #{params[:gateway]}")
|
@@ -50,4 +51,11 @@ class Supercharged::GatewayNotificationsController < ApplicationController
|
|
50
51
|
@persistent_logger ||= Logger.new("log/supercharged/gateway_notifications.log")
|
51
52
|
end
|
52
53
|
|
54
|
+
def check_any_params
|
55
|
+
if request.raw_post.blank?
|
56
|
+
head :bad_request
|
57
|
+
false
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
53
61
|
end
|
@@ -3,12 +3,6 @@ module Supercharged::ChargesHelper
|
|
3
3
|
# JS finds order input by this fake id because id and name will be integration specific
|
4
4
|
FAKE_ORDER_ID = "[payment_order_id]"
|
5
5
|
|
6
|
-
DEFAULT_AMOUNT_FIELD_OPTIONS = {
|
7
|
-
role: "charge-amount",
|
8
|
-
required: true,
|
9
|
-
min: 0
|
10
|
-
}
|
11
|
-
|
12
6
|
def charge_form_for(service_name, options = {}, &block)
|
13
7
|
raise ArgumentError, "Missing block" unless block_given?
|
14
8
|
|
@@ -27,9 +21,9 @@ module Supercharged::ChargesHelper
|
|
27
21
|
end
|
28
22
|
|
29
23
|
def charge_form_amount_field(service, options = {})
|
30
|
-
amount_field_name = service.mappings[:amount] || raise("Undefined amount field mapping")
|
24
|
+
amount_field_name = service.mappings[:amount] || raise(ArgumentError, "Undefined amount field mapping")
|
31
25
|
|
32
|
-
options =
|
26
|
+
options = default_amount_field_options.merge(options)
|
33
27
|
|
34
28
|
number_field_tag amount_field_name, nil, options
|
35
29
|
end
|
@@ -42,4 +36,14 @@ module Supercharged::ChargesHelper
|
|
42
36
|
options
|
43
37
|
end
|
44
38
|
|
39
|
+
def default_amount_field_options
|
40
|
+
{
|
41
|
+
role: "charge-amount",
|
42
|
+
required: true,
|
43
|
+
data: {
|
44
|
+
min_value: Charge.min_amount
|
45
|
+
}
|
46
|
+
}
|
47
|
+
end
|
48
|
+
|
45
49
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
class GatewayResponse < ActiveRecord::Base
|
2
|
+
belongs_to :charge
|
3
|
+
|
4
|
+
serialize :params
|
5
|
+
|
6
|
+
def response=(response)
|
7
|
+
self.success = response.success?
|
8
|
+
self.authorization = response.authorization
|
9
|
+
self.message = response.message
|
10
|
+
self.params = response.params
|
11
|
+
rescue ActiveMerchant::ActiveMerchantError => e
|
12
|
+
self.success = false
|
13
|
+
self.authorization = nil
|
14
|
+
self.message = e.message
|
15
|
+
self.params = {}
|
16
|
+
end
|
17
|
+
end
|
@@ -8,10 +8,16 @@ module Supercharged
|
|
8
8
|
|
9
9
|
belongs_to :user
|
10
10
|
has_many :gateway_input_notifications
|
11
|
+
has_many :gateway_responses
|
11
12
|
|
12
|
-
validates :amount, presence: true
|
13
|
+
validates :amount, presence: true, numericality: {
|
14
|
+
greater_than_or_equal_to: ->(model) {
|
15
|
+
model.class.min_amount
|
16
|
+
}
|
17
|
+
}
|
13
18
|
|
14
19
|
scope :latest, order("created_at DESC")
|
20
|
+
scope :by_gateway, ->(gateway_name) { where(gateway_name: gateway_name.to_s) }
|
15
21
|
|
16
22
|
state_machine :state, initial: :new do
|
17
23
|
# store_audit_trail
|
@@ -34,12 +40,77 @@ module Supercharged
|
|
34
40
|
end
|
35
41
|
end
|
36
42
|
|
43
|
+
def self.with_token(token)
|
44
|
+
where(gateway_token: token).first
|
45
|
+
end
|
46
|
+
|
37
47
|
# require implicit amount from gateway, not from user
|
38
48
|
def approve(real_amount)
|
39
49
|
self.real_amount = real_amount
|
40
50
|
set_ok!
|
41
51
|
end
|
42
52
|
|
53
|
+
def self.min_amount
|
54
|
+
1
|
55
|
+
end
|
56
|
+
|
57
|
+
def setup_purchase(options)
|
58
|
+
response = gateway.setup_purchase(amount_in_cents,
|
59
|
+
ip: options[:id],
|
60
|
+
return_url: options[:return_url],
|
61
|
+
cancel_return_url: options[:cancel_return_url]
|
62
|
+
)
|
63
|
+
|
64
|
+
if response.success?
|
65
|
+
update_attributes!(
|
66
|
+
gateway_token: response.token,
|
67
|
+
ip_address: options[:id]
|
68
|
+
)
|
69
|
+
end
|
70
|
+
|
71
|
+
response.token
|
72
|
+
end
|
73
|
+
|
74
|
+
def complete(options = {})
|
75
|
+
get_purchase_details
|
76
|
+
|
77
|
+
response = process_purchase
|
78
|
+
|
79
|
+
approve(amount) if response.success?
|
80
|
+
|
81
|
+
response.success?
|
82
|
+
end
|
83
|
+
|
84
|
+
def gateway
|
85
|
+
Supercharged::Helpers.gateway(gateway_name)
|
86
|
+
end
|
87
|
+
|
88
|
+
private
|
89
|
+
|
90
|
+
def amount_in_cents
|
91
|
+
amount * 100
|
92
|
+
end
|
93
|
+
|
94
|
+
def get_purchase_details
|
95
|
+
details = gateway.details_for(gateway_token)
|
96
|
+
self.gateway_payer_id = details.payer_id
|
97
|
+
end
|
98
|
+
|
99
|
+
def process_purchase
|
100
|
+
response = gateway.purchase(amount_in_cents,
|
101
|
+
ip: ip_address,
|
102
|
+
token: gateway_token,
|
103
|
+
payer_id: gateway_payer_id
|
104
|
+
)
|
105
|
+
|
106
|
+
save_gateway_response(response)
|
107
|
+
|
108
|
+
response
|
109
|
+
end
|
110
|
+
|
111
|
+
def save_gateway_response(response)
|
112
|
+
GatewayResponse.create!(charge: self, action: "purchase", amount: amount, response: response)
|
113
|
+
end
|
43
114
|
end
|
44
115
|
end
|
45
116
|
end
|
data/config/routes.rb
CHANGED
@@ -8,7 +8,12 @@ module ActionDispatch::Routing
|
|
8
8
|
}
|
9
9
|
controllers.merge!(options[:controllers]) if options[:controllers]
|
10
10
|
|
11
|
-
resources :charges, only: [:new, :create], controller: controllers[:charges]
|
11
|
+
resources :charges, only: [:new, :create], controller: controllers[:charges] do
|
12
|
+
collection do
|
13
|
+
post :setup_purchase
|
14
|
+
get :complete
|
15
|
+
end
|
16
|
+
end
|
12
17
|
|
13
18
|
match "gateways/:gateway/result" => "#{controllers[:gateway_notifications]}#create", as: :gateways_result
|
14
19
|
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'rails/generators'
|
2
|
+
require 'rails/generators/migration'
|
3
|
+
require 'rails/generators/active_record'
|
4
|
+
|
5
|
+
module Supercharged
|
6
|
+
module Generators
|
7
|
+
class MigrationsGenerator < Rails::Generators::Base
|
8
|
+
include Rails::Generators::Migration
|
9
|
+
|
10
|
+
source_root File.expand_path("../templates", __FILE__)
|
11
|
+
|
12
|
+
desc "Generates migration for Supercharged"
|
13
|
+
|
14
|
+
def self.next_migration_number(dirname)
|
15
|
+
ActiveRecord::Generators::Base.next_migration_number(dirname)
|
16
|
+
end
|
17
|
+
|
18
|
+
def copy_migration
|
19
|
+
migration_template 'install_migration.rb', 'db/migrate/install_supercharged.rb'
|
20
|
+
end
|
21
|
+
|
22
|
+
def mount_engine
|
23
|
+
route "supercharged"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
class InstallSupercharged < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
create_table "gateway_notifications", :force => true do |t|
|
4
|
+
t.text "params"
|
5
|
+
t.string "status"
|
6
|
+
t.string "external_transaction_id"
|
7
|
+
t.integer "charge_id"
|
8
|
+
t.datetime "created_at", :null => false
|
9
|
+
t.datetime "updated_at", :null => false
|
10
|
+
t.string "gateway"
|
11
|
+
end
|
12
|
+
|
13
|
+
add_index "gateway_notifications", ["charge_id"], :name => "index_gateway_input_notifications_on_charge_id"
|
14
|
+
|
15
|
+
create_table "gateway_responses", :force => true do |t|
|
16
|
+
t.integer "charge_id", :null => false
|
17
|
+
t.string "action", :null => false
|
18
|
+
t.decimal "amount"
|
19
|
+
t.boolean "success", :null => false
|
20
|
+
t.string "authorization"
|
21
|
+
t.string "message"
|
22
|
+
t.text "params"
|
23
|
+
t.datetime "created_at", :null => false
|
24
|
+
t.datetime "updated_at", :null => false
|
25
|
+
end
|
26
|
+
|
27
|
+
add_index "gateway_responses", ["charge_id"], :name => "index_gateway_responses_on_charge_id"
|
28
|
+
|
29
|
+
create_table "charges_state_transitions", :force => true do |t|
|
30
|
+
t.integer "charges_id"
|
31
|
+
t.string "charges_type"
|
32
|
+
t.string "event"
|
33
|
+
t.string "from"
|
34
|
+
t.string "to"
|
35
|
+
t.datetime "created_at"
|
36
|
+
end
|
37
|
+
|
38
|
+
add_index "charges_state_transitions", ["charges_id"], :name => "index_charges_state_transitions_on_charges_id"
|
39
|
+
|
40
|
+
create_table "charges", :force => true do |t|
|
41
|
+
t.integer "amount", :null => false
|
42
|
+
t.integer "user_id", :null => false
|
43
|
+
t.string "external_transaction_id"
|
44
|
+
t.text "params"
|
45
|
+
t.string "state", :default => "new", :null => false
|
46
|
+
t.datetime "created_at", :null => false
|
47
|
+
t.datetime "updated_at", :null => false
|
48
|
+
t.string "error"
|
49
|
+
t.integer "approved_by"
|
50
|
+
t.text "reject_reason"
|
51
|
+
t.decimal "real_amount"
|
52
|
+
t.string "gateway_name"
|
53
|
+
t.string "gateway_token"
|
54
|
+
t.string "gateway_payer_id"
|
55
|
+
t.string "ip_address"
|
56
|
+
end
|
57
|
+
|
58
|
+
add_index "charges", ["approved_by"], :name => "index_charges_on_approved_by"
|
59
|
+
add_index "charges", ["gateway_name"], :name => "index_charges_on_gateway_name"
|
60
|
+
add_index "charges", ["gateway_token"], :name => "index_charges_on_gateway_token"
|
61
|
+
add_index "charges", ["state"], :name => "index_charges_on_state"
|
62
|
+
add_index "charges", ["user_id"], :name => "index_charges_on_user_id"
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Supercharged::Helpers
|
2
|
+
|
3
|
+
@gateways = {}
|
4
|
+
|
5
|
+
def self.gateway(name)
|
6
|
+
@gateways[name.to_sym] || raise("Gateway not registered")
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.init_gateway(name, options)
|
10
|
+
klass = gateway_class_by_name(name)
|
11
|
+
gateway = klass.new(options)
|
12
|
+
add_gateway(name, gateway)
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def self.gateway_class_by_name(name)
|
18
|
+
"ActiveMerchant::Billing::#{name.to_s.camelcase}Gateway".classify.constantize
|
19
|
+
rescue NameError
|
20
|
+
raise "Unknown gateway '#{name}'"
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.add_gateway(name, gateway)
|
24
|
+
@gateways[name.to_sym] = gateway
|
25
|
+
end
|
26
|
+
end
|
data/lib/supercharged/version.rb
CHANGED
data/lib/supercharged.rb
CHANGED
data/test/fake_app.rb
CHANGED
@@ -2,11 +2,7 @@ require 'active_record'
|
|
2
2
|
require 'action_controller/railtie'
|
3
3
|
|
4
4
|
# database
|
5
|
-
|
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
|
5
|
+
ActiveRecord::Base.configurations = {'test' => {adapter: 'sqlite3', database: ':memory:'}}
|
10
6
|
ActiveRecord::Base.establish_connection('test')
|
11
7
|
|
12
8
|
# config
|
@@ -39,6 +35,20 @@ ActiveRecord::Base.silence do
|
|
39
35
|
|
40
36
|
add_index "gateway_notifications", ["charge_id"], :name => "index_gateway_input_notifications_on_charge_id"
|
41
37
|
|
38
|
+
create_table "gateway_responses", :force => true do |t|
|
39
|
+
t.integer "charge_id", :null => false
|
40
|
+
t.string "action", :null => false
|
41
|
+
t.decimal "amount"
|
42
|
+
t.boolean "success", :null => false
|
43
|
+
t.string "authorization"
|
44
|
+
t.string "message"
|
45
|
+
t.text "params"
|
46
|
+
t.datetime "created_at", :null => false
|
47
|
+
t.datetime "updated_at", :null => false
|
48
|
+
end
|
49
|
+
|
50
|
+
add_index "gateway_responses", ["charge_id"], :name => "index_gateway_responses_on_charge_id"
|
51
|
+
|
42
52
|
create_table "charges_state_transitions", :force => true do |t|
|
43
53
|
t.integer "charges_id"
|
44
54
|
t.string "charges_type"
|
@@ -48,6 +58,11 @@ ActiveRecord::Base.silence do
|
|
48
58
|
t.datetime "created_at"
|
49
59
|
end
|
50
60
|
|
61
|
+
create_table "users", :force => true do |t|
|
62
|
+
t.datetime "created_at", :null => false
|
63
|
+
t.datetime "updated_at", :null => false
|
64
|
+
end
|
65
|
+
|
51
66
|
add_index "charges_state_transitions", ["charges_id"], :name => "index_charges_state_transitions_on_charges_id"
|
52
67
|
|
53
68
|
create_table "charges", :force => true do |t|
|
@@ -58,14 +73,19 @@ ActiveRecord::Base.silence do
|
|
58
73
|
t.string "state", :default => "new", :null => false
|
59
74
|
t.datetime "created_at", :null => false
|
60
75
|
t.datetime "updated_at", :null => false
|
61
|
-
t.integer "user_transaction_id"
|
62
76
|
t.string "error"
|
63
77
|
t.integer "approved_by"
|
64
78
|
t.text "reject_reason"
|
65
79
|
t.decimal "real_amount"
|
80
|
+
t.string "gateway_name"
|
81
|
+
t.string "gateway_token"
|
82
|
+
t.string "gateway_payer_id"
|
83
|
+
t.string "ip_address"
|
66
84
|
end
|
67
85
|
|
68
86
|
add_index "charges", ["approved_by"], :name => "index_charges_on_approved_by"
|
87
|
+
add_index "charges", ["gateway_name"], :name => "index_charges_on_gateway_name"
|
88
|
+
add_index "charges", ["gateway_token"], :name => "index_charges_on_gateway_token"
|
69
89
|
add_index "charges", ["state"], :name => "index_charges_on_state"
|
70
90
|
add_index "charges", ["user_id"], :name => "index_charges_on_user_id"
|
71
91
|
|
@@ -92,3 +112,6 @@ end
|
|
92
112
|
|
93
113
|
# helpers
|
94
114
|
Object.const_set(:ApplicationHelper, Module.new)
|
115
|
+
|
116
|
+
class User < ActiveRecord::Base
|
117
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
describe Supercharged::ChargesController do
|
4
|
+
describe "create action" do
|
5
|
+
context "authorized" do
|
6
|
+
let(:fake_user) { User.create! }
|
7
|
+
|
8
|
+
before do
|
9
|
+
Supercharged::ChargesController.any_instance.stubs(:current_user).returns(fake_user)
|
10
|
+
end
|
11
|
+
|
12
|
+
context "correct conditions" do
|
13
|
+
it "response contains id in json" do
|
14
|
+
post :create, charge: { amount: 100 }
|
15
|
+
|
16
|
+
assert_response :success
|
17
|
+
|
18
|
+
expected = {"charge"=>{"id"=>1}}
|
19
|
+
JSON.parse(@response.body).must_equal(expected)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
context "bad conditions" do
|
24
|
+
it "response contains errors in json" do
|
25
|
+
post :create, charge: { amount: 0 }
|
26
|
+
|
27
|
+
assert_response 422
|
28
|
+
|
29
|
+
expected = {"errors"=>{"amount"=>["must be greater than or equal to 1"]}}
|
30
|
+
JSON.parse(@response.body).must_equal(expected)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -17,7 +17,7 @@ describe Supercharged::GatewayNotificationsController do
|
|
17
17
|
|
18
18
|
GatewayNotification.any_instance.stubs(:adapter).returns(adapter)
|
19
19
|
|
20
|
-
post :create, gateway: "webmoney"
|
20
|
+
post :create, gateway: "webmoney", amount: 100
|
21
21
|
|
22
22
|
charge.reload
|
23
23
|
charge.state_name.must_equal :ok
|
@@ -33,7 +33,7 @@ describe Supercharged::GatewayNotificationsController do
|
|
33
33
|
|
34
34
|
GatewayNotification.any_instance.stubs(:adapter).returns(adapter)
|
35
35
|
|
36
|
-
post :create, gateway: "webmoney"
|
36
|
+
post :create, gateway: "webmoney", amount: 100
|
37
37
|
|
38
38
|
charge.reload
|
39
39
|
charge.state_name.must_equal :error
|
@@ -47,7 +47,7 @@ describe Supercharged::GatewayNotificationsController do
|
|
47
47
|
|
48
48
|
GatewayNotification.any_instance.stubs(:adapter).returns(adapter)
|
49
49
|
|
50
|
-
post :create, gateway: "webmoney"
|
50
|
+
post :create, gateway: "webmoney", amount: 100
|
51
51
|
|
52
52
|
charge.reload
|
53
53
|
charge.state_name.must_equal :error
|
@@ -58,5 +58,14 @@ describe Supercharged::GatewayNotificationsController do
|
|
58
58
|
|
59
59
|
end
|
60
60
|
end
|
61
|
+
|
62
|
+
context "without any payload params" do
|
63
|
+
it "returns bad_request" do
|
64
|
+
# head :bad_request
|
65
|
+
post :create, gateway: "paypal"
|
66
|
+
|
67
|
+
assert_response :bad_request
|
68
|
+
end
|
69
|
+
end
|
61
70
|
end
|
62
71
|
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class Supercharged::ChargesHelperTest < ActionView::TestCase
|
4
|
+
def test_min_value
|
5
|
+
field = charge_form_amount_field(sample_service, { min: 500 })
|
6
|
+
field.must_include 'min="500"'
|
7
|
+
end
|
8
|
+
|
9
|
+
def sample_service
|
10
|
+
stub(mappings: { amount: "amount" })
|
11
|
+
end
|
12
|
+
end
|
@@ -8,11 +8,27 @@ describe Charge do
|
|
8
8
|
|
9
9
|
subject.errors[:amount][0].must_equal "can't be blank"
|
10
10
|
end
|
11
|
+
|
12
|
+
it "min amount is required" do
|
13
|
+
subject.amount = 0
|
14
|
+
subject.valid?
|
15
|
+
|
16
|
+
subject.errors[:amount][0].must_equal "must be greater than or equal to 1"
|
17
|
+
|
18
|
+
subject.amount = 100
|
19
|
+
subject.valid?.must_equal true
|
20
|
+
end
|
11
21
|
end
|
12
22
|
|
13
23
|
describe "states" do
|
14
24
|
subject { Charge.create!({user_id: 1, amount: 10}, without_protection: true) }
|
15
25
|
|
26
|
+
describe "initial state" do
|
27
|
+
it "initial state is new" do
|
28
|
+
Charge.new.state_name.must_equal :new
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
16
32
|
describe "#approve" do
|
17
33
|
it "changes state and real_amount" do
|
18
34
|
subject.approve(5)
|
@@ -7,5 +7,15 @@ describe GatewayNotification do
|
|
7
7
|
GatewayNotification.create!(gateway: "webmoney")
|
8
8
|
}.must_raise GatewayNotification::EmptyChargeIdError
|
9
9
|
end
|
10
|
+
|
11
|
+
it "charge id is inherited from adapter" do
|
12
|
+
gateway_notification = GatewayNotification.new
|
13
|
+
|
14
|
+
adapter = stub(item_id: 42)
|
15
|
+
gateway_notification.stubs(:adapter).returns(adapter)
|
16
|
+
|
17
|
+
gateway_notification.save!
|
18
|
+
gateway_notification.charge_id.must_equal 42
|
19
|
+
end
|
10
20
|
end
|
11
21
|
end
|
data/test/test_helper.rb
CHANGED
metadata
CHANGED
@@ -1,153 +1,182 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: supercharged
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
5
|
-
|
6
|
-
- 1
|
7
|
-
- 0
|
8
|
-
- 0
|
9
|
-
version: 1.0.0
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 2.0.0
|
5
|
+
prerelease:
|
10
6
|
platform: ruby
|
11
|
-
authors:
|
7
|
+
authors:
|
12
8
|
- divineforest
|
13
9
|
autorequire:
|
14
10
|
bindir: bin
|
15
11
|
cert_chain: []
|
16
|
-
|
17
|
-
|
18
|
-
|
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"
|
12
|
+
date: 2013-06-27 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
30
15
|
name: rails
|
31
|
-
requirement:
|
32
|
-
|
33
|
-
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '3.1'
|
34
22
|
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
23
|
prerelease: false
|
45
|
-
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '3.1'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: state_machine
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
46
38
|
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
39
|
prerelease: false
|
57
|
-
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: state_machine-audit_trail
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
58
54
|
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
55
|
prerelease: false
|
69
|
-
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: activemerchant
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
70
|
type: :runtime
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
version:
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
- !ruby/object:Gem::Dependency
|
78
79
|
name: strong_parameters
|
79
|
-
requirement:
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ! '>='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
type: :runtime
|
80
87
|
prerelease: false
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
- 0
|
89
|
-
version: "0"
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ! '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
- !ruby/object:Gem::Dependency
|
90
95
|
name: sqlite3
|
91
|
-
requirement:
|
92
|
-
|
93
|
-
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ! '>='
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
94
102
|
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
103
|
prerelease: false
|
106
|
-
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ! '>='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
- !ruby/object:Gem::Dependency
|
111
|
+
name: minitest
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
114
|
+
requirements:
|
115
|
+
- - ~>
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '3.0'
|
107
118
|
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
119
|
prerelease: false
|
118
|
-
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
none: false
|
122
|
+
requirements:
|
123
|
+
- - ~>
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '3.0'
|
126
|
+
- !ruby/object:Gem::Dependency
|
127
|
+
name: minitest-spec-context
|
128
|
+
requirement: !ruby/object:Gem::Requirement
|
129
|
+
none: false
|
130
|
+
requirements:
|
131
|
+
- - ! '>='
|
132
|
+
- !ruby/object:Gem::Version
|
133
|
+
version: '0'
|
119
134
|
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
135
|
prerelease: false
|
130
|
-
|
136
|
+
version_requirements: !ruby/object:Gem::Requirement
|
137
|
+
none: false
|
138
|
+
requirements:
|
139
|
+
- - ! '>='
|
140
|
+
- !ruby/object:Gem::Version
|
141
|
+
version: '0'
|
142
|
+
- !ruby/object:Gem::Dependency
|
143
|
+
name: minitest-rails
|
144
|
+
requirement: !ruby/object:Gem::Requirement
|
145
|
+
none: false
|
146
|
+
requirements:
|
147
|
+
- - ! '>='
|
148
|
+
- !ruby/object:Gem::Version
|
149
|
+
version: '0'
|
131
150
|
type: :development
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
version:
|
151
|
+
prerelease: false
|
152
|
+
version_requirements: !ruby/object:Gem::Requirement
|
153
|
+
none: false
|
154
|
+
requirements:
|
155
|
+
- - ! '>='
|
156
|
+
- !ruby/object:Gem::Version
|
157
|
+
version: '0'
|
158
|
+
- !ruby/object:Gem::Dependency
|
139
159
|
name: mocha
|
140
|
-
requirement:
|
160
|
+
requirement: !ruby/object:Gem::Requirement
|
161
|
+
none: false
|
162
|
+
requirements:
|
163
|
+
- - ! '>='
|
164
|
+
- !ruby/object:Gem::Version
|
165
|
+
version: '0'
|
166
|
+
type: :development
|
141
167
|
prerelease: false
|
168
|
+
version_requirements: !ruby/object:Gem::Requirement
|
169
|
+
none: false
|
170
|
+
requirements:
|
171
|
+
- - ! '>='
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: '0'
|
142
174
|
description:
|
143
175
|
email:
|
144
176
|
executables: []
|
145
|
-
|
146
177
|
extensions: []
|
147
|
-
|
148
178
|
extra_rdoc_files: []
|
149
|
-
|
150
|
-
files:
|
179
|
+
files:
|
151
180
|
- app/assets/javascripts/charge_form.js.coffee.erb
|
152
181
|
- app/assets/javascripts/supercharged.js
|
153
182
|
- app/controllers/supercharged/charges_controller.rb
|
@@ -155,49 +184,61 @@ files:
|
|
155
184
|
- app/helpers/supercharged/charges_helper.rb
|
156
185
|
- app/models/charge.rb
|
157
186
|
- app/models/gateway_notification.rb
|
187
|
+
- app/models/gateway_response.rb
|
158
188
|
- app/models/payment_state_transition.rb
|
159
189
|
- app/models/supercharged/charge/base.rb
|
160
190
|
- config/routes.rb
|
191
|
+
- lib/generators/supercharged/migrations_generator.rb
|
192
|
+
- lib/generators/supercharged/templates/install_migration.rb
|
161
193
|
- lib/supercharged/activemerchant.rb
|
194
|
+
- lib/supercharged/helpers.rb
|
162
195
|
- lib/supercharged/version.rb
|
163
196
|
- lib/supercharged.rb
|
164
|
-
- lib/tasks/supercharged_tasks.rake
|
165
197
|
- MIT-LICENSE
|
166
198
|
- Rakefile
|
167
199
|
- README.md
|
168
|
-
|
200
|
+
- test/fake_app.rb
|
201
|
+
- test/supercharged/controllers/charges_controller_test.rb
|
202
|
+
- test/supercharged/controllers/gateway_notifications_controller_test.rb
|
203
|
+
- test/supercharged/helpers/charges_helper_test.rb
|
204
|
+
- test/supercharged/models/charge_test.rb
|
205
|
+
- test/supercharged/models/geneway_notification_test.rb
|
206
|
+
- test/test_helper.rb
|
169
207
|
homepage:
|
170
208
|
licenses: []
|
171
|
-
|
172
209
|
post_install_message:
|
173
210
|
rdoc_options: []
|
174
|
-
|
175
|
-
require_paths:
|
211
|
+
require_paths:
|
176
212
|
- lib
|
177
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
213
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
214
|
+
none: false
|
215
|
+
requirements:
|
216
|
+
- - ! '>='
|
217
|
+
- !ruby/object:Gem::Version
|
218
|
+
version: '0'
|
219
|
+
segments:
|
182
220
|
- 0
|
183
|
-
|
184
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
221
|
+
hash: -708610607516919495
|
222
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
223
|
+
none: false
|
224
|
+
requirements:
|
225
|
+
- - ! '>='
|
226
|
+
- !ruby/object:Gem::Version
|
227
|
+
version: '0'
|
228
|
+
segments:
|
189
229
|
- 0
|
190
|
-
|
230
|
+
hash: -708610607516919495
|
191
231
|
requirements: []
|
192
|
-
|
193
232
|
rubyforge_project:
|
194
|
-
rubygems_version: 1.
|
233
|
+
rubygems_version: 1.8.23
|
195
234
|
signing_key:
|
196
235
|
specification_version: 3
|
197
236
|
summary: MVC solution for charges in rails
|
198
|
-
test_files:
|
237
|
+
test_files:
|
199
238
|
- test/fake_app.rb
|
239
|
+
- test/supercharged/controllers/charges_controller_test.rb
|
200
240
|
- test/supercharged/controllers/gateway_notifications_controller_test.rb
|
241
|
+
- test/supercharged/helpers/charges_helper_test.rb
|
201
242
|
- test/supercharged/models/charge_test.rb
|
202
243
|
- test/supercharged/models/geneway_notification_test.rb
|
203
244
|
- test/test_helper.rb
|