payment gateway

This commit is contained in:
2025-12-03 23:23:32 +03:30
parent 4e2bf34ab4
commit 2248f18fe8
4 changed files with 142 additions and 277 deletions
+25 -109
View File
@@ -1,14 +1,6 @@
import { BadRequestException, Injectable, NotFoundException } from '@nestjs/common';
import axios from 'axios';
import {
IPaymentRequest,
IPaymentResponse,
IPaymentVerifyRequest,
IPaymentVerifyResponse,
PaymentGatewayEnum,
PaymentMethodEnum,
PaymentStatusEnum,
} from '../interface/payment';
import { PaymentGatewayService } from './payment-gateway.service';
import { PaymentGatewayEnum, PaymentMethodEnum, PaymentStatusEnum } from '../interface/payment';
import { Payment } from '../entities/payment.entity';
import { EntityManager, RequiredEntityData } from '@mikro-orm/core';
import { Order } from '../../orders/entities/order.entity';
@@ -17,7 +9,10 @@ import { CreatePaymentDto } from '../dto/create-payment.dto';
@Injectable()
export class PaymentsService {
constructor(private readonly em: EntityManager) {}
constructor(
private readonly em: EntityManager,
private readonly paymentGatewayService: PaymentGatewayService,
) {}
async initializePayment(
paymentMethodId: string,
@@ -56,7 +51,7 @@ export class PaymentsService {
gateway,
});
const paymentUrl = this.zarinpalPaymentUrl(gateway, authority);
const paymentUrl = this.paymentGatewayService.zarinpalPaymentUrl(gateway, authority);
return { paymentUrl };
}
@@ -64,7 +59,14 @@ export class PaymentsService {
async createPayment(domain: string, dto: CreatePaymentDto) {
const { amount, orderId, merchantId, gateway, paymentMethod } = dto;
const { authority } = await this.requestToGateway(paymentMethod, amount, orderId, merchantId, domain, gateway);
const { authority } = await this.paymentGatewayService.requestToGateway(
paymentMethod,
amount,
orderId,
merchantId,
domain,
gateway,
);
const payment = this.em.create(Payment, {
amount,
@@ -78,78 +80,6 @@ export class PaymentsService {
return { authority, payment };
}
async requestToGateway(
paymentMethod: PaymentMethodEnum,
amount: number,
orderId: string,
merchantId: string | null | undefined,
domain: string,
gateway: PaymentGatewayEnum | null | undefined,
): Promise<{ authority: string | null }> {
// For non-online payment methods, no gateway request is needed
if (paymentMethod !== PaymentMethodEnum.Online) {
return { authority: null };
}
// Online payments require merchantId and gateway
if (!merchantId) {
throw new BadRequestException('Merchant ID is required for online payments');
}
if (!gateway) {
throw new BadRequestException('Payment gateway is required for online payments');
}
// Handle ZarinPal gateway
if (gateway === PaymentGatewayEnum.ZarinPal) {
const callbackUrl = `${domain}/payments/${PaymentGatewayEnum.ZarinPal}/${orderId}/callback`;
try {
const gatewayResponse = await this.requestToZarinPalGateway({
amount,
merchantId,
description: `Payment for order #${orderId}`,
callbackUrl,
metadata: {
orderId,
},
});
// Check gateway response code (typically 100 means success for Iranian gateways)
if (gatewayResponse.code !== 100 && gatewayResponse.code !== 0) {
throw new BadRequestException(`Payment gateway error: ${gatewayResponse.message || 'Unknown error'}`);
}
if (!gatewayResponse.authority) {
throw new BadRequestException('Payment gateway did not return an authority token');
}
return { authority: gatewayResponse.authority };
} catch (error) {
if (error instanceof BadRequestException) {
throw error;
}
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
throw new BadRequestException(`Failed to connect to payment gateway: ${errorMessage}`);
}
}
// If gateway is not supported, throw an error
throw new BadRequestException(`Unsupported payment gateway: ${String(gateway)}`);
}
private async requestToZarinPalGateway(requestPayment: IPaymentRequest): Promise<IPaymentResponse> {
const gatewayPaymentUrl = 'https://payment.zarinpal.com/pg/v4/payment/request.json';
const response = await axios.post<IPaymentResponse>(gatewayPaymentUrl, requestPayment);
return response.data;
}
zarinpalPaymentUrl(gateway: PaymentGatewayEnum | null, authority: string | null) {
if (gateway === PaymentGatewayEnum.ZarinPal) {
return `https://payment.zarinpal.com/pg/StartPay/${authority}`;
}
return null;
}
async verifyPayment(authority: string, orderId: string): Promise<Payment> {
// Find payment by authority and orderId
const payment = await this.em.findOne(
@@ -199,7 +129,7 @@ export class PaymentsService {
// Handle ZarinPal gateway verification
if (payment.gateway === PaymentGatewayEnum.ZarinPal) {
try {
const verifyResponse = await this.verifyZarinPalPayment({
const verifyResponse = await this.paymentGatewayService.verifyZarinPalPayment({
merchantId: paymentMethod.merchantId,
amount: verifyAmount,
authority,
@@ -250,29 +180,15 @@ export class PaymentsService {
throw new BadRequestException(`Unsupported payment gateway: ${String(payment.gateway)}`);
}
private async verifyZarinPalPayment(verifyRequest: IPaymentVerifyRequest): Promise<IPaymentVerifyResponse> {
const gatewayVerifyUrl = 'https://payment.zarinpal.com/pg/v4/payment/verify.json';
try {
const response = await axios.post<IPaymentVerifyResponse>(gatewayVerifyUrl, verifyRequest);
if (!response.data) {
throw new BadRequestException('Empty response from payment gateway');
}
return response.data;
} catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
throw new BadRequestException(`Failed to verify payment with gateway: ${errorMessage}`);
}
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
update(_id: number, _updatePaymentDto: unknown) {
return `This action updates a #${_id} payment`;
}
// update(_id: number, _updatePaymentDto: unknown) {
// return `This action updates a #${_id} payment`;
// }
findAll() {
return this.em.find(Payment, {});
}
// findAll() {
// return this.em.find(Payment, {});
// }
remove(id: number) {
return `This action removes a #${id} payment`;
}
// remove(id: number) {
// return `This action removes a #${id} payment`;
// }
}