digital-products について
digital-productsは、デジタル商品の配信を設定する開発者やストア運営者向けのガイドスキルです。各プラットフォームの標準機能、アプリ、独自の安全なダウンロード方式を扱います。
主な機能
- 01プラットフォーム別の配信方式を比較
- 02ダウンロード回数と有効期限を設定
- 03ライセンスキーの配信方法を案内
- 04サブスクリプションコンテンツのアクセス管理
- 05S3署名付きリンクを利用
活用シーン
- 01ファイルのダウンロードと配信メールを設定
- 02ソフトウェア購入者にライセンスキーを割り当て
- 03サブスクリプションコンテンツのアクセスを管理
- 04標準機能のないストア向けに安全な配信を設計
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ finsilabs/ awesome-ecommerce-skills --skill digital-productsインストール後、Claude Code で/digital-productsを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
tile.jsonevals/license-key-pool-management-and-inventor/criteria.jsontask.mdevals/post-purchase-access-provisioning-and-de/criteria.jsontask.mdevals/secure-presigned-download-urls/criteria.jsontask.md詳細
デジタル商品
概要
電子書籍、ソフトウェアライセンス、テンプレート、音楽、コースなどのデジタル商品を販売するには、決済後の安全な配信、不正共有を防ぐためのダウンロード回数制限、有効期限付きのアクセスリンクが必要です。主要なプラットフォームでは、これらの機能が標準搭載されているか(WooCommerce)、成熟したアプリで対応できます(Shopify、BigCommerce)。まずはプラットフォーム標準のソリューションを使用してください。複雑なライセンスキー管理や、サブスクリプション加入者限定のコンテンツライブラリなど、アプリでは対応できないユースケースがある場合にのみ、独自の配信インフラを構築してください。
このスキルを使うタイミング
- 既存のストアにダウンロード可能な商品(PDF、ソフトウェア、音声、テンプレート)を追加するとき
- ソフトウェア商品のライセンスキー配信システムを実装するとき
- デジタルコンテンツライブラリへのアクセスを制限するサブスクリプションを構築するとき
- 誰でもアクセスできるダウンロード URL を、安全で有効期限付きの署名付き URL に置き換えるとき
基本的な手順
Step 1:プラットフォームを確認し、適切なツールを選ぶ
| プラットフォーム | 推奨ツール | 理由 |
|---|---|---|
| Shopify | Sky Pilot または Fileflare | Sky Pilot はファイル、ライセンスキー、ストリーミング動画、ダウンロード回数制限に対応しています。Fileflare は基本的なファイルダウンロード向けの、よりシンプルな選択肢です |
| WooCommerce | WooCommerce Downloadable Products(標準搭載) | WooCommerce は、ダウンロード回数制限、有効期限、安全なトークン URL など、デジタル商品の機能を標準でサポートしています |
| BigCommerce | Downloadable Digital Products(標準搭載)または Fileflare | BigCommerce はファイルの添付とダウンロード配信に標準で対応しています |
| カスタム/ヘッドレス | S3 署名付き URL を使った配信を構築 | プラットフォームにデジタル商品の標準サポートがない場合に必要です |
Step 2:プラットフォーム固有のセットアップ
Shopify
Shopify はデジタル商品の標準サポートがないため、アプリが必要です。Sky Pilot は最も多機能な選択肢です。
Sky Pilot のセットアップ:
- Shopify App Store から Sky Pilot をインストールします
- Sky Pilot → Files → Upload に移動し、デジタルファイル(PDF、ZIP など)をアップロードします
- Sky Pilot → Products に移動し、アップロードしたファイルを特定の商品またはバリエーションに紐付けます
- 配信設定を行います:
- Download limit:標準的な商品では 3~5 回、サブスクリプションでは無制限に設定します
- Link expiry:標準は 48~72 時間です。顧客はアカウントから新しいリンクをリクエストできます
- Automatic delivery:デフォルトで有効です。顧客には決済直後にダウンロードメールが届きます
- Sky Pilot → Settings → Email template で配信メールをカスタマイズします
ライセンスキー商品の場合:
- Sky Pilot で License Keys → Import に移動し、ライセンスキーの CSV をアップロードします
- ライセンスキープールを商品に紐付けます
- Sky Pilot は購入ごとにキーを自動で割り当て、配信メールに含めます
サブスクリプション加入者限定コンテンツの場合(Sky Pilot + Recharge):
- Sky Pilot は Recharge と連携します。有効なサブスクリプションがある顧客には自動的にアクセス権が付与され、サブスクリプションがキャンセルされるとアクセス権が取り消されます
WooCommerce
WooCommerceにはデジタル商品の配信機能が組み込まれているため、基本的な用途であれば追加のプラグインは不要です。
ダウンロード商品を設定する方法:
- WooCommerce → Products → Add Productに移動します
- Downloadableにチェックを入れます(配送を省略する場合は、必要に応じてVirtualにもチェックを入れます)
- Product Data → Downloadable Filesで、次の操作を行います。
- Add Fileをクリックし、ファイルをアップロードするか、URLを入力します
- ファイル名:アカウントページで購入者に表示される名前です
- アクセス設定を行います。
- Download limit:回数を入力します(例:
5)。無制限にする場合は空欄にします - Download expiry:購入後の日数を入力します(例:
365)。期限なしにする場合は空欄にします
- Download limit:回数を入力します(例:
- WooCommerceは購入ごとに、トークンベースの安全なダウンロードURLを自動生成します
ダウンロードに顧客アカウントを必須にする方法:
- WooCommerce → Settings → Accounts & Privacyに移動します
- Grant access to downloadable products after paymentとRequire login to downloadを有効にします
ライセンスキー商品の場合:
- License Manager for WooCommerce(無料プラグイン)をインストールします
- License Manager → Add License Keysに移動し、ライセンスキーを一括インポートします
- ライセンスキープールを商品に紐付けます
- プラグインは注文確認メールと顧客のアカウントページでキーを配信します
BigCommerce
デジタル商品の配信設定(組み込み機能):
- Products → Add Productに移動します
- Filesでデジタルファイルをアップロードします(BigCommerceは最大512 MBのファイルに対応しています)
- 注文ごとのファイルのダウンロード回数を制限するには、Max downloadsを設定します
- BigCommerceは支払い後、安全なリンクを記載したダウンロードメールを自動送信します
より高度な配信を行う場合:
- BigCommerce App MarketplaceからSendOwlまたはFileflareをインストールします
- これらのアプリでは、ライセンスキー管理、ダウンロード分析、ストリーミング動画配信が追加されます
カスタム/ヘッドレス
ヘッドレスストアフロントでは、S3の署名付きURLを使用して安全なファイル配信を実装します。S3キーをそのまま公開してはいけません。
import { S3Client, GetObjectCommand } from '@aws-sdk/client-s3';
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
const s3 = new S3Client({ region: process.env.AWS_REGION });
// Generate a short-lived presigned URL for each download attempt
export async function generateDownloadUrl(orderId: string, digitalProductId: string) {
const access = await db.orderDigitalAccess.findUnique({
where: { orderId_digitalProductId: { orderId, digitalProductId } },
include: { digitalProduct: true },
});
if (!access) throw new Error('No access record found');
if (access.expiresAt && access.expiresAt < new Date()) throw new Error('Download access has expired');
if (access.maxDownloads && access.downloadCount >= access.maxDownloads) throw new Error('Download limit reached');
// Atomically increment download count
await db.orderDigitalAccess.update({ where: { id: access.id }, data: { downloadCount: { increment: 1 } } });
// 60-second presigned URL — short enough to prevent sharing, long enough for the redirect
const command = new GetObjectCommand({
Bucket: process.env.DIGITAL_PRODUCTS_BUCKET,
Key: access.digitalProduct.fileStorageKey,
ResponseContentDisposition: `attachment; filename="${access.digitalProduct.fileName}"`,
});
return getSignedUrl(s3, command, { expiresIn: 60 });
}
// Provision access after payment — call this from your payment webhook
export async function provisionDigitalAccess(orderId: string) {
const order = await db.orders.findUnique({
where: { id: orderId },
include: { items: { include: { variant: { include: { digitalProduct: true } } } } },
});
for (const item of order.items.filter(i => i.variant.digitalProduct)) {
const dp = item.variant.digitalProduct;
await db.orderDigitalAccess.upsert({
where: { orderId_digitalProductId: { orderId, digitalProductId: dp.id } },
create: {
orderId, digitalProductId: dp.id, downloadCount: 0,
maxDownloads: dp.downloadLimit,
expiresAt: dp.accessDurationDays ? new Date(Date.now() + dp.accessDurationDays * 86400000) : null,
},
update: {}, // Idempotent — webhooks can fire multiple times
});
}
}
// License key pool management
export async function assignLicenseKey(orderId: string, productId: string) {
return db.$transaction(async tx => {
const license = await tx.digitalProductLicenses.findFirst({
where: { productId, status: 'available' },
});
if (!license) throw new Error(`No available license keys for product ${productId}`);
await tx.digitalProductLicenses.update({
where: { id: license.id },
data: { status: 'sold', orderId, soldAt: new Date() },
});
return license.licenseKey;
});
}
ステップ3:購入後の配信メールを設定する
すべてのプラットフォームでは配信メールが自動送信されます。内容が明確で、プロフェッショナルな印象になるようカスタマイズします。
含める内容:
- 注文確認番号
- 商品名と説明
- ダウンロードリンク(または目立つように表示したライセンスキー)
- 該当する場合は、ダウンロード回数の上限と有効期限
- 商品へのアクセス方法やインストール方法
- 困ったときのサポート連絡先
Shopify(Sky Pilot): Sky Pilot → Settings → Email Templateでカスタマイズします
WooCommerce: WooCommerce → Settings → Emails → Customer Processing Orderでカスタマイズします(ダウンロードリンクは自動で含まれます)
BigCommerce: Marketing → Transactional Emails → Order Status Notificationでカスタマイズします
ステップ4:ライセンスキーの在庫を監視する
ライセンスキー商品では、キーがなくなる前にアラートを設定します。
- Sky Pilot:**License Keys → [Product]**に移動すると、残りのキー数を確認できます。Sky Pilotは、在庫が設定したしきい値を下回るとメールで通知します
- License Manager for WooCommerce:ダッシュボードで商品ごとの残りのキー数を確認でき、色分けされた警告も表示されます
- カスタム:毎日確認を実行し、利用可能なキーが10個を下回ったときに通知します
ベストプラクティス
- 支払いが確認されるまでデジタル商品を絶対に提供しないでください — 注文作成イベントではなく、支払い確認の Webhook をトリガーにして提供します。カード決済の失敗は注文作成後に発生することがあります
- ヘッドレス実装では、有効期間の短いダウンロードリンク(60 秒)を使用します — ページのレンダリング時ではなく、ダウンロードページが読み込まれるたびに URL を新しく生成します
- ほとんどの商品にはダウンロード回数の上限を設定します — 標準は 3~5 回です。サブスクリプションによるアクセスは無制限にします。上限を設けることで、気軽なファイル共有を防げます
- ファイルはプライベートバケットに保存します — S3/GCS バケットを公開しないでください。すべてのアクセスには、署名付き URL の生成を経由させる必要があります
- ライセンスキーの在庫を監視します — キーが不足するとサポートへの問い合わせやチャージバックにつながります。初期在庫の 20%を下回ったときにアラートが送信されるよう設定します
- 配信用の専用メールを別途送信します — ライセンスキーを一般的な注文確認メールに含めないでください。配信に特化したメールのほうが、顧客が見つけやすく、後から参照しやすくなります
よくある落とし穴
| 問題 | 解決策 |
|---|---|
| 顧客がクリックする前にダウンロードリンクの有効期限が切れる | ヘッドレス構成では、メール送信時ではなく、ページが読み込まれるたびに新しい署名付き URL を生成します |
| 支払いに失敗した後にデジタル商品が提供される | 注文作成ではなく、支払い成功の Webhook(Stripe の payment_intent.succeeded)のみをトリガーにして提供します |
| ライセンスキーが二重に割り当てられる | キーの割り当てには、ステータスをアトミックに確認するデータベーストランザクションを使用します。キーを選択してから更新するという 2 段階の処理は避けてください |
| アカウントの削除後に顧客がアクセスできなくなる | ダウンロードアクセスはユーザーアカウント ID だけでなく、order_id と email に紐付けて保存します。注文番号によるアクセス復旧を可能にします |
| 大容量ファイルのダウンロードがタイムアウトする | S3 の署名付き URL を使用して、S3 から顧客のブラウザーに直接ファイルを配信します。サーバーを経由してプロキシしないでください |
関連スキル
- @inventory-tracking
- @low-stock-alerts
- @product-data-modeling