2026-04-15-4
This commit is contained in:
@@ -0,0 +1,101 @@
|
||||
import { Controller, Get, Post, Body, Query, UseGuards, Req, Logger, BadRequestException } from '@nestjs/common';
|
||||
import { BillingService } from './billing.service';
|
||||
import { PayMobProvider } from './providers/paymob.provider';
|
||||
import { BinanceProvider } from './providers/binance.provider';
|
||||
import { FirebaseAuthGuard } from '../auth/guards/firebase-auth.guard';
|
||||
import { ApiTags, ApiOperation, ApiBearerAuth } from '@nestjs/swagger';
|
||||
import { PaymentProvider, PaymentStatus } from './entities/transaction.entity';
|
||||
|
||||
@ApiTags('billing')
|
||||
@Controller('billing')
|
||||
export class BillingController {
|
||||
private readonly logger = new Logger(BillingController.name);
|
||||
|
||||
constructor(
|
||||
private billingService: BillingService,
|
||||
private paymobProvider: PayMobProvider,
|
||||
private binanceProvider: BinanceProvider,
|
||||
) {}
|
||||
|
||||
@ApiBearerAuth()
|
||||
@UseGuards(FirebaseAuthGuard)
|
||||
@Get('subscription')
|
||||
@ApiOperation({ summary: 'Get current subscription details' })
|
||||
async getSubscription(@Req() req: any) {
|
||||
return this.billingService.getSubscription(req.tenant.id);
|
||||
}
|
||||
|
||||
@ApiBearerAuth()
|
||||
@UseGuards(FirebaseAuthGuard)
|
||||
@Post('checkout')
|
||||
@ApiOperation({ summary: 'Initialize a checkout session' })
|
||||
async checkout(@Req() req: any, @Body() body: { plan: string; provider: PaymentProvider }) {
|
||||
const tenantId = req.tenant.id;
|
||||
const plan = body.plan;
|
||||
const amount = plan === 'PRO' ? 40 : 0; // Price logic
|
||||
|
||||
if (body.provider === PaymentProvider.PAYMOB) {
|
||||
const { paymentKey, orderId } = await this.paymobProvider.createPaymentKey(tenantId, amount, plan);
|
||||
|
||||
// Iframe URL (Using the new provided Iframe ID)
|
||||
const iframeId = this.billingService.getIframeId();
|
||||
const checkoutUrl = `https://accept.paymob.com/api/acceptance/iframes/${iframeId}?payment_token=${paymentKey}`;
|
||||
|
||||
return { checkoutUrl, orderId };
|
||||
}
|
||||
|
||||
if (body.provider === PaymentProvider.BINANCE) {
|
||||
return this.binanceProvider.createOrder(tenantId, amount, plan);
|
||||
}
|
||||
|
||||
throw new BadRequestException('Invalid payment provider');
|
||||
}
|
||||
|
||||
/**
|
||||
* PayMob Transaction Processed Webhook
|
||||
* This is called by PayMob when a transaction is attempted
|
||||
*/
|
||||
@Post('webhooks/paymob')
|
||||
@ApiOperation({ summary: 'PayMob Payment Webhook' })
|
||||
async handlePaymobWebhook(@Body() body: any, @Query('hmac') hmac: string) {
|
||||
this.logger.log(`📥 PayMob Webhook Received: Transaction ID ${body.obj?.id}`);
|
||||
|
||||
// 1. Verify HMAC
|
||||
if (!this.paymobProvider.verifyHmac(body.obj, hmac)) {
|
||||
this.logger.error('❌ PayMob HMAC Verification Failed');
|
||||
throw new BadRequestException('Invalid signature');
|
||||
}
|
||||
|
||||
// 2. Process payment if successful
|
||||
if (body.obj.success === true) {
|
||||
// Extract metadata from extra_description (format: tenantId|plan)
|
||||
const extraDesc = body.obj.order?.shipping_data?.extra_description || "";
|
||||
const [tenantId, plan] = extraDesc.split('|');
|
||||
|
||||
if (!tenantId) {
|
||||
this.logger.error(`❌ PayMob Webhook failed: No tenantId in metadata. Raw: ${extraDesc}`);
|
||||
return { status: 'error', message: 'No tenantId found' };
|
||||
}
|
||||
|
||||
await this.billingService.processSuccessfulPayment(
|
||||
body.obj.id.toString(),
|
||||
PaymentProvider.PAYMOB,
|
||||
body.obj.amount_cents / 100,
|
||||
{
|
||||
tenantId,
|
||||
plan: plan || 'PRO'
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
return { status: 'success' };
|
||||
}
|
||||
|
||||
@ApiBearerAuth()
|
||||
@UseGuards(FirebaseAuthGuard)
|
||||
@Get('invoices')
|
||||
@ApiOperation({ summary: 'Get payment history' })
|
||||
async getInvoices(@Req() req: any) {
|
||||
return this.billingService.getTransactionHistory(req.tenant.id);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user