story-research-zapwall/lib/platformCommissions.ts
Nicolas Cantu 90ff8282f1 feat: Implémentation système de commissions systématique et incontournable
- Création lib/platformCommissions.ts : configuration centralisée des commissions
  - Articles : 800 sats (700 auteur, 100 plateforme)
  - Avis : 70 sats (49 lecteur, 21 plateforme)
  - Sponsoring : 0.046 BTC (0.042 auteur, 0.004 plateforme)

- Validation des montants à chaque étape :
  - Publication : vérification du montant avant publication
  - Paiement : vérification du montant avant acceptation
  - Erreurs explicites si montant incorrect

- Tracking des commissions sur Nostr :
  - Tags author_amount et platform_commission dans événements
  - Interface ContentDeliveryTracking étendue
  - Traçabilité complète pour audit

- Logs structurés avec informations de commission
- Documentation complète du système

Les commissions sont maintenant systématiques, validées et traçables.
2025-12-27 21:11:09 +01:00

152 lines
4.0 KiB
TypeScript

import { PLATFORM_NPUB, PLATFORM_BITCOIN_ADDRESS } from './platformConfig'
/**
* Platform commission configuration
* Defines commission rates and split amounts for all payment types
*/
export const PLATFORM_COMMISSIONS = {
/**
* Article payment commission
* Total: 800 sats
* Author: 700 sats
* Platform: 100 sats
*/
article: {
total: 800,
author: 700,
platform: 100,
},
/**
* Review reward commission
* Total: 70 sats
* Reviewer: 49 sats
* Platform: 21 sats
*/
review: {
total: 70,
reviewer: 49,
platform: 21,
},
/**
* Sponsoring commission
* Total: 0.046 BTC (4,600,000 sats)
* Author: 0.042 BTC (4,200,000 sats)
* Platform: 0.004 BTC (400,000 sats)
*/
sponsoring: {
total: 0.046,
author: 0.042,
platform: 0.004,
totalSats: 4_600_000,
authorSats: 4_200_000,
platformSats: 400_000,
},
} as const
/**
* Platform Lightning address/node for receiving commissions
* This should be configured with the platform's Lightning node
*/
export const PLATFORM_LIGHTNING_ADDRESS = process.env.NEXT_PUBLIC_PLATFORM_LIGHTNING_ADDRESS || ''
/**
* Calculate commission split for article payment
*/
export function calculateArticleSplit(totalAmount: number = PLATFORM_COMMISSIONS.article.total): {
author: number
platform: number
total: number
} {
if (totalAmount !== PLATFORM_COMMISSIONS.article.total) {
throw new Error(`Invalid article payment amount: ${totalAmount}. Expected ${PLATFORM_COMMISSIONS.article.total} sats`)
}
return {
author: PLATFORM_COMMISSIONS.article.author,
platform: PLATFORM_COMMISSIONS.article.platform,
total: PLATFORM_COMMISSIONS.article.total,
}
}
/**
* Calculate commission split for review reward
*/
export function calculateReviewSplit(totalAmount: number = PLATFORM_COMMISSIONS.review.total): {
reviewer: number
platform: number
total: number
} {
if (totalAmount !== PLATFORM_COMMISSIONS.review.total) {
throw new Error(`Invalid review reward amount: ${totalAmount}. Expected ${PLATFORM_COMMISSIONS.review.total} sats`)
}
return {
reviewer: PLATFORM_COMMISSIONS.review.reviewer,
platform: PLATFORM_COMMISSIONS.review.platform,
total: PLATFORM_COMMISSIONS.review.total,
}
}
/**
* Calculate commission split for sponsoring
*/
export function calculateSponsoringSplit(totalAmount: number = PLATFORM_COMMISSIONS.sponsoring.total): {
author: number
platform: number
total: number
authorSats: number
platformSats: number
totalSats: number
} {
if (totalAmount !== PLATFORM_COMMISSIONS.sponsoring.total) {
throw new Error(
`Invalid sponsoring amount: ${totalAmount} BTC. Expected ${PLATFORM_COMMISSIONS.sponsoring.total} BTC`
)
}
return {
author: PLATFORM_COMMISSIONS.sponsoring.author,
platform: PLATFORM_COMMISSIONS.sponsoring.platform,
total: PLATFORM_COMMISSIONS.sponsoring.total,
authorSats: PLATFORM_COMMISSIONS.sponsoring.authorSats,
platformSats: PLATFORM_COMMISSIONS.sponsoring.platformSats,
totalSats: PLATFORM_COMMISSIONS.sponsoring.totalSats,
}
}
/**
* Verify that a payment amount matches expected commission split
*/
export function verifyPaymentSplit(
type: 'article' | 'review' | 'sponsoring',
totalAmount: number,
authorAmount?: number,
platformAmount?: number
): boolean {
switch (type) {
case 'article':
const articleSplit = calculateArticleSplit(totalAmount)
return (
articleSplit.author === (authorAmount ?? 0) && articleSplit.platform === (platformAmount ?? 0)
)
case 'review':
const reviewSplit = calculateReviewSplit(totalAmount)
return (
reviewSplit.reviewer === (authorAmount ?? 0) && reviewSplit.platform === (platformAmount ?? 0)
)
case 'sponsoring':
const sponsoringSplit = calculateSponsoringSplit(totalAmount)
return (
sponsoringSplit.authorSats === (authorAmount ?? 0) &&
sponsoringSplit.platformSats === (platformAmount ?? 0)
)
default:
return false
}
}