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
@@ -0,0 +1,201 @@
|
|
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
|
+
<link rel="icon" type="image/x-icon" href="https://account.veritrans.co.id/favicon.ico" />
|
14
|
+
</h3>
|
15
|
+
</header>
|
16
|
+
|
17
|
+
<section class="recurring-page">
|
18
|
+
<h4>Recurring / One Click</h4>
|
19
|
+
|
20
|
+
<section class="cards">
|
21
|
+
Saved cards:
|
22
|
+
<ul>
|
23
|
+
|
24
|
+
</ul>
|
25
|
+
</section>
|
26
|
+
|
27
|
+
<form id="add_card_form">
|
28
|
+
<input type="hidden" name="token_id" id="card_token">
|
29
|
+
<fieldset>
|
30
|
+
<legend>Credit Card Recurring</legend>
|
31
|
+
<p>
|
32
|
+
<label for="card_number">Card number</label>
|
33
|
+
<input type="text" id="card_number" style="width: 150px" value="4811 1111 1111 1114">
|
34
|
+
<br>
|
35
|
+
<small style="margin-left: 100px" class="card-numbers">
|
36
|
+
<a onclick="$('#card_number').val('4811 1111 1111 1114')">success Visa 1</a>
|
37
|
+
<a onclick="$('#card_number').val('4411 1111 1111 1118')">success Visa 2</a>
|
38
|
+
<a onclick="$('#card_number').val('5810 1111 1111 1112')">success MasterCard 1</a>
|
39
|
+
<a onclick="$('#card_number').val('5410 1111 1111 1116')">success MasterCard 2</a>
|
40
|
+
<a href="http://docs.veritrans.co.id/en/api/test_credentials.html" target="_blank">documentation</a>
|
41
|
+
</small>
|
42
|
+
</p>
|
43
|
+
|
44
|
+
<p>
|
45
|
+
<label for="card_cvc">Security Code</label>
|
46
|
+
<input type="text" id="card_cvc" style="width: 30px" placeholder="cvc" value="123">
|
47
|
+
</p>
|
48
|
+
|
49
|
+
<p>
|
50
|
+
<label for="card_exp">Expiration date</label>
|
51
|
+
<input type="text" id="card_exp" placeholder="MM / YY" value="12 / 16">
|
52
|
+
</p>
|
53
|
+
|
54
|
+
</fieldset>
|
55
|
+
|
56
|
+
<input id="add_card_btn" type="submit" value="Add Card">
|
57
|
+
</form>
|
58
|
+
|
59
|
+
<script src="//api.sandbox.veritrans.co.id/v2/assets/veritrans.js"></script>
|
60
|
+
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
|
61
|
+
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery.payment/1.0.2/jquery.payment.js"></script>
|
62
|
+
<script src="//cdnjs.cloudflare.com/ajax/libs/magnific-popup.js/1.0.1/jquery.magnific-popup.js"></script>
|
63
|
+
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/magnific-popup.js/0.9.9/magnific-popup.css">
|
64
|
+
|
65
|
+
<script type="text/javascript">
|
66
|
+
Veritrans.client_key = "<%= Veritrans.config.client_key %>";
|
67
|
+
|
68
|
+
// Here is a difference, for
|
69
|
+
Veritrans.url = "<%= Veritrans.config.api_host %>/v2/card/register";
|
70
|
+
|
71
|
+
|
72
|
+
function createTokenData() {
|
73
|
+
return {
|
74
|
+
card_number: $('#card_number').val().replace(/\s/g, ''),
|
75
|
+
card_cvv: $('#card_cvc').val(),
|
76
|
+
card_exp_month: $('#card_exp').val().match(/(\d+) \//)[1],
|
77
|
+
card_exp_year: '20' + $('#card_exp').val().match(/\/ (\d+)/)[1]
|
78
|
+
};
|
79
|
+
}
|
80
|
+
|
81
|
+
$(document).ready(function () {
|
82
|
+
$('#card_number').payment('formatCardNumber');
|
83
|
+
$('#card_cvc').payment('formatCardCVC');
|
84
|
+
$('#card_exp').payment('formatCardExpiry');
|
85
|
+
|
86
|
+
renderCards();
|
87
|
+
|
88
|
+
$('#add_card_form').on('submit', function (event) {
|
89
|
+
var form = this;
|
90
|
+
$('#submit_btn').attr('disabled', true).val("Processing ...");
|
91
|
+
event.preventDefault();
|
92
|
+
|
93
|
+
Veritrans.token(createTokenData, function (data) {
|
94
|
+
console.log('Token data:', data);
|
95
|
+
// if we get url then it's 3d-secure transaction
|
96
|
+
// so we need to open that page
|
97
|
+
// this callback function will be called again after user confirm 3d-secure
|
98
|
+
// you can also redirect on server side
|
99
|
+
if (data.redirect_url) {
|
100
|
+
$.magnificPopup.open({
|
101
|
+
items: { type: 'iframe', src: data.redirect_url }
|
102
|
+
});
|
103
|
+
$.magnificPopup.instance.content.find('iframe').height(590);
|
104
|
+
// if no redirect_url and we have token_id then just make charge request
|
105
|
+
} else if (data.saved_token_id) {
|
106
|
+
$('#card_token').val(data.saved_token_id);
|
107
|
+
//form.submit();
|
108
|
+
$.magnificPopup.close();
|
109
|
+
saveCard(data, createTokenData());
|
110
|
+
renderCards();
|
111
|
+
// if no redirect_url and no token_id, then it should be error
|
112
|
+
} else {
|
113
|
+
alert(data.validation_messages ? data.validation_messages.join("\n") : data.status_message);
|
114
|
+
$('#submit_btn').removeAttr('disabled').removeAttr("value");
|
115
|
+
}
|
116
|
+
});
|
117
|
+
});
|
118
|
+
});
|
119
|
+
|
120
|
+
function loadCards () {
|
121
|
+
return localStorage.recurring ? JSON.parse(localStorage.recurring) : [];
|
122
|
+
}
|
123
|
+
|
124
|
+
function saveCard (tokenInfo, cardInfo) {
|
125
|
+
var data = loadCards();
|
126
|
+
|
127
|
+
var row = {
|
128
|
+
token_id: tokenInfo.saved_token_id,
|
129
|
+
card_number: tokenInfo.masked_card,
|
130
|
+
card_expiry: cardInfo.card_exp_month + " / " + cardInfo.card_exp_year
|
131
|
+
};
|
132
|
+
data.push(row);
|
133
|
+
|
134
|
+
localStorage.recurring = JSON.stringify(data);
|
135
|
+
}
|
136
|
+
|
137
|
+
function renderCards () {
|
138
|
+
var cards = loadCards();
|
139
|
+
|
140
|
+
var container = $('section.cards ul');
|
141
|
+
container.empty();
|
142
|
+
cards.forEach(function (card) {
|
143
|
+
var el = $('<li>').html(
|
144
|
+
"Card Number: <code>" + card.card_number + "</code> " +
|
145
|
+
"Expiry: <i>" + card.card_expiry + "</i> "
|
146
|
+
);
|
147
|
+
el.attr('data-id', card.id);
|
148
|
+
el.attr('token-id', card.token_id);
|
149
|
+
$('<a>').text('Charge card').bind('click', chargeSavedCard).attr('href', '#').appendTo(el);
|
150
|
+
$('<a>').text('Remove').bind('click', removeSavedCard).attr('href', '#').appendTo(el);
|
151
|
+
$('<small>').text('Token ' + card.token_id).appendTo(el);
|
152
|
+
el.appendTo(container);
|
153
|
+
})
|
154
|
+
}
|
155
|
+
|
156
|
+
function chargeSavedCard (e) {
|
157
|
+
e.preventDefault();
|
158
|
+
var token = $(e.target).closest('li').attr('token-id');
|
159
|
+
var amount = prompt("Please enter amount (Rp.)", 15000);
|
160
|
+
|
161
|
+
if (amount === "" || amount === null || parseInt(amount, 10) <= 0) {
|
162
|
+
console.log("Canceled");
|
163
|
+
return;
|
164
|
+
}
|
165
|
+
|
166
|
+
// Create iFrame and submit form in it
|
167
|
+
var html = '<form action="' + window.location.protocol + '//' + window.location.host + '/charge_vtdirect" method="post">' +
|
168
|
+
'<input type=hidden name=recurring value=1>' +
|
169
|
+
'<input type=hidden name=token_id value="' + token + '">' +
|
170
|
+
'<input type=hidden name=gross_amount value="' + parseInt(amount, 10) + '">' +
|
171
|
+
'<input type=submit style="display: none">' +
|
172
|
+
'</form>' + "<script>console.log(document.forms[0]); document.forms[0].submit()<\/script>";
|
173
|
+
|
174
|
+
var content = 'data:text/html;charset=utf-8,' + encodeURI(html);
|
175
|
+
$.magnificPopup.open({
|
176
|
+
items: { type: 'iframe', src: content}
|
177
|
+
});
|
178
|
+
}
|
179
|
+
|
180
|
+
function removeSavedCard (e) {
|
181
|
+
e.preventDefault();
|
182
|
+
var tokenId = $(e.target).closest('li').attr('token-id');
|
183
|
+
var cards = loadCards();
|
184
|
+
|
185
|
+
cards.forEach(function (card, index) {
|
186
|
+
if (card.token_id == tokenId) {
|
187
|
+
if (confirm("Remove card " + card.card_number + "?")) {
|
188
|
+
cards.splice(index, 1);
|
189
|
+
console.log(cards);
|
190
|
+
}
|
191
|
+
}
|
192
|
+
});
|
193
|
+
|
194
|
+
localStorage.recurring = JSON.stringify(cards);
|
195
|
+
renderCards();
|
196
|
+
}
|
197
|
+
|
198
|
+
</script>
|
199
|
+
|
200
|
+
</body>
|
201
|
+
</html>
|
data/example/response.erb
CHANGED
@@ -6,7 +6,7 @@
|
|
6
6
|
|
7
7
|
<% if @result.redirect_url %>
|
8
8
|
<section>
|
9
|
-
<p>Here's redirect url <a href='<%= @
|
9
|
+
<p>Here's redirect url <a href='<%= @vtweb_url %>'><%= @vtweb_url %></a> </p>
|
10
10
|
</section>
|
11
11
|
<% end %>
|
12
12
|
|
@@ -24,5 +24,14 @@
|
|
24
24
|
</pre>
|
25
25
|
</code>
|
26
26
|
|
27
|
+
<% if @cahrge_params %>
|
28
|
+
<strong>Request Body</strong>
|
29
|
+
<code>
|
30
|
+
<pre>
|
31
|
+
<%= JSON.pretty_generate(@cahrge_params) %>
|
32
|
+
</pre>
|
33
|
+
</code>
|
34
|
+
<% end %>
|
35
|
+
|
27
36
|
<a href="/">Go back</a>
|
28
37
|
</section>
|
data/example/sinatra.rb
CHANGED
@@ -5,6 +5,12 @@ require 'json'
|
|
5
5
|
require 'veritrans'
|
6
6
|
require 'sinatra'
|
7
7
|
|
8
|
+
begin
|
9
|
+
require 'tilt/erubis'
|
10
|
+
rescue LoadError => error
|
11
|
+
puts "Warning: Can not load 'tilt', continue"
|
12
|
+
end
|
13
|
+
|
8
14
|
Veritrans.setup do
|
9
15
|
config.load_yml "./veritrans.yml#development"
|
10
16
|
|
@@ -20,6 +26,8 @@ end
|
|
20
26
|
set :public_folder, "."
|
21
27
|
set :views, "."
|
22
28
|
|
29
|
+
set :run, $0 == __FILE__
|
30
|
+
|
23
31
|
def generate_order_id
|
24
32
|
"testing-#{rand.round(4)}-#{Time.now.to_i}"
|
25
33
|
end
|
@@ -28,31 +36,135 @@ get "/" do
|
|
28
36
|
erb :index
|
29
37
|
end
|
30
38
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
39
|
+
get "/recurring" do
|
40
|
+
erb :recurring
|
41
|
+
end
|
42
|
+
|
43
|
+
get "/localization" do
|
44
|
+
erb :localization
|
45
|
+
end
|
46
|
+
|
47
|
+
get "/points" do
|
48
|
+
erb :points
|
49
|
+
end
|
50
|
+
|
51
|
+
get "/widget" do
|
52
|
+
response = Veritrans.create_widget_token(
|
35
53
|
transaction_details: {
|
36
54
|
order_id: generate_order_id,
|
37
|
-
gross_amount:
|
55
|
+
gross_amount: 30_000
|
38
56
|
}
|
39
57
|
)
|
58
|
+
@token_id = response.data[:token_id]
|
59
|
+
erb :widget
|
60
|
+
end
|
40
61
|
|
62
|
+
get "/widget/confirm/:transaction_id" do
|
63
|
+
@result = Veritrans.status(params[:transaction_id])
|
41
64
|
erb :response
|
42
65
|
end
|
43
66
|
|
67
|
+
post "/charge_vtdirect" do
|
68
|
+
@charge_params = {
|
69
|
+
payment_type: "credit_card",
|
70
|
+
credit_card: {
|
71
|
+
token_id: params[:token_id]
|
72
|
+
},
|
73
|
+
transaction_details: {
|
74
|
+
order_id: generate_order_id,
|
75
|
+
gross_amount: params[:gross_amount].to_f,
|
76
|
+
}
|
77
|
+
}
|
78
|
+
|
79
|
+
if params[:recurring] == "1"
|
80
|
+
@charge_params[:credit_card][:recurring] = true
|
81
|
+
end
|
82
|
+
|
83
|
+
if params[:points_amount]
|
84
|
+
@charge_params[:credit_card][:point_redeem_amount] = params[:points_amount]
|
85
|
+
@charge_params[:credit_card][:bank] = "bni"
|
86
|
+
end
|
87
|
+
|
88
|
+
@result = Veritrans.charge(@charge_params)
|
89
|
+
|
90
|
+
if params[:format] == "json"
|
91
|
+
content_type :json
|
92
|
+
@result.response.body
|
93
|
+
else
|
94
|
+
erb :response
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
44
98
|
get "/charge_vtweb" do
|
45
|
-
|
99
|
+
vtweb_options = {}
|
100
|
+
|
101
|
+
if params[:enabled_payments] && params[:enabled_payments].size > 0
|
102
|
+
vtweb_options[:enabled_payments] = params[:enabled_payments]
|
103
|
+
end
|
104
|
+
|
105
|
+
if params[:credit_card_3d_secure] && params[:credit_card_3d_secure] != ""
|
106
|
+
vtweb_options[:credit_card_3d_secure] = params[:credit_card_3d_secure] == "true"
|
107
|
+
end
|
108
|
+
|
109
|
+
if params[:bin_promo] && params[:bin_promo] != ""
|
110
|
+
vtweb_options[:credit_card_bins] = params[:bin_promo]
|
111
|
+
end
|
112
|
+
|
113
|
+
if params[:installment]
|
114
|
+
vtweb_options[:payment_options] = {
|
115
|
+
installment: {
|
116
|
+
required: true,
|
117
|
+
installment_terms: {}
|
118
|
+
}
|
119
|
+
}
|
120
|
+
|
121
|
+
if params[:installment]['bni']
|
122
|
+
vtweb_options[:payment_options][:installment][:installment_terms][:bni] = [3, 6, 12]
|
123
|
+
end
|
124
|
+
|
125
|
+
if params[:installment][:mandiri]
|
126
|
+
vtweb_options[:payment_options][:installment][:installment_terms][:mandiri] = [3, 6, 12]
|
127
|
+
end
|
128
|
+
|
129
|
+
if params[:installment]['bca']
|
130
|
+
vtweb_options[:payment_options][:installment][:installment_terms][:bca] = [3, 6, 12]
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
if request.env["HTTP_REFERER"]
|
135
|
+
vtweb_options[:finish_redirect_url] = request.env["HTTP_REFERER"]
|
136
|
+
vtweb_options[:unfinish_redirect_url] = request.env["HTTP_REFERER"]
|
137
|
+
vtweb_options[:error_redirect_url] = request.env["HTTP_REFERER"]
|
138
|
+
end
|
139
|
+
|
140
|
+
@cahrge_params = {
|
46
141
|
payment_type: "VTWEB",
|
142
|
+
vtweb: vtweb_options,
|
47
143
|
transaction_details: {
|
48
144
|
order_id: generate_order_id,
|
49
145
|
gross_amount: 100_000
|
50
146
|
}
|
51
|
-
|
147
|
+
}
|
148
|
+
|
149
|
+
@result = Veritrans.charge(@cahrge_params)
|
150
|
+
|
151
|
+
if @result.redirect_url
|
152
|
+
if params[:locale].to_s != ""
|
153
|
+
@vtweb_url = "#{@result.redirect_url}?locale=#{params[:locale]}"
|
154
|
+
else
|
155
|
+
@vtweb_url = @result.redirect_url
|
156
|
+
end
|
157
|
+
end
|
52
158
|
|
53
159
|
erb :response
|
54
160
|
end
|
55
161
|
|
162
|
+
get "/check_points/:token_id" do
|
163
|
+
@result = Veritrans.inquiry_points(params[:token_id])
|
164
|
+
content_type :json
|
165
|
+
@result.response.body
|
166
|
+
end
|
167
|
+
|
56
168
|
post "/webhook" do
|
57
169
|
post_body = request.body.read
|
58
170
|
request_data = Veritrans.decode_notification_json(post_body)
|
@@ -73,4 +185,4 @@ post "/webhook" do
|
|
73
185
|
end
|
74
186
|
|
75
187
|
return "ok"
|
76
|
-
end
|
188
|
+
end
|
data/example/style.css
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
body {
|
2
2
|
max-width: 900px;
|
3
3
|
margin: 0 auto;
|
4
|
+
font-size: 16px;
|
5
|
+
background: white;
|
4
6
|
}
|
5
7
|
a {
|
6
8
|
color: #00E;
|
@@ -24,7 +26,9 @@ section + section {
|
|
24
26
|
}
|
25
27
|
|
26
28
|
label {
|
27
|
-
display: inline-block;
|
29
|
+
display: inline-block;
|
30
|
+
min-width: 100px;
|
31
|
+
vertical-align: top;
|
28
32
|
}
|
29
33
|
.card-numbers {
|
30
34
|
margin-left: 100px;
|
@@ -42,4 +46,81 @@ label {
|
|
42
46
|
|
43
47
|
fieldset {
|
44
48
|
margin: 25px 0 12px;
|
45
|
-
}
|
49
|
+
}
|
50
|
+
|
51
|
+
.white-popup {
|
52
|
+
position: relative;
|
53
|
+
background: #FFF;
|
54
|
+
padding: 20px;
|
55
|
+
width: auto;
|
56
|
+
max-width: 600px;
|
57
|
+
margin: 20px auto;
|
58
|
+
}
|
59
|
+
|
60
|
+
.white-popup pre code {
|
61
|
+
white-space: pre-wrap;
|
62
|
+
}
|
63
|
+
|
64
|
+
#vtweb_form label {
|
65
|
+
min-width: 180px;
|
66
|
+
}
|
67
|
+
|
68
|
+
#vtweb_form ul {
|
69
|
+
display: inline-block;
|
70
|
+
vertical-align: top;
|
71
|
+
margin-top: 0;
|
72
|
+
padding-left: 0px;
|
73
|
+
}
|
74
|
+
|
75
|
+
#vtweb_form ul li {
|
76
|
+
list-style-type: none;
|
77
|
+
margin-bottom: 1px;
|
78
|
+
font-size: 15px;
|
79
|
+
}
|
80
|
+
|
81
|
+
#vtweb_form small {
|
82
|
+
margin-left: 184px;
|
83
|
+
}
|
84
|
+
|
85
|
+
.recurring-page .cards {
|
86
|
+
border: 1px dashed #acf;
|
87
|
+
padding: 10px;
|
88
|
+
}
|
89
|
+
|
90
|
+
.recurring-page .cards ul {
|
91
|
+
padding-left: 0;
|
92
|
+
margin-bottom: 3px;
|
93
|
+
}
|
94
|
+
|
95
|
+
.recurring-page .cards ul li {
|
96
|
+
list-style-type: none;
|
97
|
+
display: block;
|
98
|
+
background: #f5f5f5;
|
99
|
+
padding: 7px 9px;
|
100
|
+
box-shadow: inset 0px 0px 2px rgba(0, 0, 64, 0.25);
|
101
|
+
}
|
102
|
+
|
103
|
+
.recurring-page .cards ul li + li {
|
104
|
+
margin-top: 10px;
|
105
|
+
}
|
106
|
+
|
107
|
+
.recurring-page .cards ul li code {
|
108
|
+
margin: 0 18px 0 4px;
|
109
|
+
}
|
110
|
+
.recurring-page .cards ul li i {
|
111
|
+
margin: 0 35px 0 4px;
|
112
|
+
font-style: normal;
|
113
|
+
}
|
114
|
+
|
115
|
+
.recurring-page .cards ul li a {
|
116
|
+
margin-right: 10px;
|
117
|
+
}
|
118
|
+
|
119
|
+
.recurring-page .cards ul li small {
|
120
|
+
display: block;
|
121
|
+
margin-top: 7px;
|
122
|
+
color: #666;
|
123
|
+
font-family: monospace;
|
124
|
+
font-size: 10px;
|
125
|
+
}
|
126
|
+
|