Preparation / Prerequisite for SNAP

Setup Credentials for SNAP API

Account Setup

You can skip this step if you have created a sandbox account previously.

  1. Contact our team ([email protected]) to start creating your account. If you're eligible, a dedicated account manager will be assigned to support you every step of the way.
  2. Your account manager will require ask for some basic informations to setup your merchant account.
  3. Verify your email.
  4. You can start accessing your dashboard test account.

List of Credentials for SNAP API:

Following credentials will be automatically generated for you in sandbox dashboard under Menu Settings > API Key:

  • Client Key (Will be used as X-Client-Key and X-Partner-ID in request header)
  • Client Secret Key (Will be used for encrypting symmetric X-Signature)
  • Merchant ID (Will be used for several API request body such as Balance Inquiry)
  • Public Key given by Durianpay (Will be used to verify signature from us)

Additionally you need to generate Merchant's Private & Public Key Pair from your end

  • Merchant Private Key will be used for encrypting asymmetric X-Signature
  • Merchant Public Key will be used by Durianpay to verify signature of request transaction from the merchant

All credentials above will need to be setup for both live and sandbox environment. Live credentials will be given only after merchant 's legal document & functionality test is approved by Durianpay.


Generate Your Private/Public Key Pair

You can refer to following steps to generate private/public key pair from your system.

Generate Private Key

openssl genrsa -out rsa_private_key.pem 2048

Generate Public Key

openssl rsa -in rsa_private_key.pem -out rsa_public_key.pem -pubout

You need to generate different public-private key pair for sandbox mode and live mode

Merchant Key Exchange Procedure

In order to securely exchanging keys between you and Durianpay. Here's following procedure that will need to be adhered:

Sharing your public key to Durianpay

  1. Please ensure the file of your public key should only be in a compressed format (i.e. file type: .zip or .rar) and the file should be password protected.
  2. Email your compressed public key to [email protected]
  3. Email the password of the public key file to [email protected]
  4. Durianpay team will setup your public key in our system and notify you once it's setup.

Request Header

Each SNAP Request requires specific header value. There are 2 kind of header for requests, to generate B2B Access Token, and Transaction (disbursement, payment, etc)

B2B Access Token Header

HeaderDescriptionFormat
X-TIMESTAMPRequest timestamp ISO 8601datetime
X-SIGNATURESecurity signatureBase64 encoded string
X-CLIENT-KEYMerchant identifier keyString
Content-TypeType of data being sentMIME type

Transaction Request Header

Each SNAP Request requires specific header value. There are 2 kind of header for requests, to generate B2B Access Token, and Transaction (disbursement, payment, etc)

HeaderDescriptionFormat
X-TIMESTAMPRequest timestampISO 8601 datetime
X-SIGNATURESecurity signatureBase64 encoded string
X-PARTNER-IDPartner identifierAlphanumeric string
X-EXTERNAL-IDUnique messaging reference identifier generated by merchant and should be unique within the same dayRandom Alphanumeric string
CHANNEL-IDChannel identifierNumeric string
Content-TypeType of data being sentMIME type
AuthorizationAuthorization tokenJWT (Started with 'Bearer ')

Generate Signature

There are two use kind of signature to be generated, one is to obtain access token, and another is for transaction. Signature for B2B Access Token is encrypted in an asymmetrically way, while for transaction signature is encrypted in a symmetrically way.

Generate Signature To Obtain Access Token (B2B)

You can find the required structure of the string to be encrypted as below:

<X-CLIENT-KEY>|<X-TIMESTAMP> // (on ISO8601 format)
//Example: merchant_client_key|2024-05-13T14:53:06.991+07:00

which then signed using SHA256withRSA with Private/Public Key pair merchant has generated.

X-Signature: aw3o6HM68vJDLO4nxAPgK0it5nd6zik3bUgMzqLiTrIB7w1QbnCLDo/IMVjaYsbPk9s=

Generate Signature For Transaction API

You can find the required structure of the string to be encrypted as below:

<HTTP METHOD> + ":" + <RELATIVE PATH URL> + ":" + <B2B ACCESS TOKEN> + ":" + LowerCase(HexEncode(SHA-256(Minify(<HTTP BODY>)))) + ":" + <X-TIMESTAMP>
  // Example: POST:/v1.0/balance-inquiry:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJEdXJpYW4gTW9uZXkiLC :f806c49e8cd175aa9dd5dd8e0a49648c885954d:2024-05-13T15:07:07+07:00

which then encrypted using HMAC-SHA512 with merchant's Secret Key that Durianpay give.

X-Signature: tnNHFw5ZCWjnHFd9UGAB5iDLuwo+89efml8F1CP8vaqut/PfNWac/XLiCkLt3zGHTppZKPxz/PPptqM2alsOFA==

Generate Access Token (B2B)

📘

B2B Access Token will be used for all transactional APIs. The token itself will have 900 seconds (15 minutes) valid time by default.

There is no refresh access token nor the access token will be auto refreshed, so you will need to generate another access token if it has passed its expiry time

curl --location 'https://api.durianpay.id/v1.0/access-token/b2b' \
--header 'X-TIMESTAMP: 2024-05-13T15:32:36.422+07:00' \
--header 'X-SIGNATURE: KLJ9KthaIiZSzPqHUMuJRsKOZtYCqqWd5DhfWqYC7OjZ2Qlu0PvQ2wRQF5vfDWsz/qFYEexOXb47+oQBjMhAz4XRNhXBBfwoWxrGTG8iU8EYSDVfChW0NqAWjWIQolhB4UcHn5SybPFRON3rvAOIQTzuEshUA1PNAln8jOKaRmY=' \
--header 'X-CLIENT-KEY: client_id' \
--header 'Content-Type: application/json' \
--data '{
    "grantType": "AUTHORIZATION_CODE"
}'
import requests

url = 'https://api.durianpay.id/v1.0/access-token/b2b'
headers = {
    'X-TIMESTAMP': '2024-05-13T15:32:36.422+07:00',
    'X-SIGNATURE': 'KLJ9KthaIiZSzPqHUMuJRsKOZtYCqqWd5DhfWqYC7OjZ2Qlu0PvQ2wRQF5vfDWsz/qFYEexOXb47+oQBjMhAz4XRNhXBBfwoWxrGTG8iU8EYSDVfChW0NqAWjWIQolhB4UcHn5SybPFRON3rvAOIQTzuEshUA1PNAln8jOKaRmY=',
    'X-CLIENT-KEY': 'client_id',
    'Content-Type': 'application/json'
}
data = {
    "grantType": "AUTHORIZATION_CODE"
}

response = requests.post(url, headers=headers, json=data)

print(response.text)
OkHttpClient client = new OkHttpClient();

String url = "https://api.durianpay.id/v1.0/access-token/b2b";
String timestamp = "2024-05-13T15:32:36.422+07:00";
String signature = "KLJ9KthaIiZSzPqHUMuJRsKOZtYCqqWd5DhfWqYC7OjZ2Qlu0PvQ2wRQF5vfDWsz/qFYEexOXb47+oQBjMhAz4XRNhXBBfwoWxrGTG8iU8EYSDVfChW0NqAWjWIQolhB4UcHn5SybPFRON3rvAOIQTzuEshUA1PNAln8jOKaRmY=";
String clientKey = "client_id";
String contentType = "application/json";

Gson gson = new Gson();
String jsonBody = gson.toJson(Map.of("grantType", "AUTHORIZATION_CODE"));

RequestBody requestBody = RequestBody.create(MediaType.parse(contentType), jsonBody);

Request request = new Request.Builder()
    .url(url)
    .addHeader("X-TIMESTAMP", timestamp)
    .addHeader("X-SIGNATURE", signature)
    .addHeader("X-CLIENT-KEY", clientKey)
    .addHeader("Content-Type", contentType)
    .post(requestBody)
    .build();
Response response = client.newCall(request).execute();
const request = require('request');

const url = 'https://api.durianpay.id/v1.0/access-token/b2b';
const headers = {
    'X-TIMESTAMP': '2024-05-13T15:32:36.422+07:00',
    'X-SIGNATURE': 'KLJ9KthaIiZSzPqHUMuJRsKOZtYCqqWd5DhfWqYC7OjZ2Qlu0PvQ2wRQF5vfDWsz/qFYEexOXb47+oQBjMhAz4XRNhXBBfwoWxrGTG8iU8EYSDVfChW0NqAWjWIQolhB4UcHn5SybPFRON3rvAOIQTzuEshUA1PNAln8jOKaRmY=',
    'X-CLIENT-KEY': 'client_id',
    'Content-Type': 'application/json'
};
const data = {
    "grantType": "AUTHORIZATION_CODE"
};

request.post({
    url: url,
    headers: headers,
    json: data
}, (error, response, body) => {
    if (error) {
        console.error('Request error:', error);
    } else {
        console.log('Response status code:', response.statusCode);
        console.log('Response body:', body);
    }
});
require 'net/http'
require 'uri'
require 'json'

url = URI.parse('https://api.durianpay.id/v1.0/access-token/b2b')
headers = {
  'X-TIMESTAMP' => '2024-05-13T15:32:36.422+07:00',
  'X-SIGNATURE' => 'KLJ9KthaIiZSzPqHUMuJRsKOZtYCqqWd5DhfWqYC7OjZ2Qlu0PvQ2wRQF5vfDWsz/qFYEexOXb47+oQBjMhAz4XRNhXBBfwoWxrGTG8iU8EYSDVfChW0NqAWjWIQolhB4UcHn5SybPFRON3rvAOIQTzuEshUA1PNAln8jOKaRmY=',
  'X-CLIENT-KEY' => 'client_id',
  'Content-Type' => 'application/json'
}
data = { 'grantType' => 'AUTHORIZATION_CODE' }

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

request = Net::HTTP::Post.new(url.request_uri, headers)
request.body = data.to_json

response = http.request(request)
puts "Response code: #{response.code}"
puts "Response body: #{response.body}"
{
    "responseCode": "2007300",
    "responseMessage": "Successful",
    "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJEdXJpYW4gTW9uZXkiLCJleHAiOjE3MTU1OTAwNTYsImlhdCI6MTcxNTU4OTE1NiwibWVyY2",
    "tokenType": "BearerToken",
    "expiresIn": "2024-05-13 08:47:36.452492159 +0000 UTC"
  }

Response Definition

Format

SNAP API has its own standardized Response Code and Response Message for each of endpoint. In general, the response code will consist of 7 digits with HTTP code, Service Code, and Case Code scheme.

When doing request of balance query to https://api.durianpay.id/v1.0/balance-inquiry with the same X-External-ID twice, merchant will receive error with code 4091100 where:

  • 409 is HTTP code for Conflict
  • 11 is service code for balance inquiry, and
  • 00 together with 409 HTTP code indicates same X-External-ID or partnerReferenceNo is being used twice.
    Reference

HTTP code used is following the standard defined by RFC 9110

Service Code

Service Code is the code that indicates which service being requested depending of its relative path. For example, B2B Access Token generation has a service code of 73, Balance Inquiry has a service code of 11, etc. Please refer to the table below for list of services implemented by Durianpay.

Table of Service Code

Service NameService CodeRelative Path
Access Token (B2B)73/v1.0/access-token/b2b
Balance Inquiry11/v1.0/balance-inquiry
Account Inquiry (External)16/v1.0/account-inquiry-external
E-Wallet Account Inquiry37/v1.0/emoney/account-inquiry
Transfer Interbank18/v1.0/transfer-interbank
Transfer E-Wallet38/v1.0/emoney/topup
Transfer Status Inquiry36/v1.0/transfer/status

Response Code List

In general, there are two types of response to a request in SNAP, Successful and Failed. Successful response is indicated with HTTP code of 2xx while 4xx and 5xx indicates error (from client side or server/Durianpay side).

Please refer to table below for list of Response Code, Response Message, and its meaning.

Successful Response

HTTP CodeService CodeCase CodeResponse MessageDescription
200any00SuccessfulSuccessful
202any00Request In ProgressTransaction still on proces

Error Response

HTTP CodeService CodeCase CodeResponse MessageDescription
400any00Bad RequestGeneral request failed error, including message parsing failed.
400any01Invalid Field Format {field name}Invalid format
400any02Invalid Mandatory Field {field name}Missing or invalid format on mandatory field
401any00Unauthorized [reason]General unauthorized error (No Interface Def, API is Invalid, Oauth Failed, Verify Client Secret Fail, Client Forbidden Access API, Unknown Client, Key not Found, Invalid Signature)
401any01Invalid Token (B2B)Token found in request is invalid (Access Token Not Exist, Access Token Expiry)
401any03Token Not Found (B2B)Token not found in the system. This occurs on any API that requires token as input parameter
403any00Transaction ExpiredTransaction expired
403any18Inactive Card/Account/CustomerIndicates inactive account
404any01Transaction Not FoundTransaction not found
404any11Invalid Card/Account/Customer [info]/Virtual AccountCard information may be invalid, or the card account may be blacklisted, or Virtual Account number may be invalid.
409any00ConflictCannot use same X-EXTERNAL-ID in same day
409any01Duplicate partnerReferenceNoTransaction has previously been processed indicates the same partnerReferenceNo already created
500any00General ErrorGeneral Error
500any01Internal Server ErrorUnknown Internal Server Failure, Please inquiry the status or retry the process again
504any00TimeoutTimeout from the issue