# Scanner API Guidelines

## Introduction <a href="#undefined" id="undefined"></a>

This document is a development guideline prepared for implementing additional functions after NFT holder verification.

Understanding of SDKs such as web3.js and ethers may be required to implement blockchain-related functions.&#x20;

If you develop an API endpoint server by referring to this document, you can add the features of the example below.

> Check how many NFTs the user who authenticated the wallet has \
> Send NFTs or tokens to users recognized by the scanner Offline \
> Stamp tour using FAVORLET and FAVORLET Scanner \
> Access check and record management at offline holder party\
> Various functions suitable for other situations

Please refer to the Basic usage guideline page for basic usage guide for FAVORLET scanner.

{% embed url="<https://docs.favorlet.io/scanner/guide>" %}

## How to set End point URL

<figure><img src="https://1971795026-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fj5aTd2uJmNjHSmwmQF91%2Fuploads%2FS5rEA6YcKk35rYFHm753%2Fimage.png?alt=media&#x26;token=841db96d-18c6-4238-aac0-34da934a8967" alt=""><figcaption></figcaption></figure>

You can receive a request to the registered API server after scanning the user's authentication QR code by putting it in the `service endpoint URL` on the scanner setting screen.&#x20;

On the scanner settings screen, you can set in the following three ways.

1. If only the `collection contract address` is entered: Only the scanner's NFT holder will be verified.&#x20;
2. If you only put the `service endpoint URL`: Skip scanner's NFT holder verification and send request to API server.&#x20;
3. If you put both the `collection contract address` and `service endpoint URL`: Send a request to the API server after verifying the scanner's NFT holder.

{% hint style="warning" %}
Either the `collection contract address` or the `service endpoint URL` must be entered
{% endhint %}

## Flow

The flow of communication between FAVORLET, Scanner, and Server is as below.

<figure><img src="https://1971795026-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fj5aTd2uJmNjHSmwmQF91%2Fuploads%2Ftzbmx9RxggimswVWiTN2%2FGroup%2047%20(1).png?alt=media&#x26;token=612269fa-0107-454d-b3ca-91151ac90423" alt=""><figcaption></figcaption></figure>

1. FAVORLET users generate a verification QR.&#x20;
2. The scanner checks the QR code whether it is valid or not and passes the wallet address and other information to the endpoint URL (API server) you set.&#x20;
3. The requested API server responds with a scanner for success or failure.

## Specifications

### **Request**

This is the data that FAVORLET scanner request to the API server set as the service endpoint URL.

**Method**: `POST`

**Header**: `Content-Type:application/json`

**Body**

```json
{
    "walletAddress": "0xd464B499639A267Da03721b2DBa7469896732947",
    "contractAddress": "0x8F5Aa6b6DCD2D952A22920E8fE3f798471D05901", // 홀더 인증을 설정한 경우
    "signature": "0xbe1b5bc3fe1378a4bd5198dd37a6c620e8a49f84347f3e4cb4eb8e8b4b0fb16545ad365d2cf19b05dfb84f88adfb922214c0d4f9ff29227f780c2f4fea0b2a9a1c" // signature 설명 아래 참고
    "expireTime": "2022-10-05T08:22:45.000Z"
}
```

**Parameter Description**

<table data-header-hidden><thead><tr><th width="208.33333333333331">key</th><th width="88">type</th><th>description</th></tr></thead><tbody><tr><td><code>walletAddress</code></td><td>string</td><td>This is the wallet address delivered through QR verification.</td></tr><tr><td><code>contractAddress</code></td><td>string</td><td>This is the address registered in the <code>collection contract address</code> on the scanner settings screen. <br>If left blank, no data will appear.</td></tr><tr><td><code>signature</code></td><td>string</td><td>This is the data for verifying whether <code>walletAddress</code> and <code>expireTime</code> have changed. The usage of the value is optional, see below for detailed explanation.</td></tr><tr><td><code>expireTime</code></td><td>string</td><td>The expiration date for the QR code generated by the user. The format is <code>YYYY-MM-DDTHH:mm:ss.sssZ</code>.</td></tr></tbody></table>

{% hint style="info" %}
what is`signature`?

블록체인에서 문자열에 대해 지갑의 private key로 서명하여 나오는 데이터로, 이 `signature` 를 다시 복구했을 때 지갑 주소가 나온다면 해당 지갑으로 서명한 데이터임을 검증할 수 있습니다.\
This is the data that comes from signing the string with the private key of the wallet on the blockchain.

If the wallet address is found when this signature is recovered, it can be verified that the data was signed by the wallet.

Favorlet에서 QR 인증 시 `"i-love-fingerlabs_" + walletAddress + "_" + expireTime` 텍스트로\
서명하게 되며, `walletAddress`과 `expireTime`값을 검증할 수 있습니다.

Favorlet에서 QR 인증에 대한 메세지 서명은 이더리움 prefix를 사용하게 되며, 자바스크립트에서는 `ethers`와 `web3.js` 라이브러리를 사용하여 서명한 주소를 복구할 수 있습니다.

자바스크립트 라이브러리 중 `caver-js`는 클레이튼 prefix를 사용하므로 복구할 수 없습니다.

* 이더리움 prefix:`"\x19Ethereum Signed Message:\n" + message.length + message`
* 클레이튼 prefix: `"\x19Klaytn Signed Message:\n" + message.length + message`

메세지 서명과 관련된 자세한 내용을 알고 싶다면 [Ethereum Signed Message Verification Tool](https://info.etherscan.com/verify-signature-tool/)를 참조 바라며, 검증에 대한 방법은 본 문서 아래의 서버 예제를 참고 바랍니다.
{% endhint %}

### **Response**

This is the data that the API server you set as the service endpoint URL should respond with FAVORLET scanner.

**Header**: `Content-Type:application/json`

* **Case1: success**

  ```json
  {
      "success": true,
      "title": "Success! 😀",
      "reason": "Authentication succeeded."
  }
  ```
* **Case2: fail**

  ```json
  {
      "success": false,
      "title": "Fail! 😥",
      "reason": "Authentication failed."
  }
  ```

**Screenshot**

<figure><img src="https://1971795026-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Fj5aTd2uJmNjHSmwmQF91%2Fuploads%2F2wltnUGkVnRR1Vk1za4e%2Fimage.png?alt=media&#x26;token=033c2901-6dbc-4650-b202-36b9f649ef65" alt=""><figcaption><p>(좌) success / (우) fail</p></figcaption></figure>

**Parameter Description**

<table data-header-hidden><thead><tr><th width="136">key</th><th width="115.33333333333331">type</th><th>description</th></tr></thead><tbody><tr><td><code>success</code></td><td>boolean</td><td>요청에 대한 성공 여부이며 <code>true</code>또는 <code>false</code>를 반환해야 합니다.</td></tr><tr><td><code>title</code></td><td>string</td><td>스캐너에서 요청 결과 팝업 제목에 표시되는 텍스트입니다.</td></tr><tr><td><code>reason</code></td><td>string</td><td>스캐너에서 요청 결과 팝업 설명에 표시되는 텍스트입니다.</td></tr></tbody></table>

{% hint style="info" %}
응답 결과는 JSON 형태로 응답해야 하며 Response status code는 스캐너에서 참조하지 않습니다.
{% endhint %}

## **API 서버 예제 (Node.js)**

**1. Node.js 설치**

{% embed url="<https://nodejs.org/ko/download/>" %}

**2. 프로젝트 폴더 생성 및 이동**

```shell
mkdir api_endpoint_example
cd api_endpoint_example
```

**3. 패키지 설치**

```shell
npm install express ethers
```

**4.** `index.js` **파일 작성**

```javascript
const express = require('express');
const ethers = require('ethers');

const app = express();
app.use(express.json());

// signature 인증
const qrVerify = (walletAddress, signature, expireTime) => {
  // recover할 메세지 생성
  const msgHash = ethers.utils.hashMessage(`i-love-fingerlabs_${walletAddress}_${expireTime}`);
  // 메세지에 대해 Byte Array 타입으로 변경
  const msgHashBytes = ethers.utils.arrayify(msgHash);

  // signature로 서명한 주소 recover
  const recoverAddress = ethers.utils.recoverAddress(msgHashBytes, signature);

  // recover한 주소와 walletAddress 비교, QR 만료 시간 확인
  if (recoverAddress !== walletAddress || new Date(expireTime) < new Date()) {
    throw new Error('Validation Failed.');
  }
};

// 요청받을 API 엔드포인트
app.post('/', (req, res) => {
  const { walletAddress, signature, expireTime } = req.body;

  //walletAddress, signature, expireTime에 대해 검증
  qrVerify(walletAddress, signature, expireTime);


  /**
   * 작업할 코드
   * ex) NFT mint, Token airdrop, Holder pass...
   */

  // 성공
  res.send({
    success: true,
    title: "Success! 😀",
    reason: "Authentication succeeded."
  });
});

// 에러 핸들러
app.use((err, req, res, next) => {
  // 실패
  res.send({
    success: false,
    title: "Fail! 😥",
    reason: "Authentication failed."
  });
});

// 3000번 포트로 listen
app.listen(3000, () => {
  console.log('running => http://localhost:3000');
});
```

**5. 터미널에서 실행**

```shell
node index.js
```

**6. 새 터미널에서 테스트**

```shell
curl --location --request POST 'http://localhost:3000' \
--header 'Content-Type: application/json' \
--data-raw '{
    "walletAddress": "0xe13F97c9b97BC41BF2540fc2e8CA6998439100AB",
    "signature": "0xf5c6c3c9817146d22818c5367a6e77e4de295bb3432c79d117b8b9bf72ae954c5a2dd93b9fa9417112fb71160bac1e06c22cf22e5a9da8c97ff08f9759bd02a91b",
    "expireTime": "2023-04-10T03:16:25.168Z"
}'
```

**7. 결과 확인**

```json
{
  "success":true,
  "title":"Success! 😀",
  "reason":"Authentication succeeded."
}
```

{% hint style="info" %}
만약 스캐너앱의 기본 기능 외에 다른 액션을 추가하고 싶으신데 개발에 어려움을 겪고 있다면 <mark style="background-color:red;"><favorlet@fingerlabs.io></mark> 로 이메일 주시길 바랍니다.
{% endhint %}
