결제구독
구독 플랜을 통해 판매자는 사용자가 수락할 수 있는 청구 조건을 게시할 수 있습니다. 사용자가 구독하면, 판매자 또는 승인된 수금자가 각 청구 기간마다 플랜 금액까지 수금할 수 있습니다.
이 가이드는 전체 흐름을 구성 요소로 보여줍니다. 판매자가 플랜을 생성하고, 구독자가 이를 수락하며, 판매자 또는 수금자가 생성된 구독 PDA에서 결제를 수금합니다.
설치
pnpm add @solana/subscriptions @solana/kit @solana/kit-plugin-rpc @solana/kit-plugin-signer @solana-program/token
플랜 생성
판매자가 플랜을 소유합니다. 플랜 PDA는 판매자 주소와 planId에서 파생됩니다.
import { address, createClient } from '@solana/kit';import { solanaLocalRpc } from '@solana/kit-plugin-rpc';import { signer } from '@solana/kit-plugin-signer';import { findPlanPda, subscriptionsProgram } from '@solana/subscriptions';const merchantClient = createClient().use(signer(merchantSigner)).use(solanaLocalRpc({ rpcUrl: 'http://127.0.0.1:8899' })).use(subscriptionsProgram());const planId = 1n;const tokenMint = address('TOKEN_MINT_ADDRESS_HERE');const amount = 5_000_000n;const periodHours = 720n;const metadataUri = 'https://example.com/plan.json';const destinations = [merchantSigner.address];const pullers = [address('PULLER_WALLET_ADDRESS_HERE')];await merchantClient.subscriptions.instructions.createPlan({planId,mint: tokenMint,amount,periodHours,endTs: 0n,destinations,pullers,metadataUri,}).sendTransaction();const [planPda] = await findPlanPda({owner: merchantSigner.address,planId,});
플랜 업데이트
판매자는 생성 후 변경 가능한 플랜 필드를 업데이트할 수 있습니다. 기존 구독자는 자신이 수락한 조건을 유지하며, 새로운 구독자는 현재 플랜 조건을 수락합니다.
import { PlanStatus } from '@solana/subscriptions';const updatedMetadataUri = 'https://example.com/updated-plan.json';const updatedPullers = [address('NEW_PULLER_WALLET_ADDRESS_HERE')];await merchantClient.subscriptions.instructions.updatePlan({owner: merchantSigner,planPda,status: PlanStatus.Active,endTs: 0n,pullers: updatedPullers,metadataUri: updatedMetadataUri,}).sendTransaction();
구독
구독자가 현재 플랜 약관에 동의합니다. 구독 PDA는 플랜 PDA와 구독자 주소로부터 파생됩니다.
import { createClient } from '@solana/kit';import { solanaLocalRpc } from '@solana/kit-plugin-rpc';import { signer } from '@solana/kit-plugin-signer';import { findAssociatedTokenPda, TOKEN_PROGRAM_ADDRESS } from '@solana-program/token';import {fetchMaybeSubscriptionAuthority,findSubscriptionAuthorityPda,findSubscriptionDelegationPda,subscriptionsProgram,} from '@solana/subscriptions';const subscriberClient = createClient().use(signer(subscriberSigner)).use(solanaLocalRpc({ rpcUrl: 'http://127.0.0.1:8899' })).use(subscriptionsProgram());const [subscriberAta] = await findAssociatedTokenPda({mint: tokenMint,owner: subscriberSigner.address,tokenProgram: TOKEN_PROGRAM_ADDRESS,});const [subscriptionAuthorityPda] = await findSubscriptionAuthorityPda({user: subscriberSigner.address,tokenMint,});const subscriptionAuthority = await fetchMaybeSubscriptionAuthority(subscriberClient.rpc,subscriptionAuthorityPda,);if (!subscriptionAuthority.exists) {await subscriberClient.subscriptions.instructions.initSubscriptionAuthority({tokenMint,tokenProgram: TOKEN_PROGRAM_ADDRESS,userAta: subscriberAta,}).sendTransaction();}await subscriberClient.subscriptions.instructions.subscribe({merchant: merchantSigner.address,planId,tokenMint,}).sendTransaction();const [subscriptionPda] = await findSubscriptionDelegationPda({planPda,subscriber: subscriberSigner.address,});
결제 수집
판매자 또는 화이트리스트에 등록된 풀러가 수집에 서명합니다. 플랜이 목적지 허용
목록을 사용하는 경우, 수신자 토큰 계정의 소유자가 destinations에 등록되어
있어야 합니다.
const receiverAta = address('MERCHANT_TOKEN_ACCOUNT_ADDRESS_HERE');await merchantClient.subscriptions.instructions.transferSubscription({caller: merchantOrPullerSigner,delegator: subscriberSigner.address,tokenMint,subscriptionPda,planPda,amount: 200_000n,receiverAta,tokenProgram: TOKEN_PROGRAM_ADDRESS,}).sendTransaction();
취소 및 철회
취소는 구독이 종료됨을 표시합니다. 철회는 취소 만료 기간이 경과한 후 구독 PDA를 닫습니다. 구독자가 두 트랜잭션 모두에 서명합니다.
await subscriberClient.subscriptions.instructions.cancelSubscription({subscriber: subscriberSigner,planPda,subscriptionPda,}).sendTransaction();// Run this after the cancelled subscription's expiresAtTs has elapsed.await subscriberClient.subscriptions.instructions.revokeSubscription({authority: subscriberSigner,planPda,subscriptionPda,}).sendTransaction();
참고사항
amount는 기본 단위입니다. 소수점 6자리 토큰의 경우,5_000_000는5토큰을 의미합니다.- TypeScript SDK는 플랜 조건을 생략하면
subscribe중에 실시간 플랜 조건을 가져옵니다. - Rust
SubscribeBuilder에는 예상 플랜 조건이 필요합니다. 먼저 플랜 계정을 가져와 디코딩한 다음, 해당 필드를SubscribeData를 통해 전달하세요. - 판매자 또는
pullers에 등록된 지갑만 결제를 수금할 수 있습니다. - 구독자는 설정, 취소 및 철회 트랜잭션에 서명합니다. 판매자 또는 승인된 수금자는 수금 트랜잭션에 서명합니다.
Is this page helpful?