ZahlungenAbonnements

Abonnementplan

Ein Abonnementplan ermöglicht es einem Händler, Abrechnungsbedingungen zu veröffentlichen, die Nutzer akzeptieren können. Nachdem ein Nutzer abonniert hat, kann der Händler oder ein autorisierter Einzieher bis zum Planbetrag in jeder Abrechnungsperiode einziehen.

Diese Anleitung zeigt den vollständigen Ablauf als Bausteine. Der Händler erstellt einen Plan, der Abonnent akzeptiert ihn, und der Händler oder Einzieher zieht Zahlungen aus dem resultierenden Abonnement-PDA ein.

Installation

pnpm add @solana/subscriptions @solana/kit @solana/kit-plugin-rpc @solana/kit-plugin-signer @solana-program/token

Plan erstellen

Der Händler ist Eigentümer des Plans. Der Plan-PDA wird von der Händleradresse und planId abgeleitet.

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,
});

Plan aktualisieren

Der Händler kann veränderbare Planfelder nach der Erstellung aktualisieren. Bestehende Abonnenten behalten die von ihnen akzeptierten Bedingungen bei, während neue Abonnenten die aktuellen Planbedingungen akzeptieren.

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();

Abonnieren

Der Abonnent akzeptiert die aktuellen Planbedingungen. Die Abonnement-PDA wird vom Plan-PDA und der Adresse des Abonnenten abgeleitet.

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,
});

Eine Zahlung einziehen

Der Händler oder ein auf der Whitelist stehender Einzieher signiert den Einzug. Wenn der Plan eine Ziel-Allowlist verwendet, muss der Eigentümer des Empfänger-token account in destinations aufgeführt sein.

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();

Kündigen und Widerrufen

Das Kündigen markiert das Abonnement als beendet. Das Widerrufen schließt die Abonnement-PDA, nachdem die Kündigungsfrist abgelaufen ist. Der Abonnent signiert beide Transaktionen.

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();

Hinweise

  • amount ist in Basiseinheiten angegeben. Bei einem Token mit 6 Dezimalstellen bedeutet 5_000_000 5 Token.
  • Das TypeScript SDK ruft während subscribe die aktuellen Planbedingungen ab, wenn Sie diese weglassen.
  • Der Rust SubscribeBuilder benötigt die erwarteten Planbedingungen. Rufen Sie zunächst das Plan-Konten ab und dekodieren Sie es, und übergeben Sie dann diese Felder über SubscribeData.
  • Nur der Händler oder eine in pullers aufgeführte Wallet kann Zahlungen einziehen.
  • Der Abonnent signiert Einrichtungs-, Kündigungs- und Widerrufstransaktionen. Der Händler oder autorisierte Einzieher signiert Einzugstransaktionen.

Is this page helpful?

Inhaltsverzeichnis

Seite bearbeiten
© 2026 Solana Foundation. Alle Rechte vorbehalten.