Un piano di abbonamento consente a un commerciante di pubblicare i termini di fatturazione che gli utenti possono accettare. Dopo che un utente si abbona, il commerciante o un puller autorizzato può riscuotere fino all'importo del piano per ogni periodo di fatturazione.
Questa guida mostra il flusso completo come blocchi costitutivi. Il commerciante crea un piano, l'abbonato lo accetta e il commerciante o il puller riscuote i pagamenti dal PDA di abbonamento risultante.
Installazione
pnpm add @solana/subscriptions @solana/kit @solana/kit-plugin-rpc @solana/kit-plugin-signer @solana-program/token
Crea un piano
Il commerciante possiede il piano. Il PDA del piano è derivato dall'indirizzo
del commerciante e 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,});
Aggiorna un piano
Il commerciante può aggiornare i campi modificabili del piano dopo la creazione. Gli abbonati esistenti mantengono i termini che hanno accettato, mentre i nuovi abbonati accettano i termini attuali del piano.
Si applicano alcune regole:
- Una
endTsfinita può solo essere accorciata, mai estesa o azzerata (PlanEndTsCannotExtend). - Con un piano
Sunsetè possibile rimuovere i puller (il nuovo insieme deve essere un sottoinsieme di quello attuale) per revocare un puller compromesso; lo stato,endTse i metadati rimangono bloccati. - Le modifiche funzionano durante l'ultimo periodo di fatturazione di un piano,
purché
endTsrimanga invariata.
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();
Iscrizione
Il sottoscrittore accetta i termini del piano corrente. Il PDA della sottoscrizione è derivato dal PDA del piano e dall'indirizzo del sottoscrittore.
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,});
Riscuoti un Pagamento
Il commerciante o un puller autorizzato firma la riscossione. Quando il piano
utilizza una lista consentita di destinazione, il proprietario del token account
ricevente deve essere elencato in 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();
Annulla e Revoca
L'annullamento contrassegna l'abbonamento come in scadenza. La revoca chiude il PDA dell'abbonamento dopo che è trascorso il periodo di scadenza dell'annullamento. Il sottoscrittore firma entrambe le transazioni.
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();
Riprendi un Abbonamento
Un abbonamento annullato può essere riattivato prima che venga revocato. Una
volta revocato, l'account dell'abbonamento viene chiuso e il sottoscrittore deve
abbonarsi nuovamente. La ripresa richiede
l'SubscriptionAuthority del sottoscrittore per il mint del piano, che il
programma convalida (proprietario, mint e init_id) e rifiuta se obsoleto o
reinizializzato.
await subscriberClient.subscriptions.instructions.resumeSubscription({subscriber: subscriberSigner,planPda,tokenMint,}).sendTransaction();
Note
amountè in unità base. Per un token a 6 decimali,5_000_000corrisponde a5token.- L'SDK TypeScript recupera i termini del piano in tempo reale durante
subscribese vengono omessi. - Il
SubscribeBuilderRust necessita dei termini del piano previsti. Recupera e decodifica prima l'account del piano, quindi passa quei campi tramiteSubscribeData. - Solo il merchant o un wallet elencato in
pullerspuò riscuotere i pagamenti. - Il sottoscrittore firma le transazioni di configurazione, annullamento e revoca. Il merchant o il puller autorizzato firma le transazioni di riscossione.
Is this page helpful?