so_paid 0.0.1
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.
- checksums.yaml +15 -0
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE +21 -0
- data/LICENSE.txt +22 -0
- data/README.md +47 -0
- data/Rakefile +1 -0
- data/examples/.DS_Store +0 -0
- data/examples/app/controllers/payments_controller.rb +65 -0
- data/examples/app/views/confirm.html.erb +6 -0
- data/examples/config/initializers/pay_me.rb +70 -0
- data/examples/config/routes.rb +11 -0
- data/lib/.DS_Store +0 -0
- data/lib/so_paid/configuration.rb +23 -0
- data/lib/so_paid/cybersource.rb +143 -0
- data/lib/so_paid/hop.rb +203 -0
- data/lib/so_paid/payment_vendor.rb +18 -0
- data/lib/so_paid/version.rb +3 -0
- data/lib/so_paid.rb +13 -0
- data/so_paid.gemspec +25 -0
- metadata +124 -0
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
ZDhlYThjZWZjMGY5ODM0ZmI0NDNkNTBhMjhmMTY3OTE1NzUzNmM4Mw==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
NTEzNzU1MDhhMTZiMzFjMGUyYTBmZTZkMmQ3NTRkZDFjYTc3ZDQ4YQ==
|
7
|
+
SHA512:
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
ZGE0MWQ2MWYxYWVhODUyMTNkOWViOTU2NWFmZDBmYzY2NjAwMzRlNWZmODdi
|
10
|
+
YjM1NjA4YzYyNDFhZDI1MGM5YTYwMjhmZWQ4ZDRiMDMyZWU2YWFkZjdiYjMz
|
11
|
+
YWIzZjM4M2I5Mjc4NDBkYmU2NWRhNDY4Yzc2ODA4NzRmYzZkMGY=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
MjJjOWVkOWM4NzgyZTRkMTE1NmY5OGZmNzczOTMxMDI0MWFmOWE5OWRkOTQw
|
14
|
+
NDAwMjdlNDI5ZmMwMDk0MDM4NTBmZmJhZmNjNjJmOTExMzlkOGIzM2Y4ZGM0
|
15
|
+
Y2ExZjdiNzZiZWM5Yjk4YTg1NDdhYmE4MTAwNjI3NzlmZTk5MjI=
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2014 Mike
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 mkralla11
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
# SoPaid
|
2
|
+
|
3
|
+
A no-nonsense, fully configurable Rails credit card/payment vendor gem.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'so_paid'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install so_paid
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
See examples folder for instructions and copy/pastable files. More will be added here when time is available (...rare).
|
22
|
+
|
23
|
+
## Currently supported payment vendors:
|
24
|
+
|
25
|
+
1. cybersource
|
26
|
+
* request field info and doc:
|
27
|
+
* http://apps.cybersource.com/library/documentation/dev_guides/Secure_Acceptance_WM/Secure_Acceptance_WM.pdf
|
28
|
+
2. no more yet...
|
29
|
+
|
30
|
+
|
31
|
+
## Call To Lead<sup>TM</sup>
|
32
|
+
|
33
|
+
Want to make this gem more awesome? Even though everything works, maybe you have some good ideas that you want incorporated, whether it be performance-wise, or extension-wise. Hense, the following list is for you to easily view and will be ammended for large ideas.
|
34
|
+
|
35
|
+
### The List<sup>TM</sup>:
|
36
|
+
1. Support for paypal.
|
37
|
+
2. Support for stripe (wrapping the gem that already exists).
|
38
|
+
|
39
|
+
|
40
|
+
## Contributing
|
41
|
+
|
42
|
+
1. Fork it
|
43
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
44
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
45
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
46
|
+
5. Create new Pull Request
|
47
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/examples/.DS_Store
ADDED
Binary file
|
@@ -0,0 +1,65 @@
|
|
1
|
+
class PaymentsController < ApplicationController
|
2
|
+
|
3
|
+
# Vendor will post to this post url (which can no longer be passed in at run time)
|
4
|
+
# set up this route in your cybersource/paypal/vendor's 'profile' panel on
|
5
|
+
# their website. http://yourdomain.com/payments/
|
6
|
+
def create
|
7
|
+
# process the response how you see fit,
|
8
|
+
# verify the transaction signature in the OrderProcessor class
|
9
|
+
# you have defined to process the returning post request from vendor,
|
10
|
+
# use public methods available to you using SoPaid.vendor,
|
11
|
+
# ex: SoPaid.vendor(:cybersource).verify_transaction_signature(params)
|
12
|
+
@order_processor = OrderProcessor.new(params)
|
13
|
+
@order_processor.process_transaction
|
14
|
+
render :nothing=>true, :status=>:ok
|
15
|
+
end
|
16
|
+
|
17
|
+
def confirm
|
18
|
+
get_cart_order
|
19
|
+
# Need to check if payment post is going to fail
|
20
|
+
# because the order can't save or is invalid! So, try to SAVE IT
|
21
|
+
# or else it will fail on return from Cybersource POST #notgood
|
22
|
+
# and the user won't know what happened
|
23
|
+
if @order.save
|
24
|
+
# specific payment vendor, ex: cybersource...
|
25
|
+
#...or not if you only have one defined in config
|
26
|
+
#ex: @payment_vendor = SoPaid.vendor(:cybersource).new(@order, {override_defaults_hash}, {override_config_defaults_hash})
|
27
|
+
@payment_vendor = SoPaid.vendor.new(@order)
|
28
|
+
else
|
29
|
+
flash[:error] = "Please fix all errors with your account and your order. If you are having trouble making a purchase, contact the administrator."
|
30
|
+
redirect_back_or_default root_path
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# should show the list of payments/invoices
|
35
|
+
def index
|
36
|
+
# #payments are paid orders
|
37
|
+
@payments = @user.payments
|
38
|
+
render :layout=>"accounts"
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
# This is the return_url (which can no longer be passed in at run time)
|
43
|
+
# set up this route in your cybersource/paypal/vendor's 'profile' panel on
|
44
|
+
# their website. http://yourdomain.com/redirect_from_hop
|
45
|
+
def redirect_from_hop
|
46
|
+
if session[:pay_post_made]
|
47
|
+
if current_user and current_user.orders.order('created_at DESC').first
|
48
|
+
order = current_user.orders.order('created_at DESC').first
|
49
|
+
if order.paid?
|
50
|
+
flash[:success] = "You successfully purchased, " # + fill in what user purchased
|
51
|
+
else
|
52
|
+
flash[:fail] = "Uh oh, your purchase was not completed. Your credit card was not charged. Your cart will retain the items you original wanted to purchase.<br><br><b>Please verify that you entered your credit card information correctly and try again.</b> If the Problem persists please contact the administrator.".html_safe
|
53
|
+
end
|
54
|
+
end
|
55
|
+
session[:pay_post_made] = nil
|
56
|
+
end
|
57
|
+
#redirect to index of all payments
|
58
|
+
redirect_to payments_path
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
def get_cart_order
|
63
|
+
@order = current_user.cart
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,6 @@
|
|
1
|
+
<form action="<%=@payment_vendor.hop_url(current_user)%>" method="post" >
|
2
|
+
<% @payment_vendor.generate_params.each_pair do |key, value| %>
|
3
|
+
<input type="hidden" name="<%=key%>" value="<%=value%>" >
|
4
|
+
<%end%>
|
5
|
+
<%= submit_tag "Complete Credit Card Payment", :class=>"btn btn-success btn-large" %>
|
6
|
+
</form>
|
@@ -0,0 +1,70 @@
|
|
1
|
+
SoPaid.configure do |config|
|
2
|
+
config.add_payment_vendor 'cybersource', # Name of your payment vendor
|
3
|
+
:vendor_options=>{
|
4
|
+
# The only options that need to be placed
|
5
|
+
# in this file are:
|
6
|
+
|
7
|
+
# secret_key, access_key, profile_id
|
8
|
+
|
9
|
+
# All others will be passed/filled
|
10
|
+
# by the default-defaults or by the Order object
|
11
|
+
# you pass in at instantiation time.
|
12
|
+
|
13
|
+
# NOTE: 'live' and 'test' hashes ALWAYS
|
14
|
+
# trump any 'defaults' which are outside
|
15
|
+
# of both of those hashes
|
16
|
+
# you do not need to include live or test
|
17
|
+
# hash if you don't want, and just use the default
|
18
|
+
# outer hash
|
19
|
+
|
20
|
+
:live=>{
|
21
|
+
:secret_key=>"your_key",
|
22
|
+
:access_key=>"your_key"
|
23
|
+
#...any other live options
|
24
|
+
},
|
25
|
+
:test=>{
|
26
|
+
:secret_key=>"your_key",
|
27
|
+
:access_key=>"your_key"
|
28
|
+
#...any other test options
|
29
|
+
},
|
30
|
+
#...any other DEFAULT options
|
31
|
+
:secret_key=>"",
|
32
|
+
:profile_id=>"",
|
33
|
+
:access_key=>"",
|
34
|
+
:transaction_uuid=>"",
|
35
|
+
:locale=>"en-US",
|
36
|
+
:transaction_type=>"sale",
|
37
|
+
:reference_number=>"",
|
38
|
+
:amount=>"",
|
39
|
+
:currency=>"USD",
|
40
|
+
:signed_date_time=>"",
|
41
|
+
:unsigned_field_names=>[],
|
42
|
+
# IMPORTANT: if you want to add
|
43
|
+
# any :unsigned_field_names, make sure
|
44
|
+
# you add them to both within the
|
45
|
+
# :unsigned_field_names array AND
|
46
|
+
# as a key value pair within this hash,
|
47
|
+
# used signed_field_names as an example
|
48
|
+
|
49
|
+
:signed_field_names =>
|
50
|
+
[
|
51
|
+
:profile_id,
|
52
|
+
:access_key,
|
53
|
+
:transaction_uuid,
|
54
|
+
:locale,
|
55
|
+
:transaction_type,
|
56
|
+
:reference_number,
|
57
|
+
:amount,
|
58
|
+
:currency,
|
59
|
+
:signed_date_time,
|
60
|
+
:unsigned_field_names,
|
61
|
+
:signed_field_names
|
62
|
+
],
|
63
|
+
|
64
|
+
# config options
|
65
|
+
# if mode
|
66
|
+
:config_options=>{
|
67
|
+
:test_user_email=>"payment_tester@gmail.com",
|
68
|
+
:test_mode=>false
|
69
|
+
}
|
70
|
+
end
|
data/lib/.DS_Store
ADDED
Binary file
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module SoPaid
|
2
|
+
module Configuration
|
3
|
+
|
4
|
+
mattr_accessor :vendors
|
5
|
+
self.vendors = {}
|
6
|
+
|
7
|
+
def configure
|
8
|
+
yield self
|
9
|
+
end
|
10
|
+
|
11
|
+
def add_payment_vendor(name="cybersource", options={})
|
12
|
+
new_vendor = ("SoPaid::" + name.classify).safe_constantize
|
13
|
+
raise "Payment Vendor '#{name}' gem/plugin not supported." if new_vendor.blank?
|
14
|
+
vo = options.delete(:vendor_options) || {}
|
15
|
+
co = options.delete(:config_options) || {}
|
16
|
+
vo ||= options
|
17
|
+
|
18
|
+
self.vendors[name] = new_vendor.payment_options(vo, co)
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,143 @@
|
|
1
|
+
#require 'HOP'
|
2
|
+
|
3
|
+
module SoPaid
|
4
|
+
class Cybersource < SoPaid::Hop
|
5
|
+
|
6
|
+
|
7
|
+
@@pv_defaults = {
|
8
|
+
:live=>{
|
9
|
+
},
|
10
|
+
:test=>{
|
11
|
+
},
|
12
|
+
:secret_key=>"",
|
13
|
+
:profile_id=>"",
|
14
|
+
:access_key=>"",
|
15
|
+
:transaction_uuid=>"",
|
16
|
+
:locale=>"en-US",
|
17
|
+
:transaction_type=>"sale",
|
18
|
+
:reference_number=>"",
|
19
|
+
:amount=>"",
|
20
|
+
:currency=>"USD",
|
21
|
+
:signed_date_time=>"",
|
22
|
+
:unsigned_field_names=>[],
|
23
|
+
|
24
|
+
:signed_field_names =>
|
25
|
+
[
|
26
|
+
:profile_id,
|
27
|
+
:access_key,
|
28
|
+
:transaction_uuid,
|
29
|
+
:locale,
|
30
|
+
:transaction_type,
|
31
|
+
:reference_number,
|
32
|
+
:amount,
|
33
|
+
:currency,
|
34
|
+
:signed_date_time,
|
35
|
+
:unsigned_field_names,
|
36
|
+
:signed_field_names
|
37
|
+
]
|
38
|
+
}
|
39
|
+
|
40
|
+
@@config_defaults = {
|
41
|
+
# optional for gem
|
42
|
+
:test_mode=>false,
|
43
|
+
:test_user_email=>"payment_tester@gmail.com"
|
44
|
+
}
|
45
|
+
|
46
|
+
|
47
|
+
|
48
|
+
|
49
|
+
# refer to http://apps.cybersource.com/library/documentation/dev_guides/Secure_Acceptance_WM/html/wwhelp/wwhimpl/js/html/wwhelp.htm#href=SC_api.12.1.html#1113432
|
50
|
+
# for more info on required keys
|
51
|
+
# needed signed fields:
|
52
|
+
# "access_key,profile_id,transaction_uuid,signed_field_names,unsigned_field_names,signed_date_time,locale,transaction_type,reference_number,amount,currency"
|
53
|
+
def generate_params
|
54
|
+
return @pv_order_params if @pv_order_params.present?
|
55
|
+
|
56
|
+
set_pv_fields
|
57
|
+
set_order_specific_params
|
58
|
+
set_signature
|
59
|
+
|
60
|
+
return @pv_order_params
|
61
|
+
end
|
62
|
+
|
63
|
+
|
64
|
+
def hop_url(user_email)
|
65
|
+
# test mode true or specific test email gets access to test page
|
66
|
+
if test? or @config_options[:test_user_email] == @current_user_email
|
67
|
+
test_hop_url
|
68
|
+
else
|
69
|
+
live_hop_url
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
|
77
|
+
|
78
|
+
|
79
|
+
|
80
|
+
def set_pv_fields
|
81
|
+
if live?
|
82
|
+
@pv_order_params = @pv_options[:live].presence || @pv_options
|
83
|
+
else
|
84
|
+
@pv_order_params = @pv_options[:test].presence || @pv_options
|
85
|
+
end
|
86
|
+
|
87
|
+
# we just set them above and are done with them
|
88
|
+
@pv_options.delete(:live)
|
89
|
+
@pv_options.delete(:test)
|
90
|
+
# merge pv_order_params on top of pv_options which is
|
91
|
+
# the combination of the class defaults and the configuration file defaults
|
92
|
+
@pv_order_params = merge_defaults(nil, @pv_order_params, @pv_options)
|
93
|
+
end
|
94
|
+
|
95
|
+
|
96
|
+
def set_order_specific_params
|
97
|
+
@pv_order_params[:amount] = @order.amount_cents if @pv_order_params[:amount].blank?
|
98
|
+
@pv_order_params[:reference_number] = @order.id.to_s if @pv_order_params[:reference_number].blank?
|
99
|
+
@pv_order_params[:transaction_uuid] = uniq_app_order_id if @pv_order_params[:transaction_uuid].blank?
|
100
|
+
@pv_order_params[:signed_date_time] = get_isotime if @pv_order_params[:signed_date_time].blank?
|
101
|
+
end
|
102
|
+
|
103
|
+
|
104
|
+
def set_signature
|
105
|
+
@pv_order_params[:signature] = sign_params
|
106
|
+
end
|
107
|
+
|
108
|
+
|
109
|
+
def sign_params
|
110
|
+
data = []
|
111
|
+
@pv_options[:signed_field_names].each do |key|
|
112
|
+
data << key.to_s + "=" + @pv_order_params[key].to_s
|
113
|
+
end
|
114
|
+
@pv_options[:signed_field_names] = @pv_options[:signed_field_names].join(",")
|
115
|
+
data = data.join(",")
|
116
|
+
|
117
|
+
encode_hop(data, @pv_order_params[:secret_key])
|
118
|
+
end
|
119
|
+
|
120
|
+
|
121
|
+
def uniq_app_order_id
|
122
|
+
@pv_order_params[:profile_id] + "_" + @order.id.to_s
|
123
|
+
end
|
124
|
+
|
125
|
+
|
126
|
+
|
127
|
+
def test?
|
128
|
+
@config_options[:test_mode]
|
129
|
+
end
|
130
|
+
|
131
|
+
def live?
|
132
|
+
!test?
|
133
|
+
end
|
134
|
+
|
135
|
+
def test_hop_url
|
136
|
+
"https://testsecureacceptance.cybersource.com/pay"
|
137
|
+
end
|
138
|
+
|
139
|
+
def live_hop_url
|
140
|
+
"https://secureacceptance.cybersource.com/pay"
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
data/lib/so_paid/hop.rb
ADDED
@@ -0,0 +1,203 @@
|
|
1
|
+
# CyberSource Hosted Order Page Library
|
2
|
+
#
|
3
|
+
# Inserts fields into the checkout form for posting data to the CyberSource Hosted Order page
|
4
|
+
#
|
5
|
+
|
6
|
+
# require 'openssl'
|
7
|
+
# include OpenSSL
|
8
|
+
require 'hmac-sha2'
|
9
|
+
require 'base64'
|
10
|
+
|
11
|
+
|
12
|
+
module SoPaid
|
13
|
+
class Hop
|
14
|
+
@@pv_defaults = {
|
15
|
+
|
16
|
+
}
|
17
|
+
|
18
|
+
@@config_defaults = {
|
19
|
+
# optional for gem
|
20
|
+
:test_mode=>false,
|
21
|
+
:test_user_email=>"payment_tester@gmail.com"
|
22
|
+
}
|
23
|
+
|
24
|
+
|
25
|
+
|
26
|
+
def self.payment_options(pv_options={}, config_options={})
|
27
|
+
@@pv_defaults = self.merge_defaults([:live, :test], pv_options, @@pv_defaults)
|
28
|
+
@@config_defaults = self.merge_defaults(nil, config_options, @@config_defaults)
|
29
|
+
|
30
|
+
return self
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
def initialize(order, pv_options={}, config_options={})
|
35
|
+
@pv_order_params = {}.with_indifferent_access
|
36
|
+
@pv_options = self.merge_defaults([:live, :test], pv_options, @@pv_defaults)
|
37
|
+
@config_options = self.merge_defaults(nil, config_options, @@config_defaults)
|
38
|
+
|
39
|
+
@order = order
|
40
|
+
@current_user_email = @config_options[:current_user_email] || @config_options[:current_user].try(:email) || (@order.user.try(:email) if @order.respond_to?(:user))
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
|
45
|
+
def get_isotime
|
46
|
+
Time.now.utc.iso8601
|
47
|
+
end
|
48
|
+
|
49
|
+
def encode_hop(data, key)
|
50
|
+
mac = HMAC::SHA256.new(key)
|
51
|
+
mac.update data
|
52
|
+
Base64.encode64(mac.digest).gsub "\n", ''
|
53
|
+
end
|
54
|
+
|
55
|
+
def verify_signature(data,signature)
|
56
|
+
pub_digest = encode_hop(data, secret_key)
|
57
|
+
pub_digest.eql?(signature)
|
58
|
+
end
|
59
|
+
|
60
|
+
def verify_transaction_signature(message)
|
61
|
+
verify_signature(message[:signed_field_names], message[:signature])
|
62
|
+
end
|
63
|
+
|
64
|
+
def merge_defaults(modes=[], opts={}, default_opts={})
|
65
|
+
SoPaid::Hop.merge_defaults(modes, opts, default_opts)
|
66
|
+
end
|
67
|
+
|
68
|
+
|
69
|
+
def self.merge_defaults(modes=[], opts={}, default_opts={})
|
70
|
+
modes ||= []
|
71
|
+
new_opts = opts.clone.with_indifferent_access
|
72
|
+
new_default_opts = default_opts.clone.with_indifferent_access
|
73
|
+
modes.each do |mode|
|
74
|
+
new_default_opts[mode] ||= {}
|
75
|
+
new_opts_mode = new_opts[mode].is_a?(Hash) ? new_opts[mode] : {}
|
76
|
+
new_opts_mode.each_pair do |key, value|
|
77
|
+
if value.is_a?(Array)
|
78
|
+
new_default_opts[mode][key] = ((new_default_opts[mode][key] || []) + value).uniq
|
79
|
+
else
|
80
|
+
new_default_opts[mode][key] = value
|
81
|
+
end
|
82
|
+
end
|
83
|
+
new_opts.delete(mode)
|
84
|
+
end
|
85
|
+
|
86
|
+
# if mode was not provided via configuration file,
|
87
|
+
# this defaults to the opposite/non-provided mode,
|
88
|
+
# or uses the defaults set up in this file
|
89
|
+
new_opts.each_pair do |key, value|
|
90
|
+
if value.is_a?(Array)
|
91
|
+
new_default_opts[key] = ((new_default_opts[key] || []) + value).uniq
|
92
|
+
else
|
93
|
+
new_default_opts[key] = value
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
return new_default_opts
|
98
|
+
end
|
99
|
+
|
100
|
+
|
101
|
+
# def get_microtime
|
102
|
+
# t = Time.now
|
103
|
+
# sprintf("%d%03d", t.to_i, t.usec / 1000)
|
104
|
+
# end
|
105
|
+
|
106
|
+
# def encode_hop(data, key)
|
107
|
+
# Base64.encode64(OpenSSL::HMAC.digest(OpenSSL::Digest::SHA1.new, key, data)).chomp.gsub(/\n/,'')
|
108
|
+
# end
|
109
|
+
|
110
|
+
|
111
|
+
# def insert_signature3(amount="0.00",currency="usd",orderPage_transactionType='sale')
|
112
|
+
# profile_id = get_profile_id
|
113
|
+
# timestamp = get_microtime
|
114
|
+
# data = profile_id + amount + currency + timestamp + orderPage_transactionType
|
115
|
+
# pub = get_secret_key
|
116
|
+
# serial_number = get_serial_number
|
117
|
+
# pub_digest = encode_hop(data,pub)
|
118
|
+
|
119
|
+
# sig = "<input type='hidden' name='orderPage_transactionType' value='#{orderPage_transactionType}'>\n"
|
120
|
+
# sig << "<input type='hidden' name='amount' value='#{amount}'>\n"
|
121
|
+
# sig << "<input type='hidden' name='currency' value='#{currency}'>\n"
|
122
|
+
# sig << "<input type='hidden' name='orderPage_timestamp' value='#{timestamp}'>\n"
|
123
|
+
# sig << "<input type='hidden' name='merchantID' value='#{profile_id}'>\n"
|
124
|
+
# sig << "<input type='hidden' name='orderPage_signaturePublic' value='#{pub_digest}'>\n"
|
125
|
+
# sig << "<input type='hidden' name='orderPage_version' value='4'>\n"
|
126
|
+
# sig << "<input type='hidden' name='orderPage_serialNumber' value='#{serial_number}'>\n"
|
127
|
+
# sig
|
128
|
+
# end
|
129
|
+
|
130
|
+
|
131
|
+
|
132
|
+
# def insert_map_signature(assoc_array)
|
133
|
+
# assoc_array['mechantID'] = get_profile_id
|
134
|
+
# assoc_array['orderPage_timestamp'] = get_microtime
|
135
|
+
# assoc_array['orderPage_version'] = "4"
|
136
|
+
# assoc_array['orderPage_serialNumber'] = getSerialNumber
|
137
|
+
|
138
|
+
# fields = []
|
139
|
+
# values = ''
|
140
|
+
# inputs = ''
|
141
|
+
# assoc_array.each do |key,value|
|
142
|
+
# fields << key
|
143
|
+
# values << value
|
144
|
+
# inputs << '<input type="hidden" name="'+key+'" value="'+value+'">'+"\n"
|
145
|
+
# end
|
146
|
+
|
147
|
+
# pub = get_secret_key
|
148
|
+
# pub_digest = hop_hash(values,pub)
|
149
|
+
# inputs << '<input type="hidden" name="orderPage_signaturePublic" value="'+pub_digest+'">'+"\n"
|
150
|
+
# inputs << '<input type="hidden" name="orderPage_signedFields" value="'+fields+'">'+"\n"
|
151
|
+
# inputs
|
152
|
+
# end
|
153
|
+
|
154
|
+
# def insert_signature(amount="0.00",currency="usd")
|
155
|
+
# merchant_id = get_merchant_id
|
156
|
+
# timestamp = get_microtime
|
157
|
+
# data = merchant_id + amt + curr + timestamp
|
158
|
+
# serial_number = get_serial_number
|
159
|
+
# pub_digest = hop_hash(data, get_shared_secret)
|
160
|
+
|
161
|
+
# sig = "<input type='hidden' name='amount' value='#{amount}'>\n"
|
162
|
+
# sig << "<input type='hidden' name='currency' value='#{currency}'>\n"
|
163
|
+
# sig << "<input type='hidden' name='orderPage_timestamp' value='#{timestamp}'>\n"
|
164
|
+
# sig << "<input type='hidden' name='merchantID' value='#{merchant_id}'>\n"
|
165
|
+
# sig << "<input type='hidden' name='orderPage_signaturePublic' value='#{sig_hash}'>\n"
|
166
|
+
# sig << "<input type='hidden' name='orderPage_version' value='4'>\n"
|
167
|
+
# sig << "<input type='hidden' name='orderPage_serialNumber' value='#{serial_number}'>\n"
|
168
|
+
# sig
|
169
|
+
# end
|
170
|
+
|
171
|
+
|
172
|
+
# def insert_subscription_signature(subscription_amount="0.00",
|
173
|
+
# subscription_start_date="00000000",
|
174
|
+
# subscription_frequency=nil,
|
175
|
+
# subscription_number_of_payments="0",
|
176
|
+
# subscription_automatic_renew="true"
|
177
|
+
# )
|
178
|
+
# if subscription_frequency.nil? then return end
|
179
|
+
|
180
|
+
# data = subscription_amount + subscription_start_date + subscription_frequency + subscription_number_of_payments + subscription_automatic_renew
|
181
|
+
# pub = get_shared_secret
|
182
|
+
# pub_digest = hop_hash(data, pub)
|
183
|
+
# sign = '<input type="hidden" name="recurringSubscriptionInfo_amount" value="' + subscriptionAmount + '">' + "\n"
|
184
|
+
# sig << '<input type="hidden" name="recurringSubscriptionInfo_numberOfPayments" value="' + subscriptionNumberOfPayments + '">' + "\n"
|
185
|
+
# sig << '<input type="hidden" name="recurringSubscriptionInfo_frequency" value="' + subscriptionFrequency + '">' + "\n"
|
186
|
+
# sig << '<input type="hidden" name="recurringSubscriptionInfo_automaticRenew" value="' + subscriptionAutomaticRenew + '">' + "\n"
|
187
|
+
# sig << '<input type="hidden" name="recurringSubscriptionInfo_startDate" value="' + subscriptionStartDate + '">' + "\n"
|
188
|
+
# sig << '<input type="hidden" name="recurringSubscriptionInfo_signaturePublic" value="' + pub_digest + '">' + "\n"
|
189
|
+
# sig
|
190
|
+
# end
|
191
|
+
|
192
|
+
# def insert_subscription_id_signature(subscription_id)
|
193
|
+
# if subscription_id.nil? then return end
|
194
|
+
|
195
|
+
# pub_digest = hop_hash(subscription_id, get_shared_secret)
|
196
|
+
# str = '<input type="hidden" name="paySubscriptionCreateReply_subscriptionID" value="' + subscription_id + '">' + "\n"
|
197
|
+
# str << '<input type="hidden" name="paySubscriptionCreateReply_subscriptionIDPublicSignature" value="' + pub_digest + '">' + "\n"
|
198
|
+
# str
|
199
|
+
# end
|
200
|
+
|
201
|
+
|
202
|
+
end
|
203
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module SoPaid
|
2
|
+
module PaymentVendor
|
3
|
+
|
4
|
+
def vendor(name=nil)
|
5
|
+
if name.present?
|
6
|
+
self.vendors[name.to_s]
|
7
|
+
elsif self.vendors.size == 1
|
8
|
+
# {"cybersource"=>SoPaid::Cybersource}
|
9
|
+
self.vendors.first.last
|
10
|
+
elsif self.vendors.size > 1
|
11
|
+
raise "No vendor name specified when calling SoPaid.vendor(:vendor_name). vendor_name must be specified when multiple payment vendors are loaded."
|
12
|
+
else
|
13
|
+
raise "No vendors have been loaded. Please run: rails generate payme:install. This will generate your payme.rb initilizer where you can load payment vendors."
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
data/lib/so_paid.rb
ADDED
data/so_paid.gemspec
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'so_paid/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "so_paid"
|
8
|
+
spec.version = SoPaid::VERSION
|
9
|
+
spec.authors = ["mkralla11"]
|
10
|
+
spec.email = ["mike.mkrallaproductions@gmail.com"]
|
11
|
+
spec.description = %q{A no-nonsense, fully configurable Rails credit card/payment vendor gem.}
|
12
|
+
spec.summary = %q{There was a need to consolidate all credit card processing vendors for our web apps, but configuration needed to be at the forefront of the consolidated gem. So I built SoPaid which allows for easy extension, and a generous amount of options...and should work out of the box, if you're the plug-and-play type. Examine how the code is written to see how easy it is to extend and make your own payment_vendor class that inherits from Hop.}
|
13
|
+
spec.homepage = ""
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
22
|
+
spec.add_development_dependency "rake"
|
23
|
+
spec.add_development_dependency "ruby-hmac"
|
24
|
+
spec.add_development_dependency "rspec"
|
25
|
+
end
|
metadata
ADDED
@@ -0,0 +1,124 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: so_paid
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- mkralla11
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-06-05 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.3'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.3'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ! '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ! '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: ruby-hmac
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ! '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ! '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
description: A no-nonsense, fully configurable Rails credit card/payment vendor gem.
|
70
|
+
email:
|
71
|
+
- mike.mkrallaproductions@gmail.com
|
72
|
+
executables: []
|
73
|
+
extensions: []
|
74
|
+
extra_rdoc_files: []
|
75
|
+
files:
|
76
|
+
- .gitignore
|
77
|
+
- Gemfile
|
78
|
+
- LICENSE
|
79
|
+
- LICENSE.txt
|
80
|
+
- README.md
|
81
|
+
- Rakefile
|
82
|
+
- examples/.DS_Store
|
83
|
+
- examples/app/controllers/payments_controller.rb
|
84
|
+
- examples/app/views/confirm.html.erb
|
85
|
+
- examples/config/initializers/pay_me.rb
|
86
|
+
- examples/config/routes.rb
|
87
|
+
- lib/.DS_Store
|
88
|
+
- lib/so_paid.rb
|
89
|
+
- lib/so_paid/configuration.rb
|
90
|
+
- lib/so_paid/cybersource.rb
|
91
|
+
- lib/so_paid/hop.rb
|
92
|
+
- lib/so_paid/payment_vendor.rb
|
93
|
+
- lib/so_paid/version.rb
|
94
|
+
- so_paid.gemspec
|
95
|
+
homepage: ''
|
96
|
+
licenses:
|
97
|
+
- MIT
|
98
|
+
metadata: {}
|
99
|
+
post_install_message:
|
100
|
+
rdoc_options: []
|
101
|
+
require_paths:
|
102
|
+
- lib
|
103
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
104
|
+
requirements:
|
105
|
+
- - ! '>='
|
106
|
+
- !ruby/object:Gem::Version
|
107
|
+
version: '0'
|
108
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
109
|
+
requirements:
|
110
|
+
- - ! '>='
|
111
|
+
- !ruby/object:Gem::Version
|
112
|
+
version: '0'
|
113
|
+
requirements: []
|
114
|
+
rubyforge_project:
|
115
|
+
rubygems_version: 2.2.2
|
116
|
+
signing_key:
|
117
|
+
specification_version: 4
|
118
|
+
summary: There was a need to consolidate all credit card processing vendors for our
|
119
|
+
web apps, but configuration needed to be at the forefront of the consolidated gem.
|
120
|
+
So I built SoPaid which allows for easy extension, and a generous amount of options...and
|
121
|
+
should work out of the box, if you're the plug-and-play type. Examine how the code
|
122
|
+
is written to see how easy it is to extend and make your own payment_vendor class
|
123
|
+
that inherits from Hop.
|
124
|
+
test_files: []
|