---
title: Authentication - SNAP
slug: en/nicepay-api-snap-authentication
description: Information about API SNAP Authentication provided by NICEPAY
icon: {"faIcon":"fa-solid fa-user-gear"}
createdAt: 2026-11-05T07:08:32.767Z
---

# About Authentication - SNAP

***

NICEPAY implements two authentication security mechanisms across all SNAP API, in accordance with applicable international standard, there are:

1. **Access Token**, to ensure the called API already authenticate and authorized.
2. **Signature**, to ensure the real sender and the request content don't change during the API communication process.

The implementation steps for both two authentication standards will be explained in detail on this page, covering everything from obtaining an Access Token to generating a Signature for processing transactions with NICEPAY .



## Key Information

| **Component**                | **Function**                                                                                             | **Created By**                                           |
| ---------------------------- | -------------------------------------------------------------------------------------------------------- | -------------------------------------------------------- |
| **Client ID / X-CLIENT-KEY** | Identity owned by Merchant that given by NICEPAY                                                         | NICEPAY                                                  |
| **Access Token**             | Authorization to access NICEPAY  protected resources.<br />Valid for 15 minutes.                         | NICEPAY <br />(Merchant need to send request beforehand) |
| **Signature (X-SIGNATURE)**  | Ensure the data integrity and secure non-repudiation during communication process with the on-going API. | Merchant                                                 |



## Flow

1. Merchant must create Signature beforehand. Then, proceed to request the Access Token.
2. NICEPAY verify the Signature, then return `accessToken`.
3. Merchant use `accessToken (Bearer)`  on the next API SNAP request; such as payment, transaction status checking, and refund.
4. If the used Access Token already expired, Merchant  need to request the new Access Token.



# Integration Essentials

***

Before continue to the next phase, please ensure Merchant already have these following:

1. **Client ID&#x20;**&#x66;rom NICEPAY . This value will be used as header on  **`X-CLIENT-KEY`** parameter.&#x20;
2. **RSA pairing key&#x20;**(Private Key and Public Key). Private Key only saved by Merchant, meanwhile Public Key registered to NICEPAY .
3. The accuracy of server time (NTP synchronous), because `X-TIMESTAMP` will also be validated.

:::hint{type="danger"}
**Never sending, saving in repository, or put Private Key on the application (whether on browser or mobile application)**

All Signature creation process must be done on **the server on by Merchant.**
:::

::::hint{type="info"}
:::Iframe{iframeHeight="0" code="If already registered as NICEPAY Merchant but don't have SNAP credential<a style=&#x22;color:blue; text-decoration-line:underline&#x22; href=&#x22;mailto:integration@nicepay.co.id&#x22;>NICEPAY IT Integration</a> atau Tim <a style=&#x22;color:blue; text-decoration-line:underline&#x22; href=&#x22;mailto:cs@nicepay.co.id&#x22;>NICEPAY Customer Service</a> so can process the API SNAP integration."}

:::
::::



# NICEPAY Base URL

***

NICEPAY provides two environments that can be accessed by Merchant, which are Sandbox/Development and Production. All proceed request must use HTTPS with adding endpoint path after entered base URL. Ensure Client ID and Key that being use is suitable with the designated environment.

| **Environment** | **Protocol** | **Current NICEPAY Base URL** | **New NICEPAY Base URL**              |
| --------------- | ------------ | ---------------------------- | ------------------------------------- |
| Development     | HTTPS        | `https://dev.nicepay.co.id/` | `https://dev-services.nicepay.co.id/` |
| Production      | HTTPS        | `https://www.nicepay.co.id/` | `https://services.nicepay.co.id/`     |

:::hint{type="warning"}
**Please proceed the NICEPAY Base URL changes into the newest URL as soon as possible before 25 December 2026.**
:::



# Testing Credential

***

To test the NICEPAY SNAP API before proceeding with integration, please use the credentials below and specify **IONPAYTEST&#x20;**&#x61;s `clientID`.

:::hint{type="warning"}
This sandbox credential is shared among users and its associated Private Key is intended for public testing purposes only. Please use it exclusively in the **Development Environment**.

Never use this credential in the Production Environment, even when generating your **own key pair.**
:::

| **Credential**               | **Function**                                                                                              | **Used on**                                                       | **Origin**                                                                                                                                                                                                                                 | **Testing Credential Sample on Development**                                 |
| ---------------------------- | --------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------- |
| **Client ID**                | Merchant identity.<br /><br />Sent as `X-CLIENT-KEY` (Access Token) and `X-PARTNER-ID` (service request). | All request.                                                      | Given by NICEPAY.                                                                                                                                                                                                                          | IONPAYTEST                                                                   |
| **Channel ID**               | Merchant channel identity, is Client ID added with 01.                                                    | Header `CHANNEL-ID` on service request.                           | Shape from Client ID.                                                                                                                                                                                                                      | IONPAYTEST01                                                                 |
| **Client Secret \***         | `HMAC-SHA512`  key for  Symmetric Signature.                                                              | Service request (Registration, Check Status, Cancel, and others). | Given by NICEPAY and available on **Client Secret&#x20;**&#x66;ield on [Merchant Back Office](docId\:S2zo71iySoasepJteVVN7).<br /><br />Please visit [Credential Management](docId\:YNN6_h6fidW11uMcsb7nl).                                | Please contact NICEPAY IT Integration to gain the credential.                |
| **Private key (RSA 2048)**   | Signing Signature Asymmetric.                                                                             | Request Access Token.                                             | Created and saved by by Merchant.                                                                                                                                                                                                          | Please view Signature page to learn further on creating Private Key.         |
| **Public key (RSA 2048) \*** | Proceed verification on Asymmetric Signature.                                                             | Registered on NICEPAY.                                            | Created along side Private Key, then attached by Merchant with **Merchant Public Key&#x20;**&#x61;vailable on [Merchant Back Office](docId\:S2zo71iySoasepJteVVN7) - [Credential Management](docId\:YNN6_h6fidW11uMcsb7nl)  on PEM format. | Please registered your Public Key by contacting NICEPAY IT Integration team. |
| **Public key NICEPAY \***    | Proceed Signature verification on notification from NICEPAY.                                              | Needed during API SNAP Notification integration with NICEPAY.     | Given by NICEPAY through email of could be check on th&#x65;**&#x20;NICEPAY Public Key&#x20;**&#x66;ield on [Merchant Back Office](docId\:S2zo71iySoasepJteVVN7) - [Credential Management](docId\:YNN6_h6fidW11uMcsb7nl).                  | Please contact NICEPAY IT Integration team to gain the credential.           |
| **Access Token**             | Authorization service request valid for 15 minutes.                                                       | Header Authorization.                                             | Request result from Access Token.                                                                                                                                                                                                          | -                                                                            |

::::hint{type="info"}
:::Iframe{iframeHeight="0" code="<!-- <p>paste iframe code here</p> -->ℹ️ Please contact <a style=&#x22;color:blue; text-decoration-line:underline&#x22; href=&#x22;mailto:integration@nicepay.co.id&#x22;>NICEPAY Integration team</a> to gain further information related to Client Secret, Public Key (RSA 2048), and NICEPAY Public Key."}

:::
::::

The following are two additional mandatory parameters generated by the Merchant's system for each request, rather than being provided by NICEPAY:

- **X-TIMESTAMP**: the timestamp when the Merchant sends the request, formatted according to ISO 8601 with the GMT+7 time zone.
- **X-EXTERNAL-ID**: unique reference number for every transaction request.

For transactions that require notifications, such as Virtual Account transactions, make sure to prepare the Merchant's **Notification URL**. This URL must be provided as `dbProcessUrl` when using the Registration API.

## Sandbox Credential Guidance

1. Save Private Key Sandbox that given from NICEPAY team, then create Asymmetric Signature with IONPAYTEST as Client ID.&#x20;
   The complete example available on [Sample Demo  (Asymmetric)](docId\:mJ6KMxNmNAv1DNaLaNwmj) part.
2. Request Access Token to Development endpoint.
3. Once the Access Token has been obtained, generate the Symmetric Signature using the Client Secret provided by NICEPAY and include the Access Token value in the Authorization header.
4. Use NICEPAY Public Key to validate notification that sent by NICEPAY.



# Access Token API

***

Access Token is an authentication string value that given by NICEPAY to Merchant for accessing API, such as payment, check transaction status, or refund.



## Request Access Token

| **Service Code** | 73                                                                                           |
| ---------------- | -------------------------------------------------------------------------------------------- |
| **API Endpoint** | [`/nicepay/v1.0/access-token/b2b`](https://dev.nicepay.co.id/nicepay/v1.0/access-token/b2b)  |
| **HTTP Method**  | **POST**                                                                                     |
| **Description**  | Request Token for Authentication&#xD;                                                        |
| **Content-type** | `application/json`                                                                           |



## Header

***

### Request Access Token Header

| **Parameter Header Name** | **Type** | **Attribute** | **Content**                                                                                                                                                                                                                                                                              |
| ------------------------- | -------- | ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `Content-Type`            | String   | Mandatory     | `Application/json`                                                                                                                                                                                                                                                                       |
| `X-TIMESTAMP`             | String   | Mandatory     | The request timestamp in ISO 8601 format, including the time zone.<br />`YYYY-MM-DDTHH:MM:SS`<br />2022-03-21T10:11:40+07:00                                                                                                                                                             |
| `X-CLIENT KEY`            | String   | Mandatory     | NICEPAY Provided this Client ID Value, please make sure to <br /><br />(`X-CLIENT `<br />`KEY` = `CLIENT_ID`)                                                                                                                                                                            |
| `X-SIGNATURE`             | String   | Mandatory     | Generated by Merchant <br />created using Asymmetric<br />Signature SHA256withRSA algorithm<br />(`Private_Key`, `stringToSign`)<br />`stringToSign `= `client ID` + “`\|`” + `X-TIMESTAMP`<br /><br />Please refer to the following  [Signature](docId\:mJ6KMxNmNAv1DNaLaNwmj) section. |



## Body

***

### Request Access Token Parameter

| **Parameter**    | **Type** | **Required** | **Description**                                               |
| ---------------- | -------- | ------------ | ------------------------------------------------------------- |
| `grantType`      | String   | Mandatory    | Please only input this value “`client_credentials`”           |
| `additionalInfo` | Object   | Optional     | Additional Information. <br />Can be sent as empty object \{} |

### Response Access Token Parameter

| **Parameter BI**  | **Parameter NICEPAY** | **Type** | **Size** | **Required** | **Description**                                                                                                                     |
| ----------------- | --------------------- | -------- | -------- | ------------ | ----------------------------------------------------------------------------------------------------------------------------------- |
| `responseCode`    | `responseCd`          | String   |          | Optional     | [General Response Code and Message - API SNAP](docId\:bJr4kjAmZCKnlN-5zRnvW) <br /> <br />Error code to specify the error returned. |
| `responseMessage` | `responseMsg`         | String   |          | Optional     | Response Message<br /><br />Provides further insight into the API response.                                                         |
| `accessToken`     |                       | String   | 2048     | Mandatory    | A string that represents the authorization issued by NICEPAY  for Merchant, so can it can be used to access the transaction API.    |
| `tokenType`       |                       |   String |          | Mandatory    | Always has `Bearer` value.                                                                                                          |
| `expiresIn`       |                       | String   |          | Mandatory    | Token validity in second:<br />`900` (15 minutes).                                                                                  |

### Sample Parameter

:::CodeblockTabs
Sample Request

```json
curl --location 'https://dev-services.nicepay.co.id/nicepay/v1.0/access-token/b2b' \
--header 'Content-Type: application/json' \
--header 'X-CLIENT-KEY: IONPAYTEST' \
--header 'X-TIMESTAMP: 2024-12-31T10:11:40+07:00' \
--header 'X-SIGNATURE: DZ9iZR6xtWoFwFaARgo9h6mDgckZpPRLQjMTAlGqrYrJ4xah/deCsxe159H7sf9i2clJeGrsoq5ZDe4tzM+IRQhcxY47K3mYcbnPFGdP5maCC/tCCi63TScB0XUpJzpuPc4TM3G+2uIo816VcoKTXxR5v188m5yGDsGmfW4urddjI1Xor9lZdXP1c6fMH19psMN0g137GCD4tOdjeKW/gGpbvjMi0HQu9PjaJtoFxGmypdxayXfUU3VxWtSWf6AZ88UQ++iOBXfAQIX5Vtt6+fKh/1DOCEcZFe87Em1437VZcTET5dOuEXeQ+ow2PT/Or9iey0M8qQzk3Y6oWJg/sw==' \
--data '{
    "grantType": "client_credentials",
    "additionalInfo": {}
}'
```

Sample Response

```json
{
    "responseCode": "2007300",
    "responseMessage": "Successful",
    "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
    "tokenType": "Bearer",
    "expiresIn": "900"
}
```
:::



## Access Token Guidance

Include token on Authorization header in every next API SNAP request.

:::BlockQuote
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
:::

### Expiry Date and Requesting New Access Token

- Token validity around **900 seconds (15 minutes)** after publish.
- There are no refresh token. After expired, please request new token through the same endpoint.
- Save token in memory or Merchant server cache, then re-use until near the expired time. Please don't request new token on every transaction if the token expiry time is still active.&#x20;
- If a transaction request is rejected due to an invalid or expired token, please obtain a new Access Token and retry the transaction request once.



## API SNAP Service Code

The following is the Service Code list related to the service that Merchant can use after receive Access Token from NICEPAY .

| **Method**      | **Service Code** | **API Reference**                                                                                                                                                                                                                                                                                                                                                                                                                                |
| --------------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Virtual Account | `27`             | [Registration - API Virtual Account SNAP](docId\:Rfwa2zCRE7TNjXWKr-j0-)                                                                                                                                                                                                                                                                                                                                                                          |
| Virtual Account | `26`             | [Check Status Transaction - API Virtual Account SNAP](docId\:QDN7CGVv26s2XEibfWg1U)                                                                                                                                                                                                                                                                                                                                                              |
| Virtual Account | `31`             | [Cancel - API Virtual Account SNAP](docId\:i-K2pbXQYAmba7zW0aYJM)                                                                                                                                                                                                                                                                                                                                                                                |
| E-Wallet        | `73`             | [Access Token B2B & B2B2C - Account Binding API E-Wallet (Direct Debit) SNAP](docId\:OtE0XWOYTi698Exrcb_Ld)                                                                                                                                                                                                                                                                                                                                      |
| E-Wallet        | `54`             | [Registration - API E-Wallet (Direct Debit) SNAP](docId\:U__7u4PnTx3_eSWeZ-94m)                                                                                                                                                                                                                                                                                                                                                                  |
| E-Wallet        | `55`             | [Check Status Transaction - API E-Wallet (Direct Debit) SNAP](docId\:za7aN0HE8C3-2OiXvdlNa)                                                                                                                                                                                                                                                                                                                                                      |
| E-Wallet        | `58`             | [Refund Transaction - API E-Wallet (Direct Debit) SNAP](docId\:Y-cGH6ylEK9h3FgMdXuqZ)                                                                                                                                                                                                                                                                                                                                                            |
| QRIS            | `47`             | [Registration - API QRIS SNAP](docId\:FsuuZk9ZHP7pRzFCi32fm)                                                                                                                                                                                                                                                                                                                                                                                     |
| QRIS            | `51`             | [Check Status Transaction - API QRIS SNAP](docId\:a0i6Lo4dvBo6Ih5Ql6nDA)                                                                                                                                                                                                                                                                                                                                                                         |
| QRIS            | `78`             | [Refund Transaction - API QRIS SNAP](docId\:uZpKssMvaM0skERr1Fn9B)                                                                                                                                                                                                                                                                                                                                                                               |
| Payout          | `00`             | - [Registration - API Payout (Disbursement) SNAP](docId\:eonRZaDi86qH4vIEcXRrs)<br />- [Approve Transaction - API Payout (Disbursement) SNAP](docId:75T6p-QRSteur4iECzRJe)<br />- [Reject Transaction - API Payout (Disbursement) SNAP](docId\:ralDhY1OmjyJYEy_J1xll)<br />- [Cancel Transaction - API Payout (Disbursement) SNAP](docId\:CedmfUI42mOyU8_Iy9j2q)<br />- [Inquiry - API Payout (Disbursement) SNAP](docId\:Ge22wm18E02qRwDgBWMz_) |
| Payout          | `11`             | [Balance Inquiry - API Payout (Disbursement) SNAP](docId\:LFT65B6bexNuG9__o_evu)                                                                                                                                                                                                                                                                                                                                                                 |



## Common API Errors (Access Token)

The following are common issues encountered during token requests. Please refer to the [General Response Code and Message - API SNAP](docId\:bJr4kjAmZCKnlN-5zRnvW) for further information.

| **Case**                      | **Things that need to be checked**                                          |
| ----------------------------- | --------------------------------------------------------------------------- |
| Signature is not valid        | `stringToSign` format, frequent use Private Key, and encoding Base64.       |
| `Client ID` is not recognized | `X-CLIENT-KEY` value and environment (Development/Sandbox atau Production). |
| Wrong `X-TIMESTAMP` format    | ISO-8601 format with time zone, example +07:00.                             |
| Empty mandatory field         | Header `X-TIMESTAMP`, `X-CLIENT-KEY`, `X-SIGNATURE`, and `grantType`.       |
| Wrong `grantType`             | Must be filled with "`client_credentials`" value                            |



# Signature

***

Signature (`X-SIGNATURE`) is a digital signage on every API SNAP request. This signature provide three layer security, including:

1. **Data Integrity.&#x20;**&#x45;nsure request data unchange during sending request process without undetect by system. If there are data changes, digital signature is no longer valid so the request will be rejected.
2. **Non-Repudiation.&#x20;**&#x45;nsure the sender can't deny the request that has been sign with their Private Key, because that sign only can be created by the party that has the suitable Private Key. This mechanism is valid for signage with Asymmetric Signature.
3. **Replay Protection.&#x20;**&#x50;revent the old request that has been intercept for re-send and re-process as valid request. This should be done by attached `X-TIMESTAMP` on the sign data, so the request valid time could be verify.

SNAP standard from Bank Indonesia using two type of Signature depends on the called  API.

| **Type**       | **Used for**                 | **Algorithm** | **Key**               |
| -------------- | ---------------------------- | ------------- | --------------------- |
| **Asymmetric** | Request Access Token.        | SHA256withRSA | Private Key Merchant  |
| **Symmetric**  | Request layanan (transaksi). | HMAC-SHA512   | Client Secret         |



## Create Key Pair for Asymmetric Signature A (Private Key and Public Key)

Asymmetric Signature uses an RSA key pair consisting of two keys:

- **Private Key:** Used by the Merchant to sign API requests. It must be kept confidential and must never be shared.
- **Public Key:** Provided to NICEPAY for signature verification.

Both keys must use the **RSA 2048-bit** key length.

The following is a sample using OpenSSL.

**1. Created Private Key.**

:::BlockQuote
openssl genpkey -algorithm RSA -out private\_key.pem -pkeyopt rsa\_keygen\_bits:2048
:::

File `private_key.pem` has PKCS#8 (start with `-----BEGIN PRIVATE KEY-----`), that can be used directly on Java dan Node.js.

**2. Create Public Key from Private Key.**

:::BlockQuote
openssl rsa -in private\_key.pem -pubout -out public\_key.pem
:::

File `public_key.pem` start with `-----BEGIN PUBLIC KEY-----`.

**3. Give Public Key to NICEPAY.**

Only send `public_key.pem` to NICEPAY. Ensure Merchant register the Public Key for the environment that will be used (Development/Sandbox or Production). Use PEM format, which is `public_key.pem` complete with `-----BEGIN PUBLIC KEY-----` and `-----END PUBLIC KEY-----` row. Only paste the Public Key. Never paste the Private Key on any field.

**4. Save the Private Key securely.**

Save `private_key.pem` on Merchant server with limited access, like in secret manager or protected variable environment. If Private Key leak, create new key pair and register the newest Public Key to NICEPAY.

**5. Test the create Signature (optional)**

Merchant can try create Signature for the terminal to compare the result with the owned code.

:::BlockQuote
echo -n "IONPAYTEST|2024-12-31T10:11:40+07:00" | openssl dgst -sha256 -sign private\_key.pem | openssl base64 -A
:::

:::hint{type="info"}
**Format remark:** If Private Key Merchant start with `  -----BEGIN RSA PRIVATE KEY-----  `(PKCS#1), change to PKCS#8 into `openssl pkcs8 -topk8 -nocrypt -in private_key.pem -out private_key_pkcs8.pem` because Java commonly need PKCS#8 format.
:::

This command produce `X-SIGNATURE` value for that `stringToSign`.



## Signature for Access Token (Asymmetric)

**1. Prepare `stringToSign`.**

:::BlockQuote
stringToSign = X-CLIENT-KEY + "|" + X-TIMESTAMP
:::

Example:

:::BlockQuote
IONPAYTEST|2024-12-31T10:11:40+07:00
:::

`X-TIMESTAMP` value on `stringToSign` must be **identic&#x20;**&#x74;o the sent one on Header.

**2. Sign with`SHA256withRSA`.**

Use `SHA256withRSA` algorithm with Private Key the Merchant to sign `stringToSign`.

**3. Encode with Base64.**

Signature result (byte) is encode to Base64.

**4. Sent as Header.**

Input the result into `X-SIGNATURE` Header.

:::BlockQuote
X-SIGNATURE = Base64( SHA256withRSA( Private\_Key, stringToSign ) )
:::

### Sample Code

:::CodeblockTabs
```java
String stringToSign = clientId + "|" + timestamp;

java.security.Signature signer = java.security.Signature.getInstance("SHA256withRSA");
signer.initSign(privateKey); // java.security.PrivateKey
signer.update(stringToSign.getBytes(java.nio.charset.StandardCharsets.UTF_8));

String xSignature = java.util.Base64.getEncoder().encodeToString(signer.sign());
```

```nodejs
const crypto = require('crypto');
const fs = require('fs');

const clientId = 'IONPAYTEST';
const timestamp = '2024-12-31T10:11:40+07:00'; // must exactly match the X-TIMESTAMP header.

const stringToSign = `${clientId}|${timestamp}`;
const privateKey = fs.readFileSync('private_key.pem', 'utf8');

const signer = crypto.createSign('RSA-SHA256');
signer.update(stringToSign);
signer.end();

const xSignature = signer.sign(privateKey, 'base64');
```
:::

### Sample Demo  (Asymmetric)

Use the following demo data to practice and compare the Merchant result code.&#x20;

:::hint{type="warning"}
Key pair on the below specially created for demo. **Never use it on Development/Sandbox or Production.**
:::



**Data input**

:::BlockQuote
X-CLIENT-KEY : IONPAYTEST
X-TIMESTAMP  : 2024-12-31T10:11:40+07:00
:::

**stringToSign**

:::BlockQuote
IONPAYTEST|2024-12-31T10:11:40+07:00
:::

**Private Key demo** (save as `ionpaytest_sandbox_private_key.pem`)

:::BlockQuote
\-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDU0njmaK+v6zB5
fE2IvZjvz5TJ0njYWUs8lg6uHNeApLSq6PlM4K9cL//I743bHGfiQ2BIij6W7XPE
dg374SSuNF/cJXzEewMY0kgzxvBvuFMO8tVrsiOX7BM4pAOvgRhFCZo5GxVPkH3o
gVji/uF4JKK8sLE6WYVdM0Rvsuzc2I1nthROf4iPRfOLooWnHtOrByYjGg7kCT+S
koaD4ieEWMRNljlPP/J08Uo1g0Wx+7dY9DA16enfbjjs9m6xtlIq8A8H+tMEnhLl
u77Mxj5bJv8HXVTHrqTigIh8CMR2n2u9LYom1rK+cOgof6J11JPA7lt0/XUfhP1H
iy1DW5GPAgMBAAECggEAFTwG2h554RibVpPDfLBGMS9KDjUU36lRK3NhdZflHK3S
ZPcn4LbOaGlVoSvdwI3+A4JrefFpO0/n7CNHU+lr6oHJSsLmxwfrEC/zCk+AL/V5
PBWYm81DZeYlKNKcl4dO75x8qdP89VpaZbd6MfPydTVak28hz8uc1ebGnkqA/avj
I4WDOT5w81ECXe6SYy1TjHY7Lq2Gaqj79rIKF+tU6HAbKxPDR78vzFi/dpN38OYX
///Db4CU5mcJKBFfHOTE3lUYUZ3/CmfccReHxs0U9Z12mACw/pvyqz3LKslus7X1
Zu25U0YPtmIZ30HTAZ26o06GZDRAta+KeJaYHEMVyQKBgQDrB7Rq79xbaHEs+i0q
duw0w+4KlMtOFOfV/4YTcpDDYO0+beNg6V3nvIAhj8z5dlv2BUxdur7qQpR77Ixa
Th/5pnYmoKLYYAv7bVg54wTmk/rZ1hE+QJpC4Gji2tKUcEpU6Uajv3M2SYqrAPc8
r+Fhbgu+LPhknjPfflhP+7kmXQKBgQDnz4S+rTlY2i12UEbLcLpJBDvs+KZTzx1l
C6s1uL6gKfUcR6TQ0ndMZI/cDsro0UC+JuqsQrahOS2JM9YgM6wzXEVX6dE9OIVz
yYIPpY/d9Lnl8pF2Hpdd7d99hIfVs2MtvBgP5xRxhDbWkXHWRbS0JJCRggOyll6d
6vt6tNrA2wKBgQCQ5rYn++u6e2DTXwZ7mMg2G9nlsijJ4f+xIxGxab+whVCfXbaC
6D3aApBooLS51GwL+OZFHz5XRWcwI/3AK0xxaVyMohrkFgstHxP21G0QD4nnxZ/p
5oB03NAtlMWheL2LLis8B2vru1l/5l+HjdQBcoxQ2n7l4dePYeDmpwZWjQKBgEey
aY5IKYqPwKvtXFgj/5oB0z8SEO+vCZptJ4EwKNWE5Kjgh9uGnRtlpxEbCitEPVAS
3VZ2M+JduvXKsJm5IbOpAFGWof7bcntDghSw0nSF8tFqWw87mvmUTyGVUiYIm85T
dOXGRQ5vJD7tMVZirOdiv8wheF9OHumc+jcTpO0tAoGAcm/z+EqWfzeIV/6kQF3x
DZUOzC+XyoF7PI844JK7FZY9Al0cS2RkFEkrvGTjGqXIyYqv1kL5uc9CWCxR8Adf
UFxginBS+0bEgjlW0J0dPwCdSzdT+vBvodw2GPdCVBAkyS/KmYckv6pOuHufXUUv
PXxg91vzYHpJYLnNn9h+VNI=
\-----END PRIVATE KEY-----
:::

**Public Key demo** (save as `ionpaytest_sandbox_public_key.pem`)

:::BlockQuote
\-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1NJ45mivr+sweXxNiL2Y
78+UydJ42FlLPJYOrhzXgKS0quj5TOCvXC//yO+N2xxn4kNgSIo+lu1zxHYN++Ek
rjRf3CV8xHsDGNJIM8bwb7hTDvLVa7Ijl+wTOKQDr4EYRQmaORsVT5B96IFY4v7h
eCSivLCxOlmFXTNEb7Ls3NiNZ7YUTn+Ij0Xzi6KFpx7TqwcmIxoO5Ak/kpKGg+In
hFjETZY5Tz/ydPFKNYNFsfu3WPQwNenp32447PZusbZSKvAPB/rTBJ4S5bu+zMY+
Wyb/B11Ux66k4oCIfAjEdp9rvS2KJtayvnDoKH+iddSTwO5bdP11H4T9R4stQ1uR
jwIDAQAB
\-----END PUBLIC KEY-----
:::

**Result: X-SIGNATURE**

:::BlockQuote
DZ9iZR6xtWoFwFaARgo9h6mDgckZpPRLQjMTAlGqrYrJ4xah/deCsxe159H7sf9i2clJeGrsoq5ZDe4tzM+IRQhcxY47K3mYcbnPFGdP5maCC/tCCi63TScB0XUpJzpuPc4TM3G+2uIo816VcoKTXxR5v188m5yGDsGmfW4urddjI1Xor9lZdXP1c6fMH19psMN0g137GCD4tOdjeKW/gGpbvjMi0HQu9PjaJtoFxGmypdxayXfUU3VxWtSWf6AZ88UQ++iOBXfAQIX5Vtt6+fKh/1DOCEcZFe87Em1437VZcTET5dOuEXeQ+ow2PT/Or9iey0M8qQzk3Y6oWJg/sw==
:::

Since SHA256withRSA (PKCS#1 v1.5) produces the same Signature value for identical inputs and keys, the Merchant's test should generate a Signature that exactly matches the example above. The Merchant can also verify the result using the terminal:

:::BlockQuote
printf '%s' "IONPAYTEST|2024-12-31T10:11:40+07:00" | openssl dgst -sha256 -sign ionpaytest\_sandbox\_private\_key.pem | openssl base64 -A
:::



## Client Secret (Symmetric Key)

Unlike an RSA key pair, the key used for Symmetric Signature is **not generated by the Merchant**. Instead, it is the **Client Secret** issued by NICEPAY and used as the key for the HMAC-SHA512 algorithm. NICEPAY uses the same key to verify the Signature generated by the Merchant.

| ****                 | **Asymmetric (Access Token)** | **Symmetric (Service Request)**                  |
| -------------------- | ----------------------------- | ------------------------------------------------ |
| **Key**              | Private Key RSA               | Client Secret                                    |
| **Created by**       | Merchant                      | NICEPAY                                          |
| **Given to NICEPAY** | Public Key                    | None. The key must not be shared with any party. |
| **Saved by**         | Merchant (Private Key)        | Merchant andNICEPAY                              |

Client Secret Management Guideline&#x73;**:**

- Store the Client Secret on the Merchant's server using a secure *secret manager* or protected environment variables.
- Never store the Client Secret in source code, repositories, or client-side applications, including browsers and mobile applications.
- Never include the Client Secret in API requests, logs, or communications with any party, including the NICEPAY Support Team.
- Use the Client Secret corresponding to the appropriate environment, either Development/Sandbox or Production.
- If the Client Secret is suspected of being compromised or exposed, immediately contact the NICEPAY team to request a new Client Secret.



## Signature for Transaction Service Request (Symmetric)

Once the **Access Token** has been obtained, Merchant able to request NICEPAY SNAP transaction service (such as registration transaction, payment, check status and refund). Each request must be signed using a **Symmetric Signature**.&#x20;

The following headers must be included in the request:

| **Header Name** | **Required** | **Description**                                                                                                       |
| --------------- | ------------ | --------------------------------------------------------------------------------------------------------------------- |
| Content-Type    | Mandatory    | `application/json`                                                                                                    |
| Authorization   | Mandatory    | Bearer &#x7B;accessToken}                                                                                             |
| X-TIMESTAMP     | Mandatory    | Request time, ISO-8601 format on time zone GMT+7 (Jakarta). <br /><br />Example: `2024-12-31T10:11:40+07:00`          |
| X-SIGNATURE     | Mandatory    | Symmetric Signature that has been created by Merchant, with the formula below.                                        |
| X-PARTNER-ID    | Mandatory    | Client ID from NICEPAY. <br /><br />Maximum 20 characters.                                                            |
| X-EXTERNAL-ID   | Mandatory    | Unique reference number for every request.<br /><br />Maximum 40 characters.                                          |
| CHANNEL-ID      | Mandatory    | "Client ID" added with "01". <br /><br />Example: IONPAYTEST + 01 into IONPAYTEST01<br /><br />Maximum 20 characters. |

**1. Count hash body**

Minify the request body by removing whitespace and line breaks outside string values, calculate its SHA-256 hash, and convert the result to lowercase hexadecimal.

:::BlockQuote
bodyHash = LowerCase( HexEncode( SHA-256( Minify( RequestBody ) ) ) )
:::

**2. Construct stringToSign**

:::BlockQuote
stringToSign = HTTPMethod + ":" + EndpointUrl + ":" + AccessToken + ":" + bodyHash + ":" + X-TIMESTAMP
:::

`EndpointUrl` The EndpointUrl refers to the **API Endpoint** value provided on each API documentation page. Include the query string, if applicable, but exclude the domain.

For example: `/api/v1.0/transfer-va/create-va` for [Registration - API Virtual Account SNAP](docId\:Rfwa2zCRE7TNjXWKr-j0-).&#x20;

**3. Sign with HMAC-SHA512**

:::BlockQuote
X-SIGNATURE = Base64( HMAC-SHA512( clientSecret, stringToSign ) )
:::

### Sample Code

:::CodeblockTabs
```java
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.Base64;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

// requestBody = The minified JSON body must exactly match the body sent in the request.
MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
byte[] hash = sha256.digest(requestBody.getBytes(StandardCharsets.UTF_8));

StringBuilder hex = new StringBuilder();
for (byte b : hash) {
    hex.append(String.format("%02x", b)); // lowercase letters
}
String bodyHash = hex.toString();

String stringToSign = httpMethod + ":" + endpointUrl + ":" + accessToken
        + ":" + bodyHash + ":" + timestamp;

Mac mac = Mac.getInstance("HmacSHA512");
mac.init(new SecretKeySpec(clientSecret.getBytes(StandardCharsets.UTF_8), "HmacSHA512"));

String xSignature = Base64.getEncoder()
        .encodeToString(mac.doFinal(stringToSign.getBytes(StandardCharsets.UTF_8)));
```

```nodejs
const crypto = require('crypto');

const body = JSON.stringify(requestBody); //JSON.stringify already produces a minified JSON body.
const bodyHash = crypto.createHash('sha256').update(body).digest('hex').toLowerCase();

const stringToSign = [method, endpointUrl, accessToken, bodyHash, timestamp].join(':');

const xSignature = crypto
  .createHmac('sha512', clientSecret)
  .update(stringToSign)
  .digest('base64');
```
:::

The hashed request body mus&#x74;**&#x20;exactly match&#x20;**&#x77;ith the sent body in request. If the library used by Merchant to send the request modifies the JSON structure, field order or formatting after the Signature created, then the verification process will fail.

### Sample Demo  (Symmetric)

The following sample is using [Registration - API Virtual Account SNAP](docId\:Rfwa2zCRE7TNjXWKr-j0-) (Service Code `27`, Bank BMRI), with Client Secret and Access Token demo. **Don't use this demo value** to proceed the real request whether on Development or Production environment.

**Data input**

:::BlockQuote
HTTP Method   : POST
EndpointUrl   : /api/v1.0/transfer-va/create-va
AccessToken   : DEMO\_ACCESS\_TOKEN\_0123456789
X-TIMESTAMP   : 2024-12-31T10:11:40+07:00
Client Secret : DEMO\_CLIENT\_SECRET\_0123456789
:::

**Request body (minify already)**

:::BlockQuote
\{"partnerServiceId":"","customerNo":"","virtualAccountNo":"","virtualAccountName":"Jhon Doe","trxId":"2022020100000000000001","totalAmount":\{"value":"10000.00","currency":"IDR"},"additionalInfo":\{"bankCd":"BMRI","goodsNm":"Jhon Doe","dbProcessUrl":"https\://dev-services.nicepay.co.id/IONPAY\_CLIENT/paymentResult.jsp"}}
:::

**1. bodyHash**

:::BlockQuote
349290ab6d7077f53cf2904fab16b673900c336d8d24cdf2b3d520adbf89cc15
:::

**2. stringToSign**

:::BlockQuote
POST:/api/v1.0/transfer-va/create-va\:DEMO\_ACCESS\_TOKEN\_0123456789:349290ab6d7077f53cf2904fab16b673900c336d8d24cdf2b3d520adbf89cc15:2024-12-31T10:11:40+07:00
:::

**3. Result: X-SIGNATURE**

:::BlockQuote
mopZ9NrDMwSN1+06feyghmH+b01rZI185o9lnheWC22hS84om3MQUTaLN7+1mpdPuAcqddd8JAxP/8OOKnMD4w==
:::

**4. Include the Signature in the Request**

The generated Signature must be included in the `X-SIGNATURE` header, along with the other required headers listed in the table above.

:::BlockQuote
curl --location 'https\://dev-services.nicepay.co.id/nicepay/api/v1.0/transfer-va/create-va' \\
\--header 'Content-Type: application/json' \\
\--header 'Authorization: Bearer DEMO\_ACCESS\_TOKEN\_0123456789' \\
\--header 'X-TIMESTAMP: 2024-12-31T10:11:40+07:00' \\
\--header 'X-SIGNATURE: mopZ9NrDMwSN1+06feyghmH+b01rZI185o9lnheWC22hS84om3MQUTaLN7+1mpdPuAcqddd8JAxP/8OOKnMD4w==' \\
\--header 'X-PARTNER-ID: IONPAYTEST' \\
\--header 'X-EXTERNAL-ID: tXid202410142300021707638093' \\
\--header 'CHANNEL-ID: IONPAYTEST01' \\
\--data-raw '\{"partnerServiceId":"","customerNo":"","virtualAccountNo":"","virtualAccountName":"Jhon Doe","trxId":"2022020100000000000001","totalAmount":\{"value":"10000.00","currency":"IDR"},"additionalInfo":\{"bankCd":"BMRI","goodsNm":"Jhon Doe","dbProcessUrl":"https\://dev-services.nicepay.co.id/IONPAY\_CLIENT/paymentResult.jsp"}}'
:::

:::hint{type="info"}
**Important Notes:**

- The request body provided in `--data-raw` must match the request body used to generate the SHA-256 hash exactly. Any differences in formatting, whitespace, line breaks, or values may result in an invalid Signature.
- The example above uses a demo Access Token and Signature. Therefore, the request will be rejected if submitted as is.
- Before testing, replace the demo values with your actual **Access Token**, **Client Secret**, and **Merchant Client ID** corresponding to the environment you are using.
:::

Additionally, Merchant can compare the generated result with the output obtained by running the following terminal command:

:::BlockQuote
BODY='\{"partnerServiceId":"","customerNo":"","virtualAccountNo":"","virtualAccountName":"Jhon Doe","trxId":"2022020100000000000001","totalAmount":\{"value":"10000.00","currency":"IDR"},"additionalInfo":\{"bankCd":"BMRI","goodsNm":"Jhon Doe","dbProcessUrl":"https\://dev-services.nicepay.co.id/IONPAY\_CLIENT/paymentResult.jsp"}}'

BODY\_HASH=$(printf '%s' "$BODY" | openssl dgst -sha256 | awk '\{print $NF}')

STRING\_TO\_SIGN="POST:/api/v1.0/transfer-va/create-va\:DEMO\_ACCESS\_TOKEN\_0123456789:$&#x7B;BODY\_HASH}:2024-12-31T10:11:40+07:00"

printf '%s' "$STRING\_TO\_SIGN" | openssl dgst -sha512 -hmac "DEMO\_CLIENT\_SECRET\_0123456789" -binary | openssl base64 -A
:::



## Common Mistake on Signature Creation

| **Case**                                              | **Frequent issue**                                                                                     |
| ----------------------------------------------------- | ------------------------------------------------------------------------------------------------------ |
| The Signature rejected although the code looks right. | The `X-TIMESTAMP` value <br />in the `stringToSign` differs from the value sent in the request header. |
| The Signature is rejected for only some requests.     | The request body is modified after hashing (whitespace, field order, or encoding).                     |
| The Signature rejected on all request&#x73;*.*        | Private Key didn't match with registered Public Key on NICEPAY.                                        |
| Signature Rejected when switching environments.       | Using a Client ID or key from one environment in another environment.                                  |
| Weird character appears on the Signature.             | Forgot to encode `Base64` or there is new line carried along.                                          |

