veritrans 2.0.4 → 2.1.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/.gitignore +2 -1
- data/.travis.yml +11 -3
- data/CHANGELOG.md +15 -0
- data/Gemfile +10 -0
- data/Gemfile.lock +136 -97
- data/Procfile +1 -0
- data/README.md +12 -5
- data/Rakefile +7 -5
- data/api_reference.md +21 -1
- data/example/README.md +8 -0
- data/example/config.ru +6 -0
- data/example/index.erb +104 -9
- data/example/localization.erb +248 -0
- data/example/points.erb +187 -0
- data/example/recurring.erb +201 -0
- data/example/response.erb +10 -1
- data/example/sinatra.rb +120 -8
- data/example/style.css +83 -2
- data/example/veritrans.yml +0 -1
- data/example/widget.erb +52 -0
- data/lib/generators/templates/assets/credit_card_form.js +3 -2
- data/lib/generators/templates/payments_controller.rb +5 -0
- data/lib/generators/templates/veritrans.rb +20 -17
- data/lib/generators/veritrans/install_generator.rb +1 -1
- data/lib/generators/veritrans/payment_form_generator.rb +1 -1
- data/lib/veritrans.rb +73 -44
- data/lib/veritrans/api.rb +39 -5
- data/lib/veritrans/cli.rb +1 -1
- data/lib/veritrans/client.rb +15 -10
- data/lib/veritrans/config.rb +11 -4
- data/lib/veritrans/events.rb +4 -4
- data/lib/veritrans/result.rb +28 -7
- data/lib/veritrans/version.rb +2 -2
- data/spec/cli_spec.rb +1 -0
- data/spec/rails_plugin_spec.rb +72 -27
- data/spec/spec_helper.rb +1 -0
- data/spec/veritrans_client_spec.rb +57 -3
- data/spec/veritrans_config_spec.rb +1 -1
- data/spec/veritrans_events_spec.rb +2 -0
- data/spec/veritrans_snap_spec.rb +39 -0
- data/testing_webhooks.md +0 -2
- data/veritrans.gemspec +5 -5
- metadata +29 -15
data/lib/veritrans/config.rb
CHANGED
@@ -1,12 +1,14 @@
|
|
1
1
|
require 'yaml'
|
2
2
|
require 'excon'
|
3
3
|
|
4
|
-
|
4
|
+
class Veritrans
|
5
5
|
|
6
|
-
|
7
|
-
extend self
|
6
|
+
class Config
|
8
7
|
|
9
|
-
|
8
|
+
def initialize(options = nil)
|
9
|
+
@api_host = "https://api.sandbox.veritrans.co.id"
|
10
|
+
apply(options) if options
|
11
|
+
end
|
10
12
|
|
11
13
|
##
|
12
14
|
# Merhcant's Client key, used to make getToken request. (only for VT-Direct)
|
@@ -135,8 +137,13 @@ module Veritrans
|
|
135
137
|
|
136
138
|
private
|
137
139
|
|
140
|
+
AVAILABLE_KEYS = [:server_key, :client_key, :api_host, :http_options]
|
141
|
+
|
138
142
|
def apply(hash)
|
139
143
|
hash.each do |key, value|
|
144
|
+
unless AVAILABLE_KEYS.include?(key.to_s.to_sym)
|
145
|
+
raise ArgumentError, "Unknown option #{key.inspect}, available keys: #{AVAILABLE_KEYS.map(&:inspect).join(", ")}"
|
146
|
+
end
|
140
147
|
send(:"#{key}=", value)
|
141
148
|
end
|
142
149
|
end
|
data/lib/veritrans/events.rb
CHANGED
@@ -15,14 +15,14 @@
|
|
15
15
|
# All possible events:
|
16
16
|
#
|
17
17
|
# * payment.success == ['authorize', 'capture', 'settlement']
|
18
|
-
# * payment.failed == ['deny', '
|
19
|
-
# * payment.challenge # when payment.
|
18
|
+
# * payment.failed == ['deny', 'cancel', 'expire']
|
19
|
+
# * payment.challenge # when payment.fraud_status == 'challenge'
|
20
20
|
#
|
21
21
|
# * payment.authorize
|
22
22
|
# * payment.capture
|
23
23
|
# * payment.settlement
|
24
24
|
# * payment.deny
|
25
|
-
# * payment.
|
25
|
+
# * payment.cancel
|
26
26
|
# * payment.expire
|
27
27
|
#
|
28
28
|
# * error
|
@@ -32,7 +32,7 @@
|
|
32
32
|
# run Rack::URLMap.new("/" => MyApp.new, "/payment_events" => Veritrans::Events.new)
|
33
33
|
#
|
34
34
|
|
35
|
-
|
35
|
+
class Veritrans
|
36
36
|
class Events
|
37
37
|
|
38
38
|
# This is rack application
|
data/lib/veritrans/result.rb
CHANGED
@@ -1,15 +1,18 @@
|
|
1
|
-
|
1
|
+
class Veritrans
|
2
2
|
class Result
|
3
3
|
attr_reader :data
|
4
4
|
attr_reader :status
|
5
5
|
attr_reader :response
|
6
|
+
attr_reader :request_options
|
7
|
+
attr_reader :time
|
8
|
+
attr_reader :url
|
6
9
|
|
7
|
-
def initialize(response, url,
|
10
|
+
def initialize(response, url, request_options, time)
|
8
11
|
begin
|
9
12
|
if url =~ %r{/v2/.+/transcript$}
|
10
13
|
@data = {}
|
11
14
|
else
|
12
|
-
@data = Veritrans._json_decode(response.body)
|
15
|
+
@data = Veritrans::Client._json_decode(response.body)
|
13
16
|
|
14
17
|
# Failback for Hash#symbolize_keys
|
15
18
|
@data.keys.each do |key|
|
@@ -18,7 +21,7 @@ module Veritrans
|
|
18
21
|
end
|
19
22
|
|
20
23
|
rescue => e
|
21
|
-
Veritrans.logger.info "Error parsing
|
24
|
+
Veritrans.logger.info "Error parsing Veritrans response #{e.message}"
|
22
25
|
Veritrans.logger.info e.backtrace.join("\n")
|
23
26
|
@data = {}
|
24
27
|
end
|
@@ -27,7 +30,7 @@ module Veritrans
|
|
27
30
|
@status = response.status
|
28
31
|
@response = response
|
29
32
|
@url = url
|
30
|
-
@
|
33
|
+
@request_options = request_options
|
31
34
|
end
|
32
35
|
|
33
36
|
def success?
|
@@ -39,7 +42,7 @@ module Veritrans
|
|
39
42
|
@data[:status_code] == '201'
|
40
43
|
end
|
41
44
|
|
42
|
-
# Docs http://docs.veritrans.co.id/
|
45
|
+
# Docs http://docs.veritrans.co.id/en/api/status_code.html
|
43
46
|
def status_code
|
44
47
|
@data[:status_code].to_i
|
45
48
|
end
|
@@ -52,6 +55,10 @@ module Veritrans
|
|
52
55
|
@data[:redirect_url]
|
53
56
|
end
|
54
57
|
|
58
|
+
def transaction_id
|
59
|
+
@data[:transaction_id]
|
60
|
+
end
|
61
|
+
|
55
62
|
def messages
|
56
63
|
if @data[:message].present?
|
57
64
|
@data[:message].chomp(']').sub(/^\[/, '').split(',').map(&:strip)
|
@@ -75,7 +82,21 @@ module Veritrans
|
|
75
82
|
def inspect
|
76
83
|
time_ms = (@time * 1000).round
|
77
84
|
data = @data.inspect.gsub(/:([^\s]+)=>/, "\\1: ")
|
78
|
-
"
|
85
|
+
"#<#{self.class.to_s}:#{object_id} ^^ status: #{@status} time: #{time_ms}ms ^^ data: #{data}>"
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
class SnapResult < Result
|
90
|
+
def status_code
|
91
|
+
@response.status.to_i
|
92
|
+
end
|
93
|
+
|
94
|
+
def token_id
|
95
|
+
@data[:token_id]
|
96
|
+
end
|
97
|
+
|
98
|
+
def success?
|
99
|
+
status_code == 200
|
79
100
|
end
|
80
101
|
end
|
81
102
|
end
|
data/lib/veritrans/version.rb
CHANGED
@@ -1,3 +1,3 @@
|
|
1
|
-
|
2
|
-
VERSION = "2.0
|
1
|
+
class Veritrans
|
2
|
+
VERSION = "2.1.0"
|
3
3
|
end
|
data/spec/cli_spec.rb
CHANGED
data/spec/rails_plugin_spec.rb
CHANGED
@@ -6,11 +6,11 @@ require 'socket'
|
|
6
6
|
describe "Rails plugin", vcr: false do
|
7
7
|
include Capybara::DSL
|
8
8
|
|
9
|
-
MAIN_RAILS_VER = "
|
9
|
+
MAIN_RAILS_VER = "5.0.0.1"
|
10
10
|
APP_DIR = "plugin_test"
|
11
11
|
PLUGIN_DIR = File.expand_path("..", File.dirname(__FILE__))
|
12
12
|
|
13
|
-
RAILS_VERSIONS = ["4.0.13", "4.1.
|
13
|
+
RAILS_VERSIONS = ["4.0.13", "4.1.16", "4.2.7.1", "5.0.0.1"]
|
14
14
|
|
15
15
|
before :all do
|
16
16
|
FileUtils.mkdir_p("#{PLUGIN_DIR}/tmp")
|
@@ -70,7 +70,7 @@ describe "Rails plugin", vcr: false do
|
|
70
70
|
end
|
71
71
|
|
72
72
|
def generate_rails_app(rails_version)
|
73
|
-
gen = "rails _#{rails_version}_ new #{APP_DIR} -B -G --skip-spring -d sqlite3 --skip-turbolinks --skip-test-unit --no-rc"
|
73
|
+
gen = "rails _#{rails_version}_ new #{APP_DIR} -B -G --skip-spring -d sqlite3 --skip-turbolinks --skip-test-unit --skip-action-cable --no-rc --skip-puma --skip-listen"
|
74
74
|
run_cmd(gen)
|
75
75
|
|
76
76
|
gemfile_content = "
|
@@ -116,19 +116,27 @@ development:
|
|
116
116
|
end
|
117
117
|
end
|
118
118
|
|
119
|
-
Capybara.app_host = "http://
|
119
|
+
Capybara.app_host = "http://127.0.0.1:#{@rails_port}"
|
120
120
|
|
121
121
|
#puts "RAILS_DIR: #{@app_abs_path}"
|
122
122
|
|
123
|
-
|
123
|
+
failed = 0
|
124
|
+
while failed < 10
|
124
125
|
begin
|
125
126
|
run_cmd("curl #{Capybara.app_host}/payments/new > /dev/null")
|
126
127
|
break
|
127
128
|
rescue Object => error
|
129
|
+
failed += 1
|
128
130
|
p error
|
129
131
|
puts "Retry"
|
132
|
+
sleep 0.3
|
130
133
|
end
|
131
134
|
end
|
135
|
+
|
136
|
+
if failed == 10
|
137
|
+
puts `tail -100 #{@app_abs_path}/log/development.log`
|
138
|
+
raise Exception, "can not start rails server"
|
139
|
+
end
|
132
140
|
end
|
133
141
|
|
134
142
|
def stop_rails_app
|
@@ -152,39 +160,72 @@ development:
|
|
152
160
|
FileUtils.remove_entry_secure(@rails_dir) if @rails_dir
|
153
161
|
end
|
154
162
|
|
155
|
-
|
163
|
+
def submit_payment_form(card_number)
|
164
|
+
# CREATE PAYMENT 1
|
165
|
+
visit "/payments/new"
|
166
|
+
|
167
|
+
fill_in 'credit_card_number', with: card_number
|
168
|
+
|
169
|
+
click_button "Pay via VT-Direct"
|
170
|
+
puts "Clicked Pay"
|
171
|
+
|
172
|
+
# Waiting for get token request in javascript...
|
173
|
+
Timeout.timeout(60.seconds) do
|
174
|
+
loop do
|
175
|
+
#puts "Path: #{current_path}"
|
176
|
+
break if current_path != "/payments/new"
|
177
|
+
sleep 1
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
Timeout.timeout(10.seconds) do
|
182
|
+
loop do
|
183
|
+
break if page.body =~ /<body>/
|
184
|
+
sleep 0.1
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
RAILS_VERSIONS.each_with_index do |rails_version, spec_index|
|
190
|
+
next if rails_version.start_with?("5") && RUBY_VERSION < "2.2.2"
|
191
|
+
|
156
192
|
it "should tests plugin (Rails #{rails_version})" do
|
157
193
|
# PREPARE APP
|
158
194
|
install_rails_in_tmp(rails_version)
|
159
195
|
run_rails_app
|
160
196
|
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
197
|
+
card_numbers = [
|
198
|
+
"5481 1611 1111 1081",
|
199
|
+
"5410 1111 1111 1116",
|
200
|
+
"4011 1111 1111 1112",
|
201
|
+
"4411 1111 1111 1118",
|
202
|
+
"4811 1111 1111 1114",
|
203
|
+
"3528 6647 7942 9687",
|
204
|
+
"3528 2033 2456 4357"
|
205
|
+
]
|
206
|
+
spec_index = (spec_index + RUBY_VERSION.gsub(/[^\d]/, '').to_i) % card_numbers.size
|
207
|
+
card_number = card_numbers[spec_index]
|
165
208
|
|
166
|
-
|
167
|
-
Timeout.timeout(30.seconds) do
|
168
|
-
loop do
|
169
|
-
break if current_path != "/payments/new"
|
170
|
-
sleep 0.1
|
171
|
-
end
|
172
|
-
end
|
209
|
+
submit_payment_form(card_number)
|
173
210
|
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
211
|
+
if page.body =~ /too many transactions/
|
212
|
+
puts "!!!!"
|
213
|
+
puts "Merchant has sent too many transactions to the same card number"
|
214
|
+
puts "!!!!"
|
215
|
+
#page.should have_content("Merchant has sent too many transactions to the same card number")
|
216
|
+
puts "Wait 10 seconds and retry"
|
217
|
+
sleep 10
|
218
|
+
submit_payment_form(card_number)
|
179
219
|
end
|
180
220
|
|
181
|
-
if page.body
|
182
|
-
page.
|
183
|
-
else
|
184
|
-
page.should have_content("Success, Credit Card transaction is successful")
|
221
|
+
if page.body !~ /transaction is successful/
|
222
|
+
puts page.body
|
185
223
|
end
|
224
|
+
page.should have_content("Success, Credit Card transaction is successful")
|
186
225
|
|
187
|
-
|
226
|
+
order_info = ActiveSupport::JSON.decode(find("pre").text)
|
227
|
+
puts "Order Info: #{order_info}"
|
228
|
+
created_order_id = order_info["order_id"]
|
188
229
|
#Capybara::Screenshot.screenshot_and_open_image
|
189
230
|
|
190
231
|
# CREATE PAYMENT 2
|
@@ -208,6 +249,10 @@ development:
|
|
208
249
|
Veritrans::CLI.test_webhook(["#{Capybara.app_host}/payments/receive_webhook"])
|
209
250
|
end
|
210
251
|
|
252
|
+
if result2 !~ /status: 200/
|
253
|
+
puts `tail -40 #{@app_abs_path}/log/development.log`
|
254
|
+
end
|
255
|
+
|
211
256
|
result2.should =~ /status: 200/
|
212
257
|
result2.should =~ /body: ok/
|
213
258
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
describe Veritrans do
|
1
|
+
describe Veritrans::Client do
|
2
2
|
|
3
3
|
before do
|
4
4
|
hide_const("Rails")
|
@@ -21,11 +21,11 @@ describe Veritrans do
|
|
21
21
|
|
22
22
|
result = Veritrans.request_with_logging(:get, Veritrans.config.api_host + "/ping", {})
|
23
23
|
|
24
|
-
api_request.headers["Host"].should == "api.sandbox.veritrans.co.id"
|
24
|
+
api_request.headers["Host"].should == "api.sandbox.veritrans.co.id:443"
|
25
25
|
end
|
26
26
|
|
27
27
|
it "should use Veritrans.http_options to attach hedaers", vcr: false do
|
28
|
-
Veritrans
|
28
|
+
Veritrans.config.stub(:http_options) do
|
29
29
|
{ headers: { "X-Rspec" => "ok" } }
|
30
30
|
end
|
31
31
|
|
@@ -40,6 +40,47 @@ describe Veritrans do
|
|
40
40
|
api_request.headers["X-Rspec"].should == "ok"
|
41
41
|
end
|
42
42
|
|
43
|
+
it "should be able to create other instance of client" do
|
44
|
+
#Veritrans.logger = Logger.new(STDOUT)
|
45
|
+
|
46
|
+
VCR.configure do |c|
|
47
|
+
c.allow_http_connections_when_no_cassette = true
|
48
|
+
end
|
49
|
+
|
50
|
+
other_client = Veritrans.new(
|
51
|
+
server_key: "69b61a1b-b0b1-450b-a697-37109dbbecb0",
|
52
|
+
logger: Logger.new("/dev/null")
|
53
|
+
) # M000937
|
54
|
+
|
55
|
+
result = Veritrans.charge(
|
56
|
+
payment_type: "mandiri_clickpay",
|
57
|
+
transaction_details: {
|
58
|
+
gross_amount: 10_000,
|
59
|
+
order_id: Time.now.to_s
|
60
|
+
},
|
61
|
+
mandiri_clickpay: {
|
62
|
+
card_number: "4111 1111 1111 1111".gsub(/\s/, ''),
|
63
|
+
input3: "%05d" % (rand * 100000).to_i,
|
64
|
+
input2: 10000,
|
65
|
+
input1: "1" * 10,
|
66
|
+
token: "000000"
|
67
|
+
},
|
68
|
+
)
|
69
|
+
|
70
|
+
#p result.request_options
|
71
|
+
|
72
|
+
other_result = other_client.status(result.transaction_id)
|
73
|
+
|
74
|
+
other_result.status_code.should == 404
|
75
|
+
other_result.status_message.should == "The requested resource is not found"
|
76
|
+
|
77
|
+
#p other_result.request_options
|
78
|
+
|
79
|
+
VCR.configure do |c|
|
80
|
+
c.allow_http_connections_when_no_cassette = true
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
43
84
|
it "should send charge vt-web request" do
|
44
85
|
VCR.use_cassette('charge_vtweb') do
|
45
86
|
result = Veritrans.charge('vtweb', transaction: { order_id: Time.now.to_s, gross_amount: 100_000 } )
|
@@ -127,4 +168,17 @@ describe Veritrans do
|
|
127
168
|
result.status_message.should == "The requested resource is not found"
|
128
169
|
end
|
129
170
|
end
|
171
|
+
|
172
|
+
it "should handle network exceptions", vcr: false do
|
173
|
+
Excon::Connection.any_instance.stub(:send) do
|
174
|
+
raise Excon::Errors::SocketError, Excon::Errors::Error.new("testing exception")
|
175
|
+
end
|
176
|
+
|
177
|
+
result = Veritrans.expire("not-exists")
|
178
|
+
result.status.should == "500"
|
179
|
+
result.data.should == {
|
180
|
+
status_code: "500",
|
181
|
+
status_message: "Internal server error, no response from backend. Try again later"
|
182
|
+
}
|
183
|
+
end
|
130
184
|
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
describe Veritrans::Config do
|
2
|
+
|
3
|
+
before do
|
4
|
+
hide_const("Rails")
|
5
|
+
Veritrans.logger = Logger.new("/dev/null")
|
6
|
+
Veritrans.setup do
|
7
|
+
config.load_config "./spec/configs/real_key.yml"
|
8
|
+
end
|
9
|
+
|
10
|
+
VCR.configure do |c|
|
11
|
+
c.allow_http_connections_when_no_cassette = true
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
after do
|
16
|
+
VCR.configure do |c|
|
17
|
+
c.allow_http_connections_when_no_cassette = false
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def generate_order_id
|
22
|
+
"testing-#{rand.round(4)}-#{Time.now.to_i}"
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should create widget token" do
|
26
|
+
response = Veritrans.create_snap_token(
|
27
|
+
transaction_details: {
|
28
|
+
order_id: generate_order_id,
|
29
|
+
gross_amount: 30_000
|
30
|
+
}
|
31
|
+
)
|
32
|
+
|
33
|
+
response.should be_a(Veritrans::SnapResult)
|
34
|
+
response.success?.should be_truthy
|
35
|
+
response.token_id.should be_present
|
36
|
+
response.token_id.should == response.data[:token_id]
|
37
|
+
response.inspect.should =~ /#<Veritrans::SnapResult:\d+ \^\^ status: 200 time: \d+ms \^\^ data: \{token_id: "[\da-f\-]+"\}>/
|
38
|
+
end
|
39
|
+
end
|