paygate-ruby 0.1.8 → 0.2.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +53 -38
- data/LICENSE.txt +21 -21
- data/README.md +327 -343
- data/Rakefile +3 -2
- data/data/cdbn.20230401.unl.xlsx +0 -0
- data/data/config.yml +3347 -2744
- data/lib/paygate/action_view/form_helper.rb +159 -0
- data/lib/paygate/aes.rb +152 -150
- data/lib/paygate/aes_ctr.rb +136 -133
- data/lib/paygate/configuration.rb +20 -16
- data/lib/paygate/member.rb +24 -21
- data/lib/paygate/profile.rb +33 -30
- data/lib/paygate/response.rb +27 -25
- data/lib/paygate/transaction.rb +60 -58
- data/lib/paygate/version.rb +5 -3
- data/lib/paygate-ruby.rb +3 -48
- data/lib/paygate.rb +43 -0
- data/vendor/assets/javascripts/paygate.js +86 -86
- metadata +17 -49
- data/.gitignore +0 -9
- data/CODE_OF_CONDUCT.md +0 -74
- data/Gemfile +0 -4
- data/bin/console +0 -14
- data/bin/setup +0 -8
- data/data/card_bin_20191001.xlsx +0 -0
- data/lib/paygate/helpers/form_helper.rb +0 -155
- data/paygate-ruby.gemspec +0 -24
@@ -0,0 +1,159 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Paygate
|
4
|
+
module ActionView
|
5
|
+
module FormHelper
|
6
|
+
PAYGATE_FORM_TEXT_FIELDS = {
|
7
|
+
mid: {
|
8
|
+
placeholder: 'Member ID'
|
9
|
+
},
|
10
|
+
|
11
|
+
locale: {
|
12
|
+
name: 'langcode',
|
13
|
+
default: 'KR',
|
14
|
+
placeholder: 'Language'
|
15
|
+
},
|
16
|
+
|
17
|
+
charset: {
|
18
|
+
default: 'UTF-8',
|
19
|
+
placeholder: 'Charset'
|
20
|
+
},
|
21
|
+
|
22
|
+
title: {
|
23
|
+
name: 'goodname',
|
24
|
+
placeholder: 'Title'
|
25
|
+
},
|
26
|
+
|
27
|
+
currency: {
|
28
|
+
name: 'goodcurrency',
|
29
|
+
default: 'WON',
|
30
|
+
placeholder: 'Currency'
|
31
|
+
},
|
32
|
+
|
33
|
+
amount: {
|
34
|
+
name: 'unitprice',
|
35
|
+
placeholder: 'Amount'
|
36
|
+
},
|
37
|
+
|
38
|
+
meta1: {
|
39
|
+
name: 'goodoption1',
|
40
|
+
placeholder: 'Good Option 1'
|
41
|
+
},
|
42
|
+
|
43
|
+
meta2: {
|
44
|
+
name: 'goodoption2',
|
45
|
+
placeholder: 'Good Option 2'
|
46
|
+
},
|
47
|
+
|
48
|
+
meta3: {
|
49
|
+
name: 'goodoption3',
|
50
|
+
placeholder: 'Good Option 3'
|
51
|
+
},
|
52
|
+
|
53
|
+
meta4: {
|
54
|
+
name: 'goodoption4',
|
55
|
+
placeholder: 'Good Option 4'
|
56
|
+
},
|
57
|
+
|
58
|
+
meta5: {
|
59
|
+
name: 'goodoption5',
|
60
|
+
placeholder: 'Good Option 5'
|
61
|
+
},
|
62
|
+
|
63
|
+
pay_method: {
|
64
|
+
name: 'paymethod',
|
65
|
+
default: 'card',
|
66
|
+
placeholder: 'Pay Method'
|
67
|
+
},
|
68
|
+
|
69
|
+
customer_name: {
|
70
|
+
name: 'receipttoname',
|
71
|
+
placeholder: 'Customer Name'
|
72
|
+
},
|
73
|
+
|
74
|
+
customer_email: {
|
75
|
+
name: 'receipttoemail',
|
76
|
+
placeholder: 'Customer Email'
|
77
|
+
},
|
78
|
+
|
79
|
+
card_number: {
|
80
|
+
name: 'cardnumber',
|
81
|
+
placeholder: 'Card Number'
|
82
|
+
},
|
83
|
+
|
84
|
+
expiry_year: {
|
85
|
+
name: 'cardexpireyear',
|
86
|
+
placeholder: 'Expiry Year'
|
87
|
+
},
|
88
|
+
|
89
|
+
expiry_month: {
|
90
|
+
name: 'cardexpiremonth',
|
91
|
+
placeholder: 'Expiry Month'
|
92
|
+
},
|
93
|
+
|
94
|
+
cvv: {
|
95
|
+
name: 'cardsecretnumber',
|
96
|
+
placeholder: 'CVV'
|
97
|
+
},
|
98
|
+
|
99
|
+
card_auth_code: {
|
100
|
+
name: 'cardauthcode',
|
101
|
+
placeholder: 'Card Auth Code'
|
102
|
+
},
|
103
|
+
|
104
|
+
response_code: {
|
105
|
+
name: 'replycode',
|
106
|
+
placeholder: 'Response Code'
|
107
|
+
},
|
108
|
+
|
109
|
+
response_message: {
|
110
|
+
name: 'replyMsg',
|
111
|
+
placeholder: 'Response Message'
|
112
|
+
},
|
113
|
+
|
114
|
+
tid: {
|
115
|
+
placeholder: 'TID'
|
116
|
+
},
|
117
|
+
|
118
|
+
profile_no: {
|
119
|
+
placeholder: 'Profile No'
|
120
|
+
},
|
121
|
+
|
122
|
+
hash_result: {
|
123
|
+
name: 'hashresult',
|
124
|
+
placeholder: 'Hash Result'
|
125
|
+
}
|
126
|
+
}.freeze
|
127
|
+
|
128
|
+
def paygate_open_pay_api_js_url
|
129
|
+
if Paygate.configuration.mode == :live
|
130
|
+
'https://api.paygate.net/ajax/common/OpenPayAPI.js'
|
131
|
+
else
|
132
|
+
'https://stgapi.paygate.net/ajax/common/OpenPayAPI.js'
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def paygate_open_pay_api_form(options = {})
|
137
|
+
form_tag({}, name: 'PGIOForm') do
|
138
|
+
fields = []
|
139
|
+
|
140
|
+
PAYGATE_FORM_TEXT_FIELDS.each do |key, opts|
|
141
|
+
arg_opts = options[key] || {}
|
142
|
+
fields << text_field_tag(
|
143
|
+
key,
|
144
|
+
arg_opts[:value] || opts[:default],
|
145
|
+
name: opts[:name] || key.to_s,
|
146
|
+
placeholder: arg_opts[:placeholder] || opts[:placeholder]
|
147
|
+
).html_safe
|
148
|
+
end
|
149
|
+
|
150
|
+
fields.join.html_safe
|
151
|
+
end.html_safe
|
152
|
+
end
|
153
|
+
|
154
|
+
def paygate_open_pay_api_screen
|
155
|
+
content_tag(:div, nil, id: 'PGIOscreen')
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
data/lib/paygate/aes.rb
CHANGED
@@ -1,150 +1,152 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
[
|
26
|
-
[
|
27
|
-
[
|
28
|
-
[
|
29
|
-
[
|
30
|
-
[
|
31
|
-
[
|
32
|
-
[
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
#
|
40
|
-
#
|
41
|
-
# @
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
state =
|
55
|
-
state =
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
state =
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
output
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
#
|
70
|
-
#
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
temp
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
state[
|
123
|
-
state[
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
word[
|
147
|
-
word
|
148
|
-
|
149
|
-
|
150
|
-
end
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Paygate
|
4
|
+
class Aes
|
5
|
+
# Pre-computed multiplicative inverse in GF(2^8)
|
6
|
+
S_BOX = [0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
|
7
|
+
0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
|
8
|
+
0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
|
9
|
+
0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
|
10
|
+
0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
|
11
|
+
0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
|
12
|
+
0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
|
13
|
+
0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
|
14
|
+
0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
|
15
|
+
0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
|
16
|
+
0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
|
17
|
+
0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
|
18
|
+
0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
|
19
|
+
0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
|
20
|
+
0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
|
21
|
+
0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16].freeze
|
22
|
+
|
23
|
+
# Round Constant used for the Key Expansion [1st col is 2^(r-1) in GF(2^8)]
|
24
|
+
R_CON = [[0x00, 0x00, 0x00, 0x00],
|
25
|
+
[0x01, 0x00, 0x00, 0x00],
|
26
|
+
[0x02, 0x00, 0x00, 0x00],
|
27
|
+
[0x04, 0x00, 0x00, 0x00],
|
28
|
+
[0x08, 0x00, 0x00, 0x00],
|
29
|
+
[0x10, 0x00, 0x00, 0x00],
|
30
|
+
[0x20, 0x00, 0x00, 0x00],
|
31
|
+
[0x40, 0x00, 0x00, 0x00],
|
32
|
+
[0x80, 0x00, 0x00, 0x00],
|
33
|
+
[0x1b, 0x00, 0x00, 0x00],
|
34
|
+
[0x36, 0x00, 0x00, 0x00]].freeze
|
35
|
+
|
36
|
+
# Block size (in words): no of columns in state (fixed for AES)
|
37
|
+
CIPHER_BLOCK_SIZE = 4
|
38
|
+
|
39
|
+
# AES Cipher function: encrypt 'input' state with Rijndael algorithm
|
40
|
+
# applies Nr rounds (10/12/14) using key schedule w for 'add round key' stage
|
41
|
+
# @param int[] input 16-byte (128-bit) input state array
|
42
|
+
# @param int[][] w Key schedule as 2D byte-array (Nr+1 x Nb bytes)
|
43
|
+
# @returns int[] Encrypted output state array
|
44
|
+
def self.cipher(input, w)
|
45
|
+
nr = (w.length / CIPHER_BLOCK_SIZE) - 1 # no of rounds: 10/12/14 for 128/192/256-bit keys
|
46
|
+
state = [[], [], [], []] # initialize 4x4 byte-array 'state' with input
|
47
|
+
|
48
|
+
(0...(4 * CIPHER_BLOCK_SIZE)).each do |i|
|
49
|
+
state[i % 4][(i / 4.0).floor] = input[i]
|
50
|
+
end
|
51
|
+
|
52
|
+
state = add_round_key(state, w, 0)
|
53
|
+
(1...nr).each do |round|
|
54
|
+
state = sub_bytes(state)
|
55
|
+
state = shift_rows(state)
|
56
|
+
state = mix_columns(state)
|
57
|
+
state = add_round_key(state, w, round)
|
58
|
+
end
|
59
|
+
|
60
|
+
state = sub_bytes(state)
|
61
|
+
state = shift_rows(state)
|
62
|
+
state = add_round_key(state, w, nr)
|
63
|
+
|
64
|
+
output = []
|
65
|
+
(0...(4 * CIPHER_BLOCK_SIZE)).each { |i| output[i] = state[i % 4][(i / 4.0).floor] }
|
66
|
+
output
|
67
|
+
end
|
68
|
+
|
69
|
+
# Perform Key Expansion to generate a Key Schedule
|
70
|
+
#
|
71
|
+
# @param int[] key Key as 16/24/32-byte array
|
72
|
+
# @returns int[][] Expanded key schedule as 2D byte-array (Nr+1 x Nb bytes)
|
73
|
+
def self.key_expansion(key)
|
74
|
+
nk = key.length / 4 # key length (in words): 4/6/8 for 128/192/256-bit keys
|
75
|
+
nr = nk + 6 # no of rounds: 10/12/14 for 128/192/256-bit keys
|
76
|
+
|
77
|
+
w = []
|
78
|
+
temp = []
|
79
|
+
0.upto(nk - 1) { |i| w[i] = [key[4 * i], key[(4 * i) + 1], key[(4 * i) + 2], key[(4 * i) + 3]] }
|
80
|
+
(nk...(CIPHER_BLOCK_SIZE * (nr + 1))).each do |i|
|
81
|
+
w[i] = []
|
82
|
+
0.upto(3) { |t| temp[t] = w[i - 1][t] }
|
83
|
+
if (i % nk).zero?
|
84
|
+
temp = sub_word(rotate_word(temp))
|
85
|
+
0.upto(3) { |t| temp[t] ^= R_CON[i / nk][t] }
|
86
|
+
elsif nk > 6 && i % nk == 4
|
87
|
+
temp = sub_word(temp)
|
88
|
+
end
|
89
|
+
0.upto(3) { |t| w[i][t] = w[i - nk][t] ^ temp[t] }
|
90
|
+
end
|
91
|
+
w
|
92
|
+
end
|
93
|
+
|
94
|
+
# apply SBox to state
|
95
|
+
def self.sub_bytes(state)
|
96
|
+
0.upto(3) do |r|
|
97
|
+
0.upto(CIPHER_BLOCK_SIZE - 1) { |c| state[r][c] = S_BOX[state[r][c]] }
|
98
|
+
end
|
99
|
+
state
|
100
|
+
end
|
101
|
+
|
102
|
+
# shift row r of state S left by r bytes
|
103
|
+
def self.shift_rows(state)
|
104
|
+
t = []
|
105
|
+
1.upto(3) do |r|
|
106
|
+
0.upto(3) { |c| t[c] = state[r][(c + r) % CIPHER_BLOCK_SIZE] } # shift into temp copy
|
107
|
+
0.upto(3) { |c| state[r][c] = t[c] } # and copy back
|
108
|
+
end
|
109
|
+
state # see asmaes.sourceforge.net/rijndael/rijndaelImplementation.pdf
|
110
|
+
end
|
111
|
+
|
112
|
+
# combine bytes of each col of state S
|
113
|
+
def self.mix_columns(state)
|
114
|
+
0.upto(3) do |c|
|
115
|
+
a = [] # 'a' is a copy of the current column from 'state'
|
116
|
+
b = [] # 'b' is a•{02} in GF(2^8)
|
117
|
+
0.upto(3) do |i|
|
118
|
+
a[i] = state[i][c]
|
119
|
+
b[i] = (state[i][c] & 0x80).zero? ? (state[i][c] << 1) ^ 0x011b : state[i][c] << 1
|
120
|
+
end
|
121
|
+
# a[n] ^ b[n] is a•{03} in GF(2^8)
|
122
|
+
state[0][c] = b[0] ^ a[1] ^ b[1] ^ a[2] ^ a[3] # 2*a0 + 3*a1 + a2 + a3
|
123
|
+
state[1][c] = a[0] ^ b[1] ^ a[2] ^ b[2] ^ a[3] # a0 * 2*a1 + 3*a2 + a3
|
124
|
+
state[2][c] = a[0] ^ a[1] ^ b[2] ^ a[3] ^ b[3] # a0 + a1 + 2*a2 + 3*a3
|
125
|
+
state[3][c] = a[0] ^ b[0] ^ a[1] ^ a[2] ^ b[3] # 3*a0 + a1 + a2 + 2*a3
|
126
|
+
end
|
127
|
+
state
|
128
|
+
end
|
129
|
+
|
130
|
+
# xor Round Key into state S
|
131
|
+
def self.add_round_key(state, w, rnd)
|
132
|
+
0.upto(3) do |r|
|
133
|
+
0.upto(CIPHER_BLOCK_SIZE - 1) { |c| state[r][c] ^= w[(rnd * 4) + c][r] }
|
134
|
+
end
|
135
|
+
state
|
136
|
+
end
|
137
|
+
|
138
|
+
# apply SBox to 4-byte word w
|
139
|
+
def self.sub_word(word)
|
140
|
+
0.upto(3) { |i| word[i] = S_BOX[word[i]] }
|
141
|
+
word
|
142
|
+
end
|
143
|
+
|
144
|
+
# rotate 4-byte word w left by one byte
|
145
|
+
def self.rotate_word(word)
|
146
|
+
tmp = word[0]
|
147
|
+
0.upto(2) { |i| word[i] = word[i + 1] }
|
148
|
+
word[3] = tmp
|
149
|
+
word
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|