# eformsign API Service

## Create a Service Class

{% code title="EformSignService.java" %}

```java
import java.math.BigInteger;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.client.WebClient;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;

import reactor.core.publisher.Mono;
```

{% endcode %}

```java
@Service
public class EformSignService {
	
    private static String apiKey = "your api key";
    private static String privateKeyHex = "your private key";
    private static String publickKeyHex = "your public key";
    private WebClient webClient = WebClient.create();
```

<mark style="color:red;">Provide your API key, private key, and public key.</mark>

## Verify eformsign Signature&#x20;

```java
/**
 * verifySignature - verify Webhook signature using data and PublicKey
 * 
 * @input	response header(signature) and body(data)
 * @input	publickKey
 * @return	true/false
 */
public static boolean verifySignature(String resSignature, String resData) {
	
    Signature signature;
    boolean valid = false;
    
    try {
    	KeyFactory publicKeyFact = KeyFactory.getInstance("EC");
    	X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(new BigInteger(publickKeyHex,16).toByteArray());
    	PublicKey publicKey = publicKeyFact.generatePublic(x509KeySpec);
    	
    	signature = Signature.getInstance("SHA256withECDSA");
    	signature.initVerify(publicKey);
    	signature.update(resData.getBytes("UTF-8"));
    	
    	if(signature.verify(new BigInteger(resSignature,16).toByteArray())) {valid = true;}
    	else {valid = false;}
        	
    } catch (Exception e) {
    	e.printStackTrace(); 
    }

return valid;
}
```

## Get Access Token

```
// Access Token Structure for your understanding. Do not add to the project.
{
  "api_key": {
    "name": "Tutorial",
    "alias": "Tutorial",
    "company": {
      "company_id": "2b99ef0cac464344b1983d44ec4af427",
      "name": "eformsign Bootcamp",
      "api_url": "https://sg-api.eformsign.com"
    }
  },
  "oauth_token": {
    "expires_in": 3600,
    "token_type": "JWT",
    "refresh_token": "8a43b797-xxxx-xxxx-xxxx-3ea8f4b872e0",
    "access_token": "eyJh...WijgnU" // 4Kbytes
  }
}
```

```java
/**
 * getEformsignSignature - generate eformsign signature using privateKey
 * 
 * @input	privateKey
 * @return	execution_time, eformsign_signature
 */
private static String[] getEformsignSignature() throws Exception {

    KeyFactory keyFact = KeyFactory.getInstance("EC");
    PKCS8EncodedKeySpec psks8KeySpec = new PKCS8EncodedKeySpec(new BigInteger(privateKeyHex,16).toByteArray());
    PrivateKey privateKey = keyFact.generatePrivate(psks8KeySpec);

    String execution_time = String.valueOf(new Date().getTime());
    Signature ecdsa = Signature.getInstance("SHA256withECDSA");
    ecdsa.initSign(privateKey);
    ecdsa.update(execution_time.getBytes("UTF-8"));			
    String eformsign_signature = new BigInteger(ecdsa.sign()).toString(16);

    String[] res = new String[2];
    res[0]= execution_time;
    res[1]= eformsign_signature;
    return res;
}  
```

```java
/**
 * getAccessToken
 * 
 * @method	POST
 * @path	/api_auth/refresh_token
 * @header	Bearer API Key    
 * @header	eformsign_signature    
 * @body	memberId "john.kim@forcs.com"
 * @body	execution_time
 * @return	access token in JSON
 * accessToken expires in 3600 seconds
 */
public Mono<JsonNode> getAccessToken(String memberId) throws Exception {
	
    String base64ApiKey = Base64.getEncoder().encodeToString(apiKey.getBytes("UTF-8"));
    String[] signature = getEformsignSignature();
    String execution_time = signature[0];
    String eformsign_signature = signature[1];

    Map<String, Object> reqBody = new HashMap<>();
    reqBody.put("execution_time", execution_time);
    reqBody.put("member_id", memberId);
    
    return webClient.post()
            .uri("https://api.eformsign.com", uriBuilder -> uriBuilder
                .path("/v2.0/api_auth/access_token")
                .build())
            .header("Authorization", "Bearer " + base64ApiKey)
            .header("eformsign_signature", eformsign_signature)
            .bodyValue(reqBody)
            .retrieve()
            .onStatus(HttpStatus::isError, res -> res.bodyToMono(String.class)
                .flatMap(error -> Mono.error(new RuntimeException(error))))
            .bodyToMono(JsonNode.class);
}
```

## Get Refresh Token

```java
/**
 * getRefreshToken - call when the token expired (3600 seconds have passed)
 * 
 * @method	POST
 * @path	/v2.0/api_auth/refresh_token
 * @header	access_token
 * @param	refresh_token
 * @return	new access_token
 */
public Mono<JsonNode> getRefreshToken(JsonNode token) {

    return webClient.post()
	.uri(token.get("apiUrl").asText(), uriBuilder -> uriBuilder
            .path("/v2.0/api_auth/refresh_token")
            .queryParam("refresh_token", token.get("refreshToken").asText())
            .build())
        .header("Authorization", "Bearer " + token.get("accessToken").asText())
        .retrieve()
        .onStatus(HttpStatus::isError, res -> res.bodyToMono(String.class)
            .flatMap(error -> Mono.error(new RuntimeException(error))))
        .bodyToMono(JsonNode.class);
}
```

## Download Documents&#x20;

```java
/**
 * Download Document File
 * 
 * @method	GET
 * @path	/v2.0/api/documents/{document_id}/download_files
 * @header	access_token
 * @param	fileType   file type : "document" | "audit_trail" | "document,audit_trail"
 * @return	files in byte stream
 */
public  Mono<byte[]> downloadDocument(JsonNode token,  String documentId, String fileType) {
	
    return webClient.get()
        .uri(token.get("apiUrl").asText(), uriBuilder -> uriBuilder
            .path("/v2.0/api/documents/" + documentId + "/download_files")
            .queryParam("file_type", fileType)
            .build(documentId))
        .header("Authorization", "Bearer " + token.get("accessToken").asText())
        .retrieve()
	.onStatus(HttpStatus::isError, res -> res.bodyToMono(String.class)
	    .flatMap(error -> Mono.error(new RuntimeException(error))))
	.bodyToMono(byte[].class);
}
```

## Delete Document

```java
/**
 * Delete Document
 * 
 * @method		DELETE
 * @path		/v2.0/api/documents/
 * @header		access_token
 * @body		document id array
 * @return		{"code": "-1", "message": "Completed.", "status": "200"}
 */
public  Mono<JsonNode> deleteDocument(JsonNode token, String documentId) {
		
    ObjectMapper objectMapper = new ObjectMapper();
    ObjectNode objectNode = objectMapper.createObjectNode();
    ArrayNode arrayNode = objectNode.withArray("document_ids");
    arrayNode.add(documentId);

    return webClient.method(HttpMethod.DELETE)
        .uri(token.get("apiUrl").asText(), uriBuilder -> uriBuilder
            .path("/v2.0/api/documents/")
            .build())
        .header("Authorization", "Bearer " + token.get("accessToken").asText())
        .bodyValue(objectNode)
        .retrieve()
        .onStatus(HttpStatus::isError, res -> res.bodyToMono(String.class)
            .flatMap(error -> Mono.error(new RuntimeException(error))))
        .bodyToMono(JsonNode.class);
} 
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://efs.ozeform.io/sample-project/eformsign-api-service.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
