План подписки позволяет продавцу опубликовать условия оплаты, которые пользователи могут принять. После того как пользователь подписывается, продавец или утвержденный сборщик может собирать до суммы плана в каждый расчетный период.
Это руководство показывает полный процесс в виде строительных блоков. Продавец создает план, подписчик принимает его, а продавец или сборщик собирает платежи из полученного 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?