Un plan d'abonnement permet à un commerçant de publier des conditions de facturation que les utilisateurs peuvent accepter. Après qu'un utilisateur s'abonne, le commerçant ou un préleveur autorisé peut collecter jusqu'au montant du plan à chaque période de facturation.
Ce guide présente le flux complet sous forme de blocs de construction. Le commerçant crée un plan, l'abonné l'accepte, et le commerçant ou le préleveur collecte les paiements depuis le PDA d'abonnement résultant.
Installation
pnpm add @solana/subscriptions @solana/kit @solana/kit-plugin-rpc @solana/kit-plugin-signer @solana-program/token
Créer un plan
Le commerçant est propriétaire du plan. Le PDA du plan est dérivé de l'adresse
du commerçant et de 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,});
Mettre à jour un plan
Le commerçant peut mettre à jour les champs modifiables du plan après sa création. Les abonnés existants conservent les conditions qu'ils ont acceptées, tandis que les nouveaux abonnés acceptent les conditions actuelles du plan.
Quelques règles s'appliquent :
- Un
endTsfini ne peut être que raccourci, jamais prolongé ou effacé (PlanEndTsCannotExtend). - Dans un plan
Sunset, vous pouvez supprimer des pullers (le nouvel ensemble doit être un sous-ensemble de l'ensemble actuel) pour révoquer un puller compromis ; le statut, leendTset les métadonnées restent figés. - Les modifications sont possibles pendant la dernière période de facturation
d'un plan, à condition que le
endTsreste inchangé.
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();
S'abonner
L'abonné accepte les conditions du plan en vigueur. Le PDA d'abonnement est dérivé du PDA du plan et de l'adresse de l'abonné.
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,});
Collecter un paiement
Le marchand ou un puller whitelisté signe la collecte. Lorsque le plan utilise
une liste d'autorisation de destinations, le propriétaire du token account
récepteur doit être répertorié dans 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();
Annuler et Révoquer
L'annulation marque l'abonnement comme se terminant. La révocation ferme le PDA de l'abonnement une fois le délai d'expiration de l'annulation écoulé. L'abonné signe les deux transactions.
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();
Reprendre un Abonnement
Un abonnement annulé peut être réactivé avant d'être révoqué. Une fois révoqué,
le compte d'abonnement est clôturé et l'abonné doit
s'abonner à nouveau. La reprise nécessite le
SubscriptionAuthority de l'abonné pour le mint du plan, que le programme
valide (propriétaire, mint et init_id) et rejette s'il est périmé ou
réinitialisé.
await subscriberClient.subscriptions.instructions.resumeSubscription({subscriber: subscriberSigner,planPda,tokenMint,}).sendTransaction();
Notes
amountest exprimé en unités de base. Pour un token à 6 décimales,5_000_000correspond à5tokens.- Le SDK TypeScript récupère les conditions du plan en temps réel lors de
subscribesi vous ne les fournissez pas. - L'
SubscribeBuilderRust nécessite les conditions du plan attendues. Récupérez et décodez d'abord le compte du plan, puis transmettez ces champs viaSubscribeData. - Seul le marchand ou un portefeuille répertorié dans
pullerspeut collecter les paiements. - L'abonné signe les transactions de configuration, d'annulation et de révocation. Le marchand ou le collecteur autorisé signe les transactions de collecte.
Is this page helpful?