Introduction
SNAP or Standar Nasional Open API is a regulation established by Bank Indonesia through the Governor of Bank Indonesia Decree No.23/10/KEP.GBI/2021 dated August 16 2021, concerning the establishment of Standar Nasional Open API (Application Programming Interface), managed by ASPI (Indonesian Payment System Association).
SNAP is a national protocol and instruction that facilitates interconnection between applications in the payment transaction process standard for payment gateway and it is determined by Bank Indonesia.
Currently, Duitku as one of the payment gateways is on the move to implement these standards. Here is the flow that has been prepared and adjusted for you.
SNAP API
Here is the host that used for API:
- Production:
https://snap.duitku.com
- Development:
https://snapdev.duitku.com
SNAP Registration at Duitku
To use the standarized API by SNAP, merchant need to obtain approval from ASPI in the form of a recommendation letter. To receive this letter, merchant must conduct developer site test and functionality test.
Developer Site Test: This test is conducted on the ASPI page to ensure that the API complies with SNAP's technical and security standards. To conduct the developer site test, merchant must register on the registration page at ASPI. Merchant can perform the test by following the developer site test guideline on the ASPI website. The developer site test includes at least 1 positive test scenario and 1 negative scenario for each sub-API used.
Functionality Test: Once the developer site test results meet the standards, merchant can proceed with the provided Functionality Test. The functionality test is conducted to test the SNAP API components that have been integrated with Duitku end-to-end.
Before starting SNAP integration, you need to register for SNAP at Duitku. Please send the public key, information, and required documents using the email registered with Duitku to [email protected].
You need to send 2 separate emails to Duitku. To facilitate the registration process, you must use the primary email registered with Duitku. Below are the information and documents you need to attach when sending these emails:
First Email:
- Project code sandbox
- API Services. The API services provided by Duitku:
- Virtual Account
- Direct Debit Redirect
- Direct Debit Linking
- QRIS-MPM
- Zip file containing Public Key in .pem format and protected by a password. For instructions on creating a public key, refer to the Public Key and Private Key guidelines.
- SNAP payment URL to receive payment notifications. This URL must follow the payment notification standard and match the required API services:
You can use the email format here .
Second Email:
- Password to open the zip file containing the public key sent previously
API Authentication
The authentication in the SNAP API there is:
Public Key and Private Key
Create an RSA key pair for your access. Save the private key and keep it safe and secure. Then you need to send the public key to Duitku to give you access to request the API. Make sure you generate the key within 2048-bit.
Here is an example of how you can create an RSA public and private key:
- You could use Git OpenSSL.
- Git OpenSSL usually needs to run on its directory. Run the command below. (Windows Example)
cd "..\..\Program Files\Git\usr\bin"
- Then, create the private key using these commands:
openssl genrsa -out C:\Users\{your_users}\Desktop\PrivateKey.pem 2048
- After you create the private key you can create the public key using the private key with the commands below:
openssl rsa -in C:\Users\{your_users}\Desktop\PrivateKey.pem -pubout -out C:\Users\{your_users}\Desktop\Publickey.pem
Now, you should have the RSA key pair. Next, you need to save and put the private key on your project and send the public key to Duitku. You might need the private key in your project so your project would have to keep the access.
Bearer Token
When you're working with web APIs, a Bearer Token is an access token that is often used for authorization purposes. This token acts like a security key that a server uses to verify that the request is coming from an authenticated user or service.
To use a Bearer Token with the SNAP API, you need to obtain the token by providing your credentials to the authentication endpoint.
Get Token API
To use this code example you might need to install npm axios and jsrsasign.
Install the package on your project using npm:
npm install axios jsrsasign jsrsasign-util
Copy the code below:
(select javascript tag to show the example code)
const axios = require("axios")
const rs = require('jsrsasign')
const rsu = require('jsrsasign-util')
const CryptoJS = require('crypto-js')
const partnerId = "DXXXX"
const date = new Date()
const headers = headersAuthGenerator(partnerId, date)
const body = {
"grantType": "client_credentials"
}
axios.post("https://snapdev.duitku.com/auth/v1.0/access-token/b2b/", body, {headers: headers })
.then(response => res.json(response.data))
.catch(err => res.json(err.response.data))
//Function to create headers
function headersAuthGenerator(partnerId, date){
let stringToSign = `${partnerId}|${toIsoString(date)}`
//read your private key from your private key file
let privateKey = rs.KEYUTIL.getKey(rsu.readFile("./mykey/privatekey.pem"))
let sign = new rs.KJUR.crypto.Signature({"alg": "SHA256withRSA"});
sign.init(privateKey)
let hash = sign.signString(stringToSign)
const signature = CryptoJS.enc.Base64.stringify(CryptoJS.enc.Hex.parse(hash))
let headers = {
"X-TIMESTAMP": toIsoString(date),
"X-SIGNATURE": signature,
"X-CLIENT-KEY": partnerId
}
return headers
}
//function to iso string helper
function toIsoString(date) {
var tzo = -date.getTimezoneOffset(),
dif = tzo >= 0 ? '+' : '-',
pad = function(num) {
return (num < 10 ? '0' : '') + num;
};
return date.getFullYear() +
'-' + pad(date.getMonth() + 1) +
'-' + pad(date.getDate()) +
'T' + pad(date.getHours()) +
':' + pad(date.getMinutes()) +
':' + pad(date.getSeconds()) +
dif + pad(Math.floor(Math.abs(tzo) / 60)) +
':' + pad(Math.abs(tzo) % 60);
}
Method : HTTP POST
Type : application/json
Development : https://snapdev.duitku.com/auth/v1.0/access-token/b2b
Production : https://snap.duitku.com/auth/v1.0/access-token/b2b
Service Code : 73
Header :
Parameter | Type | Required | Description | Example |
---|---|---|---|---|
Content-Type | string | ✗ |
String represents indicate the media type of the resource. | application/json |
X-TIMESTAMP | string | ✓ |
ISO-8601 | 2022-09-16T13:00:00+07:00 |
X-SIGNATURE | string | ✓ |
Non-Repudiation & Integrity checking X-Signature with asymmetric algorithm. Formula: stringToSign = Client Key + “|” + Timestamp signature = SHA256withRSA(Private_Key, stringToSign) |
|
X-CLIENT-KEY | string | ✓ |
Project Id provided by Duitku. | DXXXX |
Body :
{
"grantType": "client_credentials"
}
Parameter | Type | Required | Description | Example |
---|---|---|---|---|
grantType | string | ✓ |
The value is "client_credentials" |
"client_credentials" |
Response :
{
"responseCode": "2007300",
"responseMessage": "Successful",
"accessToken": "ZGMyNDA3NWQtNmM4Ny00NGNiLTQ2NTAtMDhkYWMxNTAzNzY0",
"tokenType": "Bearer",
"expiresIn": "900"
}
Code | Message | Keterangan |
---|---|---|
2007300 | Successful | Successful processing and obtaining the accessToken . |
4007301 | Invalid Field Format {grantType} | Incorrect value for the parameter grantType . |
4007302 | Invalid Mandatory Field {grantType} | Missing parameter grantType . |
4007302 | Invalid Client Key or Timestamp or Signature | Error occurred among Client Key, Timestamp, or Signature. |
4017300 | Invalid Client Key | Error in the Client Key. |
4017300 | Invalid Signature | Error in the Signature. |
Signature
The Signature security mechanism for the Payment API offers two distinct approaches. Firstly, utilizing the symmetric HMAC_512 cryptographic function, where an access token is integral to the process. Alternatively, the asymmetric SHA256withRSA cryptographic function can be employed, functioning independently of an access token.
It is advisable for clients to adopt the symmetric HMAC_512 cryptographic function in conjunction with an access token when generating signatures for the Payment Method API. This method ensures a robust and secure approach.
Conversely, the Notify Payment API will exclusively leverage the asymmetric SHA256withRSA cryptographic function, ensuring a specific and tailored security measure implemented by Duitku.
It is crucial to note that these specifications align with the Payment API SNAP document provided by Bank Indonesia, which can be accessed here.
Duitku's SNAP API relies on the use of API keys for request authentication, a crucial aspect that warrants special attention due to its numerous security features. Ensuring the utmost security of these API keys is paramount.
In the transaction process involving Duitku's API, it is imperative for our partners to furnish the API Key as the designated credential, provided by Duitku. This key serves as the means of authenticating transactions through the bearer auth method, employing the header -H "Authorization: Bearer "
. It is essential to note that all API requests must be made via HTTPS to guarantee a secure communication channel. Requests made through HTTP will not be successful.
Furthermore, any attempt to access the API without proper authentication will result in failure, emphasizing the significance of adhering to the authentication protocols in place.
Your cooperation in following these security measures is greatly appreciated, as it ensures a robust and secure environment for API transactions.
Symmetric
To use this code example you might need to install npm crypto-js.
Install the package on your project using npm:
npm i crypto-js
Copy the code below:
(select javascript tag to show the example code)
const CryptoJS = require('crypto-js')
function symetricGenerator(date, method, endpoint, body, accessToken, clientSecret){
let minifyBody = JSON.stringify(body)
let encBody = CryptoJS.SHA256(minifyBody).toString()
let stringToSign = `${method}:${endpoint}:${accessToken}:${encBody.toLowerCase()}:${date}`
let signature = CryptoJS.HmacSHA512(stringToSign, clientSecret).toString(CryptoJS.enc.Base64)
console.log("minifyBody : ", minifyBody)
console.log("encBody : ", encBody)
console.log("stringToSign : ", stringToSign)
return signature
}
The formula:
stringToSign = HttpMethod + “:” + Endpoint + “:” + AccessToken + “:” + LowerCase(HexEncode(SHA-256(Minify(RequestBody)))) + “:” + Timestamp
hash = HMAC_SHA512(stringToSign, secretKey)
signature = Base64(hash)
Explanation:
Compose a variable stringToSign
.
HttpMethod
is a string of a method name that is in use. They could bePOST
,PUT
, orDELETE
.- The
endpoint
is a relative URL or full path of URL without host or domain. AccessToken
is a token value that you get from API Get Token.RequestBody
is a payload that you would like to send.Timestamp
uses ISO-8601.secretKey
is client secret or project API key.
Hash stringToSign
using HMAC_SHA512
cryptographic and your secret key (the current other name is the API key). Then, encode with Base 64.
Put the value to X-SIGNATURE
.
Asymmetric
Asymmetric for the get auth token API
To use this code example you might need to install npm crypto-js and jsrsasign.
Install the package on your project using npm:
npm i crypto-js jsrsasign jsrsasign-util
Copy the code below:
(select javascript tag to show the example code)
const rs = require('jsrsasign')
const rsu = require('jsrsasign-util')
const CryptoJS = require('crypto-js')
function asymmetricGetAuthHelper(partnerId, date){
let stringToSign = `${partnerId}|${toIsoString(date)}`
let privateKey = rs.KEYUTIL.getKey(rsu.readFile("./mykey/privatekey.pem")) //your private key
let sign = new rs.KJUR.crypto.Signature({"alg": "SHA256withRSA"});
sign.init(privateKey)
let hash = sign.signString(stringToSign)
const signature = CryptoJS.enc.Base64.stringify(CryptoJS.enc.Hex.parse(hash))
return signature
}
The formula:
stringToSign = ClientKey + “|” + Timestamp
hash = SHA256withRSA(stringToSign,privateKey)
signature = Base64(hash)
Explanation:
Compose a variable stringToSign
.
ClientKey
is a project ID.Timestamp
uses ISO-8601.privateKey
is a generated RSA key by you. See Public Key and Private Key to see how generate this.
Hash stringToSign
using SHA256withRSA
cryptographic and your secret key (the current other name is the API key). Then, encode with Base 64.
Put the value to X-SIGNATURE
.
Asymmetric for payment or notification
To use this code example you might need to install npm crypto-js and jsrsasign.
Install the package on your project using npm:
npm i crypto-js jsrsasign jsrsasign-util
Copy the code below:
(select javascript tag to show the example code)
const rs = require('jsrsasign')
const rsu = require('jsrsasign-util')
const CryptoJS = require('crypto-js')
function paymentSignatureValidator(body, date, signature, url){
let minifyBody = JSON.stringify(body)
let encBody = CryptoJS.SHA256(minifyBody).toString()
let stringToSign = `POST:${url}:${encBody.toLowerCase()}:${date}`
let publicKey = rs.KEYUTIL.getKey(rsu.readFile("./duitkukey/duitku_publickey.pem"))
let sign = new rs.KJUR.crypto.Signature({"alg": "SHA256withRSA"})
sign.init(publicKey)
sign.updateString(stringToSign)
const isSignatureValid = sign.verify(CryptoJS.enc.Hex.stringify(CryptoJS.enc.Base64.parse(signature)))
console.log("minifyBody", minifyBody)
console.log("encBody", encBody)
console.log("isSignatureValid", isSignatureValid)
return isSignatureValid
}
The formula:
stringToSign = HttpMethod + “:” + Endpoint + “:” + LowerCase(HexEncode(SHA-256(Minify(RequestBody)))) + “:” + Timestamp
hash = SHA256withRSA(stringToSign, publicKey)
signature = Base64(hash)
Explanation:
Compose a variable stringToSign
.
HttpMethod
is a string of a method name that is in use. They should bePOST
.- The
endpoint
is a relative URL or full path of URL without host or domain. RequestBody
is a payload that is being sent from Duitku.Timestamp
uses ISO-8601.publicKey
is a generated RSA key for you to validate that has been given by Duitku.
Hash stringToSign
using SHA256withRSA
cryptographic and your secret key (the current other name is the API key). Then, encode with Base 64.
Put the value to X-SIGNATURE
.
Virtual Account
Interaction Between Virtual Account APIs
The lifecycle of a virtual account typically involves several stages, each handled by a different API. The interaction between these APIs ensures smooth management of virtual accounts.
Create Virtual Account
The process begins with the Create Virtual Account API, which is used for creating a new virtual account with a unique virtual account number (virtualAccountNo) and a transaction identifier (trxId).
Update Virtual Account
After creation, if there is a need to change details like the account name or amount, the Update Virtual Account API can be used. This API allows modifications to be made using the same virtualAccountNo and trxId provided during creation. Inquiry Virtual Account
At any point, to fetch current details of a virtual account, such as status, name, or amount, the Inquiry Virtual Account API is used. This keeps one informed about the state of a virtual account using its virtualAccountNo and associated trxId. Delete Virtual Account
Finally, when a virtual account is to be made unusable, the Delete Virtual Account API comes into play. It expires the virtual account to ensure it can no longer receive payments, leveraging the original virtualAccountNo and trxId for identification.
Each API interaction requires the virtualAccountNo and trxId to match the information initially provided, ensuring secure and accurate operations across the virtual account's lifecycle.
Create VA
Method : HTTP POST
Type : application/json
Development : https://snapdev.duitku.com/merchant/va/v1.0/transfer-va/create-va
Production : https://snap.duitku.com/merchant/va/v1.0/transfer-va/create-va
Service Code : 27
Header :
Parameter | Type | Required | Description | Example |
---|---|---|---|---|
Content-Type | string | ✗ |
String represents indicate the media type of the resource. | application/json |
X-TIMESTAMP | string | ✓ |
ISO-8601 | 2022-09-16T13:00:00+07:00 |
X-SIGNATURE | string | ✓ |
Refer to symmetric. | |
X-PARTNER-ID | string | ✓ |
Project ID. | DXXXX |
X-EXTERNAL-ID | string | ✓ |
Unique request ID. | |
CHANNEL-ID | string | ✓ |
Value should be DUITKU |
DUITKU |
Authorization | string | ✓ |
Authentication with bearer token | Bearer ZGMyNDA3NWQtNmM4Ny00NGNiLTQ2NTAtMDhkYWMxNTAzNzY0 |
Body :
{
"partnerServiceId": "123456",
"customerNo": "1234567890",
"virtualAccountNo": "1234561234567890",
"virtualAccountName": "John Doe",
"trxId": "Transaction-0001",
"totalAmount": {
"value": "120000.00",
"currency": "IDR"
},
"virtualAccountTrxType": "C",
"expiredDate": "2022-10-18T23:27:43+0700",
"additionalInfo": {
"minAmount": "0.00",
"maxAmount": "0.00"
},
}
Parameter | Type | Required | Description | Example |
---|---|---|---|---|
partnerServiceId | String(10) | ✓ |
Prefix from Duitku. | 123456 |
customerNo | String(20) | ✓ |
Number for VA. | 1234567890 |
virtualAccountNo | String(28) | ✓ |
PartnerServiceId + CustomerNo. Recommendation: For a maximum of 16 digits characters. |
1234561234567890 |
virtualAccountName | String(20) | ✓ |
The name will be displayed on the bank side. | John Doe |
trxId | String(50) | ✓ |
Transaction ID. Unique in every create VA. | Transaction-0001 |
ExpiredDate | string | ✓ |
ISO-8601. | 2022-09-16T13:00:00+07:00 |
virtualAccountTrxType | String(1) | ✓ |
Close Amount : C Open Amount : O |
C |
totalAmount | Object | ✓ |
See the table below. | |
additionalInfo | Object | ✓ |
See the table below. |
totalAmount:
Parameter | Type | Required | Description | Example |
---|---|---|---|---|
value | String | ✓ |
ISO4217 with 2 decimal for Open Amount set “0.00”. |
100000.00 |
currency | String(3) | ✓ |
Currency code, only receive IDR . |
IDR |
additionalInfo:
Parameter | Type | Required | Description | Example |
---|---|---|---|---|
minAmount | String | ✓ |
ISO4217 Open Amount set it's equally with your minimun that has been informed by Duitku. Close Amount set “0.00”. |
10000.00 |
maxAmount | String | ✓ |
ISO4217 Open Amount set it's equally with your maximum that has been informed by Duitku. Close Amount set “0.00”. |
10000.00 |
Response :
{
"responseCode": "2002700",
"responseMessage": "Successful",
"virtualAccountData": {
"partnerServiceId": "98886699",
"customerNo": "20000001",
"virtualAccountNo": "9888669920000001",
"virtualAccountName": "SNAP VA",
"trxId": "CHECK-002",
"totalAmount": {
"value": "120000.00",
"currency": "IDR"
},
"expiredDate": "2022-10-18T23:27:43+0700",
"additionalInfo": {
"minAmount": "0.00",
"maxAmount": "0.00"
},
}
}
As you can see in the JSON response example here, you should focus on the virtualAccountData
variable. As for the responseCode
and responseMessage
, these indicate the status of your request process.
Code | Message | Description |
---|---|---|
2002700 | Successful | The process was successful, and data was obtained. |
4002701 | Invalid Field Format virtualAccountTrxType | Incorrect value for the parameter virtualAccountTrxType . |
4002701 | Invalid Field Format duplicated TrxId | Incorrect value for the parameter TrxId or it has already been used. |
4002701 | Invalid Field Format maxAmount should not be greater than 50000000 | Incorrect value for the parameter maxAmount . |
4002701 | Invalid Field Format totalAmount should not be less than 10000 | Incorrect value for the parameter totalAmount . |
4002701 | Invalid Field Format totalAmount should not be greater than 50000000 | Incorrect value for the parameter totalAmount . |
4002701 | Invalid Field Format totalAmount.Currency | Incorrect value for the parameter totalAmount in the Currency part |
4002701 | Invalid Field Format totalAmount.Value | Incorrect value for the parameter totalAmount in the value part. |
4002701 | Invalid Field Format totalAmount Value must be greater than 0.00 for Close Amount and must be filled in 0.00 if Open Amount | Incorrect value for the parameter totalAmount in the value part. |
4002701 | Invalid Field Format minAmount should not be less than 10000 | Incorrect value for the parameter minAmount . |
4002701 | Invalid Field Format trxId | Incorrect value for the parameter trxId . |
4002701 | Invalid Field Format virtualAccountNo | Incorrect value for the parameter virtualAccountNo . |
4002701 | Invalid Field Format virtualAccountName | Incorrect value for the parameter virtualAccountName . |
4002701 | Invalid Field Format partnerServiceId | Incorrect value for the parameter partnerServiceId . |
4002701 | Invalid Field Format customerNo | Incorrect value for the parameter customerNo . |
4002701 | Invalid Field Format param,totalAmount.value | Incorrect format for the parameter totalAmount in the value part. |
4002701 | Invalid Field Format param,virtualAccountName | Incorrect format for the parameter virtualAccountName . |
4002701 | Invalid Field Format param,customerNo | Incorrect format for the parameter customerNo . |
4002701 | Invalid Field Format param,virtualAccountTrxType | Incorrect format for the parameter virtualAccountTrxType . |
4002701 | Invalid Field Format param,virtualAccountNo | Incorrect format for the parameter virtualAccountNo . |
4002701 | Invalid Field Format param,partnerServiceId | Incorrect format for the parameter partnerServiceId . |
4002701 | Invalid Field Format param,trxId | Incorrect format for the parameter trxId . |
4002701 | Invalid Field Format param,expiredDate | Incorrect format for the parameter expiredDate . |
4002702 | Invalid Mandatory Field virtualAccountTrxType | Missing parameter virtualAccountTrxType . |
4002702 | Invalid Mandatory Field expiredDate | Missing parameter expiredDate . |
4002702 | Invalid Mandatory Field totalAmount | Missing parameter totalAmount . |
4002702 | Invalid Mandatory Field virtualAccountName | Missing parameter virtualAccountName . |
4002702 | Invalid Mandatory Field partnerServiceId | Missing parameter partnerServiceId . |
4002702 | Invalid Mandatory Field customerNo | Missing parameter customerNo . |
4002702 | Invalid Mandatory Field virtualAccountNo | Missing parameter virtualAccountNo . |
4002702 | Invalid Mandatory Field trxId | Missing parameter trxId . |
4012700 | Unauthorized Signature | Error in the signature. |
4012700 | Unauthorized stringToSign | Error in the signature. |
4012700 | Unauthorized Client | Error in the Client ID. |
4012701 | Invalid Access Token | Expired or incorrect Access Token. |
4042712 | Invalid Bill/Virtual Account Already Exists | Virtual Account number is already registered. |
4092700 | Conflict | Error in the External ID. |
Virtual Account Type
There are two kinds of virtual account types is currently exist.
- Close Amount
Initiated billed amount. The virtual account comes with the amount that has been billed. - Open Amount
There is no specific amount on the bill. It may allow the users to determine the amount to put. Such as putting a top-up amount.
Close Amount Example
For an example, you can refer to the JavaScript section.
To use this code example you might need to install npm axios and crypto-js.
Install the package on your project using npm:
npm i crypto-js axios
Copy the code below:
(select javascript tag to show the example code)
const axios = require("axios")
const CryptoJS = require('crypto-js')
const host = "https://snapdev.duitku.com"
const url = "/merchant/va/v1.0/transfer-va/create-va"
const date = new Date()
const expiredDate = new Date()
expiredDate.setDate(expiredDate.getDate() + 1)
const config = {
partnerId: "DXXXXX",
partnerServiceId: "123456",
channelId: "Duitku",
clientSecret: "x66485x31358x0e008c2dxx30f1152a6",
accessToken: "ZmY0YWFjOTUtMWEwYi00ZjAxLTlmYmQtMDhkYzIzMTIxZmZh"
}
const customerNo = "1234567890"
const virtualAccountName = "John Doe" //displayed as name on the bank side
const trxId = "Transaction-00001" //need unique in every request
const amount = 12000
const body = {
partnerServiceId: config.partnerServiceId,
customerNo,
virtualAccountNo: `${config.partnerServiceId}${customerNo}`,
virtualAccountName,
trxId,
totalAmount: {
"value": `${amount}.00`,
"currency": "IDR"
},
virtualAccountTrxType: "C",
expiredDate: toIsoString(expiredDate),
additionalInfo: {
"minAmount": "0.00",
"maxAmount": "0.00"
}
}
const headers = headersSymetricGenerator(date, "POST", url, body)
console.log(headers, body)
axios.post(host + url, body, {headers: headers })
.then(response => {
console.log(response.data)
})
.catch(err => {
console.log(err.response.data)
})
//Header symmetric generate
function headersSymetricGenerator(date, method, endpoint, body){
let minifyBody = JSON.stringify(body)
let encBody = CryptoJS.SHA256(minifyBody).toString()
let stringToSign = `${method}:${endpoint}:${config.accessToken}:${encBody.toLowerCase()}:${toIsoString(date)}`
let signature = CryptoJS.HmacSHA512(stringToSign, config.clientSecret).toString(CryptoJS.enc.Base64)
let headers = {
"X-TIMESTAMP": toIsoString(date),
"X-SIGNATURE": signature,
"X-PARTNER-ID": config.partnerId,
"X-EXTERNAL-ID": Math.floor(1000000000000000000 + Math.random() * 9000000000000000000).toString(),
"channel-id": config.channelId,
Authorization: `bearer ${config.accessToken}`
}
console.log("minifyBody : ", minifyBody)
console.log("encBody : ", encBody)
console.log("stringToSign : ", stringToSign)
return headers
}
//helper function for timestamp
function toIsoString(date) {
var tzo = -date.getTimezoneOffset(),
dif = tzo >= 0 ? '+' : '-',
pad = function(num) {
return (num < 10 ? '0' : '') + num;
};
return date.getFullYear() +
'-' + pad(date.getMonth() + 1) +
'-' + pad(date.getDate()) +
'T' + pad(date.getHours()) +
':' + pad(date.getMinutes()) +
':' + pad(date.getSeconds()) +
dif + pad(Math.floor(Math.abs(tzo) / 60)) +
':' + pad(Math.abs(tzo) % 60);
}
Open Amount Example
For an example, you can refer to the JavaScript section.
To use this code example you might need to install npm axios and crypto-js.
Install the package on your project using npm:
npm i crypto-js axios
Copy the code below:
(select javascript tag to show the example code)
const axios = require("axios")
const CryptoJS = require('crypto-js')
const host = "https://snapdev.duitku.com"
const url = "/merchant/va/v1.0/transfer-va/create-va"
const date = new Date()
const expiredDate = new Date()
expiredDate.setDate(expiredDate.getDate() + 1)
const config = {
partnerId: "DXXXXX",
partnerServiceId: "123456",
channelId: "Duitku",
clientSecret: "x66485x31358x0e008c2dxx30f1152a6",
accessToken: "ZmY0YWFjOTUtMWEwYi00ZjAxLTlmYmQtMDhkYzIzMTIxZmZh"
}
const customerNo = "1234567890"
const virtualAccountName = "John Doe" //displayed as name on the bank side
const trxId = "Transaction-00001" //need unique in every request
const minAmount= 12000 //ask Duitku for your minimum transaction limit
const maxAmount= 12000 //ask Duitku for your maximum transaction limit
const body = {
partnerServiceId: config.partnerServiceId,
customerNo,
virtualAccountNo: `${config.partnerServiceId}${customerNo}`,
virtualAccountName,
trxId,
totalAmount: {
"value": `0.00`,
"currency": "IDR"
},
virtualAccountTrxType: "O",
expiredDate: toIsoString(expiredDate),
additionalInfo: {
"minAmount": `${minAmount}.00`,
"maxAmount": `${maxAmount}.00`
}
}
const headers = headersSymetricGenerator(date, "POST", url, body)
console.log(headers, body)
axios.post(host + url, body, {headers: headers })
.then(response => {
console.log(response.data)
})
.catch(err => {
console.log(err.response.data)
})
//Header symmetric generate
function headersSymetricGenerator(date, method, endpoint, body){
let minifyBody = JSON.stringify(body)
let encBody = CryptoJS.SHA256(minifyBody).toString()
let stringToSign = `${method}:${endpoint}:${config.accessToken}:${encBody.toLowerCase()}:${toIsoString(date)}`
let signature = CryptoJS.HmacSHA512(stringToSign, config.clientSecret).toString(CryptoJS.enc.Base64)
let headers = {
"X-TIMESTAMP": toIsoString(date),
"X-SIGNATURE": signature,
"X-PARTNER-ID": config.partnerId,
"X-EXTERNAL-ID": Math.floor(1000000000000000000 + Math.random() * 9000000000000000000).toString(),
"channel-id": config.channelId,
Authorization: `bearer ${config.accessToken}`
}
console.log("minifyBody : ", minifyBody)
console.log("encBody : ", encBody)
console.log("stringToSign : ", stringToSign)
return headers
}
//helper function for timestamp
function toIsoString(date) {
var tzo = -date.getTimezoneOffset(),
dif = tzo >= 0 ? '+' : '-',
pad = function(num) {
return (num < 10 ? '0' : '') + num;
};
return date.getFullYear() +
'-' + pad(date.getMonth() + 1) +
'-' + pad(date.getDate()) +
'T' + pad(date.getHours()) +
':' + pad(date.getMinutes()) +
':' + pad(date.getSeconds()) +
dif + pad(Math.floor(Math.abs(tzo) / 60)) +
':' + pad(Math.abs(tzo) % 60);
}
To run this project example you might need to install npm crypto-js and axios. Install the package on your project using npm:
npm i crypto-js axios
Update VA
const axios = require("axios")
const toIsoString = require('./toIsoString.js')
const headersSymetricGenerator = require('./headersSymetricGenerator.js')
const config = require('./config.js')
const host = "https://snapdev.duitku.com"
const url = "/merchant/va/v1.0/transfer-va/update-va"
const date = new Date()
const expiredDate = new Date()
expiredDate.setDate(expiredDate.getDate() + 1)
const customerNo = "1234567890" //need equal with the one that would like to update
const virtualAccountName = "John Doe Update" //displayed as name on the bank side
const trxId = "Transaction-00001" //need equal with the one that would like to update
const amount = 24000
const body = {
partnerServiceId: config.partnerServiceId,
customerNo,
virtualAccountNo: `${config.partnerServiceId}${customerNo}`,
virtualAccountName,
trxId,
totalAmount: {
"value": `${amount}.00`,
"currency": "IDR"
},
virtualAccountTrxType: "C",
expiredDate: toIsoString(expiredDate),
additionalInfo: {
"minAmount": "0.00",
"maxAmount": "0.00"
}
}
const headers = headersSymetricGenerator(date, "PUT", url, body)
console.log(headers, body)
axios.put(host + url, body, {headers: headers })
.then(response => {
console.log(response.data)
})
.catch(err => {
console.log(err.response.data)
})
To modify the details of an existing virtual account, such as the account name or amount, the Update Virtual Account API is used.
Method : HTTP PUT
Type : application/json
Development : https://snapdev.duitku.com/merchant/va/v1.0/transfer-va/update-va
Production : https://snap.duitku.com/merchant/va/v1.0/transfer-va/update-va
Service Code : 28
Header :
Parameter | Type | Required | Description | Example |
---|---|---|---|---|
Content-Type | string | ✗ |
String represents indicate the media type of the resource. | application/json |
X-TIMESTAMP | string | ✓ |
ISO-8601 | 2022-09-16T13:00:00+07:00 |
X-SIGNATURE | string | ✓ |
Refer to symmetric. | |
X-PARTNER-ID | string | ✓ |
Project ID. | DXXXX |
X-EXTERNAL-ID | string | ✓ |
Unique request ID. | |
CHANNEL-ID | string | ✓ |
Value should be DUITKU |
DUITKU |
Authorization | string | ✓ |
Authentication with bearer token | Bearer ZGMyNDA3NWQtNmM4Ny00NGNiLTQ2NTAtMDhkYWMxNTAzNzY0 |
Body :
{
"partnerServiceId": "123456",
"customerNo": "1234567890",
"virtualAccountNo": "1234561234567890",
"virtualAccountName": "John Doe Update",
"trxId": "Transaction-0001",
"totalAmount": {
"value": "150000.00",
"currency": "IDR"
},
"virtualAccountTrxType": "C",
"expiredDate": "2022-10-18T23:27:43+0700",
"additionalInfo": {
"minAmount": "0.00",
"maxAmount": "0.00"
},
}
Parameter | Type | Required | Description | Example |
---|---|---|---|---|
partnerServiceId | String(10) | ✓ |
Prefix from Duitku. | 123456 |
customerNo | String(20) | ✓ |
Number for VA. | 1234567890 |
virtualAccountNo | String(28) | ✓ |
PartnerServiceId + CustomerNo. Recommendation: For a maximum of 16 digits characters. |
1234561234567890 |
virtualAccountName | String(20) | ✓ |
The name will be displayed on the bank side. | John Doe Update |
trxId | String(50) | ✓ |
Transaction ID. Unique in every create VA. Should be match with the one trxId that would like to be edited. |
Transaction-0001 |
ExpiredDate | string | ✓ |
ISO-8601. | 2022-09-16T13:00:00+07:00 |
virtualAccountTrxType | String(1) | ✓ |
Close Amount : C Open Amount : O |
C |
totalAmount | Object | ✓ |
See the table below. | |
additionalInfo | Object | ✓ |
See the table below. |
totalAmount:
Parameter | Type | Required | Description | Example |
---|---|---|---|---|
value | String | ✓ |
ISO4217 with 2 decimal for Open Amount set “0.00”. |
150000.00 |
currency | String(3) | ✓ |
Currency code, only receive IDR . |
IDR |
additionalInfo:
Parameter | Type | Required | Description | Example |
---|---|---|---|---|
minAmount | String | ✓ |
ISO4217 Open Amount set it's equally with your minimun that has been informed by Duitku. Close Amount set “0.00”. |
10000.00 |
maxAmount | String | ✓ |
ISO4217 Open Amount set it's equally with your maximum that has been informed by Duitku. Close Amount set “0.00”. |
10000.00 |
Response :
{
"responseCode": "2002800",
"responseMessage": "Successful",
"virtualAccountData": {
"partnerServiceId": "123456",
"customerNo": "1234567890",
"virtualAccountNo": "1234561234567890",
"virtualAccountName": "John Doe Update",
"trxId": "Transaction-0001",
"totalAmount": {
"value": "150000.00",
"currency": "IDR"
},
"virtualAccountTrxType": "C",
"expiredDate": "2022-10-18T23:27:43+0700",
"additionalInfo": {
"minAmount": "0.00",
"maxAmount": "0.00"
},
}
}
The virtualAccountData
will return VA information.
Code | Message | Description |
---|---|---|
2002800 | Successful | The process was successful, and data was obtained. |
4002801 | Invalid Field Format virtualAccountTrxType | Incorrect value for the parameter virtualAccountTrxType . |
4002801 | Invalid Field Format maxAmount should not be greater than 50000000 | Incorrect value for the parameter maxAmount . |
4002801 | Invalid Field Format totalAmount.Currency | Incorrect value for the parameter totalAmount in the Currency part. |
4002801 | Invalid Field Format totalAmount.Value | Incorrect value for the parameter totalAmount in the value part. |
4002801 | Invalid Field Format param,totalAmount.value | Incorrect value for the parameter totalAmount in the value part. |
4002801 | Invalid Field Format param,virtualAccountName | Incorrect value for the parameter virtualAccountName . |
4002801 | Invalid Field Format param,customerNo | Incorrect value for the parameter customerNo . |
4002802 | Invalid Mandatory Field virtualAccountTrxType | Missing parameter virtualAccountTrxType . |
4002802 | Invalid Mandatory Field expiredDate | Missing parameter expiredDate . |
4002802 | Invalid Mandatory Field totalAmount | Missing parameter totalAmount . |
4002802 | Invalid Mandatory Field virtualAccountName | Missing parameter virtualAccountName . |
4002802 | Invalid Mandatory Field partnerServiceId | Missing parameter partnerServiceId . |
4012800 | Unauthorized Signature | Error in the signature. |
4012800 | Unauthorized stringToSign | Error in the signature. |
4012800 | Unauthorized Client | Error in the Client ID. |
4012801 | Invalid Access Token | Expired or incorrect Access Token. |
4032800 | Transaction Expired | The transaction to be updated has expired or is inactive. |
4042812 | Invalid Bill/Virtual Account Not Found | Combination of Virtual Account number and Transaction ID not found. |
4092800 | Conflict | Error in the External ID. |
Inquiry VA
const axios = require("axios")
const headersSymetricGenerator = require('./headersSymetricGenerator.js')
const config = require('./config.js')
const host = "https://snapdev.duitku.com"
const url = "/merchant/va/v1.0/transfer-va/inquiry-va"
const date = new Date()
const customerNo = "1234567890" //need equal with the one that would like to update
const trxId = "Transaction-00001" //need equal with the one that would like to update
const body = {
partnerServiceId: config.partnerServiceId,
customerNo,
virtualAccountNo: `${config.partnerServiceId}${customerNo}`,
trxId,
}
const headers = headersSymetricGenerator(date, "POST", url, body)
console.log(headers, body)
axios.post(host + url, body, {headers: headers })
.then(response => {
console.log(response.data)
})
.catch(err => {
console.log(err.response.data)
})
To obtain details about a specific virtual account, such as the status, name, and amount, the Inquiry Virtual Account API can be utilized.
Method : HTTP POST
Type : application/json
Development : https://snapdev.duitku.com/merchant/va/v1.0/transfer-va/inquiry-va
Production : https://snap.duitku.com/merchant/va/v1.0/transfer-va/inquiry-va
Service Code : 30
Header :
Parameter | Type | Required | Description | Example |
---|---|---|---|---|
Content-Type | string | ✗ |
String represents indicate the media type of the resource. | application/json |
X-TIMESTAMP | string | ✓ |
ISO-8601 | 2022-09-16T13:00:00+07:00 |
X-SIGNATURE | string | ✓ |
Refer to symmetric. | |
X-PARTNER-ID | string | ✓ |
Project ID. | DXXXX |
X-EXTERNAL-ID | string | ✓ |
Unique request ID. | |
CHANNEL-ID | string | ✓ |
Value should be DUITKU |
DUITKU |
Authorization | string | ✓ |
Authentication with bearer token | Bearer ZGMyNDA3NWQtNmM4Ny00NGNiLTQ2NTAtMDhkYWMxNTAzNzY0 |
Body :
{
"partnerServiceId": "123456",
"customerNo": "1234567890",
"virtualAccountNo": "1234561234567890",
"trxId": "Transaction-0001"
}
Parameter | Type | Required | Description | Example |
---|---|---|---|---|
partnerServiceId | String(10) | ✓ |
Prefix from Duitku. | 123456 |
customerNo | String(20) | ✓ |
Number for VA. | 1234567890 |
virtualAccountNo | String(28) | ✓ |
PartnerServiceId + CustomerNo. Recommendation: For a maximum of 16 digits characters. |
1234561234567890 |
trxId | String(50) | ✓ |
Transaction ID. Unique in every create VA. Should be match with the one trxId that would like to be see. |
Transaction-0001 |
Response :
{
"virtualAccountData": {
"partnerServiceId": "123456",
"customerNo": "1234567890",
"virtualAccountNo": "1234561234567890",
"virtualAccountName": "John Doe update",
"trxId": "Transaction-0001",
"totalAmount": {
"value": "100000.00",
"currency": "IDR"
},
"virtualAccountTrxType": "C",
"expiredDate": "2022-10-18T14:01:15+07:00",
"additionalInfo": {
"minAmount": "0.00",
"maxAmount": "0.00"
},
},
"responseCode": "2003000",
"responseMessage": "Successful"
}
Status code 200
Success
The virtualAccountData
will return the match information.
Delete VA
const axios = require("axios")
const headersSymetricGenerator = require('./headersSymetricGenerator.js')
const config = require('./config.js')
const host = "https://snapdev.duitku.com"
const url = "/merchant/va/v1.0/transfer-va/delete-va"
const date = new Date()
const customerNo = "1234567890" //need equal with the one that would like to update
const trxId = "Transaction-00001" //need equal with the one that would like to update
const body = {
partnerServiceId: config.partnerServiceId,
customerNo,
virtualAccountNo: `${config.partnerServiceId}${customerNo}`,
trxId,
}
const headers = headersSymetricGenerator(date, "DELETE", url, body)
console.log(headers, body)
axios.delete(host + url, {
headers: headers,
data: body
})
.then(response => {
console.log(response.data)
})
.catch(err => {
console.log(err.response.data)
})
To render a virtual account unusable and to mark its status as expired, the Delete Virtual Account API can be invoked. This API ensures that the particular virtual account cannot receive payments anymore
Method : HTTP DELETE
Type : application/json
Development : https://snapdev.duitku.com/merchant/va/v1.0/transfer-va/delete-va
Production : https://snap.duitku.com/merchant/va/v1.0/transfer-va/delete-va
Service Code : 31
Header :
Parameter | Type | Required | Description | Example |
---|---|---|---|---|
Content-Type | string | ✗ |
String represents indicate the media type of the resource. | application/json |
X-TIMESTAMP | string | ✓ |
ISO-8601 | 2022-09-16T13:00:00+07:00 |
X-SIGNATURE | string | ✓ |
Refer to symmetric. | |
X-PARTNER-ID | string | ✓ |
Project ID. | DXXXX |
X-EXTERNAL-ID | string | ✓ |
Unique request ID. | |
CHANNEL-ID | string | ✓ |
Value should be DUITKU |
DUITKU |
Authorization | string | ✓ |
Authentication with bearer token | Bearer ZGMyNDA3NWQtNmM4Ny00NGNiLTQ2NTAtMDhkYWMxNTAzNzY0 |
Body :
{
"partnerServiceId": "123456",
"customerNo": "1234567890",
"virtualAccountNo": "1234561234567890",
"trxId": "Transaction-0001"
}
Parameter | Type | Required | Description | Example |
---|---|---|---|---|
partnerServiceId | String(10) | ✓ |
Prefix from Duitku. | 123456 |
customerNo | String(20) | ✓ |
Number for VA. | 1234567890 |
virtualAccountNo | String(28) | ✓ |
PartnerServiceId + CustomerNo. Recommendation: For a maximum of 16 digits characters. |
1234561234567890 |
trxId | String(50) | ✓ |
Transaction ID. Unique in every create VA. Should be match with the one trxId that would like to be delete. |
Transaction-0001 |
Response :
{
"responseCode": "2002800",
"responseMessage": "Successful",
"virtualAccountData": {
"partnerServiceId": "123456",
"customerNo": "1234567890",
"virtualAccountNo": "1234561234567890",
"trxId": "Transaction-0001"
}
}
Status code 200
Success
The virtualAccountData
will return the deleted VA information.
Helper function and config example
const config = {
partnerId: "DXXXXX",
partnerServiceId: "123456",
channelId: "Duitku",
clientSecret: "x66485x31358x0e008c2dxx30f1152a6",
accessToken: "ZmY0YWFjOTUtMWEwYi00ZjAxLTlmYmQtMDhkYzIzMTIxZmZh"
}
module.exports = config
//helper function for timestamp
function toIsoString(date) {
var tzo = -date.getTimezoneOffset(),
dif = tzo >= 0 ? '+' : '-',
pad = function(num) {
return (num < 10 ? '0' : '') + num;
};
return date.getFullYear() +
'-' + pad(date.getMonth() + 1) +
'-' + pad(date.getDate()) +
'T' + pad(date.getHours()) +
':' + pad(date.getMinutes()) +
':' + pad(date.getSeconds()) +
dif + pad(Math.floor(Math.abs(tzo) / 60)) +
':' + pad(Math.abs(tzo) % 60);
}
module.exports = toIsoString
//Header symmetric generate
const CryptoJS = require('crypto-js')
const config = require('./config.js')
const toIsoString = require('./toIsoString.js')
function headersSymetricGenerator(date, method, endpoint, body){
let minifyBody = JSON.stringify(body)
let encBody = CryptoJS.SHA256(minifyBody).toString()
let stringToSign = `${method}:${endpoint}:${config.accessToken}:${encBody.toLowerCase()}:${toIsoString(date)}`
let signature = CryptoJS.HmacSHA512(stringToSign, config.clientSecret).toString(CryptoJS.enc.Base64)
let headers = {
"X-TIMESTAMP": toIsoString(date),
"X-SIGNATURE": signature,
"X-PARTNER-ID": config.partnerId,
"X-EXTERNAL-ID": Math.floor(1000000000000000000 + Math.random() * 9000000000000000000).toString(),
"channel-id": config.channelId,
Authorization: `bearer ${config.accessToken}`
}
console.log("minifyBody : ", minifyBody)
console.log("encBody : ", encBody)
console.log("stringToSign : ", stringToSign)
return headers
}
module.exports = headersSymetricGenerator
Payment VA
Payment is a webhook or notification when the user has already paid. To receive this information, you need to create an API to receive the notification. Here are the things you need to do when you receive payment.
- Secure: you need to make your payment secure. Because the payment information just will always be through this API. We need to ensure a robust and secure environment. You might see the image of the sequence diagram at the top of this page.
- Whitelist Duitku IP if it's needed.
IP range :
182.23.85.0/28
103.177.101.177/28
- Always validate the signature.
- Optionally you may check through Inquiry Status to make sure that your user has already paid the transaction.
- Whitelist Duitku IP if it's needed.
IP range :
- Update: you need to update your transaction status or you might register the payment on your transaction.
Payment API
To have a payment we need an API, as an example, we use express js. Here the project directory like:
.
├── app.js
├── controllers
│ └── SnapController.js
├── helpers
│ └── paymentSignatureValidator.js
└── routes
└── index.js
3 directories, 4 files
To run this project example you might need to install npm express, jsrsasign, and crypto-js. Install the package on your project using npm:
npm i express crypto-js axios jsrsasign jsrsasign-util
Here is the project code example:
app.js
const express = require('express')
const app = express()
const port = 3000
const router = require('./routes')
app.use('', router);
app.listen(port, () => {
console.log(`Example app listening on port ${port}`)
})
routes/index.js
cconst bodyParser = require('body-parser');
const SnapController = require('../controllers/SnapController');
const router = require('express').Router();
router.post('/callback/v1.0/transfer-va/payment', bodyParser.json() , SnapController.PaymentVa)
module.exports = router
controllers/SnapController
const paymentSignatureValidator = require("../helpers/paymentSignatureValidator")
class SnapController{
static PaymentVa(req, res){
const body = req.body
const url = req.url
const date = new Date()
const xExternalId = req.headers["x-external-id"]
const xPartnerId = req.headers["x-partner-id"]
const xSignature = req.headers["x-signature"]
const xTimestamp = req.headers["x-timestamp"]
const channelId = req.headers["channel-id"]
let isValid = paymentSignatureValidator(body, xTimestamp, xSignature, url)
if(isValid){
/**
* You may create your logic here after validating the signature
* You also may hit inquiry status and get the value here
*/
//Your code here
res.send({
message: "Received and validated"
})
}else{
res.send({
message: "Received but not validated"
})
}
}
}
module.exports = SnapController
helpers/paymentSignatureValidator.js
const rs = require('jsrsasign')
const rsu = require('jsrsasign-util');
const toIsoString = require('./toIsoString');
const CryptoJS = require('crypto-js')
function paymentSignatureValidator(body, date, signature, url){
let minifyBody = JSON.stringify(body)
let encBody = CryptoJS.SHA256(minifyBody).toString()
let stringToSign = `POST:${url}:${encBody.toLowerCase()}:${date}`
let publicKey = rs.KEYUTIL.getKey(rsu.readFile("./duitkukey/duitku_publickey.pem"))
let sign = new rs.KJUR.crypto.Signature({"alg": "SHA256withRSA"})
sign.init(publicKey)
sign.updateString(stringToSign)
const isSignatureValid = sign.verify(CryptoJS.enc.Hex.stringify(CryptoJS.enc.Base64.parse(signature)))
console.log("minifyBody", minifyBody)
console.log("encBody", encBody)
console.log("isSignatureValid", isSignatureValid)
return isSignatureValid
}
module.exports = paymentSignatureValidator
Method : HTTP POST
Type : application/json
Url : https://yourdomain.com/v1.0/transfer-va/payment
Endpoint : /v1.0/transfer-va/payment
Service Code : 25
Header :
Parameter | Type | Required | Description | Example |
---|---|---|---|---|
X-TIMESTAMP | string | ✓ |
ISO-8601 | 2022-09-16T13:00:00+07:00 |
X-SIGNATURE | string | ✓ |
Refer to Asymmetric. | |
X-PARTNER-ID | string | ✓ |
Project ID. | DXXXX |
X-EXTERNAL-ID | string | ✓ |
Unique request ID. | |
CHANNEL-ID | string | ✓ |
Value should be DUITKU-PAYMENT |
DUITKU-PAYMENT |
Body :
{
"partnerServiceId" : "123456",
"customerNo" : "1234567890",
"virtualAccountNo" : "1234561234567890",
"paymentRequestId" : "46181",
"trxId" : "Transaction-0001",
"paidAmount" : {
"value" : "100000.00",
"currency" : "IDR"
},
"additionalInfo" : {
"reference" : "D0001YB1CUE2ET3027DK",
"paymentCode" : "M2"
}
}
Parameter | Type | Required | Description | Example |
---|---|---|---|---|
partnerServiceId | String(10) | ✓ |
Prefix from Duitku. | 123456 |
customerNo | String(20) | ✓ |
Number for VA. | 1234567890 |
virtualAccountNo | String(28) | ✓ |
PartnerServiceId + CustomerNo. Recommendation: For a maximum of 16 digits characters. |
1234561234567890 |
paymentRequestId | String(20) | ✓ |
The name will be displayed on the bank side. | 46181 |
trxId | String(50) | ✓ |
Transaction ID. Unique from every create VA. | Transaction-0001 |
paidAmount | Object | ✓ |
See the table below. | |
additionalInfo | Object | ✓ |
See the table below. |
paidAmount:
Parameter | Type | Required | Description | Example |
---|---|---|---|---|
value | String | ✓ |
ISO4217 with 2 decimal for Open Amount set “0.00”. |
100000.00 |
currency | String(3) | ✓ |
Currency code, only receive IDR . |
IDR |
additionalInfo:
Parameter | Type | Required | Description | Example |
---|---|---|---|---|
reference | String | ✓ |
Reference from Duitku. | D0001YB1CUE2ET3027DK |
paymentCode | String | ✓ |
Payment method. | M2 |
Response :
{
"responseCode" : "2002500",
"responseMessage" : "Successful",
"virtualAccountData" : {
"partnerServiceId" : "123456",
"customerNo" : "1234567890",
"virtualAccountNo" : "1234561234567890",
"virtualAccountName" : "John Doe",
"paymentRequestId" : "46181",
"paidAmount" : {
"value" : "100000.00",
"currency" : "IDR"
}
}
}
As you can see in the JSON response example here, you should focus on the virtualAccountData
variable. As for the responseCode
and responseMessage
, these indicate the status of your request process.
Code | Message | Description |
---|---|---|
2002500 | Successful | The process was successful, and data was obtained. |
4002501 | Invalid Field Format {fieldName} | Incorrect value for the parameter {fieldName} . |
4002502 | Invalid Mandatory Field {fieldName} | Missing parameter {fieldName} . |
4012500 | Unauthorized Signature | Error in the signature. |
4012501 | Invalid Access Token | Expired or incorrect Access Token. |
4012501 | Invalid Bill/Virtual Account Not Found | Virtual Account number not found. |
4042512 | Bill not found | Input bill was not found. |
4042513 | Invalid amount | Incorrect value for the amount. |
4042514 | Bill already paid | Bill has already been paid. |
4092500 | Conflict | Error in the External ID. |
5002500 | General Error | Error during the request process. |
5042500 | Time Out | Time has expired. |
Inquiry Status VA
Inquiry Status Scheme
This API will help you to inquire about the payment status. As you can see in the sequence above. You might need to inquire when there is a confirmation payment from the user. Or as security optional you might inquire about status after receiving payment. It helps you to make sure the payment status is genuine.
Inquiry Status Request
To run this project example you might need to install npm crypto-js and axios. Install the package on your project using npm:
npm i crypto-js axios
Copy the code below:
(select javascript tag to show the example code)
const axios = require("axios")
const CryptoJS = require('crypto-js')
const customerNo = "1234567890"
const trxId = "Transaction-00001"
const host = "https://snapdev.duitku.com"
const url = "/merchant/va/v1.0/transfer-va/status"
const date = new Date()
const config = {
partnerId: "DXXXXX",
partnerServiceId: "123456",
channelId: "Duitku",
clientSecret: "x66485x31358x0e008c2dxx30f1152a6",
accessToken: "ZmY0YWFjOTUtMWEwYi00ZjAxLTlmYmQtMDhkYzIzMTIxZmZh"
}
const body = {
partnerServiceId: config.partnerServiceId,
customerNo,
virtualAccountNo: `${config.partnerServiceId}${customerNo}`,
inquiryRequestId: trxId,
}
const headers = headersSymetricGenerator(date, "POST", url, body)
console.log(headers, body)
axios.post(host + url, body, {headers: headers })
.then(response => {
console.log(response.data)
})
.catch(err => {
console.log(err.response.data)
})
//Header symmetric generate
function headersSymetricGenerator(date, method, endpoint, body){
let minifyBody = JSON.stringify(body)
let encBody = CryptoJS.SHA256(minifyBody).toString()
let stringToSign = `${method}:${endpoint}:${config.accessToken}:${encBody.toLowerCase()}:${toIsoString(date)}`
let signature = CryptoJS.HmacSHA512(stringToSign, config.clientSecret).toString(CryptoJS.enc.Base64)
let headers = {
"X-TIMESTAMP": toIsoString(date),
"X-SIGNATURE": signature,
"X-PARTNER-ID": config.partnerId,
"X-EXTERNAL-ID": Math.floor(1000000000000000000 + Math.random() * 9000000000000000000).toString(),
"channel-id": config.channelId,
Authorization: `bearer ${config.accessToken}`
}
console.log("minifyBody : ", minifyBody)
console.log("encBody : ", encBody)
console.log("stringToSign : ", stringToSign)
return headers
}
//helper function for timestamp
function toIsoString(date) {
var tzo = -date.getTimezoneOffset(),
dif = tzo >= 0 ? '+' : '-',
pad = function(num) {
return (num < 10 ? '0' : '') + num;
};
return date.getFullYear() +
'-' + pad(date.getMonth() + 1) +
'-' + pad(date.getDate()) +
'T' + pad(date.getHours()) +
':' + pad(date.getMinutes()) +
':' + pad(date.getSeconds()) +
dif + pad(Math.floor(Math.abs(tzo) / 60)) +
':' + pad(Math.abs(tzo) % 60);
}
Method : HTTP POST
Type : application/json
Development : https://snapdev.duitku.com/merchant/va/v1.0/transfer-va/status
Production : https://snap.duitku.com/merchant/va/v1.0/transfer-va/status
Service Code : 26
Header :
Parameter | Type | Required | Description | Example |
---|---|---|---|---|
Content-Type | string | ✗ |
String represents indicate the media type of the resource. | application/json |
X-TIMESTAMP | string | ✓ |
ISO-8601 | 2022-09-16T13:00:00+07:00 |
X-SIGNATURE | string | ✓ |
Refer to symmetric. | |
X-PARTNER-ID | string | ✓ |
Project ID. | DXXXX |
X-EXTERNAL-ID | string | ✓ |
Unique request ID. | |
CHANNEL-ID | string | ✓ |
Value should be DUITKU |
DUITKU |
Authorization | string | ✓ |
Authentication with bearer token | Bearer ZGMyNDA3NWQtNmM4Ny00NGNiLTQ2NTAtMDhkYWMxNTAzNzY0 |
Body :
{
"partnerServiceId" : "123456",
"customerNo" : "1234567890",
"virtualAccountNo" : "1308301000000001",
"inquiryRequestId" : "1234561234567890"
}
Parameter | Type | Required | Description | Example |
---|---|---|---|---|
partnerServiceId | String(10) | ✓ |
Prefix from Duitku. | 123456 |
customerNo | String(20) | ✓ |
Number for VA. | 1234567890 |
virtualAccountNo | String(28) | ✓ |
PartnerServiceId + CustomerNo. Recommendation: For a maximum of 16 digits characters. |
1234561234567890 |
inquiryRequestId | String(50) | ✓ |
TrxID from Create VA. | Transaction-0001 |
Response :
{
"responseCode" : "2002600",
"responseMessage" : "Successful",
"virtualAccountData" : {
"partnerServiceId" : "123456",
"customerNo" : "1234567890",
"virtualAccountNo" : "1234561234567890",
"inquiryRequestId" : "46181",
"paymentRequestId" : "46181",
"paidAmount" : {
"value" : "100000.00",
"currency" : "IDR"
},
"totalAmount" : {
"value" : "100000.00",
"currency" : "IDR"
},
"transactionDate" : "2022-09-14T16:00:00+07:00",
"trxDateTime" : "2022-09-29T10:00:00+07:00",
"paymentFlagStatus" : "00",
"paymentFlagReason" : {
"english" : "SUCCESS",
"indonesia" : "SUKSES"
}
}
}
Status code 200
Success
See in the JSON example. You'll gonna need to focus on the variable virtualAccountData
. For responseCode
and responseMessage
, these are indicating your process request.
See the value from paymentFlagStatus
in virtualAccountData
. It indicates the payment status.
Status for Payments They are:
00
: SUCCESS
01
: PROCESS
02
: EXPIRED
Direct Debit
Account Binding
The API service used to connect channels with merchant applications using the customer's registered phone number on their account.
Method : HTTP POST
Type : application/json
Development : https://snapdev.duitku.com/merchant/registration/v1.0/registration-account-binding
Production : https://snap.duitku.com/merchant/registration/v1.0/registration-account-binding
Service Code : 07
Header :
Parameter | Type | Required | Description | Example |
---|---|---|---|---|
Content-Type | string | ✓ |
String represents indicate the media type of the resource. | application/json |
X-TIMESTAMP | string | ✓ |
ISO-8601 | 2022-09-16T13:00:00+07:00 |
X-SIGNATURE | string | ✓ |
Refer to symmetric. | |
X-PARTNER-ID | string | ✓ |
Project ID. | DXXXX |
X-EXTERNAL-ID | string | ✓ |
Unique request ID. | |
CHANNEL-ID | string | ✓ |
Value should be OL or SL |
OL |
Authorization | string | ✓ |
Authentication with bearer token | Bearer ZGMyNDA3NWQtNmM4Ny00NGNiLTQ2NTAtMDhkYWMxNTAzNzY0 |
Body :
{
"phoneNo": "0857181XX",
"merchantId": "DXX01",
"additionalInfo": {
"customerUniqueId": "123"
},
"redirectUrl": "https://example.com"
}
Parameter | Type | Required | Description | Example |
---|---|---|---|---|
phoneNo | String(15) | ✓ |
Customer's phone number. | 0812345xxxx |
merchantId | String(10) | ✓ |
Merchant Code from Duitku. | D00XXX |
redirectUrl | String(255) | ✓ |
The response returned after entering the PIN. | http://{returnUrl}?resultCode=00&phoneNumber=0812345xxx&state=accountLink |
additionalInfo | Object | ✓ |
See the table below. |
additionalInfo:
Parameter | Type | Required | Description | Example |
---|---|---|---|---|
customerUniqueId | String(50) | ✓ |
The ID used as a unique identifier for the customer in your application. | 1234567 |
Response :
{
"responseCode": "2000700",
"responseMessage": "Success",
"redirectUrl": "https://webview.byte-stack.net/
cellblockui/partner/activation?authType=2FA
&submissionType=redirect"
}
Code | Message | Description |
---|---|---|
2000700 | Successful | Successful processing and obtaining redirectUrl . |
4000700 | Bad Request, Invalid Merchant Request | Error in the request with merchantId . |
4000700 | Service Not Implemented | Service not registered with the request CHANNEL-ID . |
4000701 | Invalid Field Format {,PhoneNo} | Incorrect value for the parameter PhoneNo . |
4000701 | Invalid Field Format {,RedirectUrl} | Incorrect value for the parameter RedirectUrl . |
4000702 | Missing Mandatory Field {additionalInfo.CustomerUniqueId} | Missing parameter CustomerUniqueId . |
4000702 | Invalid Mandatory Field, [additionalInfo.CustomerUniqueId] | Missing parameter additionalInfo . |
4000702 | Missing Mandatory Field {RedirectUrl} | Missing parameter RedirectUrl . |
4000702 | Missing Mandatory Field {MerchantId} | Missing parameter MerchantId . |
4000702 | Missing Mandatory Field {PhoneNo} | Missing parameter PhoneNo . |
4010700 | Unauthorized Signature | Error in the signature. |
4010700 | Unauthorized stringToSign | Error in the signature. |
4010700 | Unauthorized Client | Error in the PARTNER-ID. |
4010701 | Invalid Access Token | Expired or incorrect Access Token. |
4030700 | Transaction Expired | Transaction to be updated has expired or is inactive. |
4030715 | Transaction Not Permitted:Akun terkunci dalam waktu 30 menit | Requested transaction has exceeded the maximum limit of 6 requests. |
4040701 | Invalid Request | Incorrect value for the parameter additionalInfo . |
4090700 | Conflict | Error in the External ID. |
Account Binding Inquiry
The API service used for checking the account status with the merchant application using the customer's registered phone number that has been registered with the account.
Method : HTTP POST
Type : application/json
Development : https://snapdev.duitku.com/merchant/registration/v1.0/registration-account-inquiry
Production : https://snap.duitku.com/merchant/registration/v1.0/registration-account-inquiry
Service Code : 08
Header :
Parameter | Type | Required | Description | Example |
---|---|---|---|---|
Content-Type | string | ✓ |
String represents indicate the media type of the resource. | application/json |
X-TIMESTAMP | string | ✓ |
ISO-8601 | 2022-09-16T13:00:00+07:00 |
X-SIGNATURE | string | ✓ |
Refer to symmetric. | |
X-PARTNER-ID | string | ✓ |
Project ID. | DXXXX |
X-EXTERNAL-ID | string | ✓ |
Unique request ID. | |
CHANNEL-ID | string | ✓ |
Value should be OL or SL |
OL |
Authorization | string | ✓ |
Authentication with bearer token | Bearer ZGMyNDA3NWQtNmM4Ny00NGNiLTQ2NTAtMDhkYWMxNTAzNzY0 |
Body :
{
"additionalInfo": {
"credentialCode": "95e8ad58-7acc-ee11-9f5e-9440c9319978"
}
}
Parameter | Type | Required | Description | Example |
---|---|---|---|---|
additionalInfo | Object | ✓ |
See the table below. |
additionalInfo:
Parameter | Type | Required | Description | Example |
---|---|---|---|---|
credentialCode | String(50) | ✓ |
Credential code for authentication. | C0B32F16-2A51-ED11-8135-96724827090C |
Response :
{
"responseCode": "2000800",
"responseMessage": "Success",
"accountNo": "0xxxxxx9006",
"additionalInfo": {
"balanceCash": 1181712,
"accountStatus": "LINKED"
}
}
Code | Message | Description |
---|---|---|
2000800 | Successful | Successfully processed. |
2000801 | Invalid Token:Anda Tidak Memiliki Akses | Error in the request with credentialCode. |
4000802 | Missing Mandatory Field {AdditionalInfo.CredentialCode} | Missing parameter CredentialCode. |
4000802 | Invalid Mandatory Header X-EXTERNAL-ID | Missing parameter X-EXTERNAL-ID. |
4000802 | Invalid Mandatory Header CHANNEL-ID | Missing parameter CHANNEL-ID. |
4010800 | Unauthorized Signature | Error in the signature. |
4010800 | Unauthorized stringToSign | Error in the signature. |
4010800 | Unauthorized Client | Error in the PARTNER-ID. |
4010801 | Invalid Access Token | Expired or incorrect Access Token. |
4040801 | Invalid Request | Format error during the request. |
5000801 | Internal Server Error | Error during the request process. |
Account Unbinding
The API service used when customers want to terminate their channel connection with the merchant application using the customer's registered phone number on their account.
Method : HTTP POST
Type : application/json
Development : https://snapdev.duitku.com/merchant/registration/v1.0/registration-account-unbinding
Production : https://snap.duitku.com/merchant/registration/v1.0/registration-account-unbinding
Service Code : 09
Header :
Parameter | Type | Required | Description | Example |
---|---|---|---|---|
Content-Type | string | ✓ |
String represents indicate the media type of the resource. | application/json |
X-TIMESTAMP | string | ✓ |
ISO-8601 | 2022-09-16T13:00:00+07:00 |
X-SIGNATURE | string | ✓ |
Refer to symmetric. | |
X-PARTNER-ID | string | ✓ |
Project ID. | DXXXX |
X-EXTERNAL-ID | string | ✓ |
Unique request ID. | |
CHANNEL-ID | string | ✓ |
Value should be OL or SL |
OL |
Authorization | string | ✓ |
Authentication with bearer token | Bearer ZGMyNDA3NWQtNmM4Ny00NGNiLTQ2NTAtMDhkYWMxNTAzNzY0 |
Body :
{
"merchantId": "DXX01",
"tokenId": "C0B32F16-2A51-ED11-8135-96724827090C"
}
Parameter | Tipe | Required | Description | Example |
---|---|---|---|---|
merchantId | String(10) | ✓ |
Merchant Code from Duitku. | D00XXX |
tokenId | String(25) | ✓ |
Credential code for authentication. | C0B32F16-2A51-ED11-8135-96724827090C |
Response :
{
"responseCode": "2000900",
"responseMessage": "Success",
"unlinkResult": "Success"
}
Code | Message | Description |
---|---|---|
2000900 | Successful | Successfully processed. |
4000902 | Invalid Mandatory Header X-EXTERNAL-ID | Missing parameter X-EXTERNAL-ID |
4000902 | Invalid Mandatory Header CHANNEL-ID | Missing parameter CHANNEL-ID . |
4010900 | Unauthorized Signature | Error in the signature. |
4010900 | Unauthorized stringToSign | Error in the signature. |
4010900 | Unauthorized Client | Error in the PARTNER-ID. |
4010901 | Invalid Access Token | Expired or incorrect Access Token |
5000901 | Internal Server Error | Error during the request process. |
Debit Payment Linking
This API is used for Debit Payment services, utilizing an Account Linking process beforehand for the request process. There are 2 types of transactions in this API: Manual Debit and Auto Debit. This API supports ShopeePay and OVO payment methods, using the values SL
and OL
respectively.
Method : HTTP POST
Type : application/json
Development : https://snapdev.duitku.com/merchant/debit/v1.0/debit/payment-host-to-host
Production : https://snap.duitku.com/merchant/debit/v1.0/debit/payment-host-to-host
Service Code : 54
Header :
Parameter | Type | Required | Description | Example |
---|---|---|---|---|
Content-Type | string | ✓ |
String represents indicate the media type of the resource. | application/json |
X-TIMESTAMP | string | ✓ |
ISO-8601 | 2022-09-16T13:00:00+07:00 |
X-SIGNATURE | string | ✓ |
Refer to symmetric. | |
X-PARTNER-ID | string | ✓ |
Project ID. | DXXXX |
X-EXTERNAL-ID | string | ✓ |
Unique request ID. | |
CHANNEL-ID | string | ✓ |
Value should be OL or SL |
OL |
Authorization | string | ✓ |
Authentication with bearer token | Bearer ZGMyNDA3NWQtNmM4Ny00NGNiLTQ2NTAtMDhkYWMxNTAzNzY0 |
Request Body:
{
"partnerReferenceNo": "INV1708588643",
"chargeToken": "OL",
"bankCardToken": "4a17a847-94d0-ee11-9f5e-9440c9319978",
"merchantId": "D0XXX",
"amount": {
"value": "1000.00",
"currency": "IDR"
},
"additionalInfo": {
"productDetails": "Tes pembayaran Direct debit Duitku",
"additionalParam": "test additional param",
"phoneNumber": "0821XXXXXXX",
"email": "[email protected]",
"returnUrl": "http:\/\/example.com\/return",
"itemDetails": [
{
"name": "Test Item 1",
"price": 500,
"quantity": 1
},
{
"name": "Test Item 2",
"price": 500,
"quantity": 1
}
],
"transactionType": "M",
"merchantUserInfo": "",
"customerDetail": {
"firstName": "John",
"lastName": "Doe",
"email": "[email protected]",
"phoneNumber": "0821XXXXXXX",
"billingAddress": {
"firstName": "John",
"lastName": "Doe",
"address": "Jl. Kembangan Raya",
"city": "Jakarta",
"postalCode": "11530",
"phone": "0821XXXXXXX",
"countryCode": "ID"
},
"shippingAddress": {
"firstName": "John",
"lastName": "Doe",
"address": "Jl. Kembangan Raya",
"city": "Jakarta",
"postalCode": "11530",
"phone": "0821XXXXXXX",
"countryCode": "ID"
}
}
},
"payOptionDetails": [
{
"payMethod": "CASH",
"transAmount": {
"value": "1000.00",
"currency": "IDR"
}
}
]
}
Parameter | Type | Required | Description | Example |
---|---|---|---|---|
partnerReferenceNo | String(64) | ✓ |
Transaction number from merchant. | INV123456 |
chargeToken | String(50) | ✓ |
The payment method code provided by Duitku, for which the value must match the CHANNEL-ID in the header. | OL |
bankCardToken | String(50) | ✓ |
Credential code for authentication. | 67af67e7-e0ce-ee11-9f5e-9440c9319978 |
merchantId | String(2) | ✗ |
Merchant code from Duitku. | D00XXX |
payOptionDetails | Object Array | ✗ |
See the table payOptionDetails below. | |
additionalInfo | Object | ✓ |
See the table additionalInfo below. | |
amount | Object | ✓ |
See the table amount below. |
additionalInfo
Parameter | Type | Required | Description | Example |
---|---|---|---|---|
productDetails | String(255) | ✗ |
Description about product/service on sale. | |
transactionType | String(20) | C |
Here are the values used: M: Used for Manual Debit payments that require a PIN process. A: Used for Auto Debit payments that do not require a PIN process. |
M |
phoneNumber | String(15) | ✗ |
Customer's phone number. | 0857181XX |
String(60) | ✗ |
Customer's email. | [email protected] | |
additionalParam | String(255) | ✗ |
Additional parameter for merchant purpose. | |
returnUrl | String(255) | ✓ |
Link to redirect after transaction is completed or cancelled. | http://example.com/return |
merchantUserInfo | String(255) | ✗ |
Customer's username or email on merchant site. | 1234567 |
itemDetails | object | ✗ |
See the table itemDetails below. | |
customerDetail | object | ✗ |
See the table customerDetail below. |
Item Details
Parameter | Type | Required | Description | Example |
---|---|---|---|---|
name | string(255) | ✓ |
Name of the item. | Apel |
quantity | integer | ✓ |
Quantity of the item bought. | 10 |
price | integer | ✓ |
Price of the Item. Note: Don't add decimal. | 50000 |
Customer Detail
Parameter | Type | Required | Description | Example |
---|---|---|---|---|
firstName | string(255) | ✗ |
Customer's first name. | John |
lastName | string(255) | ✗ |
Customer's last name. | Doe |
string(255) | ✗ |
Customer's email. | [email protected] | |
phoneNumber | string(255) | ✗ |
Customer's phone number. | 08123456789 |
billingAddress | object | ✗ |
See the table address below | |
shippingAddress | object | ✗ |
See the table address below |
Address
Parameter | Type | Required | Description | Example |
---|---|---|---|---|
firstName | string(255) | ✗ |
Customer's first name. | John |
lastName | string(255) | ✗ |
Customer's last name. | Doe |
address | string(255) | ✗ |
Billing address or shipping address. | Jl. Kembangan Raya |
city | string(255) | ✗ |
City of the address. | Jakarta |
postalCode | string(255) | ✗ |
Postal code of the address. | 11530 |
phone | string(255) | ✗ |
Phone number for billing or shipping. | 08123456789 |
countryCode | string(255) | ✗ |
ISO 3166-1 alpha-3. | ID |
payOptionDetails
Parameter | Type | Required | Description | Example |
---|---|---|---|---|
payMethod | String(255) | ✗ |
The type of balance you use can be filled in CASH . |
CASH |
transAmount | object | ✓ |
See the table amount below |
amount
Parameter | Type | Required | Description | Example |
---|---|---|---|---|
value | String | ✓ |
ISO4217 with 2 decimal. |
10000.00 |
currency | String(3) | ✓ |
Currency code, only receive IDR . |
IDR |
Response :
Example Response Manual Payment Linking
{
"responseCode": "2025400",
"responseMessage": "Request In Progress",
"referenceNo": "DXXXXX24OWAA7JJHALBAL6J",
"partnerReferenceNo": "INV1708597771",
"webRedirectUrl": "https://webview.byte-stack.net/
cellblockui/v2/paymentPin?action=payment&submission
Type=redirect&destination=https://snapdev.duitku.com"
}
Example Response Auto Payment Linking
{
"responseCode": "2005400",
"responseMessage": "Success",
"referenceNo": "DXXXXX24OWAA7JJHALBAL6J",
"partnerReferenceNo": "INV1708597771"
}
Code | Message | Description |
---|---|---|
2005400 | Successful | Success. |
2025400 | Request In Progress | Transaction is being processed. |
4005400 | Bad Request, Invalid Merchant Request | Error in Request merchantId . |
4005400 | Service Not Implemented | Service is not registered on request CHANNEL-ID . |
4005401 | Invalid Field Format | Incorrect value for that parameter. |
4005402 | Missing Mandatory Field {AdditionalInfo.ReturnUrl} | Parameter ReturnUrl is missing. |
4015400 | Unauthorized Signature | Error in Signature. |
4015400 | Unauthorized stringToSign | Error in Signature. |
4015400 | Unauthorized Client | Error in PARTNER-ID. |
4015401 | Invalid Access Token | Access Token expired or incorrect. |
4035400 | Transaction Expired | Transaction to be updated has expired or is not active. |
4035414 | Insufficient Fund | Your balance is insufficient for this transaction. |
4045401 | Invalid Request | Incorrect value for parameter. |
4045413 | Invalid Amount, Amount must be equal with total price item details | Error in value parameter amount and price in item detail. |
4095400 | Conflict | Error in External ID. |
Debit Payment Redirect
This API is used for Debit Payment Redirect services without requiring an Account Linking process beforehand. When making an API request, the chargeToken
parameter must be included. Currently, the supported channels are ShopeePay Apps and Dana, with the values SA
and DA
respectively.
Method : HTTP POST
Type : application/json
Development : https://snapdev.duitku.com/merchant/debit/v1.0/debit/payment-host-to-host
Production : https://snap.duitku.com/merchant/debit/v1.0/debit/payment-host-to-host
Service Code : 54
Header :
{
"partnerReferenceNo": "INV1708588643",
"chargeToken": "DA",
"bankCardToken": "4a17a847-94d0-ee11-9f5e-9440c9319978",
"merchantId": "D0XXX",
"validUpTo":"2022-09-16T13:00:00+07:00",
"amount": {
"value": "1000.00",
"currency": "IDR"
},
"additionalInfo": {
"productDetails": "Tes pembayaran Direct debit Duitku",
"additionalParam": "test additional param",
"phoneNumber": "0821XXXXXXX",
"email": "[email protected]",
"returnUrl": "http:\/\/example.com\/return",
"itemDetails": [
{
"name": "Test Item 1",
"price": 500,
"quantity": 1
},
{
"name": "Test Item 2",
"price": 500,
"quantity": 1
}
],
"merchantUserInfo": "",
"customerDetail": {
"firstName": "John",
"lastName": "Doe",
"email": "[email protected]",
"phoneNumber": "0821XXXXXXX",
"billingAddress": {
"firstName": "John",
"lastName": "Doe",
"address": "Jl. Kembangan Raya",
"city": "Jakarta",
"postalCode": "11530",
"phone": "0821XXXXXXX",
"countryCode": "ID"
},
"shippingAddress": {
"firstName": "John",
"lastName": "Doe",
"address": "Jl. Kembangan Raya",
"city": "Jakarta",
"postalCode": "11530",
"phone": "0821XXXXXXX",
"countryCode": "ID"
}
}
},
"payOptionDetails": [
{
"payMethod": "CASH",
"transAmount": {
"value": "1000.00",
"currency": "IDR"
}
}
]
}
Parameter | Type | Required | Description | Example |
---|---|---|---|---|
Content-Type | string | ✓ |
String represents indicate the media type of the resource. | application/json |
X-TIMESTAMP | string | ✓ |
ISO-8601 | 2022-09-16T13:00:00+07:00 |
X-SIGNATURE | string | ✓ |
Refer to symmetric. | |
X-PARTNER-ID | string | ✓ |
Project ID. | DXXXX |
X-EXTERNAL-ID | string | ✓ |
Unique request ID. | |
CHANNEL-ID | string | ✓ |
Value should be DA or SA |
DA |
Authorization | string | ✓ |
Authentication with bearer token | Bearer ZGMyNDA3NWQtNmM4Ny00NGNiLTQ2NTAtMDhkYWMxNTAzNzY0 |
Request Body:
Parameter | Type | Required | Description | Example |
---|---|---|---|---|
partnerReferenceNo | String(64) | ✓ |
Transaction number from merchant. | INV123456 |
chargeToken | String(50) | ✓ |
The payment method code provided by Duitku, for which the value must match the CHANNEL-ID in the header. | DA |
merchantId | String(2) | ✗ |
Merchant code from Duitku. | D00XXX |
payOptionDetails | Object Array | ✗ |
See the table payOptionDetails below. | |
additionalInfo | Object | ✓ |
See the table additionalInfo below. | |
amount | Object | ✓ |
See the table amount below. | |
validUpTo | String | ✗ |
Transaction expiry time on ISO-8601. | 2022-09-16T13:00:00+07:00 |
additionalInfo
Parameter | Type | Required | Description | Example |
---|---|---|---|---|
productDetails | String(255) | ✗ |
Description about product/service on sale. | |
phoneNumber | String(15) | ✗ |
Customer's phone number. | 0857181XX |
String(60) | ✗ |
Customer's email. | [email protected] | |
additionalParam | String(255) | ✗ |
Additional parameter for merchant purpose. | |
returnUrl | String(255) | ✓ |
Link to redirect after transaction is completed or cancelled. | http://example.com/return |
merchantUserInfo | String(255) | ✗ |
Customer's username or email on merchant site. | 1234567 |
itemDetails | object | ✗ |
See the table itemDetails below. | |
customerDetail | object | ✗ |
See the table customerDetail below. |
Item Details
Parameter | Type | Required | Description | Example |
---|---|---|---|---|
name | string(255) | ✓ |
Name of the item. | Apel |
quantity | integer | ✓ |
Quantity of the item bought. | 10 |
price | integer | ✓ |
Price of the Item. Note: Don't add decimal. | 50000 |
Customer Detail
Parameter | Type | Required | Description | Example |
---|---|---|---|---|
firstName | string(255) | ✗ |
Customer's first name. | John |
lastName | string(255) | ✗ |
Customer's last name. | Doe |
string(255) | ✗ |
Customer's email. | [email protected] | |
phoneNumber | string(255) | ✗ |
Customer's phone number. | 08123456789 |
billingAddress | object | ✗ |
See the table address below | |
shippingAddress | object | ✗ |
See the table address below |
Address
Parameter | Type | Required | Description | Example |
---|---|---|---|---|
firstName | string(255) | ✗ |
Customer's first name. | John |
lastName | string(255) | ✗ |
Customer's last name. | Doe |
address | string(255) | ✗ |
Billing address or shipping address. | Jl. Kembangan Raya |
city | string(255) | ✗ |
City of the address. | Jakarta |
postalCode | string(255) | ✗ |
Postal code of the address. | 11530 |
phone | string(255) | ✗ |
Phone number for billing or shipping. | 08123456789 |
countryCode | string(255) | ✗ |
ISO 3166-1 alpha-3. | ID |
payOptionDetails
Parameter | Type | Required | Description | Example |
---|---|---|---|---|
payMethod | String(255) | ✗ |
The type of balance you use can be filled in CASH . |
CASH |
transAmount | object | ✓ |
See the table amount below |
amount
Parameter | Type | Required | Description | Example |
---|---|---|---|---|
value | String | ✓ |
ISO4217 with 2 decimal. |
10000.00 |
currency | String(3) | ✓ |
Currency code, only receive IDR . |
IDR |
Response :
{
"responseCode": "2005400",
"responseMessage": "Success",
"referenceNo": "DXXXXX24OWAA7JJHALBAL6J",
"partnerReferenceNo": "INV1708597771"
}
Code | Message | Description |
---|---|---|
2005400 | Success | Success. |
4005400 | Service Not Implemented | Service is not registered on request CHANNEL-ID . |
4005401 | Invalid Field Format | Incorrect value for that parameter. |
4005402 | Missing Mandatory Field {AdditionalInfo.ReturnUrl} | Parameter ReturnUrl is missing. |
4015400 | Unauthorized Signature | Error in Signature. |
4015400 | Unauthorized stringToSign | Error in Signature. |
4015400 | Unauthorized Client | Error in PARTNER-ID. |
4015401 | Invalid Access Token | Access Token expired or incorrect. |
4035400 | Transaction Expired | Transaction to be updated has expired or is not active. |
4035414 | Insufficient Fund | Your balance is insufficient for this transaction. |
4045401 | Invalid Request | Incorrect value for parameter. |
4045413 | Invalid Amount, Amount must be equal with total price item details | Error in value parameter amount and price in item detail. |
4095400 | Conflict | Error in External ID. |
Debit Payment Notify (CALLBACK)
Payment is a webhook or notification when the user has already paid. To receive this information, you need to create an API to receive the notification. Here are the things you need to do when you receive payment.
- Secure: you need to make your payment secure. Because the payment information just will always be through this API. We need to ensure a robust and secure environment. You might see the image of the sequence diagram at the top of this page.
- Whitelist Duitku IP if it's needed.
IP range :
182.23.85.0/28
103.177.101.177/28
- Always validate the signature.
- Optionally you may check through Debit Payment Status to make sure that your user has already paid the transaction.
- Whitelist Duitku IP if it's needed.
IP range :
- Update: you need to update your transaction status or you might register the payment on your transaction.
Method : HTTP POST
Type : application/json
Url : https://yourdomain.com/v1.0/debit/notify
Endpoint : /v1.0/debit/notify
Service Code : 56
Header :
Parameter | Type | Required | Description | Example |
---|---|---|---|---|
Content-Type | string | ✓ |
String represents indicate the media type of the resource. | application/json |
X-TIMESTAMP | string | ✓ |
ISO-8601 | 2022-09-16T13:00:00+07:00 |
X-SIGNATURE | string | ✓ |
Refer to asymmetric. | |
X-PARTNER-ID | string | ✓ |
Project ID. | DXXXX |
X-EXTERNAL-ID | string | ✓ |
Unique request ID. | |
CHANNEL-ID | string | ✓ |
The value used must correspond to the transaction details generated earlier. The value should be OL , SA , DA , or SL . |
OL |
Body :
{
"originalReferenceNo": "DXX116FL4KD00EBRDE5",
"originalPartnerReferenceNo": "BMA-140",
"latestTransactionStatus": "00",
"amount": {
"value": "2001.00",
"currency": "IDR"
},
"additionalInfo": {
"productDetails": "tes product",
"additionalParam": "tes 123",
"paymentMethod": "OL",
"publisherOrderId": "OLZQ476GYOYZBSREF",
"merchantUserId": "[email protected]",
"settlementDate": "2022-11-20"
}
}
Parameter | Type | Required | Description | Example |
---|---|---|---|---|
originalPartnerReferenceNo | string(64) | ✓ |
The value comes from the PartnerReferenceNo parameter in the debit payment response. |
INV1708656785 |
originalReferenceNo | string(64) | ✓ |
The value comes from the ReferenceNo parameter in the debit payment response. |
D00XXXWIG5BV1WW8F3RMF |
latestTransactionStatus | String(2) | ✓ |
Status code of the transaction. | 00 |
amount | Object | ✓ |
See the table below. | |
additionalInfo | Object | ✓ |
See the table below. |
amount
Parameter | Type | Required | Description | Example |
---|---|---|---|---|
value | String | ✓ |
ISO4217 with 2 decimal. |
10000.00 |
currency | String(3) | ✓ |
Currency code, only receive IDR . |
IDR |
additionalInfo
Parameter | Type | Required | Description | Example |
---|---|---|---|---|
productDetails | String(50) | ✗ |
Description about product/service on sale. | Tes payment |
additionalParam | String(255) | ✗ |
Additional parameter for merchant purpose. | |
paymentMethod | String(2) | ✓ |
Payment method. | OL |
publisherOrderId | String(30) | ✓ |
Unique transaction payment number from Duitku. | OLZQ476GYOYZBSREF |
merchantUserId | String(255) | ✗ |
Customer's username or email on your site. | [email protected] |
settlementDate | String(10) | ✗ |
Settlement date estimation information. Format: YYYY-MM-DD |
2023-07-25 |
Response :
{
"responseCode": "2005600",
"responseMessage": "Successful",
}
Code | Message | Description |
---|---|---|
2005600 | Successful | Successfully processed and obtained data. |
4005601 | Invalid Field Format {fieldName} | Incorrect value for parameter {fieldName}. |
4005602 | Invalid Mandatory Field {fieldName} | Parameter {fieldName} is missing. |
4015600 | Unauthorized Signature | Error in Signature. |
4015601 | Invalid Access Token | Expired or incorrect Access Token. |
4045614 | Bill already paid | Bill has already been paid. |
4095600 | Conflict | Error in External ID. |
5005600 | General Error | Error occurred during request processing. |
5045600 | Time Out | Expired time. |
Debit Payment Status
This API will help you to inquire about the payment status. As you can see in the sequence above. You might need to inquire when there is a confirmation payment from the user. Or as security optional you might inquire about status after receiving Debit Payment Notify. It helps you to make sure the payment status is genuine.
Method : HTTP POST
Type : application/json
Development : https://snapdev.duitku.com/merchant/debit/v1.0/debit/status
Production : https://snap.duitku.com/merchant/debit/v1.0/debit/status
Service Code : 55
Header :
Parameter | Type | Required | Description | Example |
---|---|---|---|---|
Content-Type | string | ✓ |
String represents indicate the media type of the resource. | application/json |
X-TIMESTAMP | string | ✓ |
ISO-8601 | 2022-09-16T13:00:00+07:00 |
X-SIGNATURE | string | ✓ |
Refer to symmetric. | |
X-PARTNER-ID | string | ✓ |
Project ID. | DXXXX |
X-EXTERNAL-ID | string | ✓ |
Unique request ID. | |
CHANNEL-ID | string | ✓ |
The value used must correspond to the transaction details generated earlier. The value should be OL , SA , DA , or SL . |
OL |
Authorization | string | ✓ |
Authentication with bearer token | Bearer ZGMyNDA3NWQtNmM4Ny00NGNiLTQ2NTAtMDhkYWMxNTAzNzY0 |
Body :
{
"originalPartnerReferenceNo": "INV1708656785",
"originalReferenceNo": "D00XXXWIG5BV1WW8F3RMF",
"serviceCode": "55"
}
Parameter | Type | Required | Description | Example |
---|---|---|---|---|
originalPartnerReferenceNo | string(64) | ✓ |
The value comes from the PartnerReferenceNo parameter in the debit payment response. |
INV1708656785 |
originalReferenceNo | string(64) | ✓ |
The value comes from the ReferenceNo parameter in the debit payment response. |
D00XXXWIG5BV1WW8F3RMF |
serviceCode | string(2) | ✓ |
Transaction type indicator (service code for requesting transaction debit status). | 55 |
Response :
{
"responseCode": "2005500",
"responseMessage": "SUCCESS",
"originalReferenceNo": "D0XX17PGP6GGKB86UBQM",
"originalPartnerReferenceNo": "12313131313131",
"serviceCode": "55",
"latestTransactionStatus": "00",
"transactionStatusDesc": "Success",
"transAmount": {
"value": "1000.00",
"currency": "IDR"
}
}
Code | Message | Description |
---|---|---|
2005500 | SUCCESS | Successfully processed. |
2005501 | Invalid Token:Anda Tidak Memiliki Akses | Error in Request credentialCode . |
4005502 | Missing Mandatory Field {originalPartnerReferenceNo} | Parameter originalPartnerReferenceNo is missing. |
4005502 | Missing Mandatory Field {originalReferenceNo} | Parameter originalReferenceNo is missing. |
4005502 | Missing Mandatory Field {serviceCode} | Parameter serviceCode is missing. |
4005502 | Invalid Mandatory Header X-EXTERNAL-ID | Parameter X-EXTERNAL-ID is missing. |
4005502 | Invalid Mandatory Header CHANNEL-ID | Parameter CHANNEL-ID is missing. |
4015500 | Unauthorized Signature | Error in Signature. |
4015500 | Unauthorized stringToSign | Error in Signature. |
4015500 | Unauthorized Client | Error in PARTNER-ID. |
4015501 | Invalid Access Token | Expired or incorrect Access Token. |
4045501 | Invalid Request | Format error during request. |
5005501 | Internal Server Error | Error occurred during request processing. |
Transactions status
For detail of response latestTransactionStatus
See the table below.
Status | Description |
---|---|
00 | Successful |
03 | Pending |
04 | Refunded |
06 | Failed |
07 | Not Found |
Debit Payment Refund
The API service used for refunds from completed transactions.
Method : HTTP POST
Type : application/json
Development : https://snapdev.duitku.com/merchant/debit/v1.0/debit/refund
Production : https://snap.duitku.com/merchant/debit/v1.0/debit/refund
Service Code : 58
Header :
Parameter | Type | Required | Description | Example |
---|---|---|---|---|
Content-Type | string | ✓ |
String represents indicate the media type of the resource. | application/json |
X-TIMESTAMP | string | ✓ |
ISO-8601 | 2022-09-16T13:00:00+07:00 |
X-SIGNATURE | string | ✓ |
Refer to symmetric. | |
X-PARTNER-ID | string | ✓ |
Project ID. | DXXXX |
X-EXTERNAL-ID | string | ✓ |
Unique request ID. | |
CHANNEL-ID | string | ✓ |
The value used must correspond to the transaction details generated earlier. The value should be OL , SA , DA , or SL . |
OL |
Authorization | string | ✓ |
Authentication with bearer token | Bearer ZGMyNDA3NWQtNmM4Ny00NGNiLTQ2NTAtMDhkYWMxNTAzNzY0 |
Body :
{
"partnerRefundNo": "RFD-143",
"originalPartnerReferenceNo": "INV-143",
"originalReferenceNo": "DXX01OYA8GFJXA130B1P",
"refundAmount": {
"value": "2001.00",
"currency": "IDR"
}
}
Parameter | Type | Required | Description | Example |
---|---|---|---|---|
partnerRefundNo | String(64) | ✓ |
Unique code generate from merchant. | RFD-143 |
originalPartnerReferenceNo | string(64) | ✓ |
The value comes from the PartnerReferenceNo parameter in the debit payment response. |
INV1708656785 |
originalReferenceNo | string(64) | ✓ |
The value comes from the ReferenceNo parameter in the debit payment response. |
D00XXXWIG5BV1WW8F3RMF |
refundAmount | Object | ✓ |
See the table below. |
amount
Parameter | Type | Required | Description | Example |
---|---|---|---|---|
value | String | ✓ |
ISO4217 with 2 decimal. |
10000.00 |
currency | String(3) | ✓ |
Currency code, only receive IDR . |
IDR |
Response :
{
"responseCode": "2005800",
"responseMessage": "SUCCESS",
"originalReferenceNo": "D0001OYA8GFJXA130B1P",
"originalPartnerReferenceNo": "INV-143",
"partnerRefundNo": "RFD-143",
"refundTime": "2023-03-01T10:08:45Z",
"refundNo": "REFUND-OLOSFPBRGMNFI6RJS-1677665317584"
}
Code | Message | Description |
---|---|---|
2005800 | Successful | Successfully processed. |
4005802 | Invalid Mandatory Header X-EXTERNAL-ID | Parameter X-EXTERNAL-ID is missing. |
4005802 | Invalid Mandatory Header CHANNEL-ID | Parameter CHANNEL-ID is missing. |
4015800 | Unauthorized Signature | Error in Signature. |
4015800 | Unauthorized stringToSign | Error in Signature. |
4015800 | Unauthorized Client | Error in PARTNER-ID. |
4015801 | Invalid Access Token | Expired or incorrect Access Token. |
5005801 | Internal Server Error | Error occurred during request processing. |
QRIS
Generate QR MPM
API Generate QR MPM (Merchant Presented Mode) is an API service that can be used by merchants to obtain a QR code. The merchant can then display the QR code to accept payments from customers by allowing them to scan it using the organizer's application.
Method : HTTP POST
Type : application/json
Development : https://snapdev.duitku.com/merchant/qris/v1.0/qr/qr-mpm-generate
Production : https://snap.duitku.com/merchant/qris/v1.0/qr/qr-mpm-generate
Service Code : 47
Header :
{
"partnerReferenceNo": "INV1709543217",
"validityPeriod": "2022-09-16T13:00:00+07:00",
"amount": {
"value": "321.00",
"currency": "IDR"
},
"additionalInfo": {
"productDetails": "Tes pembayaran Qris Duitku",
"additionalParam": "test additional param",
"phoneNumber": "08221XXXXXXXX",
"email": "[email protected]",
"returnUrl": "http:\/\/example.com\/return",
"itemDetails": [
{
"name": "Test Item 1",
"price": 101,
"quantity": 1
},
{
"name": "Test Item 2",
"price": 220,
"quantity": 1
}
],
"customerDetail": {
"firstName": "John",
"lastName": "Doe",
"email": "[email protected]",
"phoneNumber": "08221XXXXXXXX",
"billingAddress": {
"firstName": "John",
"lastName": "Doe",
"address": "Jl. Kembangan Raya",
"city": "Jakarta",
"postalCode": "11530",
"phone": "08221XXXXXXXX",
"countryCode": "ID"
},
"shippingAddress": {
"firstName": "John",
"lastName": "Doe",
"address": "Jl. Kembangan Raya",
"city": "Jakarta",
"postalCode": "11530",
"phone": "08221XXXXXXXX",
"countryCode": "ID"
}
}
}
}
Parameter | Type | Required | Description | Example |
---|---|---|---|---|
Content-Type | string | ✓ |
String represents indicate the media type of the resource. | application/json |
X-TIMESTAMP | string | ✓ |
ISO-8601 | 2022-09-16T13:00:00+07:00 |
X-SIGNATURE | string | ✓ |
Refer to symmetric. | |
X-PARTNER-ID | string | ✓ |
Project ID. | DXXXX |
X-EXTERNAL-ID | string | ✓ |
Unique request ID. | |
CHANNEL-ID | string | ✓ |
Below is the list of values that can be used: SP(ShopeePay) GQ(GudangVoucher) DQ(Dana) NQ(Nobu) |
SP |
Authorization | string | ✓ |
Authentication with bearer token. | Bearer ZGMyNDA3NWQtNmM4Ny00NGNiLTQ2NTAtMDhkYWMxNTAzNzY0 |
Request Body:
Parameter | Type | Required | Description | Example |
---|---|---|---|---|
partnerReferenceNo | String(64) | ✓ |
Transaction number from merchant. | INV123456 |
validityPeriod | String(4) | ✗ |
Transaction expiry time on ISO-8601. | 2022-09-16T13:00:00+07:00 |
additionalInfo | Object | ✓ |
See the table additionalInfo below. | |
amount | Object | ✓ |
See the table amount below. |
additionalInfo
Parameter | Type | Required | Description | Example |
---|---|---|---|---|
productDetails | String(255) | ✗ |
Description about product/service on sale. | |
phoneNumber | String(15) | ✗ |
Customer's phone number. | 0857181XX |
String(60) | ✗ |
Customer's email. | [email protected] | |
additionalParam | String(255) | ✗ |
Additional parameter for merchant purpose. | |
returnUrl | String(255) | ✓ |
Link to redirect after transaction is completed or cancelled. | http://example.com/return |
itemDetails | object | ✗ |
See the table itemDetails below. | |
customerDetail | object | ✗ |
See the table customerDetail below. |
Item Details
Parameter | Type | Required | Description | Example |
---|---|---|---|---|
name | string(255) | ✓ |
Name of the item. | Apel |
quantity | integer | ✓ |
Quantity of the item bought | 10 |
price | integer | ✓ |
Price of the Item. Note: Don't add decimal. | 50000 |
Customer Detail
Parameter | Type | Required | Description | Example |
---|---|---|---|---|
firstName | string(255) | ✗ |
Customer's first name. | John |
lastName | string(255) | ✗ |
Customer's last name. | Doe |
string(255) | ✗ |
Customer's email. | [email protected] | |
phoneNumber | string(255) | ✗ |
Customer's phone number. | 08123456789 |
billingAddress | object | ✗ |
See the table address below | |
shippingAddress | object | ✗ |
See the table address below |
Address
Parameter | Type | Required | Description | Example |
---|---|---|---|---|
firstName | string(255) | ✗ |
Customer's first name. | John |
lastName | string(255) | ✗ |
Customer's last name. | Doe |
address | string(255) | ✗ |
Billing address or shipping address. | Jl. Kembangan Raya |
city | string(255) | ✗ |
City of the address. | Jakarta |
postalCode | string(255) | ✗ |
Postal code of the address. | 11530 |
phone | string(255) | ✗ |
Phone number for billing or shipping. | 08123456789 |
countryCode | string(255) | ✗ |
ISO 3166-1 alpha-3. | ID |
amount
Parameter | Type | Required | Description | Example |
---|---|---|---|---|
value | String | ✓ |
ISO4217 with 2 decimal. |
10000.00 |
currency | String(3) | ✓ |
Currency code, only receive IDR . |
IDR |
Response :
{
"partnerReferenceNo": "INV1709717052",
"referenceNo": "D1XXXXXXOWPHSWXXWTW4MP5",
"redirectUrl": "https://sandbox.duitku.com/TopUp/v2/
TopUpQrisPaymentPage.aspx?reference=GQ24ELJK4A78KUGJV40",
"responseCode": "2004700",
"responseMessage": "SUCCESS",
"qrContent": "00020101021226730021COM.GUDANGVOUCHER.WWW0118
93600916300373281602151142230919000010303UME51450015ID.OR.G
PNQR.WWW02151142230919000010303UME5204970553033605403321580
2ID5906DUITKU6015JAKARTA SELATAN610511530624101066527840520
24030627840636662CSX0703A016304EA56"
}
Code | Message | Description |
---|---|---|
2004700 | Success | Transaction success generated. |
4004700 | Service Not Implemented | Service is not registered on request CHANNEL-ID . |
4004701 | Invalid Field Format | Incorrect value for that parameter. |
4004702 | Missing Mandatory Field, {PartnerReferenceNo} | Parameter PartnerReferenceNo is missing. |
4004702 | Missing Mandatory Field, {Amount} | Parameter Amount is missing. |
4004702 | Missing Mandatory Field, {AdditionalInfo.ReturnUrl} | Parameter AdditionalInfo.ReturnUrl is missing. |
4014700 | Unauthorized Signature | Error in Signature. |
4014700 | Unauthorized stringToSign | Error in Signature. |
4014700 | Unauthorized Client | Error in PARTNER-ID. |
4014701 | Invalid Access Token | Access Token expired or incorrect. |
4044701 | Invalid Request | Incorrect value for parameter. |
4044713 | Invalid Amount, Amount must be equal with total price item details | Error in value parameter amount and price in item detail. |
4094700 | Conflict | Error in External ID. |
4094700 | The expired in field must be at least 30. | For the validityPeriod you can set it to a minimum of 30 minutes |
Payment Notify (CALLBACK)
Payment is a webhook or notification when the user has already paid. To receive this information, you need to create an API to receive the notification. Here are the things you need to do when you receive payment.
- Secure: you need to make your payment secure. Because the payment information just will always be through this API. We need to ensure a robust and secure environment. You might see the image of the sequence diagram at the top of this page.
- Whitelist Duitku IP if it's needed.
IP range :
182.23.85.0/28
103.177.101.177/28
- Always validate the signature.
- Optionally you may check through Query Payment to make sure that your user has already paid the transaction.
- Whitelist Duitku IP if it's needed.
IP range :
- Update: you need to update your transaction status or you might register the payment on your transaction.
Method : HTTP POST
Type : application/json
Url : https://yourdomain.com/v1.0/qr/qr-mpm-notify
Endpoint : /v1.0/qr/qr-mpm-notify
Service Code : 52
Header :
Parameter | Type | Required | Description | Example |
---|---|---|---|---|
X-TIMESTAMP | string | ✓ |
ISO-8601 | 2022-09-16T13:00:00+07:00 |
X-SIGNATURE | string | ✓ |
Refer to symmetric. | |
X-PARTNER-ID | string | ✓ |
Project ID. | DXXXX |
X-EXTERNAL-ID | string | ✓ |
Unique request ID. | |
CHANNEL-ID | string | ✓ |
The value used must correspond to the transaction details generated previously. Below is the list of values that can be used: SP ,GQ ,DQ or NQ |
SP |
Body :
{
"originalReferenceNo": "DXX116FL4KD00EBRDE5",
"originalPartnerReferenceNo": "BMA-140",
"latestTransactionStatus": "00",
"amount": {
"value": "2001.00",
"currency": "IDR"
},
"additionalInfo": {
"productDetails": "tes product",
"additionalParam": "tes 123",
"issuerCode": "93600916",
"paymentMethod": "GQ",
"publisherOrderId": "GQ241EKHWNVPFBC86LV",
"settlementDate": "2024-04-20"
}
}
Parameter | Type | Required | Description | Example |
---|---|---|---|---|
originalPartnerReferenceNo | string(64) | ✓ |
The value comes from the PartnerReferenceNo parameter in the debit payment response. |
INV1708656785 |
originalReferenceNo | string(64) | ✓ |
The value comes from the ReferenceNo parameter in the debit payment response. |
D00XXXWIG5BV1WW8F3RMF |
latestTransactionStatus | String(2) | ✓ |
Status code of the transaction. | 00 |
amount | Object | ✓ |
See the table below. | |
additionalInfo | Object | ✓ |
See the table below. |
amount
Parameter | Type | Required | Description | Example |
---|---|---|---|---|
value | String | ✓ |
ISO4217 with 2 decimal. |
10000.00 |
currency | String(3) | ✓ |
Currency code, only receive IDR . |
IDR |
additionalInfo
Parameter | Type | Required | Description | Example |
---|---|---|---|---|
productDetails | String(50) | ✗ |
Description about product/service on sale. | Tes payment |
additionalParam | String(255) | ✗ |
Additional parameter for merchant purpose. | |
issuerCode | String(15) | ✗ |
The payment issuer used. | 972131 |
publisherOrderId | String(30) | ✓ |
Unique transaction payment number from Duitku. | GQ241EKHWNVPFBC86LV |
paymentMethod | String(40) | ✗ |
Payment method code for being used. | GQ |
settlementDate | String(10) | ✗ |
Settlement date estimation information. Format: YYYY-MM-DD |
2023-07-25 |
Response :
{
"responseCode": "2005200",
"responseMessage": "Successful",
}
Code | Message | Description |
---|---|---|
2005200 | Successful | Successfully processed and obtained data. |
4005201 | Invalid Field Format {fieldName} | Incorrect value for parameter {fieldName}. |
4005202 | Invalid Mandatory Field {fieldName} | Parameter {fieldName} is missing. |
4015200 | Unauthorized Signature | Error in Signature. |
4015201 | Invalid Access Token | Expired or incorrect Access Token. |
4045214 | Bill already paid | Bill has already been paid. |
4095200 | Conflict | Error in External ID. |
5005200 | General Error | Error occurred during request processing. |
5045200 | Time Out | Expired time. |
Query Payment
This API will help you to inquire about the payment status. As you can see in the sequence above. You might need to inquire when there is a confirmation payment from the user. Or as security optional you might inquire about status after receiving Payment Notify. It helps you to make sure the payment status is genuine.
Method : HTTP POST
Type : application/json
Development : https://snapdev.duitku.com/merchant/qris/v1.0/qr/qr-mpm-query
Production : https://snap.duitku.com/merchant/qris/v1.0/qr/qr-mpm-query
Service Code : 51
Header :
Parameter | Type | Required | Description | Example |
---|---|---|---|---|
Content-Type | string | ✓ |
String represents indicate the media type of the resource. | application/json |
X-TIMESTAMP | string | ✓ |
ISO-8601 | 2022-09-16T13:00:00+07:00 |
X-SIGNATURE | string | ✓ |
Refer to symmetric. | |
X-PARTNER-ID | string | ✓ |
Project ID. | DXXXX |
X-EXTERNAL-ID | string | ✓ |
Unique request ID. | |
CHANNEL-ID | string | ✓ |
The value used must correspond to the transaction details generated previously. Below is the list of values that can be used: SP ,GQ ,DQ or NQ |
SP |
Authorization | string | ✓ |
Authentication with bearer token. | Bearer ZGMyNDA3NWQtNmM4Ny00NGNiLTQ2NTAtMDhkYWMxNTAzNzY0 |
Body :
{
"originalPartnerReferenceNo": "INV1708656785",
"originalReferenceNo": "D00XXXWIG5BV1WW8F3RMF",
"serviceCode": "51"
}
Parameter | Type | Required | Description | Example |
---|---|---|---|---|
originalPartnerReferenceNo | string(64) | ✓ |
The value comes from the PartnerReferenceNo parameter in the debit payment response. |
INV1708656785 |
originalReferenceNo | string(64) | ✓ |
The value comes from the ReferenceNo parameter in the debit payment response. |
D00XXXWIG5BV1WW8F3RMF |
serviceCode | string(2) | ✓ |
Transaction type indicator (service code for requesting transaction debit status). | 51 |
Response :
{
"responseCode": "2005100",
"responseMessage": "SUCCESS",
"originalReferenceNo": "DXXXX424A5WNCS7ZDKAHVP5",
"originalPartnerReferenceNo": "INV1709787234",
"serviceCode": "51",
"transactionStatusDesc": "Success",
"latestTransactionStatus": "00",
"amount": {
"value": "321.00",
"currency": "IDR"
}
}
Code | Message | Description |
---|---|---|
2005100 | Success | Transaction successfully processed. |
2005101 | Invalid Token:Anda Tidak Memiliki Akses | Error in Request credentialCode . |
4005101 | Invalid Field Format | Incorrect value for that parameter. |
4005102 | Missing Mandatory Field, {originalPartnerReferenceNo} | Parameter originalPartnerReferenceNo is missing. |
4005102 | Missing Mandatory Field, {originalReferenceNo} | Parameter originalReferenceNo is missing. |
4005102 | Missing Mandatory Field, {serviceCode} | Parameter serviceCode is missing. |
4005102 | Invalid Mandatory Header X-EXTERNAL-ID | Parameter X-EXTERNAL-ID is missing. |
4005102 | Invalid Mandatory Header CHANNEL-ID | Parameter CHANNEL-ID is missing. |
4015100 | Unauthorized Signature | Error in Signature. |
4015100 | Unauthorized stringToSign | Error in Signature. |
4015100 | Unauthorized Client | Error in PARTNER-ID. |
4015101 | Invalid Access Token | Expired or incorrect Access Token. |
4045101 | Invalid Request | Format error during request. |
5005101 | Internal Server Error | Error occurred during request processing. |
Transactions status
For detail of response latestTransactionStatus
See the table below.
Status | Description |
---|---|
00 | Successful |
03 | Pending |
04 | Refunded |
06 | Failed |
07 | Not Found |
Refund Payment
The API service used for refunds from completed transactions.
Method : HTTP POST
Type : application/json
Development : https://snapdev.duitku.com/merchant/qris/v1.0/qr/qr-mpm-refund
Production : https://snap.duitku.com/merchant/qris/v1.0/qr/qr-mpm-refund
Service Code : 78
Header :
Parameter | Type | Required | Description | Example |
---|---|---|---|---|
Content-Type | string | ✓ |
String represents indicate the media type of the resource. | application/json |
X-TIMESTAMP | string | ✓ |
ISO-8601 | 2022-09-16T13:00:00+07:00 |
X-SIGNATURE | string | ✓ |
Refer to symmetric. | |
X-PARTNER-ID | string | ✓ |
Project ID. | DXXXX |
X-EXTERNAL-ID | string | ✓ |
Unique request ID. | |
CHANNEL-ID | string | ✓ |
The value used must correspond to the transaction details generated previously. Below is the list of values that can be used: SP ,GQ ,DQ or NQ |
SP |
Authorization | string | ✓ |
Authentication with bearer token | Bearer ZGMyNDA3NWQtNmM4Ny00NGNiLTQ2NTAtMDhkYWMxNTAzNzY0 |
Body :
{
"partnerRefundNo": "RFD-143",
"originalPartnerReferenceNo": "INV-143",
"originalReferenceNo": "DXX01OYA8GFJXA130B1P",
"refundAmount": {
"value": "2001.00",
"currency": "IDR"
}
}
Parameter | Type | Required | Description | Example |
---|---|---|---|---|
partnerRefundNo | String(64) | ✓ |
Unique code generate from merchant. | RFD-143 |
originalPartnerReferenceNo | string(64) | ✓ |
The value comes from the PartnerReferenceNo parameter in the debit payment response. |
INV1708656785 |
originalReferenceNo | string(64) | ✓ |
The value comes from the ReferenceNo parameter in the debit payment response. |
D00XXXWIG5BV1WW8F3RMF |
refundAmount | Object | ✓ |
See the table below. |
amount
Parameter | Type | Required | Description | Example |
---|---|---|---|---|
value | String | ✓ |
ISO4217 with 2 decimal. |
10000.00 |
currency | String(3) | ✓ |
Currency code, only receive IDR . |
IDR |
Response :
{
"responseCode": "2007800",
"responseMessage": "SUCCESS",
"originalReferenceNo": "D0001OYA8GFJXA130B1P",
"originalPartnerReferenceNo": "INV-143",
"partnerRefundNo": "RFD-143",
"refundTime": "2023-03-01T10:08:45Z",
"refundNo": "REFUND-OLOSFPBRGMNFI6RJS-1677665317584"
}
Code | Message | Description |
---|---|---|
2007800 | Successful | Successfully processed. |
4007802 | Invalid Mandatory Header X-EXTERNAL-ID | Parameter X-EXTERNAL-ID is missing. |
4007802 | Invalid Mandatory Header CHANNEL-ID | Parameter CHANNEL-ID is missing. |
4017800 | Unauthorized Signature | Error in Signature. |
4017800 | Unauthorized stringToSign | Error in Signature. |
4017800 | Unauthorized Client | Error in PARTNER-ID. |
4017801 | Invalid Access Token | Expired or incorrect Access Token. |
5007801 | Internal Server Error | Error occurred during request processing. |
Issuer List (QRIS)
Code | Issuer |
---|---|
93600999 | AHDI |
93600947 | Aladin Syariah |
93600567 | Allo Bank Indonesia |
93600531 | Amar |
93600822 | Astrapay |
93600116 | Bank Aceh Syariah |
93600037 | Bank Artha Graha Internasional |
93600133 | Bank BPD Bengkulu |
93600124 | Bank BPD Kalimantan Timur dan Kalimantan Utara |
93600161 | Bank Ganesha |
93600513 | Bank Ina Perdana |
93600113 | Bank Jateng |
93600123 | Bank Kalbar |
93600122 | Bank Kalsel |
93600441 | Bank KB Bukopin |
93600121 | Bank Lampung |
93600157 | Bank Maspion |
93600553 | Bank Mayora |
93600548 | Bank Multiarta Sentosa |
93600490 | Bank Neo Commerce |
93600128 | Bank NTB Syariah |
93600019 | Bank Panin |
93600132 | Bank Papua |
93600115 | Bank Pembangunan Daerah Jambi |
93600494 | Bank Raya |
93600119 | Bank Riau Kepri |
93600523 | Bank Sahabat Sampoerna |
93600152 | Bank Shinhan |
93600126 | Bank Sulsel |
93600120 | Bank Sumselbabel |
93600023 | Bank UOB Indonesia |
93600808 | Bayarind |
93600014 | BCA |
93600536 | BCA Syariah |
93600501 | BCAD |
93600815 | Bimasakti Multi Sinergi |
93600110 | BJB |
93600425 | BJB Syariah |
93600919 | BluePay |
93600009 | BNI |
93600129 | BPD Bali |
93600112 | BPD DIY |
93600130 | BPD NTT |
93600114 | BPD-JATIM |
93600002 | BRI |
93600422 | BRIS Pay |
93600200 | BTN |
93600076 | Bumi Arta |
93600031 | Citibank |
93600950 | Commonwealth |
93600915 | Dana |
93600011 | Danamon |
93600046 | DBS MAX QRIS |
93600111 | DKI |
93600899 | Doku |
93600998 | DSP |
93600827 | Fello |
93600777 | Finpay |
93600813 | GAJA |
93600914 | Go-Pay |
93600916 | Gudang Voucher |
93600484 | Hana bank |
93600789 | IMkas |
93600920 | Isaku |
93600542 | JAGO |
93600213 | Jenius |
93600812 | Kaspro |
93600911 | LinkAja |
93600008 | Mandiri Pay |
93600016 | Maybank |
93600426 | Mega |
93600821 | Midazpay |
93600485 | Motion Banking |
93600147 | Muamalat |
93600118 | Nagari |
93600814 | Netzme |
93600022 | Niaga |
93600503 | Nobu |
93600028 | OCBC |
93600811 | OTTOCASH |
93600912 | OVO |
93600820 | PAC Cash |
93600818 | Paydia |
93600917 | Paytrend |
93600013 | Permata |
93608161 | POS Indonesia |
93600167 | QNB Indonesia |
93600921 | Saldomu |
93600535 | Seabank |
93600918 | ShopeePay |
93600153 | Sinarmas |
93600816 | SPIN |
93600451 | Syariah Indonesia |
93600898 | T-Money |
93600828 | TrueMoney |
93600835 | Virgo |
93600830 | YODU |
93600817 | Yukk |
93600825 | Zipay |
Testing
After you go through the integration steps above, you can check your payment. The following is a list of payment trials that can be used in the Sandbox Environment:
VA
Demo transaction virtual account sandbox Click-here.
E-Wallet
Shopee
For shopee testing you can download shopeeapp staging apk here.
Gudang Voucher
For Gudang Voucher can use within virtual account demo success.
Changelog
Version 1.0
- API Documentation created
- SNAP VA
- SNAP Direct Debit Linking
- SNAP Direct Debit Redirect
- SNAP Registration AccountLink
- SNAP QR MPM