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.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -1
  3. data/.travis.yml +11 -3
  4. data/CHANGELOG.md +15 -0
  5. data/Gemfile +10 -0
  6. data/Gemfile.lock +136 -97
  7. data/Procfile +1 -0
  8. data/README.md +12 -5
  9. data/Rakefile +7 -5
  10. data/api_reference.md +21 -1
  11. data/example/README.md +8 -0
  12. data/example/config.ru +6 -0
  13. data/example/index.erb +104 -9
  14. data/example/localization.erb +248 -0
  15. data/example/points.erb +187 -0
  16. data/example/recurring.erb +201 -0
  17. data/example/response.erb +10 -1
  18. data/example/sinatra.rb +120 -8
  19. data/example/style.css +83 -2
  20. data/example/veritrans.yml +0 -1
  21. data/example/widget.erb +52 -0
  22. data/lib/generators/templates/assets/credit_card_form.js +3 -2
  23. data/lib/generators/templates/payments_controller.rb +5 -0
  24. data/lib/generators/templates/veritrans.rb +20 -17
  25. data/lib/generators/veritrans/install_generator.rb +1 -1
  26. data/lib/generators/veritrans/payment_form_generator.rb +1 -1
  27. data/lib/veritrans.rb +73 -44
  28. data/lib/veritrans/api.rb +39 -5
  29. data/lib/veritrans/cli.rb +1 -1
  30. data/lib/veritrans/client.rb +15 -10
  31. data/lib/veritrans/config.rb +11 -4
  32. data/lib/veritrans/events.rb +4 -4
  33. data/lib/veritrans/result.rb +28 -7
  34. data/lib/veritrans/version.rb +2 -2
  35. data/spec/cli_spec.rb +1 -0
  36. data/spec/rails_plugin_spec.rb +72 -27
  37. data/spec/spec_helper.rb +1 -0
  38. data/spec/veritrans_client_spec.rb +57 -3
  39. data/spec/veritrans_config_spec.rb +1 -1
  40. data/spec/veritrans_events_spec.rb +2 -0
  41. data/spec/veritrans_snap_spec.rb +39 -0
  42. data/testing_webhooks.md +0 -2
  43. data/veritrans.gemspec +5 -5
  44. metadata +29 -15
@@ -4,7 +4,6 @@ development:
4
4
  client_key: VT-client-NArmatJZqzsmTmzR
5
5
  server_key: VT-server-9Htb-RxXkg7-7hznSCCjxvoY
6
6
 
7
- # api_host: http://papi.vt-stage.info:8080
8
7
  # For production use
9
8
  # api_host: https://api.veritrans.co.id
10
9
 
@@ -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
- // console.log('Get token response:');
29
- // console.log(data);
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
- # mount Veritrans::Events.new => '/vt_events'
12
-
11
+ #
12
+ # mount Veritrans::Events.new => '/vt_events'
13
+ #
13
14
  # All possible events:
14
15
  #
15
- # * payment.success == ['authorize', 'capture', 'settlement']
16
- # * payment.failed == ['deny', 'canel', 'expire']
17
- # * payment.challenge # when payment.froud_status == 'challenge'
16
+ # 'payment.success' == ['authorize', 'capture', 'settlement']
17
+ # 'payment.failed' == ['deny', 'cancel', 'expire']
18
+ # 'payment.challenge' # when payment.fraud_status == 'challenge'
18
19
  #
19
- # * payment.authorize
20
- # * payment.capture
21
- # * payment.settlement
22
- # * payment.deny
23
- # * payment.canel
24
- # * payment.expire
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.mark_paid!
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.mark_failed!
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
- # p "Event: #{event_name}"
42
+ # puts "Payment #{payment.data[:order_id]} has status #{payment.data[:transaction_status]}"
40
43
  # p payment
41
44
  # end
42
45
 
@@ -1,4 +1,4 @@
1
- module Veritrans
1
+ class Veritrans
2
2
  class InstallGenerator < ::Rails::Generators::Base
3
3
  source_root File.expand_path("../../templates", __FILE__)
4
4
 
@@ -1,4 +1,4 @@
1
- module Veritrans
1
+ class Veritrans
2
2
  class PaymentFormGenerator < ::Rails::Generators::Base
3
3
  source_root File.expand_path("../../templates", __FILE__)
4
4
 
@@ -9,67 +9,96 @@ if defined?(::Rails)
9
9
  require 'veritrans/events'
10
10
  end
11
11
 
12
- module Veritrans
13
- extend Veritrans::Client
14
- extend Veritrans::Api
12
+ class Veritrans
13
+ include Veritrans::Client
14
+ include Veritrans::Api
15
15
 
16
16
  class << self
17
- def config(&block)
18
- if block
19
- instance_eval(&block)
20
- else
21
- Veritrans::Config
22
- end
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
- alias_method :setup, :config
28
+ def decode_notification_json(input)
29
+ return Veritrans::Client._json_decode(input)
30
+ end
26
31
 
27
- # General logger
28
- # for rails apps it's === Rails.logger
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
- def logger=(value)
45
- @logger = value
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
- # Logger to file, only important information
49
- # For rails apps it will write log to RAILS_ROOT/log/veritrans.log
50
- def file_logger
51
- if !@file_logger
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
- @file_logger
49
+ if options
50
+ @config = Veritrans::Config.new(options)
60
51
  end
52
+ end
61
53
 
62
- def file_logger=(value)
63
- @file_logger = value
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
- def events
67
- Veritrans::Events if defined?(Veritrans::Events)
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
- def decode_notification_json(input)
71
- return Veritrans::Client._json_decode(input)
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
@@ -2,7 +2,7 @@
2
2
 
3
3
  require 'uri'
4
4
 
5
- module Veritrans
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 bank"
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(id)
79
- request_with_logging(:post, config.api_host + "/v2/#{URI.escape(id)}/expire", nil)
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
@@ -2,7 +2,7 @@ require 'json'
2
2
  require 'securerandom'
3
3
  require 'logger'
4
4
 
5
- module Veritrans
5
+ class Veritrans
6
6
  module CLI
7
7
  # can't find order
8
8
  class OrderNotFound < Exception; end
@@ -4,21 +4,24 @@ require "base64"
4
4
  require 'uri'
5
5
  require 'excon'
6
6
 
7
- module Veritrans
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.generate(params)
16
+ JSON.pretty_generate(params)
18
17
  end
19
18
  end
20
19
 
21
- def _json_decode(params)
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: 40,
95
- write_timeout: 40,
96
- connect_timeout: 40
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 "PAPI: socket error, can not connect"
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, options, Time.now - s_time)
119
+ Veritrans::Result.new(error_response, url, request_options, Time.now - s_time)
115
120
  end
116
121
 
117
122
  end