paygate-ruby 0.1.8 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,133 +1,136 @@
1
- require 'base64'
2
-
3
- module Paygate
4
- class AesCtr
5
-
6
- # Encrypt a text using AES encryption in Counter mode of operation
7
- #
8
- # Unicode multi-byte character safe
9
- #
10
- # @param string plaintext Source text to be encrypted
11
- # @param string password The password to use to generate a key
12
- # @param int num_bits Number of bits to be used in the key (128, 192, or 256)
13
- # @returns string Encrypted text
14
- 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])
17
-
18
- # use AES itself to encrypt password to get cipher key (using plain password as source for key
19
- # 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
- 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
25
-
26
- # initialise 1st 8 bytes of counter block with nonce (NIST SP800-38A §B.2): [0-1] = millisec,
27
- # [2-3] = random, [4-7] = seconds, together giving full sub-millisec uniqueness up to Feb 2106
28
- counter_block = []
29
- nonce = Time.now.to_i
30
- nonce_ms = nonce % 1000
31
- 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
-
37
- # and convert it to a string to go on the front of the ciphertext
38
- ctr_text = ''
39
- 0.upto(7){ |i| ctr_text += counter_block[i].chr }
40
-
41
- # generate key schedule - an expansion of the key into distinct Key Rounds for each round
42
- key_schedule = Aes.key_expansion(key)
43
- block_count = (plaintext.length / block_size.to_f).ceil
44
-
45
- cipher_text = []
46
- 0.upto(block_count - 1) do |b|
47
- # set counter (block #) in last 8 bytes of counter block (leaving nonce in 1st 8 bytes)
48
- # 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) }
51
-
52
- cipher_cntr = Aes.cipher(counter_block, key_schedule) # -- encrypt counter block --
53
- # block size is reduced on final block
54
- block_length = b < block_count - 1 ? block_size : (plaintext.length - 1) % block_size + 1
55
- cipher_char = []
56
- 0.upto(block_length - 1) do |i|
57
- cipher_char[i] = (cipher_cntr[i] ^ plaintext.bytes.to_a[b * block_size + i]).chr
58
- end
59
- cipher_text[b] = cipher_char.join
60
- end
61
-
62
- cipher_text = ctr_text + cipher_text.join
63
- cipher_text = Base64.encode64(cipher_text).gsub(/\n/, '') + "\n"; # encode in base64
64
- end
65
-
66
- # Decrypt a text encrypted by AES in counter mode of operation
67
- #
68
- # @param string ciphertext Source text to be encrypted
69
- # @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)
71
- # @returns string
72
- # 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
83
- # 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]}
87
-
88
- #generate key Schedule
89
- keySchedule = Aes.key_expansion(key);
90
-
91
- # 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]}
95
-
96
- ciphertext = ct; # ciphertext is now array of block-length strings
97
-
98
- # 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 = []
105
- 0.upto(ciphertext[b].length - 1) do |i|
106
- # -- xor plaintxt with ciphered counter byte-by-byte --
107
- plaintxtByte[i] = (cipherCntr[i] ^ ciphertext[b].bytes.to_a[i]).chr;
108
- end
109
- plaintxt[b] = plaintxtByte.join('')
110
- end
111
- plaintext = plaintxt.join('')
112
- end
113
-
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
121
- def self.urs(a, b)
122
- a &= 0xffffffff
123
- b &= 0x1f
124
- if a & 0x80000000 && b > 0 # if left-most bit set
125
- a = ((a >> 1) & 0x7fffffff) # right-shift one bit & clear left-most bit
126
- a = a >> (b - 1) # remaining right-shifts
127
- else # otherwise
128
- a = (a >> b); # use normal right-shift
129
- end
130
- a
131
- end
132
- end
133
- end
1
+ # frozen_string_literal: true
2
+
3
+ require 'base64'
4
+
5
+ module Paygate
6
+ class AesCtr
7
+ # Encrypt a text using AES encryption in Counter mode of operation
8
+ #
9
+ # Unicode multi-byte character safe
10
+ #
11
+ # @param string plaintext Source text to be encrypted
12
+ # @param string password The password to use to generate a key
13
+ # @param int num_bits Number of bits to be used in the key (128, 192, or 256)
14
+ # @returns string Encrypted text
15
+ def self.encrypt(plaintext, password, num_bits)
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)
18
+
19
+ # use AES itself to encrypt password to get cipher key (using plain password as source for key
20
+ # expansion) - gives us well encrypted key (though hashed key might be preferred for prod'n use)
21
+ num_bytes = num_bits / 8 # no bytes in key (16/24/32)
22
+ pw_bytes = []
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
29
+
30
+ # initialise 1st 8 bytes of counter block with nonce (NIST SP800-38A §B.2): [0-1] = millisec,
31
+ # [2-3] = random, [4-7] = seconds, together giving full sub-millisec uniqueness up to Feb 2106
32
+ counter_block = []
33
+ nonce = Time.now.to_i
34
+ nonce_ms = nonce % 1000
35
+ nonce_sec = (nonce / 1000.0).floor
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 }
40
+
41
+ # and convert it to a string to go on the front of the ciphertext
42
+ ctr_text = ''
43
+ 0.upto(7) { |i| ctr_text += counter_block[i].chr }
44
+
45
+ # generate key schedule - an expansion of the key into distinct Key Rounds for each round
46
+ key_schedule = Aes.key_expansion(key)
47
+ block_count = (plaintext.length / block_size.to_f).ceil
48
+
49
+ cipher_text = []
50
+ 0.upto(block_count - 1) do |b|
51
+ # set counter (block #) in last 8 bytes of counter block (leaving nonce in 1st 8 bytes)
52
+ # done in two stages for 32-bit ops: using two words allows us to go past 2^32 blocks (68GB)
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) }
55
+
56
+ cipher_cntr = Aes.cipher(counter_block, key_schedule) # -- encrypt counter block --
57
+ # block size is reduced on final block
58
+ block_length = b < block_count - 1 ? block_size : ((plaintext.length - 1) % block_size) + 1
59
+ cipher_char = []
60
+ 0.upto(block_length - 1) do |i|
61
+ cipher_char[i] = (cipher_cntr[i] ^ plaintext.bytes.to_a[(b * block_size) + i]).chr
62
+ end
63
+ cipher_text[b] = cipher_char.join
64
+ end
65
+
66
+ cipher_text = ctr_text + cipher_text.join
67
+ "#{Base64.encode64(cipher_text).delete("\n")}\n" # encode in base64
68
+ end
69
+
70
+ # Decrypt a text encrypted by AES in counter mode of operation
71
+ #
72
+ # @param string ciphertext Source text to be encrypted
73
+ # @param string password The password to use to generate a key
74
+ # @param int n_bits Number of bits to be used in the key (128, 192, or 256)
75
+ # @returns string
76
+ # Decrypted text
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
88
+ # recover nonce from 1st 8 bytes of ciphertext
89
+ counter_block = []
90
+ ctr_txt = ciphertext[0, 8]
91
+ 0.upto(7) { |i| counter_block[i] = ctr_txt.bytes.to_a[i] }
92
+
93
+ # generate key Schedule
94
+ key_schedule = Aes.key_expansion(key)
95
+
96
+ # separate ciphertext into blocks (skipping past initial 8 bytes)
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] }
100
+
101
+ ciphertext = ct; # ciphertext is now array of block-length strings
102
+
103
+ # plaintext will get generated block-by-block into array of block-length strings
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 = []
110
+ 0.upto(ciphertext[b].length - 1) do |i|
111
+ # -- xor plaintxt with ciphered counter byte-by-byte --
112
+ plaintxt_byte[i] = (cipher_cntr[i] ^ ciphertext[b].bytes.to_a[i]).chr
113
+ end
114
+ plaintxt[b] = plaintxt_byte.join
115
+ end
116
+ plaintxt.join
117
+ end
118
+
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
124
+ def self.urs(a, b)
125
+ a &= 0xffffffff
126
+ b &= 0x1f
127
+ if (a & 0x80000000) && b.positive? # if left-most bit set
128
+ a = ((a >> 1) & 0x7fffffff) # right-shift one bit & clear left-most bit
129
+ a = a >> (b - 1) # remaining right-shifts
130
+ else # otherwise
131
+ a = (a >> b); # use normal right-shift
132
+ end
133
+ a
134
+ end
135
+ end
136
+ end
@@ -1,16 +1,20 @@
1
- module Paygate
2
- class Configuration
3
- MODES = %i(live sandbox).freeze
4
-
5
- attr_reader :mode
6
-
7
- def initialize
8
- @mode = :live
9
- end
10
-
11
- 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
- end
15
- end
16
- end
1
+ # frozen_string_literal: true
2
+
3
+ module Paygate
4
+ class Configuration
5
+ MODES = %i[live sandbox].freeze
6
+
7
+ attr_reader :mode
8
+
9
+ def initialize
10
+ @mode = :live
11
+ end
12
+
13
+ def mode=(value)
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
18
+ end
19
+ end
20
+ end
@@ -1,21 +1,24 @@
1
- module Paygate
2
- class Member
3
- attr_reader :mid, :secret
4
-
5
- def initialize(mid, secret)
6
- @mid, @secret = mid, secret
7
- end
8
-
9
- def refund_transaction(txn_id, options = {})
10
- txn = Transaction.new(txn_id)
11
- txn.member = self
12
- txn.refund(options)
13
- end
14
-
15
- def profile_pay(profile_no, currency, amount)
16
- profile = Profile.new(profile_no)
17
- profile.member = self
18
- profile.purchase(currency, amount)
19
- end
20
- end
21
- end
1
+ # frozen_string_literal: true
2
+
3
+ module Paygate
4
+ class Member
5
+ attr_reader :mid, :secret
6
+
7
+ def initialize(mid, secret)
8
+ @mid = mid
9
+ @secret = secret
10
+ end
11
+
12
+ def refund_transaction(txn_id, options = {})
13
+ txn = Transaction.new(txn_id)
14
+ txn.member = self
15
+ txn.refund(options)
16
+ end
17
+
18
+ def profile_pay(profile_no, currency, amount)
19
+ profile = Profile.new(profile_no)
20
+ profile.member = self
21
+ profile.purchase(currency, amount)
22
+ end
23
+ end
24
+ end
@@ -1,30 +1,33 @@
1
- require 'uri'
2
- require 'net/http'
3
-
4
- module Paygate
5
- class Profile
6
- PURCHASE_URL = 'https://service.paygate.net/INTL/pgtlProcess3.jsp'.freeze
7
-
8
- attr_reader :profile_no
9
- attr_accessor :member
10
-
11
- def initialize(profile_no)
12
- @profile_no = profile_no
13
- end
14
-
15
- def purchase(currency, amount)
16
- # Prepare params
17
- params = { profile_no: profile_no,
18
- mid: member.mid,
19
- goodcurrency: currency,
20
- unitprice: amount }.delete_if { |_, v| v.blank? }
21
-
22
- # Make request
23
- uri = URI(PURCHASE_URL)
24
- uri.query = ::URI.encode_www_form(params)
25
- response = ::Net::HTTP.get_response(uri)
26
-
27
- Response.build_from_net_http_response(:profile_pay, response)
28
- end
29
- end
30
- end
1
+ # frozen_string_literal: true
2
+
3
+ require 'uri'
4
+ require 'net/http'
5
+
6
+ module Paygate
7
+ class Profile
8
+ PURCHASE_URL = 'https://service.paygate.net/INTL/pgtlProcess3.jsp'
9
+
10
+ attr_reader :profile_no
11
+ attr_accessor :member
12
+
13
+ def initialize(profile_no)
14
+ @profile_no = profile_no
15
+ end
16
+
17
+ def purchase(currency, amount)
18
+ # Prepare params
19
+ params = { profile_no: profile_no,
20
+ mid: member.mid,
21
+ goodcurrency: currency,
22
+ unitprice: amount }
23
+ params.compact!
24
+
25
+ # Make request
26
+ uri = URI(PURCHASE_URL)
27
+ uri.query = ::URI.encode_www_form(params)
28
+ response = ::Net::HTTP.get_response(uri)
29
+
30
+ Response.build_from_net_http_response(:profile_pay, response)
31
+ end
32
+ end
33
+ end
@@ -1,25 +1,27 @@
1
- module Paygate
2
- class Response
3
- attr_accessor :transaction_type, :http_code, :message, :body, :raw_info, :json
4
-
5
- def self.build_from_net_http_response(txn_type, response)
6
- r = new
7
- r.transaction_type = txn_type
8
- r.http_code = response.code
9
- r.message = response.message
10
- r.body = response.body
11
-
12
- 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
21
- end
22
- r
23
- end
24
- end
25
- end
1
+ # frozen_string_literal: true
2
+
3
+ module Paygate
4
+ class Response
5
+ attr_accessor :transaction_type, :http_code, :message, :body, :raw_info, :json
6
+
7
+ def self.build_from_net_http_response(txn_type, response)
8
+ r = new
9
+ r.transaction_type = txn_type
10
+ r.http_code = response.code
11
+ r.message = response.message
12
+ r.body = response.body
13
+
14
+ case txn_type
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
23
+ end
24
+ r
25
+ end
26
+ end
27
+ end
@@ -1,58 +1,60 @@
1
- require 'digest'
2
- require 'uri'
3
- require 'net/http'
4
-
5
- module Paygate
6
- class Transaction
7
- FULL_AMOUNT_IDENTIFIER = 'F'.freeze
8
-
9
- attr_reader :tid
10
- attr_accessor :member
11
-
12
- def initialize(tid)
13
- @tid = tid
14
- end
15
-
16
- def refund(options = {})
17
- # Encrypt data
18
- api_key_256 = ::Digest::SHA256.hexdigest(member.secret)
19
- aes_ctr = AesCtr.encrypt(tid, api_key_256, 256)
20
- tid_enc = "AES256#{aes_ctr}"
21
-
22
- # Prepare params
23
- params = { callback: 'callback', mid: member.mid, tid: tid_enc }
24
- params.merge!(options.slice(:amount))
25
- params[:amount] ||= FULL_AMOUNT_IDENTIFIER
26
- params[:mb_serial_no] = options[:order_id]
27
- params.delete_if { |_, v| v.blank? }
28
-
29
- # Make request
30
- uri = URI(self.class.refund_api_url)
31
- uri.query = ::URI.encode_www_form(params)
32
- response = ::Net::HTTP.get_response(uri)
33
-
34
- 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)
36
- r
37
- end
38
-
39
- # Doc: https://km.paygate.net/pages/viewpage.action?pageId=9207875
40
- def verify
41
- params = { tid: tid, verifyNum: 100 }
42
-
43
- uri = URI('https://service.paygate.net/djemals/settle/verifyReceived.jsp')
44
- uri.query = ::URI.encode_www_form(params)
45
- response = ::Net::HTTP.get_response(uri)
46
-
47
- Response.build_from_net_http_response(:verify, response)
48
- end
49
-
50
- private
51
-
52
- def self.refund_api_url
53
- (Paygate.configuration.mode == :live) ?
54
- 'https://service.paygate.net/service/cancelAPI.json' :
55
- 'https://stgsvc.paygate.net/service/cancelAPI.json'
56
- end
57
- end
58
- end
1
+ # frozen_string_literal: true
2
+
3
+ require 'digest'
4
+ require 'uri'
5
+ require 'net/http'
6
+
7
+ module Paygate
8
+ class Transaction
9
+ FULL_AMOUNT_IDENTIFIER = 'F'
10
+
11
+ attr_reader :tid
12
+ attr_accessor :member
13
+
14
+ def initialize(tid)
15
+ @tid = tid
16
+ end
17
+
18
+ def refund(options = {})
19
+ # Encrypt data
20
+ api_key256 = ::Digest::SHA256.hexdigest(member.secret)
21
+ aes_ctr = AesCtr.encrypt(tid, api_key256, 256)
22
+ tid_enc = "AES256#{aes_ctr}"
23
+
24
+ # Prepare params
25
+ params = { callback: 'callback', mid: member.mid, tid: tid_enc }
26
+ params.merge!(options.slice(:amount))
27
+ params[:amount] ||= FULL_AMOUNT_IDENTIFIER
28
+ params[:mb_serial_no] = options[:order_id]
29
+ params.compact!
30
+
31
+ # Make request
32
+ uri = URI(self.class.refund_api_url)
33
+ uri.query = ::URI.encode_www_form(params)
34
+ response = ::Net::HTTP.get_response(uri)
35
+
36
+ r = Response.build_from_net_http_response(:refund, response)
37
+ r.raw_info = OpenStruct.new(tid: tid, tid_enc: tid_enc, request_url: uri.to_s) # rubocop:disable Style/OpenStructUse
38
+ r
39
+ end
40
+
41
+ # Doc: https://km.paygate.net/pages/viewpage.action?pageId=9207875
42
+ def verify
43
+ params = { tid: tid, verifyNum: 100 }
44
+
45
+ uri = URI('https://service.paygate.net/djemals/settle/verifyReceived.jsp')
46
+ uri.query = ::URI.encode_www_form(params)
47
+ response = ::Net::HTTP.get_response(uri)
48
+
49
+ Response.build_from_net_http_response(:verify, response)
50
+ end
51
+
52
+ def self.refund_api_url
53
+ if Paygate.configuration.mode == :live
54
+ 'https://service.paygate.net/service/cancelAPI.json'
55
+ else
56
+ 'https://stgsvc.paygate.net/service/cancelAPI.json'
57
+ end
58
+ end
59
+ end
60
+ end
@@ -1,3 +1,5 @@
1
- module Paygate
2
- VERSION = '0.1.8'
3
- end
1
+ # frozen_string_literal: true
2
+
3
+ module Paygate
4
+ VERSION = '0.2.0'
5
+ end