신규: Helius가 Light Protocol을 인수했습니다
Solana Pay 가이드
블로그/개발

Solana Pay: 알아야 할 모든 것

개발자 경험 엔지니어X의 Owen VenterLinkedIn의 Owen Venter
읽는 데 8분

Solana 블록체인을 기반으로 구축된 Solana Pay는 URL 내 트랜잭션을 간소화하는 공통 언어입니다. 즉시 처리되는 트랜잭션과 1센트에도 못 미치는 수수료로 결제를 더 원활하게 만들고 사용자 경험을 개선합니다.

이러한 URL은 QR 코드나 NFC 태그로 변환하거나, 결제 또는 트랜잭션 설정이 필요할 때 사람과 앱 간에 공유할 수 있는 특별한 웹사이트 주소입니다. 덕분에 Solana 트랜잭션을 더욱 원활하고 안전하게 이용할 수 있습니다.

Solana Pay: 작동 방식

Solana Pay는 사용자가 스마트 계약과 상호작용하고 토큰을 전송하는 새로운 방식을 제공합니다. 지갑을 연결하지 않고도 링크를 통해 트랜잭션을 확인하고 서명할 수 있습니다. 지갑을 연결해야 하는 장벽이 사라집니다. 간단한 결제부터 앱과의 상호작용까지 지원하며, 트랜잭션 요청과 전송 요청을 모두 처리할 수 있습니다.

Solana Pay의 핵심 기능은 Transfer Request입니다. SOL 또는 SPL Token 전송을 위한 비대화형 요청입니다. 모든 Solana 지갑이 이 형식을 지원하지는 않지만, Phantom, Solflare, Glow 등 대부분의 인기 지갑은 지원합니다. Solana Pay 전송 요청 URL은 다음과 같습니다.

코드
solana:<recipient>?amount=<amount>&spl-token=<spl-token>&reference=<reference>&label=<label>&message=<message>&memo=<memo>

URL의 매개변수는 지갑에서 트랜잭션을 직접 구성하는 데 사용됩니다. 이 가이드에서는 Transfer Request를 중점적으로 다루지만, 거의 모든 Solana 트랜잭션을 유효한 Solana Pay URL로 변환할 수 있습니다.

매개변수

이제 각 매개변수를 살펴보겠습니다.

수신자:

SOL을 받을 사람의 공개 키입니다. SPL Token을 전송할 때는 spl-token 필드에서 토큰 유형을 지정합니다. 지갑은 이 정보를 사용해 실제 전송 대상 계정을 확인합니다.

금액

전송할 SOL 또는 토큰의 수량입니다. 1보다 작으면 소수점 앞에 0을 입력해야 합니다. 금액이 없으면 지갑에서 직접 입력하라는 메시지가 표시됩니다. SOL을 전송하는 경우 이 금액은 lamports가 아닌 SOL 단위입니다.

SPL Token

특정 유형의 SPL Token 계정에 대한 민트 주소를 나타내는 선택 필드입니다. 이 필드를 포함하지 않으면 일반 SOL 전송 트랜잭션이 됩니다.

참조값

트랜잭션에 인코딩되는 고유 식별자입니다. 특정 트랜잭션을 찾고 트랜잭션 결과를 검증하는 데 사용됩니다.

레이블

전송 요청의 출처를 나타내는 짧은 설명입니다. 스토어나 앱일 수 있습니다. 지갑은 트랜잭션 정보를 제공하기 위해 이를 사용자에게 표시합니다.

메시지

전송이 이루어지는 이유에 대한 세부 정보를 담는 인코딩된 URL입니다. 구매 항목, 주문 ID 또는 간단한 메모가 포함될 수 있습니다. 지갑에서 사용자에게 더 자세한 맥락을 제공하는 데도 사용됩니다.

메모

결제 트랜잭션에 포함되는 메모입니다. 온체인에 저장되므로 비공개 정보나 민감한 정보를 포함해서는 안 됩니다.

이러한 세부 정보를 활용하면 트랜잭션을 완전히 제어하고 지갑과 수신자 간 상호작용을 간소화할 수 있습니다. 보안을 위해 앱은 트랜잭션이 온체인에서 확인된 후에만 처리해야 합니다.

Solana Pay: URL 예시

0.5 SOL 전송:

코드
solana:CckxW6C1CjsxYcXSiDbk7NYfPLhfqAm3kSB5LEZunnSE?amount=0.5&label=Amazon&message=Thanks%20for%20shopping%20at%20Amazon&memo=ID321

5 USDC 전송:

코드
solana:CckxW6C1CjsxYcXSiDbk7NYfPLhfqAm3kSB5LEZunnSE?amount=5&spl-token=EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v

Solana Pay: Next.js 구현

무엇을 만들까요? 이 튜토리얼을 완료하면 사용자가 입력한 데이터(지갑 주소와 SOL 금액)로 Solana Pay QR 코드를 생성하는 웹 앱이 완성됩니다. 이 앱은 블록체인에서 트랜잭션 상태를 확인해 결제 상태도 표시합니다.

사전 요구 사항

  • 컴퓨터에 Node.js가 설치되어 있어야 합니다.
  • React와 TypeScript를 충분히 이해해야 합니다.
  • 자금을 보내고 받을 Solana 지갑이 필요합니다.
  • 온체인 작업을 위한 Helius API 키도 필요합니다. 지갑만 있으면 개발자 포털에서 Helius API 키를 무료로 받을 수 있습니다.

단계

Next.js에서 Solana Pay를 사용하려면 다음 단계를 따르세요.

Next.js 앱 만들기

먼저 Node가 설치되어 있는지 확인한 후 Next.js 앱을 만드세요. 터미널에 다음을 입력합니다.

코드
npx create-next-app@latest sol-pay-app

‍그러면 몇 가지 질문이 표시됩니다. 다음 옵션을 선택하세요.

생성이 완료되면 다음 명령어로 해당 디렉터리로 이동할 수 있습니다.

코드
cd sol-pay-app

디렉터리 안에서 다음 명령어로 앱을 시작할 수 있습니다.

코드
npm run dev

http://localhost:3000/으로 이동해 앱을 확인할 수 있습니다.

패키지 설치

이 앱에는 Solana web3.js와 Solana Pay라는 두 가지 패키지가 필요합니다. 또한 트랜잭션 링크의 QR 코드 버전을 생성하기 위해 “react-qr-code” 패키지를 사용합니다.

다음 명령어를 실행해 패키지를 설치하세요.

코드
npm install @solana/pay @solana/web3.js bignumber.js react-qr-code --save

프런트엔드 만들기

Solana Pay QR 코드를 표시하고, SOL을 받을 주소와 SOL 금액을 수정할 수 있는 프런트엔드를 만드세요. 보안을 위해 모든 로직, 특히 금액과 수신자 설정은 백엔드에서 처리하는 것이 좋습니다.

하지만 예제를 간단하게 유지하기 위해 앱의 로직도 프런트엔드에서 처리하겠습니다.

  1. pages 디렉터리로 이동해 index.tsx 파일을 여세요. 이 파일은 홈페이지를 나타냅니다. 기존 코드를 다음 코드로 교체하세요.
코드
export default function Home() {
  return (
    <div className="flex flex-col items-center justify-center min-h-screen bg-gray-100">
      <h1 className="mb-6 text-3xl font-bold text-orange-700">
        Helius Solana Pay Demo
      </h1>
    </div>
  );
}
  1. 페이지 상단에 import 문을 추가하세요.
코드
// imports
import {Cluster,clusterApiUrl,Connection,PublicKey,Keypair} from "@solana/web3.js";
import { encodeURL, createQR,findReference, FindReferenceError, validateTransfer } from "@solana/pay";
import BigNumber from "bignumber.js";
import { useState } from "react";
import QRCode from "react-qr-code";
  1. Helius RPC URL을 저장할 변수를 만드세요. 이 앱을 배포하려면 ENV 파일이나 프록시를 사용해 API 키를 숨겨야 합니다.
코드
// get a free Helius RPC at helius.dev
 const RPC="https://rpc.helius.xyz/?api-key="!
  1. Helius RPC URL을 사용해 Solana Connection 객체를 만드세요.
코드
console.log('Connecting to the Solana network\n');
const connection = new Connection(RPC, 'confirmed');
  1. 다음으로 사용할 변수를 선언하고 초기화합니다. 여기에는 Solana Pay URL에 필요한 모든 변수가 포함됩니다. Home() 함수 안에서 이러한 변수를 만들 수 있습니다.
코드
// URL Variables
  const [address, setAddress] = useState("");
  const [recipient, setRecipient] = useState(
    new PublicKey("CckxW6C1CjsxYcXSiDbk7NYfPLhfqAm3kSB5LEZunnSE"));
  const [amount, setAmount] = useState(new BigNumber(1));
  const [message, setMessage] = useState("Helius Demo Order");
  const reference = new Keypair().publicKey;
  const label = "Helius Super Store";
  const memo = "Helius#4098";

  // for the QR code
  const [qrCodeValue, setQrCodeValue] = useState('');
  const [paymentStatus, setPaymentStatus] = useState('');
  1. 다음으로 결제를 생성하는 함수를 작성합니다. 이 함수는 입력 필드의 변수를 가져와 encodedURL 메서드로 인코딩된 Solana Pay URL을 생성합니다. 그런 다음 URL 객체를 문자열로 변환하고 저장해 나중에 QR 코드를 표시할 때 사용합니다. 변수 아래에 다음 코드를 추가하세요.
코드
async function createPayment() {
    console.log("Creating a payment URL \n");
    setRecipient(new PublicKey(address));
    const url = encodeURL({
      recipient,
      amount,
      reference,
      label,
      message,
      memo,
    });

    setQrCodeValue(url.toString()); // convert URL object to string
    checkPayment();
  }
  1. 결제가 생성되면 사용자에게 QR 코드를 표시합니다. 사용자는 코드를 스캔해 트랜잭션을 실행할 수 있습니다. 해당 트랜잭션이 발생했고 성공했는지 검증하려면 온체인 결제 트랜잭션을 확인하는 함수를 만들어야 합니다. 위의 createPayment 함수에서 트랜잭션 생성 직후 이 함수를 호출하는 것을 확인할 수 있습니다.
코드
async function checkPayment() {
    // update payment status
    setPaymentStatus('pending');

		// search for transaction

		// validate transaction
}

이 함수는 확인된 트랜잭션 검색과 트랜잭션 검증이라는 두 부분으로 구성됩니다.

사용자가 별도의 기기에서 트랜잭션과 상호작용하므로 트랜잭션이 정확히 언제 발생했는지 알 수 없습니다. 대신 일치하는 참조값을 가진 트랜잭션이 발생했는지, 현재 상태가 어떤지 반복해서 확인합니다. 이 작업은 Solana Pay의 findReference 메서드로 수행합니다.

코드
// search for transaction
console.log('Searching for the payment\n');
    let signatureInfo;
  
    const {signature} = await new Promise((resolve, reject) => {
      
        const interval = setInterval(async () => {
            console.count('Checking for transaction...'+reference);
            try {
                signatureInfo = await findReference(connection, reference, { finality: 'confirmed' });
                console.log('\n Signature: ', signatureInfo.signature,signatureInfo);
                clearInterval(interval);
                resolve(signatureInfo);
            } catch (error: any) {
                if (!(error instanceof FindReferenceError)) {
                    console.error(error);
                    clearInterval(interval);
                    reject(error);
                }
            }
        }, 250);
    });
	// Update payment status
  setPaymentStatus('confirmed');

트랜잭션을 찾았다면 트랜잭션에서 올바른 작업이 수행되었는지 확인해야 합니다.

올바른 수신자가 토큰을 받았는지, 정확한 수량의 토큰이 전송되었는지 확인해야 합니다. 이를 위해 Solana Pay의 validateTransfer 메서드를 사용합니다.

코드
//validate transaction
    console.log('Validating the payment\n');
    try {
      await validateTransfer(connection, signature, { recipient: recipient, amount });

      // Update payment status
      setPaymentStatus('validated');
      console.log('Payment validated');
      return true;
      
  } catch (error) {
      console.error('Payment failed', error);
      return false;
  }
  1. 마지막 단계는 UI를 구성할 TSX를 추가하는 것입니다. Tailwind로 요소의 스타일을 지정합니다. UI에는 제목, 수신자 주소와 SOL 금액을 입력하는 두 개의 입력 필드, 버튼, Solana Pay QR 코드가 포함됩니다. 결제가 검증되면 Solana Pay QR 코드는 “결제 검증 완료”라는 텍스트로 바뀝니다. 버튼은 createPayment 메서드를 호출합니다.
코드
//validate transaction
return (
    <div className="flex flex-col items-center justify-center min-h-screen bg-gray-100">
      <h1 className="mb-6 text-3xl font-bold text-orange-700">
        Helius Solana Pay Demo
      </h1>
      <div className="w-full max-w-md p-6 mx-auto bg-white rounded-xl shadow-md">
        <div className="mb-4">
          <label className="block mb-2 text-sm font-medium text-gray-700">
            Address:
          </label>
          <input
            type="text"
            onChange={(e) => setAddress(e.target.value)}
            className="w-full px-3 py-2 text-sm leading-tight text-gray-700 border rounded shadow appearance-none focus:outline-none focus:shadow-outline"
          />
        </div>
        <div className="mb-4">
          <label className="block mb-2 text-sm font-medium text-gray-700">
            Amount:
          </label>
          <input
            type="number"
            onChange={(e) => setAmount(new BigNumber(e.target.value))}
            className="w-full px-3 py-2 text-sm leading-tight text-gray-700 border rounded shadow appearance-none focus:outline-none focus:shadow-outline"
          />
        </div>
        <div className="flex justify-center items-center">
        <button
          className="px-4 py-2 font-bold text-white bg-blue-500 rounded hover:bg-blue-700"
          onClick={createPayment}
        >
          Create QR Code
        </button>
        </div>
        
        <div>
        {paymentStatus === 'validated' ? <p className="mt-4 text-green-500 text-center">Payment Validated</p> : <div className="flex justify-center mt-4">
          {qrCodeValue && }
        </div>}
      </div>
      </div>
      
    </div>
  );

‍모든 단계를 완료하면 다음과 같이 작동하는 앱이 완성됩니다.

Solana Pay: 원활한 경험 만들기

결제하거나 트랜잭션을 설정할 때 Solana Pay의 인코딩 시스템은 사용자와 앱 모두에 원활한 경험을 제공합니다. 이 가이드가 Solana Pay를 이해하고 구현하는 종합적인 자료가 되어 Solana 블록체인과 더 원활하고 효율적으로 상호작용하는 데 도움이 되기를 바랍니다. 도움이 필요하면 언제든 Helius Discord에 참여하거나 Twitter로 문의하세요.

  • 완성된 코드는 index.tsx 파일에서 확인하세요.
  • Solana Pay에 대해 자세히 알아보세요.
  • Solana Pay 문서를 확인하세요.

‍

‍

‍

Helius 구독하기

최신 Solana 개발 소식을 확인하고 새 게시물 알림을 받아보세요

확대 이미지