План підписки дозволяє продавцю опублікувати умови виставлення рахунків, які користувачі можуть прийняти. Після того, як користувач підписується, продавець або затверджений збирач може стягувати до суми плану кожен розрахунковий період.
Цей посібник показує повний процес як окремі блоки. Продавець створює план, підписник приймає його, а продавець або збирач отримує платежі з отриманого 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,});
Оновлення плану
Продавець може оновлювати змінювані поля плану після створення. Існуючі підписники зберігають умови, які вони прийняли, тоді як нові підписники приймають поточні умови плану.
Діють кілька правил:
- Кінцевий
endTsможна лише скоротити, але не продовжити або очистити (PlanEndTsCannotExtend). - У плані
Sunsetможна видалити пулери (новий набір має бути підмножиною поточного), щоб відкликати скомпрометований пулер; статус,endTsі метадані залишаються незмінними. - Редагування дозволяється протягом останнього розрахункового періоду плану,
якщо
endTsне змінюється.
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,});
Прийняти Платіж
Мерчант або авторизований пулер підписує операцію збору коштів. Якщо план
використовує дозволений список одержувачів, власник token account одержувача має
бути вказаний у 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();
Відновлення підписки
Скасовану підписку можна повторно активувати до її відкликання. Після
відкликання обліковий запис підписки закривається, і підписник повинен
підписатися знову. Для відновлення потрібен
SubscriptionAuthority підписника для монети плану, який програма перевіряє
(власник, монета та init_id) і відхиляє, якщо він застарілий або повторно
ініціалізований.
await subscriberClient.subscriptions.instructions.resumeSubscription({subscriber: subscriberSigner,planPda,tokenMint,}).sendTransaction();
Примітки
amountвказується в базових одиницях. Для токена з 6 десятковими знаками5_000_000означає5токенів.- TypeScript SDK отримує актуальні умови плану під час
subscribe, якщо ви їх не вказуєте. - Rust
SubscribeBuilderпотребує очікуваних умов плану. Спочатку отримайте та декодуйте обліковий запис плану, а потім передайте ці поля черезSubscribeData. - Лише мерчант або гаманець, зазначений у
pullers, може збирати платежі. - Підписник підписує транзакції налаштування, скасування та відкликання. Мерчант або авторизований збирач підписує транзакції збору платежів.
Is this page helpful?