paygate-ruby 0.1.11 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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,22 +1,24 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Paygate
2
4
  class Aes
3
5
  # Pre-computed multiplicative inverse in GF(2^8)
4
- S_BOX = [0x63,0x7c,0x77,0x7b,0xf2,0x6b,0x6f,0xc5,0x30,0x01,0x67,0x2b,0xfe,0xd7,0xab,0x76,
5
- 0xca,0x82,0xc9,0x7d,0xfa,0x59,0x47,0xf0,0xad,0xd4,0xa2,0xaf,0x9c,0xa4,0x72,0xc0,
6
- 0xb7,0xfd,0x93,0x26,0x36,0x3f,0xf7,0xcc,0x34,0xa5,0xe5,0xf1,0x71,0xd8,0x31,0x15,
7
- 0x04,0xc7,0x23,0xc3,0x18,0x96,0x05,0x9a,0x07,0x12,0x80,0xe2,0xeb,0x27,0xb2,0x75,
8
- 0x09,0x83,0x2c,0x1a,0x1b,0x6e,0x5a,0xa0,0x52,0x3b,0xd6,0xb3,0x29,0xe3,0x2f,0x84,
9
- 0x53,0xd1,0x00,0xed,0x20,0xfc,0xb1,0x5b,0x6a,0xcb,0xbe,0x39,0x4a,0x4c,0x58,0xcf,
10
- 0xd0,0xef,0xaa,0xfb,0x43,0x4d,0x33,0x85,0x45,0xf9,0x02,0x7f,0x50,0x3c,0x9f,0xa8,
11
- 0x51,0xa3,0x40,0x8f,0x92,0x9d,0x38,0xf5,0xbc,0xb6,0xda,0x21,0x10,0xff,0xf3,0xd2,
12
- 0xcd,0x0c,0x13,0xec,0x5f,0x97,0x44,0x17,0xc4,0xa7,0x7e,0x3d,0x64,0x5d,0x19,0x73,
13
- 0x60,0x81,0x4f,0xdc,0x22,0x2a,0x90,0x88,0x46,0xee,0xb8,0x14,0xde,0x5e,0x0b,0xdb,
14
- 0xe0,0x32,0x3a,0x0a,0x49,0x06,0x24,0x5c,0xc2,0xd3,0xac,0x62,0x91,0x95,0xe4,0x79,
15
- 0xe7,0xc8,0x37,0x6d,0x8d,0xd5,0x4e,0xa9,0x6c,0x56,0xf4,0xea,0x65,0x7a,0xae,0x08,
16
- 0xba,0x78,0x25,0x2e,0x1c,0xa6,0xb4,0xc6,0xe8,0xdd,0x74,0x1f,0x4b,0xbd,0x8b,0x8a,
17
- 0x70,0x3e,0xb5,0x66,0x48,0x03,0xf6,0x0e,0x61,0x35,0x57,0xb9,0x86,0xc1,0x1d,0x9e,
18
- 0xe1,0xf8,0x98,0x11,0x69,0xd9,0x8e,0x94,0x9b,0x1e,0x87,0xe9,0xce,0x55,0x28,0xdf,
19
- 0x8c,0xa1,0x89,0x0d,0xbf,0xe6,0x42,0x68,0x41,0x99,0x2d,0x0f,0xb0,0x54,0xbb,0x16].freeze
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
20
22
 
21
23
  # Round Constant used for the Key Expansion [1st col is 2^(r-1) in GF(2^8)]
22
24
  R_CON = [[0x00, 0x00, 0x00, 0x00],
@@ -29,10 +31,10 @@ module Paygate
29
31
  [0x40, 0x00, 0x00, 0x00],
30
32
  [0x80, 0x00, 0x00, 0x00],
31
33
  [0x1b, 0x00, 0x00, 0x00],
32
- [0x36, 0x00, 0x00, 0x00] ].freeze
34
+ [0x36, 0x00, 0x00, 0x00]].freeze
33
35
 
34
36
  # Block size (in words): no of columns in state (fixed for AES)
35
- CIPHER_BLOCK_SIZE = 4.freeze
37
+ CIPHER_BLOCK_SIZE = 4
36
38
 
37
39
  # AES Cipher function: encrypt 'input' state with Rijndael algorithm
38
40
  # applies Nr rounds (10/12/14) using key schedule w for 'add round key' stage
@@ -40,8 +42,8 @@ module Paygate
40
42
  # @param int[][] w Key schedule as 2D byte-array (Nr+1 x Nb bytes)
41
43
  # @returns int[] Encrypted output state array
42
44
  def self.cipher(input, w)
43
- nr = w.length / CIPHER_BLOCK_SIZE - 1 # no of rounds: 10/12/14 for 128/192/256-bit keys
44
- state = [[],[],[],[]] # initialize 4x4 byte-array 'state' with input
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
45
47
 
46
48
  (0...(4 * CIPHER_BLOCK_SIZE)).each do |i|
47
49
  state[i % 4][(i / 4.0).floor] = input[i]
@@ -74,17 +76,17 @@ module Paygate
74
76
 
75
77
  w = []
76
78
  temp = []
77
- 0.upto(nk - 1){ |i| w[i] = [key[4 * i], key[4 * i + 1], key[4 * i + 2], key[4 * i + 3]] }
79
+ 0.upto(nk - 1) { |i| w[i] = [key[4 * i], key[(4 * i) + 1], key[(4 * i) + 2], key[(4 * i) + 3]] }
78
80
  (nk...(CIPHER_BLOCK_SIZE * (nr + 1))).each do |i|
79
81
  w[i] = []
80
- 0.upto(3){ |t| temp[t] = w[i - 1][t] }
81
- if i % nk == 0
82
+ 0.upto(3) { |t| temp[t] = w[i - 1][t] }
83
+ if (i % nk).zero?
82
84
  temp = sub_word(rotate_word(temp))
83
- 0.upto(3){ |t| temp[t] ^= R_CON[i / nk][t]}
85
+ 0.upto(3) { |t| temp[t] ^= R_CON[i / nk][t] }
84
86
  elsif nk > 6 && i % nk == 4
85
87
  temp = sub_word(temp)
86
88
  end
87
- 0.upto(3){ |t| w[i][t] = w[i - nk][t] ^ temp[t] }
89
+ 0.upto(3) { |t| w[i][t] = w[i - nk][t] ^ temp[t] }
88
90
  end
89
91
  w
90
92
  end
@@ -92,7 +94,7 @@ module Paygate
92
94
  # apply SBox to state
93
95
  def self.sub_bytes(state)
94
96
  0.upto(3) do |r|
95
- 0.upto(CIPHER_BLOCK_SIZE - 1){ |c| state[r][c] = S_BOX[state[r][c]] }
97
+ 0.upto(CIPHER_BLOCK_SIZE - 1) { |c| state[r][c] = S_BOX[state[r][c]] }
96
98
  end
97
99
  state
98
100
  end
@@ -101,20 +103,20 @@ module Paygate
101
103
  def self.shift_rows(state)
102
104
  t = []
103
105
  1.upto(3) do |r|
104
- 0.upto(3){ |c| t[c] = state[r][(c + r) % CIPHER_BLOCK_SIZE] } # shift into temp copy
105
- 0.upto(3){ |c| state[r][c] = t[c] } # and copy back
106
- end # note that this will work for nb = 4,5,6, but not 7,8 (always 4 for AES):
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
107
109
  state # see asmaes.sourceforge.net/rijndael/rijndaelImplementation.pdf
108
110
  end
109
111
 
110
112
  # combine bytes of each col of state S
111
113
  def self.mix_columns(state)
112
114
  0.upto(3) do |c|
113
- a=[] # 'a' is a copy of the current column from 'state'
114
- b=[] # 'b' is a•{02} in GF(2^8)
115
+ a = [] # 'a' is a copy of the current column from 'state'
116
+ b = [] # 'b' is a•{02} in GF(2^8)
115
117
  0.upto(3) do |i|
116
118
  a[i] = state[i][c]
117
- b[i] = (state[i][c] & 0x80 == 0) ? (state[i][c] << 1) ^ 0x011b : state[i][c] << 1
119
+ b[i] = (state[i][c] & 0x80).zero? ? (state[i][c] << 1) ^ 0x011b : state[i][c] << 1
118
120
  end
119
121
  # a[n] ^ b[n] is a•{03} in GF(2^8)
120
122
  state[0][c] = b[0] ^ a[1] ^ b[1] ^ a[2] ^ a[3] # 2*a0 + 3*a1 + a2 + a3
@@ -128,21 +130,21 @@ module Paygate
128
130
  # xor Round Key into state S
129
131
  def self.add_round_key(state, w, rnd)
130
132
  0.upto(3) do |r|
131
- 0.upto(CIPHER_BLOCK_SIZE - 1){ |c| state[r][c] ^= w[rnd * 4 + c][r]}
133
+ 0.upto(CIPHER_BLOCK_SIZE - 1) { |c| state[r][c] ^= w[(rnd * 4) + c][r] }
132
134
  end
133
135
  state
134
136
  end
135
137
 
136
138
  # apply SBox to 4-byte word w
137
139
  def self.sub_word(word)
138
- 0.upto(3){ |i| word[i] = S_BOX[word[i]] }
140
+ 0.upto(3) { |i| word[i] = S_BOX[word[i]] }
139
141
  word
140
142
  end
141
143
 
142
144
  # rotate 4-byte word w left by one byte
143
145
  def self.rotate_word(word)
144
146
  tmp = word[0]
145
- 0.upto(2){ |i| word[i] = word[i + 1] }
147
+ 0.upto(2) { |i| word[i] = word[i + 1] }
146
148
  word[3] = tmp
147
149
  word
148
150
  end
@@ -1,8 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'base64'
2
4
 
3
5
  module Paygate
4
6
  class AesCtr
5
-
6
7
  # Encrypt a text using AES encryption in Counter mode of operation
7
8
  #
8
9
  # Unicode multi-byte character safe
@@ -12,16 +13,19 @@ module Paygate
12
13
  # @param int num_bits Number of bits to be used in the key (128, 192, or 256)
13
14
  # @returns string Encrypted text
14
15
  def self.encrypt(plaintext, password, num_bits)
15
- block_size = 16 # block size fixed at 16 bytes / 128 bits (Nb=4) for AES
16
- return '' unless num_bits.in?([128, 192, 256])
16
+ block_size = 16 # block size fixed at 16 bytes / 128 bits (Nb=4) for AES
17
+ return '' unless [128, 192, 256].include?(num_bits)
17
18
 
18
19
  # use AES itself to encrypt password to get cipher key (using plain password as source for key
19
20
  # expansion) - gives us well encrypted key (though hashed key might be preferred for prod'n use)
20
- num_bytes = num_bits / 8 # no bytes in key (16/24/32)
21
+ num_bytes = num_bits / 8 # no bytes in key (16/24/32)
21
22
  pw_bytes = []
22
- 0.upto(num_bytes - 1){ |i| pw_bytes[i] = password.bytes.to_a[i] & 0xff || 0} # use 1st 16/24/32 chars of password for key #warn
23
- key = Aes.cipher(pw_bytes, Aes.key_expansion(pw_bytes)) # gives us 16-byte key
24
- key = key + key[0, num_bytes - 16] # expand key to 16/24/32 bytes long
23
+ # use 1st 16/24/32 chars of password for key #warn
24
+ 0.upto(num_bytes - 1) do |i|
25
+ pw_bytes[i] = (password.bytes.to_a[i] & 0xff) || 0
26
+ end
27
+ key = Aes.cipher(pw_bytes, Aes.key_expansion(pw_bytes)) # gives us 16-byte key
28
+ key += key[0, num_bytes - 16] # expand key to 16/24/32 bytes long
25
29
 
26
30
  # initialise 1st 8 bytes of counter block with nonce (NIST SP800-38A §B.2): [0-1] = millisec,
27
31
  # [2-3] = random, [4-7] = seconds, together giving full sub-millisec uniqueness up to Feb 2106
@@ -29,14 +33,14 @@ module Paygate
29
33
  nonce = Time.now.to_i
30
34
  nonce_ms = nonce % 1000
31
35
  nonce_sec = (nonce / 1000.0).floor
32
- nonce_rand = (rand() * 0xffff).floor
33
- 0.upto(1){ |i| counter_block[i] = urs(nonce_ms, i * 8) & 0xff }
34
- 0.upto(1){ |i| counter_block[i + 2] = urs(nonce_rand, i * 8) & 0xff }
35
- 0.upto(3){ |i| counter_block[i + 4] = urs(nonce_sec, i * 8) & 0xff }
36
+ nonce_rand = (rand * 0xffff).floor
37
+ 0.upto(1) { |i| counter_block[i] = urs(nonce_ms, i * 8) & 0xff }
38
+ 0.upto(1) { |i| counter_block[i + 2] = urs(nonce_rand, i * 8) & 0xff }
39
+ 0.upto(3) { |i| counter_block[i + 4] = urs(nonce_sec, i * 8) & 0xff }
36
40
 
37
41
  # and convert it to a string to go on the front of the ciphertext
38
42
  ctr_text = ''
39
- 0.upto(7){ |i| ctr_text += counter_block[i].chr }
43
+ 0.upto(7) { |i| ctr_text += counter_block[i].chr }
40
44
 
41
45
  # generate key schedule - an expansion of the key into distinct Key Rounds for each round
42
46
  key_schedule = Aes.key_expansion(key)
@@ -46,85 +50,84 @@ module Paygate
46
50
  0.upto(block_count - 1) do |b|
47
51
  # set counter (block #) in last 8 bytes of counter block (leaving nonce in 1st 8 bytes)
48
52
  # done in two stages for 32-bit ops: using two words allows us to go past 2^32 blocks (68GB)
49
- 0.upto(3){ |c| counter_block[15 - c] = urs(b, c * 8) & 0xff }
50
- 0.upto(3){ |c| counter_block[15 - c - 4] = urs(b / 0x100000000, c * 8) }
53
+ 0.upto(3) { |c| counter_block[15 - c] = urs(b, c * 8) & 0xff }
54
+ 0.upto(3) { |c| counter_block[15 - c - 4] = urs(b / 0x100000000, c * 8) }
51
55
 
52
56
  cipher_cntr = Aes.cipher(counter_block, key_schedule) # -- encrypt counter block --
53
57
  # block size is reduced on final block
54
- block_length = b < block_count - 1 ? block_size : (plaintext.length - 1) % block_size + 1
58
+ block_length = b < block_count - 1 ? block_size : ((plaintext.length - 1) % block_size) + 1
55
59
  cipher_char = []
56
60
  0.upto(block_length - 1) do |i|
57
- cipher_char[i] = (cipher_cntr[i] ^ plaintext.bytes.to_a[b * block_size + i]).chr
61
+ cipher_char[i] = (cipher_cntr[i] ^ plaintext.bytes.to_a[(b * block_size) + i]).chr
58
62
  end
59
63
  cipher_text[b] = cipher_char.join
60
64
  end
61
65
 
62
66
  cipher_text = ctr_text + cipher_text.join
63
- cipher_text = Base64.encode64(cipher_text).gsub(/\n/, '') + "\n"; # encode in base64
67
+ "#{Base64.encode64(cipher_text).delete("\n")}\n" # encode in base64
64
68
  end
65
69
 
66
70
  # Decrypt a text encrypted by AES in counter mode of operation
67
71
  #
68
72
  # @param string ciphertext Source text to be encrypted
69
73
  # @param string password The password to use to generate a key
70
- # @param int nBits Number of bits to be used in the key (128, 192, or 256)
74
+ # @param int n_bits Number of bits to be used in the key (128, 192, or 256)
71
75
  # @returns string
72
76
  # Decrypted text
73
- def self.decrypt(ciphertext, password, nBits)
74
- blockSize = 16 # block size fixed at 16 bytes / 128 bits (Nb=4) for AES
75
- return '' unless(nBits==128 || nBits==192 || nBits==256)
76
- ciphertext = Base64.decode64(ciphertext);
77
-
78
- nBytes = nBits/8 # no bytes in key (16/24/32)
79
- pwBytes = []
80
- 0.upto(nBytes-1){|i| pwBytes[i] = password.bytes.to_a[i] & 0xff || 0}
81
- key = Aes.cipher(pwBytes, Aes.key_expansion(pwBytes)) # gives us 16-byte key
82
- key = key.concat(key.slice(0, nBytes-16)) # expand key to 16/24/32 bytes long
77
+ def self.decrypt(ciphertext, password, n_bits)
78
+ block_size = 16 # block size fixed at 16 bytes / 128 bits (Nb=4) for AES
79
+ return '' unless [128, 192, 256].include?(n_bits)
80
+
81
+ ciphertext = Base64.decode64(ciphertext)
82
+
83
+ n_bytes = n_bits / 8 # no bytes in key (16/24/32)
84
+ pw_bytes = []
85
+ 0.upto(n_bytes - 1) { |i| pw_bytes[i] = (password.bytes.to_a[i] & 0xff) || 0 }
86
+ key = Aes.cipher(pw_bytes, Aes.key_expansion(pw_bytes)) # gives us 16-byte key
87
+ key.concat(key.slice(0, n_bytes - 16)) # expand key to 16/24/32 bytes long
83
88
  # recover nonce from 1st 8 bytes of ciphertext
84
- counterBlock = []
85
- ctrTxt = ciphertext[0,8]
86
- 0.upto(7){|i| counterBlock[i] = ctrTxt.bytes.to_a[i]}
89
+ counter_block = []
90
+ ctr_txt = ciphertext[0, 8]
91
+ 0.upto(7) { |i| counter_block[i] = ctr_txt.bytes.to_a[i] }
87
92
 
88
- #generate key Schedule
89
- keySchedule = Aes.key_expansion(key);
93
+ # generate key Schedule
94
+ key_schedule = Aes.key_expansion(key)
90
95
 
91
96
  # separate ciphertext into blocks (skipping past initial 8 bytes)
92
- nBlocks = ((ciphertext.length-8)/blockSize.to_f).ceil
93
- ct=[]
94
- 0.upto(nBlocks-1){|b|ct[b] = ciphertext[8+b*blockSize, 16]}
97
+ n_blocks = ((ciphertext.length - 8) / block_size.to_f).ceil
98
+ ct = []
99
+ 0.upto(n_blocks - 1) { |b| ct[b] = ciphertext[8 + (b * block_size), 16] }
95
100
 
96
- ciphertext = ct; # ciphertext is now array of block-length strings
101
+ ciphertext = ct; # ciphertext is now array of block-length strings
97
102
 
98
103
  # plaintext will get generated block-by-block into array of block-length strings
99
- plaintxt = [];
100
- 0.upto(nBlocks-1) do |b|
101
- 0.upto(3){|c| counterBlock[15-c] = urs(b,c*8) & 0xff}
102
- 0.upto(3){|c| counterBlock[15-c-4] = urs((b+1)/(0x100000000-1),c*8) & 0xff}
103
- cipherCntr = Aes.cipher(counterBlock, keySchedule) # encrypt counter block
104
- plaintxtByte = []
104
+ plaintxt = []
105
+ 0.upto(n_blocks - 1) do |b|
106
+ 0.upto(3) { |c| counter_block[15 - c] = urs(b, c * 8) & 0xff }
107
+ 0.upto(3) { |c| counter_block[15 - c - 4] = urs((b + 1) / (0x100000000 - 1), c * 8) & 0xff }
108
+ cipher_cntr = Aes.cipher(counter_block, key_schedule) # encrypt counter block
109
+ plaintxt_byte = []
105
110
  0.upto(ciphertext[b].length - 1) do |i|
106
111
  # -- xor plaintxt with ciphered counter byte-by-byte --
107
- plaintxtByte[i] = (cipherCntr[i] ^ ciphertext[b].bytes.to_a[i]).chr;
112
+ plaintxt_byte[i] = (cipher_cntr[i] ^ ciphertext[b].bytes.to_a[i]).chr
108
113
  end
109
- plaintxt[b] = plaintxtByte.join('')
114
+ plaintxt[b] = plaintxt_byte.join
110
115
  end
111
- plaintext = plaintxt.join('')
116
+ plaintxt.join
112
117
  end
113
118
 
114
- private
115
-
116
- # Unsigned right shift function, since Ruby has neither >>> operator nor unsigned ints
117
- #
118
- # @param a number to be shifted (32-bit integer)
119
- # @param b number of bits to shift a to the right (0..31)
120
- # @return a right-shifted and zero-filled by b bits
119
+ # Unsigned right shift function, since Ruby has neither >>> operator nor unsigned ints
120
+ #
121
+ # @param a number to be shifted (32-bit integer)
122
+ # @param b number of bits to shift a to the right (0..31)
123
+ # @return a right-shifted and zero-filled by b bits
121
124
  def self.urs(a, b)
122
125
  a &= 0xffffffff
123
126
  b &= 0x1f
124
- if a & 0x80000000 && b > 0 # if left-most bit set
127
+ if (a & 0x80000000) && b.positive? # if left-most bit set
125
128
  a = ((a >> 1) & 0x7fffffff) # right-shift one bit & clear left-most bit
126
129
  a = a >> (b - 1) # remaining right-shifts
127
- else # otherwise
130
+ else # otherwise
128
131
  a = (a >> b); # use normal right-shift
129
132
  end
130
133
  a
@@ -1,6 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Paygate
2
4
  class Configuration
3
- MODES = %i(live sandbox).freeze
5
+ MODES = %i[live sandbox].freeze
4
6
 
5
7
  attr_reader :mode
6
8
 
@@ -9,8 +11,10 @@ module Paygate
9
11
  end
10
12
 
11
13
  def mode=(value)
12
- fail 'Invalid mode. Value must be one of the following: :live, :sandbox' unless value && MODES.include?(value.to_sym)
13
- @mode = value.to_sym
14
+ value = value.to_sym
15
+ raise 'Invalid mode. Value must be one of the following: :live, :sandbox' unless value && MODES.include?(value)
16
+
17
+ @mode = value
14
18
  end
15
19
  end
16
20
  end
@@ -1,9 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Paygate
2
4
  class Member
3
5
  attr_reader :mid, :secret
4
6
 
5
7
  def initialize(mid, secret)
6
- @mid, @secret = mid, secret
8
+ @mid = mid
9
+ @secret = secret
7
10
  end
8
11
 
9
12
  def refund_transaction(txn_id, options = {})
@@ -1,9 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'uri'
2
4
  require 'net/http'
3
5
 
4
6
  module Paygate
5
7
  class Profile
6
- PURCHASE_URL = 'https://service.paygate.net/INTL/pgtlProcess3.jsp'.freeze
8
+ PURCHASE_URL = 'https://service.paygate.net/INTL/pgtlProcess3.jsp'
7
9
 
8
10
  attr_reader :profile_no
9
11
  attr_accessor :member
@@ -17,7 +19,8 @@ module Paygate
17
19
  params = { profile_no: profile_no,
18
20
  mid: member.mid,
19
21
  goodcurrency: currency,
20
- unitprice: amount }.delete_if { |_, v| v.blank? }
22
+ unitprice: amount }
23
+ params.compact!
21
24
 
22
25
  # Make request
23
26
  uri = URI(PURCHASE_URL)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Paygate
2
4
  class Response
3
5
  attr_accessor :transaction_type, :http_code, :message, :body, :raw_info, :json
@@ -10,14 +12,14 @@ module Paygate
10
12
  r.body = response.body
11
13
 
12
14
  case txn_type
13
- when :refund
14
- r.json = JSON.parse response.body.gsub(/^callback\((.*)\)$/, '\1') if response.code.to_i == 200
15
- when :profile_pay
16
- r.json = {}
17
- response.body.split('&').each do |key_value_pair|
18
- key_value_ary = key_value_pair.split('=')
19
- r.json[key_value_ary[0]] = key_value_ary[1]
20
- end
15
+ when :refund
16
+ r.json = JSON.parse response.body.gsub(/^callback\((.*)\)$/, '\1') if response.code.to_i == 200
17
+ when :profile_pay
18
+ r.json = {}
19
+ response.body.split('&').each do |key_value_pair|
20
+ key_value_ary = key_value_pair.split('=')
21
+ r.json[key_value_ary[0]] = key_value_ary[1]
22
+ end
21
23
  end
22
24
  r
23
25
  end
@@ -1,10 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'digest'
2
4
  require 'uri'
3
5
  require 'net/http'
4
6
 
5
7
  module Paygate
6
8
  class Transaction
7
- FULL_AMOUNT_IDENTIFIER = 'F'.freeze
9
+ FULL_AMOUNT_IDENTIFIER = 'F'
8
10
 
9
11
  attr_reader :tid
10
12
  attr_accessor :member
@@ -15,8 +17,8 @@ module Paygate
15
17
 
16
18
  def refund(options = {})
17
19
  # Encrypt data
18
- api_key_256 = ::Digest::SHA256.hexdigest(member.secret)
19
- aes_ctr = AesCtr.encrypt(tid, api_key_256, 256)
20
+ api_key256 = ::Digest::SHA256.hexdigest(member.secret)
21
+ aes_ctr = AesCtr.encrypt(tid, api_key256, 256)
20
22
  tid_enc = "AES256#{aes_ctr}"
21
23
 
22
24
  # Prepare params
@@ -24,7 +26,7 @@ module Paygate
24
26
  params.merge!(options.slice(:amount))
25
27
  params[:amount] ||= FULL_AMOUNT_IDENTIFIER
26
28
  params[:mb_serial_no] = options[:order_id]
27
- params.delete_if { |_, v| v.blank? }
29
+ params.compact!
28
30
 
29
31
  # Make request
30
32
  uri = URI(self.class.refund_api_url)
@@ -32,7 +34,7 @@ module Paygate
32
34
  response = ::Net::HTTP.get_response(uri)
33
35
 
34
36
  r = Response.build_from_net_http_response(:refund, response)
35
- r.raw_info = OpenStruct.new(tid: tid, tid_enc: tid_enc, request_url: uri.to_s)
37
+ r.raw_info = OpenStruct.new(tid: tid, tid_enc: tid_enc, request_url: uri.to_s) # rubocop:disable Style/OpenStructUse
36
38
  r
37
39
  end
38
40
 
@@ -47,12 +49,12 @@ module Paygate
47
49
  Response.build_from_net_http_response(:verify, response)
48
50
  end
49
51
 
50
- private
51
-
52
52
  def self.refund_api_url
53
- (Paygate.configuration.mode == :live) ?
54
- 'https://service.paygate.net/service/cancelAPI.json' :
53
+ if Paygate.configuration.mode == :live
54
+ 'https://service.paygate.net/service/cancelAPI.json'
55
+ else
55
56
  'https://stgsvc.paygate.net/service/cancelAPI.json'
57
+ end
56
58
  end
57
59
  end
58
60
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Paygate
2
- VERSION = '0.1.11'
4
+ VERSION = '0.2.0'
3
5
  end