Een abonnementsplan stelt een handelaar in staat factureringsvoorwaarden te publiceren die gebruikers kunnen accepteren. Nadat een gebruiker zich heeft geabonneerd, kan de handelaar of een goedgekeurde puller tot het planbedrag per factureringsperiode innen.
Deze handleiding toont de volledige flow als bouwstenen. De handelaar creëert een plan, de abonnee accepteert het, en de handelaar of puller int betalingen van de resulterende abonnements-PDA.
Installeren
pnpm add @solana/subscriptions @solana/kit @solana/kit-plugin-rpc @solana/kit-plugin-signer @solana-program/token
Een Plan Creëren
De handelaar is eigenaar van het plan. De plan-PDA wordt afgeleid van het
handelaarsadres en planId.
Een sponsor kan de rent van het plan financieren door de optionele payer mee te geven, terwijl de handelaar de eigenaar van het plan blijft. Het verwijderen van het plan vergoedt de eigenaar, niet de betaler, dus beheer sponsoring buiten de chain om.
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,});
Een Plan Bijwerken
De handelaar kan muteerbare planvelden bijwerken na aanmaak. Bestaande abonnees behouden de voorwaarden die zij hebben geaccepteerd, terwijl nieuwe abonnees de huidige planvoorwaarden accepteren.
Een aantal regels zijn van toepassing:
- Een eindige
endTskan alleen worden ingekort, nooit verlengd of gewist (PlanEndTsCannotExtend). - Bij een
Sunsetplan kun je pullers verwijderen (de nieuwe set moet een subset zijn van de huidige) om een gecompromitteerde puller in te trekken; status,endTsen metadata blijven bevroren. - Wijzigingen werken tijdens de laatste factureringsperiode van een plan, zolang
endTsongewijzigd blijft. - De instructie bevat de planstatus die werd waargenomen op het moment van ondertekening
(
expectedCreatedAt,expectedEndTs,expectedPullers,expectedMetadataUri). Als het actieve plan hier niet meer mee overeenkomt, wordt de update geweigerd (StalePlanApproval). Een verouderde ondertekende update kan daardoor geen verwijderde pullers herstellen of latere wijzigingen terugdraaien. DeupdatePlan-methode van de plugin-client haalt de actuele status automatisch op; bij handmatige implementatie dient u het plan zelf op te halen en de velden door te geven.
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();
Abonneren
De abonnee accepteert de huidige planvoorwaarden. De abonnements-PDA wordt afgeleid van de plan-PDA en het adres van de abonnee.
Om de initialisatie van de autoriteit en het abonnement in één enkele transactie te bundelen, geeft u de schildwacht UNKNOWN_INIT_ID (geëxporteerd door de TypeScript SDK) mee als expectedSubscriptionAuthorityInitId. Het programma accepteert de autoriteit alleen als deze is aangemaakt in de huidige slot, zodat de schildwacht werkt voor nieuwe aanmeldingen. Een terugkerende gebruiker wiens autoriteit in een eerdere slot is aangemaakt, moet de echte initId doorgeven (de plugin-client haalt deze automatisch op) of de aanroep mislukt met StaleSubscriptionAuthority.
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,});
Een Betaling Innen
De merchant of een gewhitelistte puller ondertekent de inning. Wanneer het plan
gebruikmaakt van een bestemming-allowlist, moet de eigenaar van de ontvanger
token account worden vermeld 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();
Annuleren en Intrekken
Annuleren markeert het abonnement als eindigend. Intrekken sluit de abonnements- PDA nadat de annuleringstermijn is verstreken. De abonnee ondertekent beide transacties.
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();
Onmiddellijk opzeggen
Wanneer zowel de abonnee als de huidige planeigenaar ondertekenen, laat cancelSubscriptionNow het abonnement verlopen op het moment van opzegging in plaats van aan het einde van de factureringsperiode. Het kan ook een lopende opzegging met respijtperiode verkorten. De goedkeuring is gekoppeld aan het begin van de periode zoals waargenomen op het moment van ondertekening; als het abonnement sindsdien is gewijzigd, wordt de transactie geweigerd (StaleSubscriptionApproval).
import { fetchSubscriptionDelegation } from '@solana/subscriptions';const subscription = await fetchSubscriptionDelegation(subscriberClient.rpc,subscriptionPda,);await subscriberClient.subscriptions.instructions.cancelSubscriptionNow({subscriber: subscriberSigner,merchant: merchantSigner,planPda,expectedCurrentPeriodStartTs: subscription.data.currentPeriodStartTs,}).sendTransaction();
Een Abonnement Hervatten
Een geannuleerd abonnement kan opnieuw worden geactiveerd voordat het wordt
ingetrokken. Zodra het is ingetrokken, wordt het abonnementsaccount gesloten en
moet de abonnee opnieuw abonneren. Hervatten vereist het
SubscriptionAuthority van de abonnee voor de mint van het plan, dat het
programma valideert (eigenaar, mint en init_id) en afwijst als het verouderd
of opnieuw geïnitialiseerd is.
De instructie bevat de vervaldatum die de abonnee heeft waargenomen op het moment van ondertekening (expectedExpiresAtTs); een afwijking wordt geweigerd (StaleSubscriptionApproval). Hierdoor kan een verouderde ondertekende hervatting een latere opzegging die de abonnee nooit heeft goedgekeurd, niet ongedaan maken.
import { fetchSubscriptionDelegation } from '@solana/subscriptions';const subscription = await fetchSubscriptionDelegation(subscriberClient.rpc,subscriptionPda,);await subscriberClient.subscriptions.instructions.resumeSubscription({subscriber: subscriberSigner,planPda,tokenMint,expectedExpiresAtTs: subscription.data.expiresAtTs,}).sendTransaction();
Opmerkingen
amountis in basiseenheden. Voor een token met 6 decimalen betekent5_000_0005tokens.- De TypeScript SDK haalt live plantermijnen op tijdens
subscribewanneer u deze weglaat. - De Rust
SubscribeBuilderheeft de verwachte plantermijnen nodig. Haal het planaccount op en decodeer het eerst, geef die velden vervolgens door viaSubscribeData. - Alleen de handelaar of een wallet vermeld in
pullerskan betalingen innen. - De abonnee ondertekent setup-, annulerings- en intrekkingstransacties. De handelaar of goedgekeurde puller ondertekent incassotransacties.
Is this page helpful?