Verify payment on chain before generating meme #3

Merged
nabarun merged 3 commits from ag-verify-payment into main 2025-01-27 11:42:36 +00:00
5 changed files with 83 additions and 6 deletions

View File

@ -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<NextResponse> {
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<NextResponse> {
)
}
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)

View File

@ -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)
}
}

View File

@ -34,7 +34,8 @@ export const FLUX_MODELS: FluxModelConfig[] = [
export async function generateWithFlux(
prompt: string,
modelId: string
modelId: string,
transactionSignature: string,
): Promise<FluxGenerationResult> {
try {
const response = await fetch('/api/flux', {
@ -44,7 +45,8 @@ export async function generateWithFlux(
},
body: JSON.stringify({
prompt,
modelId
modelId,
transactionSignature,
}),
})

View File

@ -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 {

View File

@ -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<boolean> {
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;
}
}