# Sample Code

Here is the sample code to generate the `Signature`:

{% hint style="info" %}
**SAMPLE ONLY!**

This is just a sample code to demonstrate how to generate the Signature on different programming language. Kindly adjust the code to suited your project's structure.
{% endhint %}

{% tabs %}
{% tab title="Java" %}

<pre class="language-java"><code class="lang-java"><strong>import javax.crypto.Mac;
</strong>import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
 
public class Signature {
 
    public static final String CLIENT_ID = "Client-Id";
    public static final String REQUEST_ID = "Request-Id";
    public static final String REQUEST_TIMESTAMP = "Request-Timestamp";
    public static final String REQUEST_TARGET = "Request-Target";
    public static final String DIGEST = "Digest";
    public static final String COLON_SYMBOL = ":";
    public static final String NEW_LINE = "\n";
    
    // Generate Digest
    public static String generateDigest(String requestBody) throws NoSuchAlgorithmException {
        MessageDigest md = MessageDigest.getInstance("SHA-256");
        md.update(requestBody.getBytes(StandardCharsets.UTF_8));
        byte[] digest = md.digest();
        return Base64.getEncoder().encodeToString(digest);
    }
    
    private static String generateSignature(String clientId, String requestId, String requestTimestamp, String requestTarget, String digest, String secret) throws InvalidKeyException, NoSuchAlgorithmException {
        // Prepare Signature Component
        System.out.println("----- Component Signature -----");
        StringBuilder component = new StringBuilder();
        component.append(CLIENT_ID).append(COLON_SYMBOL).append(clientId);
        component.append(NEW_LINE);
        component.append(REQUEST_ID).append(COLON_SYMBOL).append(requestId);
        component.append(NEW_LINE);
        component.append(REQUEST_TIMESTAMP).append(COLON_SYMBOL).append(requestTimestamp);
        component.append(NEW_LINE);
        component.append(REQUEST_TARGET).append(COLON_SYMBOL).append(requestTarget);
        // If body not send when access API with HTTP method GET/DELETE
        if(digest != null &#x26;&#x26; !digest.isEmpty()) {
            component.append(NEW_LINE);
            component.append(DIGEST).append(COLON_SYMBOL).append(digest);
        }

        System.out.println(component.toString());
        System.out.println();
 
        // Calculate HMAC-SHA256 base64 from all the components above
        byte[] decodedKey = secret.getBytes();
        SecretKey originalKey = new SecretKeySpec(decodedKey, 0, decodedKey.length, "HmacSHA256");
        Mac hmacSha256 = Mac.getInstance("HmacSHA256");
        hmacSha256.init(originalKey);
        hmacSha256.update(component.toString().getBytes());
        byte[] HmacSha256DigestBytes = hmacSha256.doFinal();
        String signature = Base64.getEncoder().encodeToString(HmacSha256DigestBytes);
        // Prepend encoded result with algorithm info HMACSHA256=
        return "HMACSHA256="+signature;
    }
    
    // Sample of Usage
    public static void main(String[] args) throws NoSuchAlgorithmException, InvalidKeyException {
         String jsonBody = new JSONObject()
                          .put("order", new JSONObject()
                            .put("invoice_number", "INV-20210124-0001")
                            .put("amount", 15000)
                          )
                          .put("virtual_account_info", new JSONObject()
                            .put("expired_time", 60)
                            .put("amount", 15000)
                          )
                          .toString();
 
        // Generate Digest from JSON Body, For HTTP Method GET/DELETE don't need generate Digest
        System.out.println("----- Digest -----");
        String digest = generateDigest(jsonBody);
        System.out.println(digest);
        System.out.println();
 
        // Generate Signature
        String headerSignature = generateSignature(
                "yourClientId",
                "yourRequestId",
                "2020-10-21T03:38:28Z",
                "/request-target/goes-here", // For merchant request to DOKU, use DOKU path here. For HTTP Notification, use merchant path here
                digest, // Set empty string for this argumentes if HTTP Method is GET/DELETE
                "secret-key-from-DOKU-back-office");
 
        System.out.println("----- Header Signature -----");
        System.out.println(headerSignature);
    }
}
</code></pre>

{% endtab %}

{% tab title="PHP" %}

```php
<?php

$clientId = "yourClientId";
$requestId = "yourRequestId";
$requestDate = "2020-10-21T03:38:28Z";
$targetPath = "/request-target/goes-here"; // For merchant request to Jokul, use Jokul path here. For HTTP Notification, use merchant path here
$secretKey = "secret-key-from-jokul-back-office";
$requestBody = array (
    'order' => array (
        'amount' => 15000,
        'invoice_number' => 'INV-20210124-0001',
    ),
    'virtual_account_info' => array (
        'expired_time' => 60,
        'reusable_status' => false,
        'info1' => 'Merchant Demo Store',
    ),
    'customer' => array (
        'name' => 'Taufik Ismail',
        'email' => 'taufik@example.com',
    ),
);

// Generate Digest
$digestValue = base64_encode(hash('sha256', json_encode($requestBody), true));
echo "Digest: " . $digestValue;
echo "\r\n\n";

// Prepare Signature Component
$componentSignature = "Client-Id:" . $clientId . "\n" . 
                      "Request-Id:" . $requestId . "\n" .
                      "Request-Timestamp:" . $requestDate . "\n" . 
                      "Request-Target:" . $targetPath . "\n" .
                      "Digest:" . $digestValue;
echo "Component Signature: \n" . $componentSignature;
echo "\r\n\n";
 
// Calculate HMAC-SHA256 base64 from all the components above
$signature = base64_encode(hash_hmac('sha256', $componentSignature, $secretKey, true));
echo "Signature: " . $signature;
echo "\r\n\n";

// Sample of Usage
$headerSignature =  "Client-Id:" . $clientId ."\n". 
                    "Request-Id:" . $requestId . "\n".
                    "Request-Timestamp:" . $requestDate ."\n".
                    // Prepend encoded result with algorithm info HMACSHA256=
                    "Signature:" . "HMACSHA256=" . $signature;
echo "your header request look like: \n".$headerSignature;
echo "\r\n\n";

```

{% endtab %}

{% tab title="Python" %}

```python
import hashlib
import hmac
import base64

# Generate Digest
def generateDigest(jsonBody):  
    return base64.b64encode(hashlib.sha256(jsonBody.encode('utf-8')).digest()).decode("utf-8")

def generateSignature(clientId, requestId, requestTimestamp, requestTarget, digest, secret):
    # Prepare Signature Component
    print("----- Signature Component -----")
    componentSignature = "Client-Id:" + clientId
    componentSignature += "\n"
    componentSignature += "Request-Id:" + requestId
    componentSignature += "\n"
    componentSignature += "Request-Timestamp:" + requestTimestamp
    componentSignature += "\n"
    componentSignature += "Request-Target:" + requestTarget
    # If body not send when access API with HTTP method GET/DELETE
    if digest:
        componentSignature += "\n"
        componentSignature += "Digest:" + digest
     
    print(componentSignature)
    message = bytes(componentSignature, 'utf-8')
    secret = bytes(secret, 'utf-8')
 
    # Calculate HMAC-SHA256 base64 from all the components above
    signature = base64.b64encode(hmac.new(secret, message, digestmod=hashlib.sha256).digest()).decode("utf-8")

    # Prepend encoded result with algorithm info HMACSHA256=
    return "HMACSHA256="+signature 

# Sample of usage

# Generate Digest from JSON Body, For HTTP Method GET/DELETE don't need generate Digest
print("----- Digest -----")
jsonBody = '{\"order\":{\"invoice_number\":\"INV-20210124-0001\",\"amount\":150000},\"virtual_account_info\":{\"expired_time\":60,\"reusable_status\":false,\"info1\":\"Merchant Demo Store\"},\"customer\":{\"name\":\"Taufik Ismail\",\"email\":\"taufik@example.com\"}}'
digest = generateDigest(jsonBody)
print(digest)
print("")

# Generate Signature
headerSignature = generateSignature(
        "yourClientId",
        "yourRequestId",
        "2020-10-21T03:38:28Z",
        "/request-target/goes-here", # For merchant request to Jokul, use Jokul path here. For HTTP Notification, use merchant path here
        digest, # Set empty string for this argumentes if HTTP Method is GET/DELETE
        "secret-key-from-jokul-back-office")
print("----- Header Signature -----")
print(headerSignature)

```

{% endtab %}

{% tab title="Node.js" %}

```javascript
const crypto = require('crypto');
  
// Generate Digest
function generateDigest(jsonBody) {
    let jsonStringHash256 = crypto.createHash('sha256').update(jsonBody,"utf-8").digest();
    
    let bufferFromJsonStringHash256 = Buffer.from(jsonStringHash256);
    return bufferFromJsonStringHash256.toString('base64'); 
}
 
function generateSignature(clientId, requestId, requestTimestamp, requestTarget, digest, secret) {
    // Prepare Signature Component
    console.log("----- Component Signature -----")
    let componentSignature = "Client-Id:" + clientId;
    componentSignature += "\n";
    componentSignature += "Request-Id:" + requestId;
    componentSignature += "\n";
    componentSignature += "Request-Timestamp:" + requestTimestamp;
    componentSignature += "\n";
    componentSignature += "Request-Target:" + requestTarget;
    // If body not send when access API with HTTP method GET/DELETE
    if (digest) {
        componentSignature += "\n";
        componentSignature += "Digest:" + digest;
    }
 
    console.log(componentSignature.toString());
    console.log();

    // Calculate HMAC-SHA256 base64 from all the components above
    let hmac256Value = crypto.createHmac('sha256', secret)
                   .update(componentSignature.toString())
                   .digest();  
      
    let bufferFromHmac256Value = Buffer.from(hmac256Value);
    let signature = bufferFromHmac256Value.toString('base64');
    // Prepend encoded result with algorithm info HMACSHA256=
    return "HMACSHA256="+signature 
}
 
// Sample of Usage

// Generate Digest from JSON Body, For HTTP Method GET/DELETE don't need generate Digest
console.log("----- Digest -----");
let jsonBody = '{\"order\":{\"invoice_number\":\"INV-20210124-0001\",\"amount\":150000},\"virtual_account_info\":{\"expired_time\":60,\"reusable_status\":false,\"info1\":\"Merchant Demo Store\"},\"customer\":{\"name\":\"Taufik Ismail\",\"email\":\"taufik@example.com\"}}';
let digest = generateDigest(jsonBody);
console.log(digest);
console.log();
  
// Generate Header Signature
let headerSignature = generateSignature(
        "yourClientId",
        "yourRequestId",
        "2020-10-21T03:38:28Z",
        "/request-target/goes-here", // For merchant request to Jokul, use Jokul path here. For HTTP Notification, use merchant path here
        digest, // Set empty string for this argumentes if HTTP Method is GET/DELETE
        "secret-key-from-jokul-back-office")
console.log("----- Header Signature -----")
console.log(headerSignature)

```

{% endtab %}

{% tab title="Ruby" %}

<pre class="language-ruby"><code class="lang-ruby"><strong>require 'openssl'
</strong>require 'base64'
require 'digest'
 
# Generate Digest
def generateDigest(jsonBody)
    return Base64.encode64(Digest::SHA256.digest(jsonBody)).strip()
end
 
def generateSignature(clientId, requestId, requestTimestamp, requestTarget, digest, secret)
    # Prepare Signature Component
    puts "----- Component Signature -----"
    componentSignature = ("Client-Id:" + clientId")
    componentSignature.concat("\n")
    componentSignature.concat("Request-Id:" + requestId)
    componentSignature.concat("\n")
    componentSignature.concat("Request-Timestamp:" + requestTimestamp)
    componentSignature.concat("\n")
    componentSignature.concat("Request-Target:" + requestTarget)
    # If body not send when access API with HTTP method GET/DELETE
    unless digest.to_s.strip.empty?
        componentSignature.concat("\n")
        componentSignature.concat("Digest:" + digest)
    end
    puts componentSignature
    puts "\n"  

    # Calculate HMAC-SHA256 base64 from all the components above
    hash = OpenSSL::HMAC.digest("sha256", secret, componentSignature)
    signature = Base64.encode64(hash).strip()

    # Prepend encoded result with algorithm info HMACSHA256=
    return "HMACSHA256="+signature 
end
 
# Sample of Usage

# Generate Digest from JSON Body, For HTTP Method GET/DELETE don't need generate Digest
puts "----- Digest -----"
jsonBody = '{\"order\":{\"invoice_number\":\"INV-20210124-0001\",\"amount\":150000},\"virtual_account_info\":{\"expired_time\":60,\"reusable_status\":false,\"info1\":\"Merchant Demo Store\"},\"customer\":{\"name\":\"Taufik Ismail\",\"email\":\"taufik@example.com\"}}'
digest = generateDigest(jsonBody)
puts digest
puts "\n"
 
# Generate Header Signature
headerSignature = generateSignature(
        "yourClientId",
        "yourRequestId",
        "2020-10-21T03:38:28Z",
        "/request-target/goes-here", # For merchant request to Jokul, use Jokul path here. For HTTP Notification, use merchant path here
        digest, # Set empty string for this argumentes if HTTP Method is GET/DELETE
        "secret-key-from-jokul-back-office")
puts "----- Header Signature -----"
puts headerSignature

</code></pre>

{% endtab %}

{% tab title="GO" %}

```go
package main
 
import (
    "crypto/hmac"
    "crypto/sha256"
    "encoding/base64"
    "fmt"
    "strings"
)
 
const CLIENT_ID = "Client-Id"
const REQUEST_ID = "Request-Id"
const REQUEST_TIMESTAMP = "Request-Timestamp"
const REQUEST_TARGET = "Request-Target"
const DIGEST = "Digest"
const SYMBOL_COLON = ":"
 
// Generate Digest
func generateDigest(jsonBody string) string {
    converted := []byte(jsonBody)
    hasher := sha256.New()
    hasher.Write(converted)
    return (base64.StdEncoding.EncodeToString(hasher.Sum(nil)))
     
}

func generateSignature(clientId string, requestId string, requestTimestamp string, requestTarget string, digest string, secret string) string {
    // Prepare Signature Component
    fmt.Println("----- Component Signature -----")
    var componentSignature strings.Builder
    componentSignature.WriteString(CLIENT_ID + SYMBOL_COLON + clientId)
    componentSignature.WriteString("\n")
    componentSignature.WriteString(REQUEST_ID + SYMBOL_COLON + requestId)
    componentSignature.WriteString("\n")
    componentSignature.WriteString(REQUEST_TIMESTAMP + SYMBOL_COLON + requestTimestamp)
    componentSignature.WriteString("\n")
    componentSignature.WriteString(REQUEST_TARGET + SYMBOL_COLON + requestTarget) 
    componentSignature.WriteString("\n")
    componentSignature.WriteString(DIGEST + SYMBOL_COLON +digest) 
    // If body not send when access API with HTTP method GET/DELETE
    if len(digest) > 0  {
        componentSignature.WriteString("\n")
        componentSignature.WriteString(DIGEST + SYMBOL_COLON +digest)
    }
 
    fmt.Println(componentSignature.String())
    fmt.Println("")
 
    // Calculate HMAC-SHA256 base64 from all the components above
    key := []byte(secret)
    h := hmac.New(sha256.New, key)
    h.Write([]byte(componentSignature.String()))
    signature := base64.StdEncoding.EncodeToString(h.Sum(nil))
    // Prepend encoded result with algorithm info HMACSHA256=
    return "HMACSHA256="+signature
}

// Sample of Usage
func main() {
  
    // Genreate Digest from JSON Body
    var jsonBody = '{\"order\":{\"invoice_number\":\"INV-20210124-0001\",\"amount\":150000},\"virtual_account_info\":{\"expired_time\":60,\"reusable_status\":false,\"info1\":\"Merchant Demo Store\"},\"customer\":{\"name\":\"Taufik Ismail\",\"email\":\"taufik@example.com\"}}'
    digest := generateDigest(jsonBody);
    fmt.Println("----- Digest -----")
    fmt.Println(digest)
    fmt.Println("")
    
    // Generate Signature
    headerSignature := generateSignature(
        "yourClientId",
        "yourRequestId",
        "2020-10-21T03:38:28Z",
        "/request-target/goes-here", // For merchant request to DOKU, use DOKU path here. For HTTP Notification, use merchant path here
        digest, // Set empty string for this argumentes if HTTP Method is GET/DELETE
        "secret-key-from-DOKU-back-office")
 
    fmt.Println("----- Header Signature -----")    
    fmt.Println(headerSignature)
}

```

{% endtab %}
{% endtabs %}
