Skip to content

Instantly share code, notes, and snippets.

@jagdeepsingh
Last active September 24, 2019 08:46
Show Gist options
  • Save jagdeepsingh/fac2379f2f14a88ebc77649579708cfa to your computer and use it in GitHub Desktop.
Save jagdeepsingh/fac2379f2f14a88ebc77649579708cfa to your computer and use it in GitHub Desktop.
Red Dot Payment - Integration with Rails

Red Dot Payment - Integration with Rails

Description Link
Home http://reddotpayment.com/
Contact us http://reddotpayment.com/contact-us/
Test Dashboard http://test.reddotpayment.com/instanpanel/

If you don't have a sandbox account setup on Red Dot Payment (RDP), you can submit a request to generate one through their Contact Us page.

After the account is setup, you will be able to access the dashboard and see all your transactions there.

Contents:

1 Direct Tokenization API

Description Link
Overview http://reddotpayment.com/developers/?page_id=586
Generate signature http://reddotpayment.com/developers/?page_id=601
API Request and Response http://reddotpayment.com/developers/?page_id=620
Response codes http://reddotpayment.com/developers/?page_id=629

Note: To use this API, you need to confirm with RDP that your sandbox account is configured to use the tokenization feature. Else, you will see the error "Your account is not setup to access RDP TOKENIZATION Feature".

1.1 Proxy server

A thing to note is that Red Dot does not allow direct AJAX requests to their servers to do the tokenization. Instead, you will need to set up a separate highly secure proxy server, where you will be sending all the request params and the server just forwards them to Red Dot for tokenization. Below is the controller code for that server:

require 'net/http'
require 'uri'

class RedDotController < ApplicationController
  def token
    uri = URI.parse(api_url)

    http = Net::HTTP.new(uri.host, uri.port)
    http.use_ssl = true

    request = Net::HTTP::Post.new(uri.request_uri, 'Content-Type' => 'application/json')
    request.body = tokenization_params.to_json
    response = http.request(request)
    response_json = JSON.parse(response.body)

    render json: response_json,
           status: (response_json['response_code'] == '0' ? :ok : :unprocessable_entity)
  end

  private

  def api_url
    Rails.env.production? ?
      'https://secure.reddotpayment.com/service/token-api' :
      'https://secure-dev.reddotpayment.com/service/token-api'
  end

  def tokenization_params
    params.permit(
      :mid,
      :order_id,
      :api_mode,
      :transaction_type,
      :payer_name,
      :payer_email,
      :ccy,
      :card_no,
      :exp_date,
      :cvv2,
      :signature
    )
  end
end

1.2 SHA-512

When you have all the card details from the user, you will need to generate the signature for your request. If you are using a modern browser, then you can use WebCrypto API (window.crypto) to do that. Else, you can download the algorithm in JavaScript from here and include it in your application. Below code assumes window.crypto is defined in your browser.

window.SHA512 = (function() {
  function SHA512() {}

  SHA512.algorithm = 'SHA-512';

  SHA512.crypto = window.crypto || window.msCrypto;

  SHA512.digestPromise = function(input) {
    if (!SHA512.crypto.subtle) {
      return null;
    }
    return crypto.subtle.digest({
      name: SHA512.algorithm
    }, SHA512.toUint8Array(input));
  };

  SHA512.toUint8Array = function(input) {
    var bytes, i;
    bytes = new Uint8Array(input.length);
    i = 0;
    while (i < input.length) {
      bytes[i] = input.charCodeAt(i);
      i++;
    }
    return bytes;
  };

  SHA512.toString = function(input) {
    var c, dataView, hex, i, len;
    dataView = new DataView(input);
    hex = '';
    i = 0;
    len = dataView.byteLength;
    while (i < len) {
      c = dataView.getUint8(i).toString(16);
      if (c.length < 2) {
        c = "0" + c;
      }
      hex += c;
      i++;
    }
    return hex;
  };

  return SHA512;
})();

1.3 Request to proxy server

var params = { payer_name: 'Jagdeep Singh',
               payer_email: 'jagdeepsingh.125k@gmail.com',
               ccy: 'SGD',
               card_no: '4111111111111111',
               exp_date: '052023',
               cvv2: '123'
               mid: '1111110001',
               order_id: 'ord10001',
               api_mode: 'direct_token_api',
               transaction_type: 'C' }

// Generate signature:
// 1. Sort the keys of params hash
// 2. Concatenate the values (sorted by keys)
// 3. Append the secret key to result of (2)
// 4. Encrypt it using SHA-512 algorithm to generate the signature
var sortedKeys = Array.from(Object.keys(params).sort());
input = '';
sortedKeys.forEach(function(key) {
  return input += params[key];
});
input += SECRET_KEY;

signaturePromise = SHA512.digestPromise(input);
signaturePromise.then(function(hash) {
  params['signature'] = SHA512.toString(hash);

  $.ajax({
    url: 'https://your-proxy-server.com/reddot/token',
    type: 'GET',
    data: params,
    dataType: 'json',
    success: function(data) {
      console.log(data);
    },
    error: function(jqXHR) {
      console.log('Error!');
    }
  });
});

1.4 Response

Sample data in success callback of above JavaScript request:

{
    "mid": "1111110001",
    "transaction_id": "10001_565062251772589934",
    "order_id": "ord10001",
    "ccy": "SGD",
    "token_id": "8407275708254242",
    "acquirer_response_code": "0",
    "acquirer_response_msg": "OK",
    "payer_id": "8407275708254242",
    "created_timestamp": "2018-10-18 12:42:54",
    "response_code": "0",
    "response_msg": "OK",
    "request_timestamp": "2018-10-18 12:42:52",
    "payer_email": "jagdeepsingh.125k@gmail.com",
    "transaction_type": "C",
    "signature": "091d322ea59cc88be46b9791afedce0b79fa9fc1...007fe8141e3e7aff89e1f1b0a0d0d3dbe143578ea"
}

created_timestamp, and request_timestamp are the times in Timezone '+0800'.

Back to Top

2 Redirect API

Description Link
Overview http://reddotpayment.com/developers/?page_id=151
Generate signature http://reddotpayment.com/developers/?page_id=216
Generating payment URL http://reddotpayment.com/developers/?page_id=231
Payment callback http://reddotpayment.com/developers/?page_id=518

Below implementation is based on "Process Flow for Redirect Payment API Silent Order Page (SOP)".

2.1 Purchase

2.1.1 Generate payment URL

You will need payer_id and payer_email from the response of Direct Tokenization to make this request.

api_url = 'https://secure.reddotpayment.com/service/payment-api'.     # For non-LIVE environments, use 'https://secure-dev.reddotpayment.com/service/payment-api'

params = {
           order_id: 'ord10002',
           redirect_url: REDIRECT_URL,
           notify_url: NOTIFY_URL,
           mid: '1111110001',
           api_mode: 'redirection_sop',
           payment_type: 'S',
           ccy: 'SGD',
           amount: 149.99,
           payer_id: '8407275708254242',
           payer_email: 'jagdeepsingh.125k@gmail.com'
         }

# Generate signature
input = %i[mid order_id payment_type amount ccy payer_id].map { |k| params[k].to_s }.join.  # Order of attributes matter
input += SECRET_KEY
params[:signature] = Digest::SHA512.hexdigest(input)

# Request
uri = URI.parse(api_url)

http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true

request = Net::HTTP::Post.new(uri.request_uri, 'Content-Type' => 'application/json')
request.body = params.to_json

response = http.request(request)
=> #<Net::HTTPOK 200 OK readbody=true>

response_json = JSON.parse(response.body)
=> {"created_timestamp"=>1540382294,
 "expired_timestamp"=>1540468694,
 "mid"=>"1111110001",
 "order_id"=>"ord10002",
 "transaction_id"=>"322000327_1112340000644294846",
 "payment_url"=>
  "https://secure.reddotpayment.com/service/payment/--SERVER--/818439d5d2b10fa802a6e31200a0f49b1455ffbf38bd1c3441cbf1f327c4459a1742c6bf112c7a087ffebf69b",
 "response_code"=>0,
 "response_msg"=>"successful",
 "signature"=>"fe5988fadef0028c084ed53d6bce393de9808...901a9e23b21a8a988d66d843c30029955bdc5d34e"}

# Response is successful is `response_code` is set to `0`
is_success = response_json['response_code'] == 0

order_id length should not be more than 20 characters.

REDIRECT_URL is the URL of your application where you expect to GET the users redirected after they confirm payment on bank page. You will receive the query parameter transaction_id when RDP redirects users to it. There, you can check the status of the payment.

NOTIFY_URL is the URL where you will handle the webhook triggered by RDP when the status of payment is changed. This should be configured to receive POST request.

Note: By default, a merchant also needs another param cvv2 to make this request. You can ask the support to disable it for your merchant.

2.1.2 Callback

You need to handle the incoming GET request to the REDIRECT_URL provided by you in previous step.

class RedDotPaymentsController < ApplicationController
  before_filter :verified_3ds?, only: :callback

  def callback
    do_this_on_payment_success
  end
  
  private

  def verified_3ds?
    @txn = fetch_transaction(params.require(:transaction_id))   # Refer section "2.3 Fetch transaction" below
    return if @txn['response_code'].to_s == '0'                 # Success
    respond_with_error                                          # Failure
  end
end

2.1.3 Notify

For the NOTIFY_URL you passed in payment URL request, you can simply return a success response from your action, if you have already handled the callback.

def notify
  head :ok
end

2.2 Authorize

All the steps for authorize are same as purchase, except for using 'A' (instead of 'S') as payment_type in request for payment URL generation.

2.3 Fetch transaction

api_url = 'https://secure.reddotpayment.com/service/Merchant_processor/query_redirection'.   # For non-LIVE environment, use 'https://secure-dev.reddotpayment.com/service/Merchant_processor/query_redirection'

params = {
  request_mid: '1111110001',
  transaction_id: TRANSACTION_ID
}

# Generate signature
input = %i[request_mid transaction_id].map { |k| params[k].to_s }.join.  # Order of attributes matter
input += SECRET_KEY
params[:signature] = Digest::SHA512.hexdigest(input)

# Request
uri = URI.parse(api_url)

http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true

request = Net::HTTP::Post.new(uri.request_uri, 'Content-Type' => 'application/json')
request.body = params.to_json

response = http.request(request)
=> #<Net::HTTPOK 200 OK readbody=true>

response_json = JSON.parse(response.body)
=> {"mid"=>"1111110001",
 "transaction_id"=>TRANSACTION_ID,
 "order_id"=>"ord10002",
 "acquirer_transaction_id"=>"123456",
 "request_amount"=>"149.99",
 "request_ccy"=>"SGD",
 "authorized_amount"=>"149.99",
 "authorized_ccy"=>"SGD",
 "response_code"=>0,
 "response_msg"=>"successful",
 "acquirer_response_code"=>"",
 "acquirer_response_msg"=>"",
 "acquirer_authorization_code"=>"",
 "created_timestamp"=>"2018-10-25 14:45:06",
 "acquirer_created_timestamp"=>"2018-10-25 14:44:53",
 "request_timestamp"=>"2018-10-25 14:44:53",
 "request_mid"=>"1111110001",
 "payer_id"=>"c3c61aa60485d...32b6e9d5fd2",
 "transaction_type"=>"S",
 "signature"=>"40a9882258b25ba1c172ffedf7f1ea4879e4797abb...1446afca54bf907af91e24b9ad78f1697e903a5fb8cf2"}

All the timestamp fields have time in Timezone '+0800'.

Back to Top

3 Merchant API

Description Link
Overview http://reddotpayment.com/developers/?page_id=548
API Request and Response http://reddotpayment.com/developers/?page_id=554

3.1 Capture

api_url = 'https://connect.reddotpayment.com/instanpanel/api/payment'   # For non-LIVE environments, use 'https://test.reddotpayment.com/instanpanel/api/payment'

params = {
           mid: '1111110001',
           transaction_id: TRANSACTION_ID,
           order_number: ORDER_ID,
           currency: 'SGD',
           amount: 99.49,
           response_type: 'json',
           action_type: 'capture'
         }
         
# Generate signature
input_ary = params.keys.sort.map { |k| "#{k}=#{params[k]}" }
input_ary << "secret_key=#{SECRET_KEY}"
input = input_ary.join('&')
params[:signature] = Digest::SHA512.hexdigest(input)

# Request
uri = URI.parse(api_url)

http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true

request = Net::HTTP::Post.new(uri.request_uri, 'Content-Type' => 'application/json')
request.body = params.to_json

response = http.request(request)
=> #<Net::HTTPOK 200 OK readbody=true>

ORDER_ID is the order_id passed in API request for authorization.

3.2 Void

Void differs from capture only in the params. You don't need to pass currency and amount for voiding a transaction.

params = {
           mid: '1111110001',
           transaction_id: TRANSACTION_ID,
           order_number: ORDER_ID,
           response_type: 'json',
           action_type: 'void'
         }

3.3 Refund

Refund differs from capture only in param action_type.

params = {
           mid: '1111110001',
           transaction_id: TRANSACTION_ID,
           order_number: ORDER_ID,
           currency: 'SGD',
           amount: 99.49,
           response_type: 'json',
           action_type: 'refund'
         }

Back to Top

4 Hosted Tokenization API

Contents:

4.1 Setup account and generate keys

Get credentials for Connect 2 Portal from RDP. You will need Company name, Email and a Password to access the admin panel.

Create a Tokenization page by providing a Redirect URL (where RDP will make a POST callback request with encrypted card token). It will generate the API credentials. Note down the following:

  • Merchant ID- xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
  • Client Key- xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
  • Client Secret- xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

4.2 Generate payment page URL

Now, let's make an API request to generate the URL for page where customers will enter the card details.

api_url = "https://connect2.api.reddotpay.sg/v1/payments/token/authTokenUrl/#{MERCHANT_ID}"

headers = { 'Content-Type' => 'application/json' }

params = {
  clientKey: CLIENT_KEY,
  clientSecret: CLIENT_SECRET,
  returnUrl: 'https://domain.com/path/to/merchant'
}

response = HTTParty.post(
  api_url,
  headers: headers,
  body: params.to_json
)

response.parsed_response
 =>  {"token"=>"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVLTkzODQtYU3IiwiaWF0IjoxNTY4MDE1MjE3LCJleHAiOjE1NjgwMTYxMTd9.fNYPN0RSNsqMyt32cLu21WfmLz3CIyXKKxaqn6Od5gk", "pageId"=>"bccc0ecb-e03d-9aa9-9384-a02aebaba4e7", "pageURI"=>"https://connect2.reddotpay.sg/m/d6e01d1d-aac0-abcd-9fff-4beae099fba7#/tokenize/eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVLTkzODQtYU3IiwiaWF0IjoxNTY4MDE1MjE3LCJleHAiOjE1NjgwMTYxMTd9.fNYPN0RSNsqMyt32cLu21WfmLz3CIyXKKxaqn6Od5gk", "rdpCustomerReference"=>"d211f4bf-612a-abcd-dcba-9fa975d4bd39"}

returnUrl represents the URL where customers will be taken to when they click "Back to merchant" button after their card is tokenized.

Store rdpCustomerReference in your database for referencing in tokenization callback. It is unique for every tokenization request.

pageURI is the URL to which you need customers to redirect to for entering card details.

4.3 Handle tokenization callback

Test cards:

Card Number Expiry CVV
4111 1111 1111 1111 09/2029 123
4532 0818 6469 8195 09/2029 123

After customer enters the card details on payment page (at pageURI) and clicks "Submits", card details are tokenized at RDP server and a POST callback request is made back to the application with the result.

class RDPController < ApplicationController
  def token_callback
    params
    # => {"messageToken"=>"eyJhbGciOiJIU6IkpXVCJ9.eyJkYXRlQWRkZWQiOiIyMDE5LTA5LTg3ZmYtZDc1ZS00ZDdhLTlkMGMtNmE3NTFjMGQwZmU3IiwiY2FyZFRva2VuRXhwaXJlc1VuaXhEYXRlIjoxNTc1OTUyNjAzLCJpYXQiOjE1NjgxNzY2MTB9.08TUGmmYa0LQvUsHDTPIBq-lR3jjc", "pageId"=>"bccc0ecb-e03d-abcd-1234-a02aebaba4e7"}
  end
end

pageId is the same as returned in response of 4.2.

messageToken is a JWT token. It contains all the details like card holder name, last digits of card, a card token, etc. Decrypt it using ruby library ruby-jwt.

def token_callback
  JWT.decode(params[:messageToken], CLIENT_SECRET)
  # => [{"dateAdded"=>"2019-09-11 04:35:58", "paymentType"=>"", "ip"=>"0.0.0.0", "returnUrl"=>"https://domain.com/path/to/save/card/token", "rdpCustomerReference"=>"d211f4bf-612a-abcd-dcba-9fa975d4bd39", "description"=>"", "dateProcessed"=>"", "cardInfo"=>{"cardHolderName"=>"Jagdeep Singh", "network"=>"Visa", "last4Digits"=>"1111"}, "cardToken"=>"411111-214287ff-d75e-abcd-1234-6a751c0d0fe7", "cardTokenExpiresUnixDate"=>1575952603, "iat"=>1568176610}, {"alg"=>"HS256", "typ"=>"JWT"}]
end

Below are some of the important fields:

  • rdpCustomerReference - It is the same as returned in response of 4.2.
  • cardInfo
    • cardHolderName - Name on card
    • network - Card type
    • last4Digits
  • cardToken - Token to be used for making charges on the card
  • cardTokenExpiresUnixDate - Expiry date of cardToken (normally 3 months in future)

4.4.1 Generate access token

auth = [CLIENT_KEY, CLIENT_SECRET].join(':')
headers = {
  'Content-Type' => 'application/x-www-form-urlencoded',
  'Authorization' => "Basic #{Base64.strict_encode64(auth)}"
}

response = HTTParty.post(
  'https://secure.api.reddotpay.sg/oauth2/token',
  headers: headers,
  body: { grant_type: 'client_credentials' }
)

response.code
 => 200

response.parsed_response
 => {"access_token"=>"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NjgyMDQyMhwnDw_kytSWcgeoYzRcqpNStSW1Qa3LnTv7alBM-BxBw-MXDaR1iPRX3a9iGUJZcgoKxVsVn_Y4YsfTg6lqBjDlgdsK51UCEWV8Jw", "expires_in"=>1568204206, "token_type"=>"Bearer"}

4.4.2 Purchase

Below is the request to make a purchase transaction using cardToken:

headers = {
  'Content-Type' => 'application/json',
  'Authorization' => ACCESS_TOKEN,
  'Accept' => 'application/json'
}

params = {
  description: "My first order",
  amount: 95.49,
  currency: 'SGD',
  merchant: 'rid:mam:merchant/MERCHANT_ID',
  account: 'rid:mam:account/ACCOUNT_ID',
  customer: {
    token: 'rid:card:repo/TOKENIZATION_REPO_ID/token/CARD_TOKEN'
  },
  reference: '123456'
}

response = HTTParty.post(
  'https://cardpay.api.reddotpay.sg/v1/order',
  headers: headers,
  body: params.to_json
)

response.code
 => 201
 
response.parsed_response
 => {"id"=>"0d09d133c8847fc6599f253a7cc639be", "account"=>{"rid"=>"rid:mam:account/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", "gateway"=>"maybank"}, "merchant"=>"rid:mam:merchant/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", "authorize"=>{"amount"=>95.49, "currency"=>"SGD"}, "capture"=>{"amount"=>95.49, "currency"=>"SGD"}, "description"=>"My first order", "state"=>"capture", "customer"=>{"token"=>"rid:card:repo/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/token/xxxxxx-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"}, "transactions"=>[{"id"=>"af1d0c2a93b0ee8bdeb7af77ddab7b98", "xid"=>"123456", "ledger"=>"rid:veritas:entry/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "amount"=>95.49, "currency"=>"SGD", "action"=>"auto-capture", "status"=>"success", "authCode"=>"657300", "date"=>{"created"=>"2019-09-13T06:21:27Z"}, "acquirer"=>{"code"=>"0", "message"=>"APPROVED OR COMPLETED"}}], "date"=>{"created"=>"2019-09-13T06:21:31Z"}}

ACCESS_TOKEN in headers is the access token generated in 4.4.1.

In the params:

  • description- The doc says this attribute is optional, but the request fails if we don't pass it.
  • merchant- MERCHANT_ID is Merchant ID generated in 4.1 above.
  • account- ACCOUNT_ID (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx) is something RDP will provide you. Contact their support.
  • customer
    • token- TOKENIZATION_REPO_ID (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx) can be found in the browser URL when you visit your tokenization page on Connect 2 portal. CARD_TOKEN is the cardToken from 4.3 above.
  • reference- It must be a unique string.

Whether the payment will be auto-captured on not, it depends on the settings of your account. Please check those with RDP. You can disable auto-capture and then every payment will be authorized only. You will have to manually capture them.

Below is the sample response for authorized transaction:

response.parsed_response
 => {"id"=>"29812ab6ecd5393e226921b8039cc134", "account"=>{"rid"=>"rid:mam:account/xxxxxx-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", "gateway"=>"maybank"}, "merchant"=>"rid:mam:merchant/xxxxxx-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", "authorize"=>{"amount"=>95.49, "currency"=>"SGD"}, "description"=>"00168-1568362255", "state"=>"authorize", "customer"=>{"token"=>"rid:card:repo/xxxxxx-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/token/xxxxxx-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"}, "transactions"=>[{"id"=>"d780471012adfc67973918f78913a139", "xid"=>"123456", "ledger"=>"rid:veritas:entry/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "amount"=>95.49, "currency"=>"SGD", "action"=>"authorize", "status"=>"success", "authCode"=>"657300", "date"=>{"created"=>"2019-09-13T08:10:52Z"}, "acquirer"=>{"code"=>"0", "message"=>"APPROVED OR COMPLETED"}}], "date"=>{"created"=>"2019-09-13T08:10:57Z"}}
headers = {
  'Content-Type' => 'application/json',
  'Authorization' => ACCESS_TOKEN,
  'Accept' => 'application/json'
}

params = {
  amount: 95.49,
  currency: 'SGD'
}

response = HTTParty.post(
  'https://cardpay.api.reddotpay.sg/v1/order/ORDER_ID/capture',
  headers: headers,
  body: params.to_json
)

response.parsed_response
 => {"id"=>"c9953ecab569f8ab932746eb4e4f5594", "account"=>{"rid"=>"rid:mam:account/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", "gateway"=>"maybank"}, "merchant"=>"rid:mam:merchant/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", "authorize"=>{"amount"=>95.49, "currency"=>"SGD"}, "capture"=>{"amount"=>95.49, "currency"=>"SGD"}, "state"=>"capture", "customer"=>{"token"=>"rid:card:repo/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/token/xxxxxx-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"}, "transactions"=>[{"id"=>"489ed57d63fcf98c89cf7b31aad6a06e", "xid"=>"123456", "ledger"=>"rid:veritas:entry/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "amount"=>95.49, "currency"=>"SGD", "action"=>"capture", "status"=>"success", "date"=>{"created"=>"2019-09-13T11:14:24Z"}, "acquirer"=>{"code"=>"0", "message"=>"APPROVED OR COMPLETED"}}], "date"=>{"created"=>"2019-09-13T11:14:24Z"}}

ORDER_ID can be found at response.parsed_response['id'] in 4.4.2 above.

response = HTTParty.post(
  'https://cardpay.api.reddotpay.sg/v1/order/ORDER_ID/void',
  headers: { 'Authorization' => ACCESS_TOKEN }
)

response.code
 => 201
 
response.parsed_response
 => {"id"=>"2a6184fa1497db409311ca0d31806807", "account"=>{"rid"=>"rid:mam:account/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", "gateway"=>"maybank"}, "merchant"=>"rid:mam:merchant/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", "state"=>"close", "customer"=>{"token"=>"rid:card:repo/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/token/xxxxxx-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"}, "transactions"=>[{"id"=>"850a34576b2f5caf907c3c4ae5a85ef1", "xid"=>"123456", "ledger"=>"rid:veritas:entry/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "action"=>"void", "status"=>"success", "date"=>{"created"=>"2019-09-20T10:43:43Z"}, "acquirer"=>{"code"=>"0", "message"=>"APPROVED OR COMPLETED"}}]}
headers = {
  'Content-Type' => 'application/json',
  'Authorization' => ACCESS_TOKEN,
  'Accept' => 'application/json'
}

params = {
  amount: 49.49,
  currency: 'SGD'
}

response = HTTParty.post(
  'https://cardpay.api.reddotpay.sg/v1/order/ORDER_ID/refund',
  headers: headers,
  body: params.to_json
)

response.code
 => 201
 
response.parsed_response
 => {"id"=>"41b0cf1597373aef00edd5cfa5ce1806", "account"=>{"rid"=>"rid:mam:account/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", "gateway"=>"maybank"}, "merchant"=>"rid:mam:merchant/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", "authorize"=>{"amount"=>95.49, "currency"=>"SGD"}, "capture"=>{"amount"=>95.49, "currency"=>"SGD"}, "refund"=>{"amount"=>49.49, "currency"=>"SGD"}, "state"=>"refund", "customer"=>{"token"=>"rid:card:repo/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/token/xxxxxx-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"}, "transactions"=>[{"id"=>"e563cec5ac9c29ba8964bc7f07117d2a", "xid"=>"12345", "ledger"=>"rid:veritas:entry/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "amount"=>49.49, "currency"=>"SGD", "action"=>"refund", "status"=>"success", "date"=>{"created"=>"2019-09-20T11:17:27Z"}, "acquirer"=>{"code"=>"0", "message"=>"APPROVED OR COMPLETED"}}]}

Back to Top

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment