diff --git a/src/app/api/flux/route.ts b/src/app/api/flux/route.ts index 24a0469..b6047d3 100644 --- a/src/app/api/flux/route.ts +++ b/src/app/api/flux/route.ts @@ -1,5 +1,8 @@ import { NextRequest, NextResponse } from 'next/server' import { fal } from "@fal-ai/client" +import { FLUX_MODELS } from '../../../services/fluxService' + +import { verifyPayment } from '../../../utils/verifyPayment' if (!process.env.FAL_AI_KEY) { throw new Error('FAL_AI_KEY is not configured in environment variables') @@ -16,7 +19,7 @@ const IMAGE_HEIGHT: number = 1024 export async function POST(req: NextRequest): Promise { try { - const { prompt, modelId } = await req.json() + const { prompt, modelId, transactionSignature } = await req.json() if (!prompt || !modelId) { return NextResponse.json( @@ -25,6 +28,28 @@ export async function POST(req: NextRequest): Promise { ) } + if (!transactionSignature) { + return NextResponse.json( + { error: 'Transaction signature is required' }, + { status: 400 } + ) + } + + const model = FLUX_MODELS.find((model) => model.modelId === modelId) + if (!model) { + return NextResponse.json( + { error: 'Invalid modelId' }, + { status: 400 } + ) + } + + const expectedAmount = model.cost; + const isPaymentVerified = await verifyPayment(transactionSignature, expectedAmount) + + if (!isPaymentVerified) { + throw new Error('Payment verification failed'); + } + console.log('Generating with Flux model:', modelId) console.log('Prompt:', prompt) diff --git a/src/app/page.tsx b/src/app/page.tsx index bbcfb5f..287c7a2 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -49,8 +49,10 @@ const Page: React.FC = (): React.ReactElement => { return { error: paymentResult.error } } + const transactionSignature = paymentResult.transactionSignature; + // Then generate image with specified model - return generateWithFlux(prompt, modelId) + return generateWithFlux(prompt, modelId, transactionSignature) } } diff --git a/src/services/fluxService.ts b/src/services/fluxService.ts index c0af3f9..7f22c2e 100644 --- a/src/services/fluxService.ts +++ b/src/services/fluxService.ts @@ -34,7 +34,8 @@ export const FLUX_MODELS: FluxModelConfig[] = [ export async function generateWithFlux( prompt: string, - modelId: string + modelId: string, + transactionSignature: string, ): Promise { try { const response = await fetch('/api/flux', { @@ -44,7 +45,8 @@ export async function generateWithFlux( }, body: JSON.stringify({ prompt, - modelId + modelId, + transactionSignature, }), }) diff --git a/src/services/paymentService.ts b/src/services/paymentService.ts index 5ecb302..a966b7a 100644 --- a/src/services/paymentService.ts +++ b/src/services/paymentService.ts @@ -1,4 +1,4 @@ -import { Connection, PublicKey, Transaction, SystemProgram } from '@solana/web3.js' +import { Connection, PublicKey, Transaction } from '@solana/web3.js' import { TOKEN_PROGRAM_ID, createTransferInstruction, @@ -23,6 +23,7 @@ const connection = new Connection( export interface PaymentResult { success: boolean + transactionSignature?: string error?: string } @@ -146,7 +147,7 @@ export async function processMTMPayment( throw new Error(`Transaction failed: ${JSON.stringify(confirmation.value.err)}`) } - return { success: true } + return { success: true, transactionSignature: signature }; } catch (error) { console.error('Payment error:', error) return { diff --git a/src/utils/verifyPayment.ts b/src/utils/verifyPayment.ts new file mode 100644 index 0000000..2f5282b --- /dev/null +++ b/src/utils/verifyPayment.ts @@ -0,0 +1,47 @@ +import { Connection } from '@solana/web3.js'; +import { TOKEN_PROGRAM_ID } from '@solana/spl-token'; + +const SOLANA_RPC_URL = process.env.NEXT_PUBLIC_SOLANA_RPC_URL; +const SOLANA_WEBSOCKET_URL = process.env.NEXT_PUBLIC_SOLANA_WEBSOCKET_URL; + +const connection = new Connection( + SOLANA_RPC_URL, + { + commitment: 'confirmed', + wsEndpoint: SOLANA_WEBSOCKET_URL, + confirmTransactionInitialTimeout: 60000, + } +); + +export async function verifyPayment( + transactionSignature: string, + expectedAmount: number, +): Promise { + try { + const transaction = await connection.getParsedTransaction(transactionSignature, 'finalized'); + if (!transaction) { + throw new Error('Transaction not found'); + } + + const transferInstruction = transaction.transaction.message.instructions.find( + (instr) => 'parsed' in instr && instr.programId.equals(TOKEN_PROGRAM_ID) + ); + + if (!transferInstruction || !('parsed' in transferInstruction)) { + throw new Error('Transfer instruction not found'); + } + + const { parsed } = transferInstruction; + const { info } = parsed; + const { amount } = info; + + if (BigInt(amount) === BigInt(expectedAmount * (10 ** 6))) { + return true; + } + + return false; + } catch (error) { + console.error('Verification error:', error); + return false; + } +}