README.md in veritrans-2.3.0 vs README.md in veritrans-2.4.0
- old
+ new
@@ -1,383 +1,352 @@
-# Veritrans ruby library
+# Midtrans Ruby library
-Veritrans gem is the library that will help you to integrate seamlessly with
-Midtrans (formerly known as Veritrans Indonesia).
+Midtrans ❤️ Ruby!
+This is the Official Ruby API client/library for Midtrans Payment API. Visit [https://midtrans.com](https://midtrans.com). More information about the product and see documentation at [http://docs.midtrans.com](https://docs.midtrans.com) for more technical details.
+
[![Gem Version](https://badge.fury.io/rb/veritrans.svg)](http://badge.fury.io/rb/veritrans)
[![Build Status](https://travis-ci.org/veritrans/veritrans-ruby.svg?branch=master)](https://travis-ci.org/veritrans/veritrans-ruby)
-To see it in action, we have made 3 examples:
+## 1. Installation
-1. Sinatra, which demonstrate in as succint code as possible. Please [have a look here](https://github.com/veritrans/veritrans-ruby/tree/master/example/sinatra)
-2. Simplepay, demonstrated how to integrate Midtrans with a simple, succint Rails project. [Have a look](https://github.com/veritrans/veritrans-ruby/tree/master/example/rails/simplepay).
-3. Cable, demonstrate a chat-commerce app where the payment is handled with Midtrans. [Have a look](https://github.com/veritrans/veritrans-ruby/tree/master/example/rails/cable).
-
-## How to use (Rails)
-
-### Add gem veritrans to Gemfile
-
+### Using Gemfile
+Add gem veritrans to Gemfile
```ruby
gem 'veritrans'
```
+Run this command in your terminal
+```ruby
+gem install veritrans
+```
+```ruby
+bundle install
+```
- bundle install
+## 2. Usage
+### 2.1 Choose Product/Method
-### Generate veritrans.yml
+We have [3 different products](https://docs.midtrans.com/en/welcome/index.html) of payment that you can use:
+- [Snap](#22A-snap) - Customizable payment popup will appear on **your web/app** (no redirection). [doc ref](https://snap-docs.midtrans.com/)
+- [Snap Redirect](#22B-snap-redirect) - Customer need to be redirected to payment url **hosted by midtrans**. [doc ref](https://snap-docs.midtrans.com/)
+- [Core API (VT-Direct)](#22C-core-api-vt-direct) - Basic backend implementation, you can customize the frontend embedded on **your web/app** as you like (no redirection). [doc ref](https://api-docs.midtrans.com/)
- rails g veritrans:install
+Choose one that you think best for your unique needs.
-### Create simple payment form (optional)
+### 2.2 Client Initialization and Configuration
- rails g veritrans:payment_form
+Get your client key and server key from [Midtrans Dashboard](https://dashboard.midtrans.com)
-### Edit api keys in config/veritrans.yml
+Create instance of Midtrans client
-```yml
-# config/veritrans.yml
-development:
- client_key: # your api client key
- server_key: # your api client key
-```
+```ruby
+require 'veritrans'
-## STEP 1: Process credit cards
+mt_client = Midtrans.new(
+ server_key: "your server key",
+ client_key: "your client key",
+ api_host: "https://api.sandbox.midtrans.com", # default
+ http_options: { }, # optional
+ logger: Logger.new(STDOUT), # optional
+ file_logger: Logger.new(STDOUT), # optional
+ )
-#### Snap
+mt_client.status("order-id-123456")
+```
-##### Pop-up
+Alternatively, you can also set config by declaring each one like below:
-This will result in payment page being a pop-up(iframe) inside your own web page, no need redirection, similar to our [demo](http://demo.midtrans.com)
-
-First, generate token in the back end provided with enough details as necessary
-and as detailed as you wish to.
-
```ruby
-response = Veritrans.create_widget_token(
- transaction_details: {
- order_id: 'THE-ITEM-ORDER-ID',
- gross_amount: 200000
- }
-)
-
-@snap_token = response.token
+Midtrans.config.server_key = "your server key"
+Midtrans.config.client_key = "your client key"
+Midtrans.config.api_host = "https://api.sandbox.midtrans.com"
```
-Then on the front end, the token is saved somewhere probably in the hidden field.
+### 2.2.A Snap
+You can see Snap example [with Sinatra](example/sinatra) and [without framework](example/snap).
-```
-<div class='cart'>
- <!-- some other codes -->
- <input type='hidden' name='snap-token' id='snap-token' value='<%= %>'>
- <%= hidden_field_tag 'snap_token', @snap_token %>
- <a href='#' class='order-button'>Order</a>
-</div>
-```
+```ruby
+# Create Snap payment page, with this version returning full API response
+create_snap_token(parameter)
-Then JavaScript can be used to invoke the SNAP dialog upon click on the
-order button.
+# Create Snap payment page, with this version returning token
+create_snap_token_string(parameter)
-```javascript
-var token = jQuery("#snap_token").val();
-
-jQuery(".order-button").on("click", function() {
- snap.pay(token, {
- onSuccess: function(res) { console.log("SUCCESS RESPONSE", res); },
- // you may also implement:
- // onPending
- // onError
- });
-});
+# Create Snap payment page, with this version returning redirect url
+create_snap_redirect_url_str(parameter)
```
+`parameter` is Object or String of JSON of [SNAP Parameter](https://snap-docs.midtrans.com/#json-objects)
-##### Redirection
+#### Get Snap Token
-This will result in redirect_url, you can redirect customer to the url, payment page is securely hosted by Midtrans.
-
```ruby
-@result = Veritrans.create_snap_redirect_url(
+result = Midtrans.create_snap_token(
transaction_details: {
- order_id: "my-unique-order-id",
- gross_amount: 100_000
+ order_id: "test-transaction-order-123",
+ gross_amount: 100000,
+ secure: true
}
)
-
-redirect_to @result.redirect_url
+@token = result.token
```
-> This is similar feature as old VT-Web
+#### Initialize Snap JS when customer click pay button
-#### VT-Web
+On frontend / html:
+Replace `PUT_TRANSACTION_TOKEN_HERE` with `transactionToken` acquired above
+```html
+<html>
+ <body>
+ <button id="pay-button">Pay!</button>
+ <pre><div id="result-json">JSON result will appear here after payment:<br></div></pre>
-> !!! WARNING NOTE: VT-Web is deprecated, please use [Snap](#Snap) instead, it has better previous VT-Web feature and many more improvements, including redirect_url.
+<!-- TODO: Remove ".sandbox" from script src URL for production environment. Also input your client key in "data-client-key" -->
+ <script src="https://app.sandbox.midtrans.com/snap/snap.js" data-client-key="<Set your ClientKey here>"></script>
+ <script type="text/javascript">
+ document.getElementById('pay-button').onclick = function(){
+ // SnapToken acquired from previous step
+ snap.pay('PUT_TRANSACTION_TOKEN_HERE', {
+ // Optional
+ onSuccess: function(result){
+ /* You may add your own js here, this is just example */ document.getElementById('result-json').innerHTML += JSON.stringify(result, null, 2);
+ },
+ // Optional
+ onPending: function(result){
+ /* You may add your own js here, this is just example */ document.getElementById('result-json').innerHTML += JSON.stringify(result, null, 2);
+ },
+ // Optional
+ onError: function(result){
+ /* You may add your own js here, this is just example */ document.getElementById('result-json').innerHTML += JSON.stringify(result, null, 2);
+ }
+ });
+ };
+ </script>
+ </body>
+</html>
+```
-*If you want to use VT-Web, add `payment_type: "VTWEB"`*
+### 2.2.B Snap Redirect
+You can see Snap example [with Sinatra](example/sinatra) and [without framework](example/snap).
+#### Get Redirection URL of a Payment Page
+
```ruby
-@result = Veritrans.charge(
- payment_type: "VTWEB",
+result = Midtrans.create_snap_redirect_url(
transaction_details: {
- order_id: "my-unique-order-id",
- gross_amount: 100_000
+ order_id: "test-transaction-order-123",
+ gross_amount: 100000,
+ secure: true
}
)
-
-redirect_to @result.redirect_url
+@redirecturl = result.redirect_url
```
-#### VT-Direct / Core API
+### 2.2.C Core API (VT-Direct)
+You can see some Core API examples [with Sinatra](example/sinatra) and [without framework](example/coreapi).
-It's little more complicated, because credit_card is sensitive data,
-you need put credit card number in our safe storage first using `midtrans.min.js` library, then send received token to with other payment details.
+Available methods for `CoreApi` class
-We don't want you to send credit card number to your server, especially for websites not using https.
+```ruby
+# charge : Do `/charge` API request to Midtrans Core API
+def charge(payment_type, data = nil)
-File: "app/views/shared/_veritrans_include.erb"
+# test_token : Do `/token` API request to Midtrans Core API
+def test_token(options = {})
+
+# point_inquiry : Do `/point_inquiry/{tokenId}` API request to Midtrans Core API
+def point_inquiry(token_id)
-```html
-<script src="https://api.midtrans.com/v2/assets/js/midtrans.min.js"></script>
+# status : Do `/{orderId}/status` API request to Midtrans Core API
+def status(payment_id)
-<script type="text/javascript">
- Veritrans.url = "<%= Veritrans.config.api_host %>/v2/token";
- Veritrans.client_key = "<%= Veritrans.config.client_key %>";
-</script>
-```
+# approve : Do `/{orderId}/approve` API request to Midtrans Core API
+def approve(payment_id, options = {})
-Payment form: (same as if you use `rails g veritrans:payment_form`)
+# deny : Do `/{orderId}/deny` API request to Midtrans Core API
+def deny(payment_id, options = {})
-```erb
-<%= form_tag "/charge_vtdirect", id: "card_form" do %>
- <%= hidden_field_tag :token_id, nil, id: "card_token" %>
- <%= hidden_field_tag :gross_amount, 30000 %>
- <p>
- <%= label_tag "card_number", "Card number" %>
- <%= text_field_tag :card_number, "4811 1111 1111 1114", name: nil, style: "width: 150px" %>
- </p>
- <p>
- <%= label_tag "card_cvc", "Security Code" %>
- <%= text_field_tag :card_cvc, "123", name: nil, style: "width: 30px", placeholder: "cvc" %>
- </p>
- <p>
- <%= label_tag "card_exp", "Expiration date" %>
- <%= text_field_tag :card_exp, "12 / 18", name: nil, placeholder: "MM / YY" %>
- </p>
- <%= submit_tag "Make payment", id: "submit_btn" %>
-<% end %>
-<iframe id="3d-secure-iframe" style="display: none; width: 500px; height: 600px"></iframe>
-```
+# cancel : Do `/{orderId}/cancel` API request to Midtrans Core API
+def cancel(payment_id, options = {})
-For sinatra:
+# expire : Do `/{orderId}/expire` API request to Midtrans Core API
+def expire(payment_id)
+
+# refund : Do `/{orderId}/refund` API request to Midtrans Core API
+def refund(payment_id, options = {})
-```html
-<form action="/charge_vtdirect" method="post" id="card_form">
- <input type="hidden" name="token_id" id="card_token">
- <input type="hidden" id="gross_amount" value="30000">
- <p>
- <label for="card_number">Card number</label>
- <input type="text" id="card_number" style="width: 150px" value="4811 1111 1111 1114">
- </p>
- <p>
- <label for="card_cvc">Security Code</label>
- <input type="text" id="card_cvc" style="width: 30px" placeholder="cvc" value="123">
- </p>
- <p>
- <label for="card_exp">Expiration date</label>
- <input type="text" id="card_exp" placeholder="MM / YY" value="12 / 18">
- </p>
- <p>
- <label for="card_secure">3D-secure</label>
- <input id="card_secure" name="card_secure" type="checkbox" value="1" />
- </p>
- <input id="submit_btn" type="submit">
-</form>
-<iframe id="3d-secure-iframe" style="display: none; width: 500px; height: 600px"></iframe>
-```
+# capture : Do `/{orderId}/capture` API request to Midtrans Core API
+def capture(payment_id, gross_amount, options = {})
-Sending "get-token" request:
+# link_payment_account : Do `/pay/account` API request to Midtrans Core API
+def link_payment_account(param)
-```js
-$(document).ready(function () {
- // function to prepare our credit card data before send
- function createTokenData() {
- return {
- card_number: $('#card_number').val(),
- card_cvv: $('#card_cvc').val(),
- card_exp_month: $('#card_exp').val().match(/(\d+) \//)[1],
- card_exp_year: '20' + $('#card_exp').val().match(/\/ (\d+)/)[1],
- gross_amount: $('#gross_amount').val(),
- secure: $('#card_secure')[0].checked
- };
- }
- // Add custom event for form submition
- $('#card_form').on('submit', function (event) {
- var form = this;
- event.preventDefault();
+# get_payment_account : Do `/pay/account/{account_id}` API request to Midtrans Core API
+def get_payment_account(account_id)
- Veritrans.token(createTokenData, function (data) {
- console.log('Token data:', data);
- // when you making 3D-secure transaction,
- // this callback function will be called again after user confirm 3d-secure
- // but you can also redirect on server side
- if (data.redirect_url) {
- // if we get url then it's 3d-secure transaction
- // so we need to open that page
- $('#3d-secure-iframe').attr('src', data.redirect_url).show();
- // if no redirect_url and we have token_id then just make charge request
- } else if (data.token_id) {
- $('#card_token').val(data.token_id);
- form.submit();
- // if no redirect_url and no token_id, then it should be error
- } else {
- alert(data.validation_messages ? data.validation_messages.join("\n") : data.status_message);
- }
- });
- });
-});
-```
+# unlink_payment_account : Do `/pay/account/{account_id}/unbind` API request to Midtrans Core API
+def unlink_payment_account(account_id)
-On a server side:
+# create_subscription : Do `/subscription` API request to Midtrans Core API
+def create_subscription(param)
-```ruby
-@result = Veritrans.charge(
- payment_type: "credit_card",
- credit_card: { token_id: params[:token_id] },
- transaction_details: {
- order_id: @payment.order_id,
- gross_amount: @payment.amount
- }
-)
-if @result.success?
- puts "Success"
-end
-```
+# get_subscription : Do `/subscription/{subscription_id}` API request to Midtrans Core API
+def get_subscription(subscription_id)
-## STEP 2: Process non credit cards payment
+# disable_subscription : Do `/subscription/{subscription_id}/disable` API request to Midtrans Core API
+def disable_subscription(subscription_id)
-We provide many payment channels to accept payment, but the API call is almost the same.
+# enable_subscription : Do `/subscription/{subscription_id}/enable` API request to Midtrans Core API
+def enable_subscription(subscription_id)
-For Snap / VT-Web in only one request, payment page will display all available payment options.
+# update_subscription : Do `/subscription/{subscription_id}` API request to Midtrans Core API
+def update_subscription(subscription_id, param)
+```
-For Core API / VT-Direct you have to specify payment method (without get token step, credit card token required only for credit card transactions).
+#### Credit Card Get Token
+Get token should be handled on Frontend please refer to [API docs](https://docs.midtrans.com/en/core-api/credit-card).
+Further example to demonstrate Core API card integration (including get card token on frontend), available on [Sinatra example](/example/sinatra)
+
+#### Credit Card Charge
+
```ruby
-@result = Veritrans.charge(
- payment_type: "bank_transfer",
- bank_transfer: { bank: 'permata' },
+result = Midtrans.charge(
+ payment_type: "credit_card",
+ credit_card: {
+ token_id: "CREDIT_CARD_TOKEN", # change with your card token,
+ authentication: true
+ },
transaction_details: {
- order_id: @payment.order_id,
- gross_amount: @payment.amount
- }
-)
-puts "Please transfer fund to account no. #{@result.permata_va_number} in bank Permata"
+ order_id: "test-transaction-12345",
+ gross_amount: 20000
+ })
+# result.data this will be Hash representation of the API JSON response:
+puts result.data
```
-See [our documentation](https://api-docs.midtrans.com/#charge-features) for other available options.
+#### Credit Card 3DS Authentication
+The credit card charge result may contains `redirect_url` for 3DS authentication. 3DS Authentication should be handled on Frontend please refer to [API docs](https://api-docs.midtrans.com/#card-features-3d-secure)
-## STEP 3: Receive notification callback
+For full example on Credit Card 3DS transaction refer to:
+- [Sinatra example](/example/sinatra) that implement Snap & Core Api
-For every transaction success and failed we will send you HTTP POST notification (aka webhook)
-First you should set callback url in our dashboard https://dashboard.sandbox.midtrans.com/settings/vtweb_configuration
+### 2.3 Handle HTTP Notification
+> **IMPORTANT NOTE**: To update transaction status on your backend/database, **DO NOT** solely rely on frontend callbacks! For security reason to make sure the status is authentically coming from Midtrans, only update transaction status based on HTTP Notification or API Get Status.
-For testing in development phase please read our [Testing webhooks tutorial](https://github.com/veritrans/veritrans-ruby/blob/master/testing_webhooks.md) and [command line tool](#command-line-tool)
+Create separated web endpoint (notification url) to receive HTTP POST notification callback/webhook.
+HTTP notification will be sent whenever transaction status is changed.
+Example also available [here](example/sinatra)
-
-For rails:
-
```ruby
-# config/routes.rb
-match "/payments/receive_webhook" => "payments#receive_webhook", via: [:post]
+post_body = JSON.parse(request.body.read)
+notification = Midtrans.status(post_body['transaction_id'])
-# app/controllers/payments_controller.rb
-def receive_webhook
- post_body = request.body.read
- callback_params = Veritrans.decode_notification_json(post_body)
+order_id = notification.data[:order_id]
+payment_type = notification.data[:payment_type]
+transaction_status = notification.data[:transaction_status]
+fraud_status = notification.data[:fraud_status]
- verified_data = Veritrans.status(callback_params['transaction_id'])
+puts "Transaction order_id: #{order_id}"
+puts "Payment type: #{payment_type}"
+puts "Transaction status: #{transaction_status}"
+puts "Fraud status: #{fraud_status}"
- if verified_data.status_code != 404
- puts "--- Transaction callback ---"
- puts "Payment: #{verified_data.data[:order_id]}"
- puts "Payment type: #{verified_data.data[:payment_type]}"
- puts "Payment status: #{verified_data.data[:transaction_status]}"
- puts "Fraud status: #{verified_data.data[:fraud_status]}" if verified_data.data[:fraud_status]
- puts "Payment amount: #{verified_data.data[:gross_amount]}"
- puts "--- Transaction callback ---"
+return "Transaction notification received. Order ID: #{order_id}. Transaction status: #{transaction_status}. Fraud status: #{fraud_status}"
- render text: "ok"
- else
- render text: "ok", :status => :not_found
- end
+# Sample transactionStatus handling logic
+if transaction_status == "capture" && fraud_status == "challange"
+ # TODO set transaction status on your databaase to 'challenge'
+elsif transaction_status == "capture" && fraud_status == "success"
+ # TODO set transaction status on your databaase to 'success'
+elsif transaction_status == "settlement"
+ # TODO set transaction status on your databaase to 'success'
+elsif transaction_status == "deny"
+ # TODO you can ignore 'deny', because most of the time it allows payment retries
+elsif transaction_status == "cancel" || transaction_status == "expire"
+ # TODO set transaction status on your databaase to 'failure'
+elsif transaction_status == "pending"
+ # Todo set transaction status on your databaase to 'pending' / waiting payment
end
```
-----
+### 2.4 Transaction Action
+For full example on transaction action refer to: [Api Reference](api_reference.md)
-#### Veritrans::Events
+## 3. Handling Error / Exception
+When using function that result in Midtrans API call e.g: `Midtrans.charge(...)` or `Midtrans.create_snap_token(...)`
+there's a chance it may throw error (`MidtransError` object), the error object will contains below properties that can be used as information to your error handling logic:
-Other option to handle callbacks is our rack-based handler
-
```ruby
-# config/routes.rb
-mount Veritrans::Events.new => '/vt_events'
+begin
+ Midtrans.create_snap_token(parameter)
+rescue MidtransError => e
+ puts e.message # Basic error message string
+ puts e.http_status_code # HTTP status code e.g: 400, 401, etc.
+ puts e.api_response # API response body in String
+ puts e.raw_http_client_data # Raw HTTP client response
+end
+```
-# config/initalizers/veritrans.rb
-Veritrans.setup do
- config.server_key = "..."
- config.client_key = "..."
+## 4. Advanced Usage
+### Override Notification URL
- events.subscribe('payment.success') do |payment|
- # payment variable is hash with params recieved from Veritrans
- # assuming you have model Order in your project
- Order.find_by(order_id: payment.order_id).mark_paid!(payment.masked_card)
- end
-
- events.subscribe('payment.failed', 'payment.challenge') do |payment|
- # payment variable is hash with params recieved from Veritrans
- # assuming you have model Order in your project
- Order.find_by(order_id: payment.order_id) ...
- end
-end
+You can opt to change or add custom notification urls on every transaction. It can be achieved by adding additional HTTP headers into charge request.
+```ruby
+# Add new notification url(s) alongside the settings on Midtrans Dashboard Portal (MAP)
+Midtrans.config.append_notif_url = "https://example.com/test1,https://example.com/test2"
+# Use new notification url(s) disregarding the settings on Midtrans Dashboard Portal (MAP)
+Midtrans.config.override_notif_url = "https://example.com/test1"
```
-#### Logging
+[More details](https://api-docs.midtrans.com/#override-notification-url)
+> **Note:** When both `appendNotifUrl` and `overrideNotifUrl` are used together then only `overrideNotifUrl` will be used.
-By default gem veritrans will show information via rails' logger. And in addition save important information to `RAILS_APP/log/veritrans.log`
+> Both header can only receive up to maximum of **3 urls**.
-It's configurable.
+### Idempotency-Key
+Is a unique value that is put on header on API request. Midtrans API accept Idempotency-Key on header to safely handle retry request
+without performing the same operation twice. This is helpful for cases where merchant didn't receive the response because of network issue or other unexpected error.
+You can opt to add idempotency key by adding additional HTTP headers into charge request.
+```ruby
+Midtrans.config.idempotency_key = "Unique-ID"
+```
+[More details](http://api-docs.midtrans.com/#idempotent-requests)
+### Log Configuration
+By default if you are using Rails, gem Veritrans will show information via Rails logger and in addition save important information to `RAILS_APP/log/Midtrans.log` <br>
+You can configure it like example below:
+
```ruby
-Veritrans.logger = Rails.logger
-Veritrans.file_logger = Logger.new("/my/important_logs/veritrans.log")
+Midtrans.logger = Rails.logger
+# To set custom logger
+Midtrans.file_logger = Logger.new("./log/midtrans.log")
```
-`Veritrans.file_logger` save information about:
+`Midtrans.file_logger` save information about:
* "charge", "cancel", "approve" api calls
* Validation errors for "charge", "cancel", "approve"
* Received http notifications
* Errors and exception while processing http notifications
----
-#### Command line tool
+### To see it in action, we have made example:
-**Installation**
+Sinatra, which demonstrate in as succint code as possible. Please [have a look here](https://github.com/veritrans/veritrans-ruby/tree/master/example/sinatra)
- $ gem install veritrans
-**Usage**
+### Get help
-Testing http notification:
-
- $ veritrans testhook http://localhost:3000/vt_events
- $ veritrans testhook -o my-order-1 -c ~/path/to/veritrans.yml http://localhost:3000/vt_events
-
-
-
-#### Get help
-
* [Veritrans gem reference](https://github.com/veritrans/veritrans-ruby/blob/master/api_reference.md)
* [Midtrans login](https://account.midtrans.com/login)
* [Midtrans registration](https://account.midtrans.com/register)
* [Midtrans documentation](http://docs.midtrans.com)
* Technical support [support@midtrans.com](mailto:support@midtrans.com)
+
+## Important Changes
+
+### v2.4.0
+- API client methods will now raise `MidtransError` when getting unexpected API response. You may need to update your error handling. [Handling Error / Exception](#3-Handling-Error--Exception)
+- Removed features: CLI, TestingLib. Mainly removed due to no longer relevant/essential to this library's purpose.