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/example/veritrans.yml
CHANGED
data/example/widget.erb
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
<!doctype html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<title>veritrans-ruby demo</title>
|
5
|
+
<link rel="icon" type="image/x-icon" href="https://account.veritrans.co.id/favicon.ico" />
|
6
|
+
<link rel="stylesheet" href="/style.css">
|
7
|
+
</head>
|
8
|
+
<body>
|
9
|
+
|
10
|
+
<header>
|
11
|
+
<h3>
|
12
|
+
<a href="/">Veritrans sinatra app</a>
|
13
|
+
</h3>
|
14
|
+
</header>
|
15
|
+
|
16
|
+
<section>
|
17
|
+
<h4>Veritrans Widget</h4>
|
18
|
+
|
19
|
+
<section>
|
20
|
+
<p>
|
21
|
+
<label>Token ID</label>
|
22
|
+
<input type="text" id="token_id" value="<%= @token_id %>" readonly>
|
23
|
+
</p>
|
24
|
+
<button id="snap_pay">Pay Now</button>
|
25
|
+
</section>
|
26
|
+
</section>
|
27
|
+
|
28
|
+
<script src="https://app.sandbox.veritrans.co.id/snap/snap.js"></script>
|
29
|
+
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
|
30
|
+
|
31
|
+
<script type="text/javascript">
|
32
|
+
$('#snap_pay').on('click', function (e) {
|
33
|
+
e.preventDefault();
|
34
|
+
snap.pay($('#token_id').val() + "?locale=en", {
|
35
|
+
env: 'sandbox',
|
36
|
+
onSuccess: function (res) {
|
37
|
+
console.log('onSuccess', res);
|
38
|
+
alert(res.status_message + "\nRedirecting to confirm page...");
|
39
|
+
window.location = "/widget/confirm/" + res.transaction_id;
|
40
|
+
},
|
41
|
+
onPending: function (res) {
|
42
|
+
console.log('onPending', res);
|
43
|
+
},
|
44
|
+
onError: function (res) {
|
45
|
+
console.log('onError', res);
|
46
|
+
}
|
47
|
+
});
|
48
|
+
});
|
49
|
+
</script>
|
50
|
+
|
51
|
+
</body>
|
52
|
+
</html>
|
@@ -25,8 +25,9 @@ $(document).ready(function () {
|
|
25
25
|
button.val("Processing ...");
|
26
26
|
|
27
27
|
Veritrans.token(VT_createTokenData, function (data) {
|
28
|
-
//
|
29
|
-
//
|
28
|
+
//console.log('Get token response:');
|
29
|
+
//console.log(JSON.stringify(VT_createTokenData()));
|
30
|
+
//console.log(JSON.stringify(data));
|
30
31
|
|
31
32
|
// if we get redirect_url then it's 3d-secure transaction
|
32
33
|
// so we need to open that page
|
@@ -5,6 +5,7 @@ class PaymentsController < ApplicationController
|
|
5
5
|
@payment = make_payment
|
6
6
|
end
|
7
7
|
|
8
|
+
# Creating example payment
|
8
9
|
def create
|
9
10
|
@payment = make_payment
|
10
11
|
|
@@ -30,6 +31,9 @@ class PaymentsController < ApplicationController
|
|
30
31
|
)
|
31
32
|
end
|
32
33
|
|
34
|
+
# Processing HTTP Notification from Veritrans
|
35
|
+
# POST request with JSON-encoded body
|
36
|
+
# If you using Veritrans::Events then you don't need this
|
33
37
|
def receive_webhook
|
34
38
|
post_body = request.body.read
|
35
39
|
|
@@ -61,6 +65,7 @@ class PaymentsController < ApplicationController
|
|
61
65
|
|
62
66
|
end
|
63
67
|
|
68
|
+
# You should replace this with your own model
|
64
69
|
private
|
65
70
|
def make_payment
|
66
71
|
@paymentKlass = Struct.new("Payment", :amount, :token_id, :order_id, :credit_card_secure) do
|
@@ -8,35 +8,38 @@ Veritrans.setup do
|
|
8
8
|
|
9
9
|
# Veritrans::Events is rack application to handle http notifications from Veritrans
|
10
10
|
# To enable it, add in config/routes.rb
|
11
|
-
#
|
12
|
-
|
11
|
+
#
|
12
|
+
# mount Veritrans::Events.new => '/vt_events'
|
13
|
+
#
|
13
14
|
# All possible events:
|
14
15
|
#
|
15
|
-
#
|
16
|
-
#
|
17
|
-
#
|
16
|
+
# 'payment.success' == ['authorize', 'capture', 'settlement']
|
17
|
+
# 'payment.failed' == ['deny', 'cancel', 'expire']
|
18
|
+
# 'payment.challenge' # when payment.fraud_status == 'challenge'
|
18
19
|
#
|
19
|
-
#
|
20
|
-
#
|
21
|
-
#
|
22
|
-
#
|
23
|
-
#
|
24
|
-
#
|
20
|
+
# 'payment.authorize'
|
21
|
+
# 'payment.capture'
|
22
|
+
# 'payment.settlement'
|
23
|
+
# 'payment.deny'
|
24
|
+
# 'payment.cancel'
|
25
|
+
# 'payment.expire'
|
25
26
|
|
26
27
|
# events.subscribe 'payment.success' do |payment|
|
27
|
-
# payment
|
28
|
+
# payment is instance of Veritrans::Result
|
29
|
+
# puts "Payment #{payment.data[:order_id]} is successful"
|
28
30
|
# end
|
29
|
-
#
|
31
|
+
#
|
30
32
|
# events.subscribe 'payment.failed' do |payment|
|
31
|
-
# payment.
|
33
|
+
# puts "Payment #{payment.data[:order_id]} is failed"
|
32
34
|
# end
|
33
|
-
#
|
35
|
+
#
|
34
36
|
# events.subscribe 'payment.challenge' do |payment|
|
37
|
+
# puts "Payment #{payment.data[:order_id]} chellenged by fraud system"
|
35
38
|
# payment.mark_challenge!
|
36
39
|
# end
|
37
|
-
#
|
40
|
+
#
|
38
41
|
# events.subscribe /.+/ do |payment, event_name|
|
39
|
-
#
|
42
|
+
# puts "Payment #{payment.data[:order_id]} has status #{payment.data[:transaction_status]}"
|
40
43
|
# p payment
|
41
44
|
# end
|
42
45
|
|
data/lib/veritrans.rb
CHANGED
@@ -9,67 +9,96 @@ if defined?(::Rails)
|
|
9
9
|
require 'veritrans/events'
|
10
10
|
end
|
11
11
|
|
12
|
-
|
13
|
-
|
14
|
-
|
12
|
+
class Veritrans
|
13
|
+
include Veritrans::Client
|
14
|
+
include Veritrans::Api
|
15
15
|
|
16
16
|
class << self
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
17
|
+
extend Forwardable
|
18
|
+
|
19
|
+
def_delegators :instance, :logger, :logger=, :config, :setup, :file_logger, :file_logger=
|
20
|
+
def_delegators :instance, :request_with_logging, :basic_auth_header, :get, :post, :delete, :make_request
|
21
|
+
def_delegators :instance, :charge, :cancel, :approve, :status, :capture, :expire
|
22
|
+
def_delegators :instance, :create_vtlink, :delete_vtlink, :inquiry_points, :create_widget_token, :create_snap_token
|
23
|
+
|
24
|
+
def events
|
25
|
+
Veritrans::Events if defined?(Veritrans::Events)
|
23
26
|
end
|
24
27
|
|
25
|
-
|
28
|
+
def decode_notification_json(input)
|
29
|
+
return Veritrans::Client._json_decode(input)
|
30
|
+
end
|
26
31
|
|
27
|
-
|
28
|
-
|
29
|
-
# for non-rails apps it's logging to stdout
|
30
|
-
def logger
|
31
|
-
return @logger if @logger
|
32
|
-
if defined?(Rails)
|
33
|
-
Rails.logger
|
34
|
-
else
|
35
|
-
unless @log
|
36
|
-
require 'logger'
|
37
|
-
@log = Logger.new(STDOUT)
|
38
|
-
@log.level = Logger::INFO
|
39
|
-
end
|
40
|
-
@log
|
41
|
-
end
|
32
|
+
def instance
|
33
|
+
@instance ||= new
|
42
34
|
end
|
43
35
|
|
44
|
-
|
45
|
-
|
36
|
+
end
|
37
|
+
|
38
|
+
def initialize(options = nil)
|
39
|
+
if options && options[:logger]
|
40
|
+
self.logger = options.delete(:logger)
|
41
|
+
options.delete("logger")
|
46
42
|
end
|
47
43
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
if defined?(Rails) && Rails.root
|
53
|
-
@file_logger = Logger.new(Rails.root.join("log/veritrans.log").to_s)
|
54
|
-
else
|
55
|
-
@file_logger = Logger.new("/dev/null")
|
56
|
-
end
|
57
|
-
end
|
44
|
+
if options && options[:file_logger]
|
45
|
+
self.file_logger = options.delete(:file_logger)
|
46
|
+
options.delete("file_logger")
|
47
|
+
end
|
58
48
|
|
59
|
-
|
49
|
+
if options
|
50
|
+
@config = Veritrans::Config.new(options)
|
60
51
|
end
|
52
|
+
end
|
61
53
|
|
62
|
-
|
63
|
-
|
54
|
+
def config(&block)
|
55
|
+
if block
|
56
|
+
instance_eval(&block)
|
57
|
+
else
|
58
|
+
@config ||= Veritrans::Config.new
|
64
59
|
end
|
60
|
+
end
|
65
61
|
|
66
|
-
|
67
|
-
|
62
|
+
alias_method :setup, :config
|
63
|
+
|
64
|
+
# General logger
|
65
|
+
# for rails apps it's === Rails.logger
|
66
|
+
# for non-rails apps it's logging to stdout
|
67
|
+
def logger
|
68
|
+
return @logger if @logger
|
69
|
+
if defined?(Rails)
|
70
|
+
Rails.logger
|
71
|
+
else
|
72
|
+
unless @log
|
73
|
+
require 'logger'
|
74
|
+
@log = Logger.new(STDOUT)
|
75
|
+
@log.level = Logger::INFO
|
76
|
+
end
|
77
|
+
@log
|
68
78
|
end
|
79
|
+
end
|
69
80
|
|
70
|
-
|
71
|
-
|
81
|
+
def logger=(value)
|
82
|
+
@logger = value
|
83
|
+
end
|
84
|
+
|
85
|
+
# Logger to file, only important information
|
86
|
+
# For rails apps it will write log to RAILS_ROOT/log/veritrans.log
|
87
|
+
def file_logger
|
88
|
+
if !@file_logger
|
89
|
+
if defined?(Rails) && Rails.root
|
90
|
+
@file_logger = Logger.new(Rails.root.join("log/veritrans.log").to_s)
|
91
|
+
else
|
92
|
+
require 'logger'
|
93
|
+
@file_logger = Logger.new("/dev/null")
|
94
|
+
end
|
72
95
|
end
|
73
96
|
|
97
|
+
@file_logger
|
74
98
|
end
|
99
|
+
|
100
|
+
def file_logger=(value)
|
101
|
+
@file_logger = value
|
102
|
+
end
|
103
|
+
|
75
104
|
end
|
data/lib/veritrans/api.rb
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
require 'uri'
|
4
4
|
|
5
|
-
|
5
|
+
class Veritrans
|
6
6
|
module Api
|
7
7
|
|
8
8
|
# POST /v2/charge { payment_type: "vtlink" }
|
@@ -46,23 +46,39 @@ module Veritrans
|
|
46
46
|
request_with_logging(:post, config.api_host + "/v2/charge", data)
|
47
47
|
end
|
48
48
|
|
49
|
+
# POST https://app.sandbox.veritrans.co.id/snap/v1/charge
|
50
|
+
def create_snap_token(options = {})
|
51
|
+
result = request_with_logging(:post, config.api_host.sub('//api.', '//app.') + "/snap/v1/charge", options)
|
52
|
+
Veritrans::SnapResult.new(result.response, result.url, result.request_options, result.time)
|
53
|
+
end
|
54
|
+
|
55
|
+
alias_method :create_widget_token, :create_snap_token
|
56
|
+
|
49
57
|
# POST /v2/{id}/cancel
|
50
58
|
# Docs http://docs.veritrans.co.id/en/api/methods.html#Cancel
|
51
59
|
def cancel(payment_id, options = {})
|
60
|
+
if !payment_id || payment_id.to_s == ""
|
61
|
+
raise ArgumentError, "parameter payment_id can not be blank (got #{payment_id.class} : #{payment_id.inspect})"
|
62
|
+
end
|
63
|
+
|
52
64
|
request_with_logging(:post, config.api_host + "/v2/#{URI.escape(payment_id)}/cancel", options)
|
53
65
|
end
|
54
66
|
|
55
67
|
# POST /v2/{id}/approve
|
56
68
|
# Docs http://docs.veritrans.co.id/en/api/methods.html#Approve
|
57
69
|
def approve(payment_id, options = {})
|
70
|
+
if !payment_id || payment_id.to_s == ""
|
71
|
+
raise ArgumentError, "parameter payment_id can not be blank (got #{payment_id.class} : #{payment_id.inspect})"
|
72
|
+
end
|
73
|
+
|
58
74
|
request_with_logging(:post, config.api_host + "/v2/#{URI.escape(payment_id)}/approve", options)
|
59
75
|
end
|
60
76
|
|
61
77
|
# GET /v2/{id}/status
|
62
78
|
# Docs http://docs.veritrans.co.id/en/api/methods.html#Status
|
63
79
|
def status(payment_id)
|
64
|
-
if !payment_id || payment_id == ""
|
65
|
-
raise ArgumentError, "parameter payment_id can not be
|
80
|
+
if !payment_id || payment_id.to_s == ""
|
81
|
+
raise ArgumentError, "parameter payment_id can not be blank (got #{payment_id.class} : #{payment_id.inspect})"
|
66
82
|
end
|
67
83
|
|
68
84
|
get(config.api_host + "/v2/#{URI.escape(payment_id)}/status")
|
@@ -71,12 +87,20 @@ module Veritrans
|
|
71
87
|
# POST /v2/capture
|
72
88
|
# Docs http://docs.veritrans.co.id/en/api/methods.html#Capture
|
73
89
|
def capture(payment_id, gross_amount, options = {})
|
90
|
+
if !payment_id || payment_id.to_s == ""
|
91
|
+
raise ArgumentError, "parameter payment_id can not be blank (got #{payment_id.class} : #{payment_id.inspect})"
|
92
|
+
end
|
93
|
+
|
74
94
|
post(config.api_host + "/v2/capture", options.merge(transaction_id: payment_id, gross_amount: gross_amount))
|
75
95
|
end
|
76
96
|
|
77
97
|
# POST /v2/{id}/expire
|
78
|
-
def expire(
|
79
|
-
|
98
|
+
def expire(payment_id)
|
99
|
+
if !payment_id || payment_id.to_s == ""
|
100
|
+
raise ArgumentError, "parameter payment_id can not be blank (got #{payment_id.class} : #{payment_id.inspect})"
|
101
|
+
end
|
102
|
+
|
103
|
+
request_with_logging(:post, config.api_host + "/v2/#{URI.escape(payment_id)}/expire", nil)
|
80
104
|
end
|
81
105
|
|
82
106
|
# POST /v2/charge { payment_type: "vtlink" }
|
@@ -91,5 +115,15 @@ module Veritrans
|
|
91
115
|
request_with_logging(:delete, config.api_host + "/v2/vtlink/#{URI.escape(id)}", options)
|
92
116
|
end
|
93
117
|
|
118
|
+
# GET /v2/point_inquiry/{token_id}
|
119
|
+
def inquiry_points(token_id)
|
120
|
+
if token_id == nil || token_id.to_s == ""
|
121
|
+
raise ArgumentError, "parameter token_id can not be bank"
|
122
|
+
end
|
123
|
+
|
124
|
+
request_with_logging(:get, config.api_host + "/v2/point_inquiry/#{token_id}", {})
|
125
|
+
end
|
126
|
+
alias_method :point_inquiry, :inquiry_points
|
127
|
+
|
94
128
|
end
|
95
129
|
end
|
data/lib/veritrans/cli.rb
CHANGED
data/lib/veritrans/client.rb
CHANGED
@@ -4,21 +4,24 @@ require "base64"
|
|
4
4
|
require 'uri'
|
5
5
|
require 'excon'
|
6
6
|
|
7
|
-
|
7
|
+
class Veritrans
|
8
8
|
module Client
|
9
|
-
extend self
|
10
9
|
|
11
10
|
# Failback for activesupport
|
12
|
-
def _json_encode(params)
|
11
|
+
def self._json_encode(params)
|
13
12
|
if defined?(ActiveSupport) && defined?(ActiveSupport::JSON)
|
14
13
|
ActiveSupport::JSON.encode(params)
|
15
14
|
else
|
16
15
|
require 'json' unless defined?(JSON)
|
17
|
-
JSON.
|
16
|
+
JSON.pretty_generate(params)
|
18
17
|
end
|
19
18
|
end
|
20
19
|
|
21
|
-
def
|
20
|
+
def _json_encode(params)
|
21
|
+
Veritrans::Client._json_encode(params)
|
22
|
+
end
|
23
|
+
|
24
|
+
def self._json_decode(params)
|
22
25
|
if defined?(ActiveSupport) && defined?(ActiveSupport::JSON)
|
23
26
|
ActiveSupport::JSON.decode(params)
|
24
27
|
else
|
@@ -69,6 +72,8 @@ module Veritrans
|
|
69
72
|
|
70
73
|
method = method.to_s.upcase
|
71
74
|
logger.info "Veritrans: #{method} #{url} #{_json_encode(params)}"
|
75
|
+
logger.info "Veritrans: Using server key: #{config.server_key}"
|
76
|
+
#puts "Veritrans: #{method} #{url} #{_json_encode(params)}"
|
72
77
|
|
73
78
|
default_options = config.http_options || {}
|
74
79
|
|
@@ -91,9 +96,9 @@ module Veritrans
|
|
91
96
|
end
|
92
97
|
|
93
98
|
connection_options = {
|
94
|
-
read_timeout:
|
95
|
-
write_timeout:
|
96
|
-
connect_timeout:
|
99
|
+
read_timeout: 120,
|
100
|
+
write_timeout: 120,
|
101
|
+
connect_timeout: 120
|
97
102
|
}.deep_merge(default_options)
|
98
103
|
|
99
104
|
s_time = Time.now
|
@@ -106,12 +111,12 @@ module Veritrans
|
|
106
111
|
Result.new(response, url, request_options, Time.now - s_time)
|
107
112
|
|
108
113
|
rescue Excon::Errors::SocketError => error
|
109
|
-
logger.info "
|
114
|
+
logger.info "Veritrans: socket error, can not connect"
|
110
115
|
error_response = Excon::Response.new(
|
111
116
|
body: '{"status_code": "500", "status_message": "Internal server error, no response from backend. Try again later"}',
|
112
117
|
status: '500'
|
113
118
|
)
|
114
|
-
Veritrans::Result.new(error_response, url,
|
119
|
+
Veritrans::Result.new(error_response, url, request_options, Time.now - s_time)
|
115
120
|
end
|
116
121
|
|
117
122
|
end
|