支払いサブスクリプション

サブスクリプションプラン

サブスクリプションプランは、販売者がユーザーが承諾できる請求条件を公開できるようにします。ユーザーがサブスクライブすると、販売者または承認されたプーラーは、各請求期間にプラン金額まで回収できます。

このガイドでは、全体的なフローをビルディングブロックとして示します。販売者がプランを作成し、サブスクライバーがそれを承諾し、販売者またはプーラーが結果として生成されるサブスクリプション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,
});

プランの更新

販売者は、作成後に変更可能なプランフィールドを更新できます。既存のサブスクライバーは承諾した条件を維持し、新しいサブスクライバーは現在のプラン条件を承諾します。

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

注意事項

  • amountは基本単位です。6桁の小数を持つトークンの場合、5_000_0005トークンを意味します。
  • TypeScript SDKは、プラン条件を省略した場合、subscribe時にリアルタイムのプラン条件を取得します。
  • RustのSubscribeBuilderには、予想されるプラン条件が必要です。まずプランアカウントを取得してデコードし、その後SubscribeDataを通じてそれらのフィールドを渡してください。
  • マーチャントまたはpullersにリストされているウォレットのみが支払いを回収できます。
  • サブスクライバーは、セットアップ、キャンセル、取り消しのトランザクションに署名します。マーチャントまたは承認されたプラーは、回収トランザクションに署名します。

Is this page helpful?

目次

ページを編集
© 2026 Solana Foundation. 無断転載を禁じます。