payment gateway
This commit is contained in:
@@ -0,0 +1,115 @@
|
||||
import { EntityManager } from '@mikro-orm/postgresql';
|
||||
import { Injectable, BadRequestException, Logger } from '@nestjs/common';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import {
|
||||
IPaymentRequest,
|
||||
IPaymentResponse,
|
||||
IPaymentVerifyRequest,
|
||||
IPaymentVerifyResponse,
|
||||
PaymentGatewayEnum,
|
||||
PaymentMethodEnum,
|
||||
} from '../interface/payment';
|
||||
import axios from 'axios';
|
||||
|
||||
@Injectable()
|
||||
export class PaymentGatewayService {
|
||||
private readonly logger = new Logger(PaymentGatewayService.name);
|
||||
private readonly zarinpalRequestUrl: string;
|
||||
private readonly zarinpalPaymentBaseUrl: string;
|
||||
private readonly zarinpalVerifyUrl: string;
|
||||
|
||||
constructor(
|
||||
private readonly em: EntityManager,
|
||||
private readonly configService: ConfigService,
|
||||
) {
|
||||
// Get Zarinpal base URL from environment or default to sandbox for development
|
||||
const zarinpalBaseUrl = this.configService.get<string>('ZARINPAL_BASE_URL') || 'https://sandbox.zarinpal.com';
|
||||
|
||||
this.zarinpalRequestUrl = `${zarinpalBaseUrl}/pg/v4/payment/request.json`;
|
||||
this.zarinpalPaymentBaseUrl = `${zarinpalBaseUrl}/pg/StartPay`;
|
||||
this.zarinpalVerifyUrl = `${zarinpalBaseUrl}/pg/v4/payment/verify.json`;
|
||||
}
|
||||
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/${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 response = await axios.post<IPaymentResponse>(this.zarinpalRequestUrl, requestPayment);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
zarinpalPaymentUrl(gateway: PaymentGatewayEnum | null, authority: string | null) {
|
||||
if (gateway === PaymentGatewayEnum.ZarinPal) {
|
||||
return `${this.zarinpalPaymentBaseUrl}/${authority}`;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
async verifyZarinPalPayment(verifyRequest: IPaymentVerifyRequest): Promise<IPaymentVerifyResponse> {
|
||||
try {
|
||||
const response = await axios.post<IPaymentVerifyResponse>(this.zarinpalVerifyUrl, 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}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user