커피 원격 주문 애플리케이션

개요

기술이 발전하면서 이제는 카페에 가지 않아도 집에서 커피를 주문하는 시대가 되었습니다. 하지만, 스마트폰과 웹 기반의 이러한 주문 방식은 주문 처리 시스템과 데이터베이스 운용이 필요하며 이를 구축, 운용하는 데에는 상당한 개발 인력과 비용이 듭니다. 따라서 이러한 온라인 커피 주문 시스템은 그 편리함에도 불구하고 충분한 자본과 인력을 갖춘 일부 대형 커피 전문점만이 운영합니다. 그런데 만약, 고성능 퍼블릭 블록체인으로 이러한 주문 시스템을 대체할 수 있다면 상황은 어떻게 달라질까요? 영세한 카페를 포함해 누구나 온라인 커피 주문 시스템을 소비자에게 서비스할 수 있을 것입니다.

여기에서는, 클레이튼 블록체인과 KAS를 활용해 커피 주문을 온라인으로 받고 또 온라인 주문 고객에게 인센티브로 KLAY를 제공할 수도 있는 주문 시스템을 만드는 방법을 안내합니다. 주문 내역은 클레이튼 블록체인에 저장되며 주문에 대한 보상으로 KLAY, NFT, NT 등을 자동으로 전달할 수 있습니다.

카페 원격 주문 시스템은 아래와 같이 3가지 Application으로 구성됩니다.

  1. customer-front: 카페 이용자가 사용하는 모바일 웹앱이며 커피를 주문할 수 있습니다.

  2. store-front: 카페 주인들이 사용하면 모바일 웹앱이며 가게 위치 등록, 메뉴 등록, 보상 정책 등록 등을 할 수 있습니다.

  3. service-backend: 카페 원격 주문 플랫폼의 서비스 주체가 운영하는 서버이며 customer-front와 store-front 사용자들의 요청을 전달받아 블록체인으로 중개하는 역할을 수행합니다. KAS API를 사용하는 주체입니다.

이 페이지의 모든 내용은 KAS로 블록체인 애플리케이션 개발을 돕는 예제입니다. 소스코드를 포함한 모든 내용은 사용자 개발 환경에 따라 다르게 적용될 수 있습니다. 소스코드를 포함한 모든 내용을 사용하는 것에 대한 모든 책임은 전적으로 사용자 본인에게 있습니다.

이 문서 혹은 KAS에 관한 문의는 개발자 포럼을 방문해 도움을 받으십시오.

시작하기

카페 원격 주문 시스템을 구성하는 3가지 Application의 소스코드는 하나의 github repository에서 확인할 수 있습니다. 다음 명령어를 수행하여 소스코드를 다운로드 받으십시오.

git clone https://github.com/ground-x/kas-bapp-klaybucks.git

빌드 및 실행

kas-bapp-klaybucks 디렉토리에서 아래 명령어를 통해 각각의 Application을 빌드하고 실행할 수 있습니다.

customer-front

cd customer
npm install
npm start

store-front

cd store
npm install
npm start

service-backend

service-backend를 실행하기 위해서는 먼저 설정 파일이 필요합니다. backend/config.template.json 파일을 복사하여 backend/config.json 파일을 생성한 후 적절한 설정값을 입력하십시오.

  • authorization: Basic 인증 방식을 사용하는 KAS API 인증 키를 입력해 주십시오.

  • kas_account: KAS Wallet API를 사용해 생성한 계정 주소를 입력해 주십시오.

  • master_contract: (Optional) 자신만의 Klaybucks Master Contract를 사용하려면 Master Contract의 주소를 입력해 주십시오. Master Contract를 배포하기 위해서는 backend/storeList.sol 파일을 사용하면 됩니다.

설정 파일을 작성한 후에는 아래 명령어를 통해 service-backend를 실행하실 수 있습니다.

cd backend
go run .

Klaybucks 데모

이 문서 혹은 KAS에 관한 문의는 개발자 포럼을 방문해 도움을 받으십시오.

애플리케이션 워크플로우

주요 기능

"Klaybucks"는 등록된 카페의 위치, 메뉴, 거래내역 등을 블록체인인 Klaytn에 기록합니다. 따라서 서비스가 중단되더라도 카페 주인은 원격 서비스에 사용한 필수 데이터들을 지속적으로 조회하고 수정할 수 있습니다. 다음은 Klaytn을 사용하는 주요 기능들입니다.

  1. 카페 주인은 카페의 위치, 메뉴 정보 그리고 보상 정책을 Contract에 기록합니다.

  2. 고객의 계정은 KAS Wallet API를 이용하여 생성됩니다.

  3. 고객은 Contract에서 Klaybucks 서비스에 등록된 주변 카페들의 위치를 확인합니다.

  4. 고객은 주문할 카페를 선택하고 Contract에서 메뉴와 보상 정책을 조회합니다.

  5. 고객은 KakaoPay 등 타 결제 시스틈을 통해 결제를 완료하고 그 내역을 Contract에 기록합니다.

  6. 카페 주인은 자신의 Contract에 등록된 결제 내역을 확인하고 주문을 승인/거부합니다.

위의 기능들은 Klayt을 Application의 Storage 중 하나로서 사용하며, 데이터를 쓰고 읽는 행위를 수생하는 내용입니다. 따라서, 데이터를 저장할 Contract를 생성하고 Contract 내부에 적절한 데이터를 구조를 정의하는 것이 중요합니다.

store-front에서는 각 카페의 주인들이 카페의 메뉴와 주문내역을 저장하는 Store Contract를 각각 배포합니다. Store Contract에는 아래와 같은 데이터 구조에 값을 저장하고 조회할 수 있는 기능을 제공하며 각 카페의 주인이 Contract Owner가 됩니다.

enum MenuStatus {activated, deactivated}
enum OrderStatus {ordered, approved, denied}
enum RewardPolicyStatus {activated, deactivated}

struct Menu {
    string name;
    uint32 price;
    uint256 reward; // digit: peb
    MenuStatus status;
}

struct Order {
    address payable customer;
    string content;
    uint32 price;
    OrderStatus status;
}

event OrderReciept(
    uint32 indexed _id,
    address indexed _from,
    string _paymentTxId,
    string _content,
    uint32 _price
);

위의 과정을 통하 배포된 각각의 Store Contract들을 Master Contract에 등록됩니다. Master Contract는 Klaybucks 서비스 운영자가 Owner가 되며, 아래와 같은 형태로 카페의 위치와 각 Store Contract의 주소를 저장합니다. Store 데이터는 Map 또는 List 형태로 관리되어 고객들이 지도에서 등록된 카페들을 한번에 조회할 수 있도록 모아주는 역할을 합니다.

enum Status {activated, deactivated}
struct Store {
    string name;
    string locationX;
    string locationY;
    address storeOwner;
    address storeContract;
    Status status;
}

Klaybucks에선는 위와 같이 구현된 Contract를 쉽고 빠르게 사용하기 위해서 KAS API를 이용하였습니다. 아래 내용은 서비스를 구성하는 주요한 기능에 대한 설명과 코드 예시입니다(상세한 구현 코드는 다음 github에서 확인 가능).

*KAS API는 service-backend에서 호출하며, customer-front나 store-front에서는 필요한 파라미터 값을 사용자에게 입력받아 service-backend로 전달합니다. 이러한 설계는 KAS 사용에 필요한 API Key를 대중에게 노출시키지 않기 위함입니다.

이 문서 혹은 KAS에 관한 문의는 개발자 포럼을 방문해 도움을 받으십시오.

카페 Contract 생성

Klaybucks에서는 두 종류의 Contract가 사용됩니다. 하나는 Master Contract이며 서비스 시작 시 단 한번만 배포됩니다. 이 Contract에는 Klaybucks에 속하는 카페들의 위치와 이름이 저장됩니다. 또 다른 종류의 Contract는 Store Contract라 불리우며, 각각의 카페가 Klaybucks에 등록 시에 자신만의 Store Contract를 배포하게 됩니다. 이 Contract에서는 해당 카페의 메뉴, 보상정책, 그리고 거래내역이 기록되게 됩니다.

Klaybucks에서는 Contract를 배포하기 위해서 Wallet API 중 스마트 컨트랙트 배포하기: 다른 계정이 트랜잭션 전송 수수료를 대신 부담하기를 사용하였습니다. 아래 코드는 golang으로 구현된 service-backend에서의 Store Contract 배포 예시입니다(실제 사용된 코드는 github에서 확인 가능). 예시에서는 해당 KAS API 사용을 위해 필요한 Store Contract의 Bytecode와 ABI가 이미 String 형태로 정의되어 있다고 가정하였습니다. Store Contract는 constructor 함수가 실행 시에 input parameter로 Store의 Name과 Owner를 입력받도록 구현되어 있습니다. 예시에서는 이 값을 사용자로부터 입력받은 후, 해당 KAS API 사용에 적합한 형태로 변환하여 사용하고 있습니다.

// DeployStoreContract handles Store Contract deploying requests with the given store name and the owner address.
func DeployStoreContract(c *gin.Context) {
    url := "https://wallet-api.klaytnapi.com/v2/tx/fd/contract/deploy"
    params := struct {
        Name string `form:"name" json:"name"`
        Owner string `form:"owner" json:"owner"`
    }{}

    // Step 1. Get user inputs from the request
    if err := c.ShouldBindJSON(&params); err != nil {
        c.String(-1, "invalid input data" + err.Error())
        return
    }

    // Step 2. Prepare field values of contract deploy transaction.
    // STORE_CONTRACT_ABI and STORE_CONTRACT_CODE are generated from the solidity code of Store Contract.
    _abi , err := abi.JSON(bytes.NewBufferString(STORE_CONTRACT_ABI))
    if err != nil {
        c.String(-1, err.Error())
        return
    }

    // Pack function convert user inputs to the rlp-encoded constructor parameter.
    inputParam, err := _abi.Constructor.Inputs.Pack(params.Name, common.HexToAddress(params.Owner))
    if err != nil {
        c.String(-1, err.Error())
        return
    }

    // input format of the KAS API, /v2/tx/fd/contract/deploy
    bodyData, err := json.Marshal(&kasDeployTxFD{
        From: KAS_ACCOUNT,
        Value: "0x0",
        GasLimit: 8000000,
        Input: STORE_CONTRACT_CODE + Encode(inputParam),
        Submit: true,
    })
    if err != nil {
        c.String(-1, err.Error())
        return
    }

    // Step 3. Call a KAS API with parameters and HTTP headers.
    // "Authorization" header should be set with your KAS API Key (e.g., "Basic xxxx...") .
    // "x-krn" header should indicate your KAS Wallet resource (e.g., "krn:1001:wallet:80:account:default").
    req, err := http.NewRequest("POST", url, bytes.NewBuffer(bodyData))
    if err != nil {
        c.String(-1, err.Error())
        return
    }
    req.Header.Add("Content-Type", "application/json")
    req.Header.Add("Authorization", KAS_API_KEY)
    req.Header.Add("x-krn", KAS_WALLET_KRN)

    res, err := httpClient.Do(req)
    if err != nil {
        c.String(-1, err.Error())
        return
    }

    // Step 4. Read and return back to the user the response of KAS.
    bodyContents, err := ioutil.ReadAll(res.Body)
    if err != nil {
        c.String(-1, err.Error())
        return
    }
    _ = res.Body.Close()

    c.String(200, string(bodyContents))
}

이 문서 혹은 KAS에 관한 문의는 개발자 포럼을 방문해 도움을 받으십시오.

카페 메뉴/보상 등록과 Klaybucks 서비스 등록

카페 주인의 자신이 배포한 카페 Contract의 주소와 위치정보를 Master Contract에 등록하여 Klaybucks 서비스에 등록할 수 있습니다. 고객들은 Klaybucks에 등록된 카페를 조회할 때 가장 먼저 조회하는 Contract입니다.

Klaybucks에서는 Contract를 실행하기 위해서 Wallet API 중 스마트 컨트랙트 실행하기: 다른 계정이 트랜잭션 전송 수수료를 대신 부담하기를 사용하였습니다. 아래 코드는 golang으로 구현된 service-backend에서의 Master Contract 실행 예시입니다(실제 사용된 코드는 github에서 확인 가능). 예시에서는 해당 KAS API 사용을 위해 필요한 Master Contract의 ABI가 이미 String 형태로 정의되어 있다고 가정하였습니다.

// AddStoreInfo registers a new store to Master Contract
func AddStoreInfo(c *gin.Context) {
    url := "https://wallet-api.klaytnapi.com/v2/tx/fd/contract/execute"
    params := struct {
        Name string `json:"name"`
        X string `json:"x"`
        Y string `json:"y"`
        Owner string `json:"owner"`
        ContractAddr string `json:"contract_addr"`
    }{}

    // Step 1. Get user inputs from the request
    if err := c.ShouldBindJSON(&params); err != nil {
        c.String(-1, "invalid input data" + err.Error())
        return
    }

    // Step 2. Prepare field values of contract execution transaction.
    // MASTER_CONTRACT_ABI is generated from the solidity code of Store Contract.
    _abi , err := abi.JSON(bytes.NewBufferString(MASTER_CONTRACT_ABI))
    if err != nil {
        c.String(-1, err.Error())
        return
    }

    // Pack function convert user inputs to the rlp-encoded parameter.
    // "addStore" is a function of Master Contract.
    inputParam, err := _abi.Pack("addStore", params.Name, params.X, params.Y, common.HexToAddress(params.Owner), common.HexToAddress(params.ContractAddr))
    if err != nil {
        c.String(-1, err.Error())
        return
    }

    // input format of the KAS API, /v2/tx/fd/contract/execute
    bodyData, err := json.Marshal(&kasExecuteTxFD{
        From: KAS_ACCOUNT,
        To: MasterContract,
        Value: "0x0",
        GasLimit: 8000000,
        Input: Encode(inputParam),
        Submit: true,
    })
    if err != nil {
        c.String(-1, err.Error())
        return
    }

    // Step 3. Call a KAS API with parameters and HTTP headers.
    // "Authorization" header should be set with your KAS API Key (e.g., "Basic xxxx...") .
    // "x-krn" header should indicate your KAS Wallet resource (e.g., "krn:1001:wallet:80:account:default").
    req, err := http.NewRequest("POST", url, bytes.NewBuffer(bodyData))
    if err != nil {
        c.String(-1, err.Error())
        return
    }
    req.Header.Add("Content-Type", "application/json")
    req.Header.Add("Authorization", KAS_API_KEY)
    req.Header.Add("x-krn", KAS_WALLET_KRN)

    res, err := httpClient.Do(req)
    if err != nil {
        c.String(-1, err.Error())
        return
    }

    // Step 4. Read and return back to the user the response of KAS.
    bodyContents, err := ioutil.ReadAll(res.Body)
    if err != nil {
        c.String(-1, err.Error())
        return
    }
    _ = res.Body.Close()

    c.String(200, string(bodyContents))
}

동일한 API를 활용하여 카페 주인은 자신의 Contract에 메뉴와 보상 정책을 등록하거나 수정할 수도 있습니다. 위 예시와 비슷한 사용을 통하여 언제든 자신의 카페의 운영 상태를 스스로 업데이트 할 수 있습니다.

이 문서 혹은 KAS에 관한 문의는 개발자 포럼을 방문해 도움을 받으십시오.

Transaction Receipt 확인

생성된 Klaytn Transaction이 블럭에 담기면 Transaction Receipt을 생성하게 되고 이 순간에 처리가 완료됩니다. 따라서, Transaction을 배포한 후에는 반드시 Transaction Receipt을 확인하여 처리된 결과값을 확인해야 합니다.

Klaybucks에서는 Transaction Receipt을 확인하기 위해 KAS Node API를 사용합니다. KAS Node API를 사용하면 Klaytn Node가 가지고 있던 API들을 동일한 방식으로 사용할 수 있습니다. 아래 코드는 golang으로 구현된 service-backend에서 klay_getTransactionReceipt method를 사용하여 Transaction Receipt을 확인하는 예시입니다.

func GetTxReceipt(c *gin.Context) {
    url := "https://node-api.klaytnapi.com/v2/klaytn"
    params := struct {
        TxHash string `form:"tx_hash" json:"tx_hash"`
    }{}

    // Step 1. Get user inputs from the request
    if err := c.ShouldBindQuery(&params); err != nil {
        c.String(-1, "invalid input data" + err.Error())
        return
    }

    // Step 2. Prepare a Klaytn node method and parameters to use KAS Node API.
    // You can see the detailed information of node methods from
    // https://ko.docs.klaytn.com/bapp/json-rpc/api-references
    bodyData, _ := json.Marshal(&kasKlaytnRPC{
        JsonRpc: "2.0",
        Method: "klay_getTransactionReceipt",
        Params: []string{params.TxHash},
        Id: 1,
    })

    // Step 3. Call a KAS API with parameters and HTTP headers.
    // "Authorization" header should be set with your KAS API Key (e.g., "Basic xxxx...") .
    // "x-krn" header should indicate your KAS Node resource (e.g., "krn:1001:node").
    req, err := http.NewRequest("POST", url, bytes.NewBuffer(bodyData))
    if err != nil {
        c.String(-1, err.Error())
        return
    }
    req.Header.Add("Content-Type", "application/json")
    req.Header.Add("Authorization", KAS_API_KEY)
    req.Header.Add("x-krn", KAS_NODE_KRN)

    res, err := httpClient.Do(req)
    if err != nil {
        c.String(-1, err.Error())
        return
    }

    // Step 4. Read and return back to the user the response of KAS.
    bodyContents, err := ioutil.ReadAll(res.Body)
    if err != nil {
        c.String(-1, err.Error())
        return
    }
    _ = res.Body.Close()

    c.String(200, string(bodyContents))
}

Transaction Receipt을 확인할 때 주의해야할 점은 Receipt이 생성되는 순간이 deterministic하지 않다는 점입니다. Klaytn 네트워크는 빠른 블럭생성 능력을 가지고 있기에 일반적으로 1초내에 Transaction이 처리될 수 있지만, 그 시간은 네트워크의 지연이나 Transaction 혼잡도에 따라 지연될 수 있습니다. 이러한 이유로 Receipt을 확인하려는 순간에 아직 Transaction이 처리되지 않는 경우가 발생할 수 있습니다. 이 경우에는 Transaction이 처리될 때까지 반복적으로 Receipt을 요청하는 것이 좋습니다.

Klaybucks에서는 아래와 같이 front 코드에서 Receipt을 반복적으로 요청합니다. 물론, golang으로 구현된 service-backend에서 반복 loop을 추가할 수도 있자만 그러한 경우에는 사용자 요청에 대한 응답이 blocking 될 수 있습니다. 이를 피하기 위해서 Klaybucks에서는 아래 예시처럼 javascript로 구현된 front 코드에서 receipt을 확인하고 존재하지 않는 경우 반복적으로 요청하는 로직을 가지고 있습니다.

function CheckTransactionReceipt(txHash) {
  let self = this;
  function getReceipt(txHash) {
    axios
      .get(klaybucks_backend + "/klaytn/receipt?tx_hash=" + txHash)
      .then((response) => {
        let result = response.data["result"];
        if (result !== null) {
          clearInterval(self.intervalId2);
          // check the result and process next steps
        }
      })
      .catch((error) => {
        console.log(error);
      });
  }

  self.intervalId2 = setInterval(getReceipt, 1000, txHash);
  setTimeout(clearInterval, 10000, self.intervalId2);
}

이 문서 혹은 KAS에 관한 문의는 개발자 포럼을 방문해 도움을 받으십시오.

고객 계정 생성

Klaybucks에서 카페 고객을 위한 customer-front를 이용하기 위해서는 Klaytn Account를 생성해야합니다. 고객이 카페 메뉴를 주문하는 경우에는 이 Account로부터 Transaction이 발생하게되고, 카페로부터 KLAY 등을 보상으로 받는 경우에도 이 Account 주소로 받게됩니다. 즉, Klaybucks에서 발생하는 고객의 행위들은 이 Account에 귀속되어 저장되게 됩니다.

위와 같이 Klaybucks 서비스의 계정을 Klaytn Account로 대체하는 경우에는 사용자가 Private Key를 관리해야한다는 부담이 존재합니다. 사용자는 복잡한 형태에 Private Key를 보관하고 관리해야하며, 분실하는 경우에는 되찾을 방법이 없게 됩니다. 이러한 블록체인의 불편한 사용성을 개선하기 위해 Klaybucks에서는 KAS의 Wallet API를 이용하여 카페 고객들의 Private Key 관리를 위임하였습니다. 아래 코드는 golang으로 구현된 service-backend에서의 고객 계정의 생성을 위해 KAS Wallet API 중 계정 생성하기를 사용하는 내용입니다.

func CreateAccount(c *gin.Context){
    url := "https://wallet-api.klaytnapi.com/v2/account"

    // Step 1. Call a KAS API with parameters and HTTP headers.
    // "Authorization" header should be set with your KAS API Key (e.g., "Basic xxxx...") .
    // "x-krn" header should indicate your KAS Wallet resource (e.g., "krn:1001:wallet:80:account:default").
    req, err := http.NewRequest("POST", url, nil)
    if err != nil {
        c.String(-1, err.Error())
        return
    }
    req.Header.Add("Content-Type", "application/json")
    req.Header.Add("Authorization", KAS_API_KEY)
    req.Header.Add("x-krn", KAS_NODE_KRN)

    res, err := httpClient.Do(req)
    if err != nil {
        c.String(-1, err.Error())
        return
    }

    // Step 2. Read and return back to the user the response of KAS.
    bodyContents, err := ioutil.ReadAll(res.Body)
    if err != nil {
        c.String(-1, err.Error())
        return
    }
    _ = res.Body.Close()

    c.String(200, string(bodyContents))
}

이 문서 혹은 KAS에 관한 문의는 개발자 포럼을 방문해 도움을 받으십시오.

카페 정보 조회

계정을 생성한 고객은 Klaybucks에 등록된 카페들의 정보를 조회하기 위하여 Master Contract에 저장된 데이터를 읽어옵니다. 그 중 자신이 이용하고 싶은 카페를 선택하면 동일한 방식을 사용하여 해당 카페의 메뉴정보를 추가로 읽어옵니다.

Klaybucks에서는 Contract 중 데이터를 조회하는 함수를 실행하기 위해 Node API를 사용하였습니다. 아래 코드는 golang으로 구현된 service-backend에서의 Master Contract 실행 예시입니다(실제 사용된 코드는 github에서 확인 가능). 예시에서는 해당 KAS API 사용을 위해 필요한 Master Contract의 ABI가 이미 String 형태로 정의되어 있다고 가정하였습니다.

func GetStores(c *gin.Context) {
    url := "https://node-api.klaytnapi.com/v2/klaytn"

    // Step 1. Prepare "klay_call" API among Node APIs
    bodyData, _ := json.Marshal(&kasKlaytnRPC{
        JsonRpc: "2.0",
        Method: "klay_call",
        Params: []interface{}{
            structCall{
                From: KASAccount,
                To: MASTER_CONTRACT,
                Gas: "0x7A1200", // 8000000
                Data: MASTER_CONTRACT_ABI_GETSTORE,
            },
            "latest",
        },
        Id: 1,
    })

    // Step 2. Call a KAS API with parameters and HTTP headers.
    // "Authorization" header should be set with your KAS API Key (e.g., "Basic xxxx...") .
    // "x-krn" header should indicate your KAS Node resource (e.g., "krn:1001:node").
    req, err := http.NewRequest("POST", url, bytes.NewBuffer(bodyData))
    if err != nil {
        c.String(-1, err.Error())
    }
    req.Header.Add("Content-Type", "application/json")
    req.Header.Add("Authorization", KAS_API_KEY)
    req.Header.Add("x-krn", KAS_NODE_KRN)

    res, err := httpClient.Do(req)
    if err != nil {
        c.String(-1, err.Error())
        return
    }

    bodyContents, err := ioutil.ReadAll(res.Body)
    if err != nil {
        c.String(-1, err.Error())
        return
    }
    res.Body.Close()

    // Step 3. Parse the return of Node API
    var rpcRet struct {
        JsonRPC string `json:"jsonrpc"`
        Id int `json:"id"`
        Result string `json:"result"`
    }

    if err := json.Unmarshal(bodyContents, &rpcRet); err != nil {
        c.String(-1, err.Error())
        return
    }

    // Step 4. Parse the store information which is stored as an array in the Contract
    _abi , err := abi.JSON(bytes.NewBufferString(MASTER_CONTRACT_ABI))
    if err != nil {
        c.String(-1, err.Error())
        return
    }

    var storeArray []struct {
        Name string `json:"name"`
        X string `json:"x"`
        Y string `json:"y"`
        StoreOwner common.Address `json:"store_owner"`
        StoreContract common.Address `json:"store_contract"`
        Status uint8 `json:"status"`
    }

    if err := _abi.Unpack(&storeArray, "getStores", hexutil.MustDecode(rpcRet.Result)); err != nil {
        c.String(-1, err.Error())
        return
    }

    c.JSON(200, storeArray)
}

고객은 위와같이 등록된 카페들을 조회한 후, 원하는 카페 메뉴를 추가 조회하기 위해 동일한 API를 사용합니다. 이후 메뉴를 선택하고 주문을 하면 2. 카페 메뉴/보상 등록과 Klaybucks 서비스 등록에서 사용된 API를 사용하여 동일한 방식으로 주문내역을 Contract에 등록합니다. 물론, 등록된 Transaction의 Receipt을 확인하기 위해 3. Transaction Receipt 확인에 언급된 내용도 수행하여야 합니다.

이 문서 혹은 KAS에 관한 문의는 개발자 포럼을 방문해 도움을 받으십시오.

주문 내역 조회

고객이 특정 카페의 Store Contract에 주문내역을 등록하게 되면, 카페 주인은 Klaytn Block 내에 포함된 Receipt을 조회하여 이를 확인할 수 있습니다. 따라서, 카페 주인은 매 블럭마다 자신의 Store Contract로부터 생성된 Receipt이 있는지를 지속적으로 확인하여야합니다.

// Retrieves the receipt logs of the given contract address
RetrieveLogs = (address) => {
  let self = this;
  function getReceipt(address) {
    let hexBlockNumber = caver.utils.toHex(self.state.blockNumber);
    axios
      .get(
        klaybucks_backend +
          "getReceipts?from_block=" +
          hexBlockNumber +
          "&to_block=" +
          hexBlockNumber +
          "&contract=" +
          address
      )
      .then((response) => {
        self.setState((current) => ({
          blockNumber: self.state.blockNumber + 1,
        }));
        if (response.data !== null) {
          let current_receipts = response.data;
          // Add validation logic for the receipts
          current_receipts.map((item) => {
            let order = qs.parse(item.content);
            let receipt = {
              index: item.index,
              order: JSON.stringify(order),
              price: item.price,
              reward: self.CalculateReward(order),
              tid: item.payment_tx_id,
              status: "ordered",
            };
            self.setState((current) => ({
              receipts: self.state.receipts.concat(receipt),
            }));
            return item;
          });
        }
      })
      .catch((error) => {
        console.log(error);
      });
  }
  self.intervalId = setInterval(getReceipt, 1000, address);
};

위의 과정을 통해 주문 내역이 확인되면 카페 메뉴/보상 등록과 Klaybucks 서비스 등록Transaction Receipt 확인에서 언급된 API를 사용하여 주문의 상태 값을 "승인" 또는 "거부"로 업데이트합니다. 이렇게 업데이트된 주문 상태내역은 고객측에서도 동일한 과정을 통하여 조회한 후 최종적으로 주문이 받아들여졌음을 확인할 수 있습니다.

이 문서 혹은 KAS에 관한 문의는 개발자 포럼을 방문해 도움을 받으십시오.

Last updated