This commit is contained in:
@@ -30,9 +30,10 @@ class PaymentGateway implements IPaymentGateway {
|
||||
|
||||
public async verifyPayment(gatewayType: GatewayProvider, data: VerifyPaymentData): Promise<any> {
|
||||
const gateway = this.getGateway(gatewayType);
|
||||
const verifyData = gatewayType === GatewayProvider.SEP
|
||||
? { refNum: data.refNum, amount: data.amount }
|
||||
: { authority: data.authority, amount: data.amount };
|
||||
const verifyData =
|
||||
gatewayType === GatewayProvider.SEP
|
||||
? { refNum: data.refNum, amount: data.amount }
|
||||
: { authority: data.authority, amount: data.amount };
|
||||
|
||||
return await gateway.verifyPayment(verifyData);
|
||||
}
|
||||
|
||||
@@ -12,19 +12,19 @@ interface SepRequestPaymentResponse {
|
||||
}
|
||||
interface SepVerifyPaymentResponse {
|
||||
TransactionDetail: {
|
||||
"RRN": string,
|
||||
"RefNum": string,
|
||||
"MaskedPan": string,
|
||||
"HashedPan": string,
|
||||
"TerminalNumber": number,
|
||||
"OrginalAmount": number,
|
||||
"AffectiveAmount": number,
|
||||
"StraceDate": string,
|
||||
"StraceNo": string,
|
||||
},
|
||||
"ResultCode": number,
|
||||
"ResultDescription": string,
|
||||
"Success": boolean
|
||||
RRN: string;
|
||||
RefNum: string;
|
||||
MaskedPan: string;
|
||||
HashedPan: string;
|
||||
TerminalNumber: number;
|
||||
OrginalAmount: number;
|
||||
AffectiveAmount: number;
|
||||
StraceDate: string;
|
||||
StraceNo: string;
|
||||
};
|
||||
ResultCode: number;
|
||||
ResultDescription: string;
|
||||
Success: boolean;
|
||||
}
|
||||
|
||||
@injectable()
|
||||
@@ -47,7 +47,7 @@ class SepGateway implements IGateway {
|
||||
const purchaseData = {
|
||||
Action: "Token",
|
||||
TerminalId: this.terminalNumber,
|
||||
Amount: amount * 10,//ریال
|
||||
Amount: amount * 10, //ریال
|
||||
RedirectUrl: `${this.callBackURL}${callBackPath}`,
|
||||
ResNum: Date.now().toString(),
|
||||
CellNumber: _mobile,
|
||||
@@ -71,8 +71,6 @@ class SepGateway implements IGateway {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
async verifyPayment(data: { authority?: string; refNum?: string; amount: number }) {
|
||||
if (!this.gatewayApiUrl || !this.terminalNumber) {
|
||||
throw new InternalError(["SEP gateway is not configured"]);
|
||||
@@ -80,13 +78,14 @@ class SepGateway implements IGateway {
|
||||
|
||||
try {
|
||||
const verifyData = { RefNum: data.refNum, TerminalNumber: this.terminalNumber };
|
||||
const response = await axios.post<SepVerifyPaymentResponse>(`${this.gatewayApiUrl}/verifyTxnRandomSessionkey/ipg/VerifyTransaction`,
|
||||
const response = await axios.post<SepVerifyPaymentResponse>(
|
||||
`${this.gatewayApiUrl}/verifyTxnRandomSessionkey/ipg/VerifyTransaction`,
|
||||
verifyData,
|
||||
{ headers: this.requestHeader }
|
||||
{ headers: this.requestHeader },
|
||||
);
|
||||
|
||||
const sepResponse = response.data;
|
||||
console.log('sepResponse verify response *', sepResponse)
|
||||
console.log("sepResponse verify response *", sepResponse);
|
||||
// Normalize SEP response to match expected format
|
||||
// SEP considers ResultCode = 0 and Success = true as successful
|
||||
const isSuccess = sepResponse.ResultCode === 0 && sepResponse.Success === true;
|
||||
@@ -95,18 +94,18 @@ class SepGateway implements IGateway {
|
||||
let amountValid = true;
|
||||
if (sepResponse.TransactionDetail && sepResponse.TransactionDetail.OrginalAmount) {
|
||||
// According to SEP docs, OrginalAmount should match the expected amount
|
||||
amountValid = sepResponse.TransactionDetail.OrginalAmount === data.amount*10;
|
||||
amountValid = sepResponse.TransactionDetail.OrginalAmount === data.amount * 10;
|
||||
if (!amountValid) {
|
||||
this.logger.warn(`SEP amount mismatch: expected ${data.amount}, got ${sepResponse.TransactionDetail.OrginalAmount}`);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
code: (isSuccess && amountValid) ? 100 : sepResponse.ResultCode, // Use 100 for success to match expected format
|
||||
code: isSuccess && amountValid ? 100 : sepResponse.ResultCode, // Use 100 for success to match expected format
|
||||
message: sepResponse.ResultDescription,
|
||||
ref_id: data.refNum, // SEP uses refNum as transaction identifier
|
||||
success: isSuccess && amountValid,
|
||||
raw_response: sepResponse // Keep original response for debugging
|
||||
raw_response: sepResponse, // Keep original response for debugging
|
||||
};
|
||||
} catch (error) {
|
||||
this.logger.error("error in SEP payment verification", error);
|
||||
|
||||
@@ -40,5 +40,5 @@ export interface NewPaymentData {
|
||||
export interface VerifyPaymentData {
|
||||
amount: number;
|
||||
authority?: string;
|
||||
refNum?:string
|
||||
refNum?: string;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { compare, hash } from "bcrypt";
|
||||
import { compare, hash } from "bcryptjs";
|
||||
import { inject, injectable } from "inversify";
|
||||
|
||||
import { LoginPassDTO } from "./DTO/loginPassword.dto";
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { hash } from "bcrypt";
|
||||
import { hash } from "bcryptjs";
|
||||
import { CallbackError, Schema, model } from "mongoose";
|
||||
|
||||
import { IAdmin } from "./Abstraction/IAdmin";
|
||||
|
||||
@@ -58,7 +58,7 @@ class OrderService {
|
||||
const province = userAddress.province as unknown as IProvince;
|
||||
console.log("city", city);
|
||||
console.log("province", province);
|
||||
if(!city?.name || !province?.name ){
|
||||
if (!city?.name || !province?.name) {
|
||||
throw new BadRequestError(AddressMessage.INVALID_ADDRESS);
|
||||
}
|
||||
const shipmentAddress: IShipmentAddress = {
|
||||
|
||||
@@ -115,7 +115,7 @@ class PaymentController extends BaseController {
|
||||
@response() res: Response,
|
||||
@requestBody() dto: VerifyOnlinePaymentDTO,
|
||||
) {
|
||||
const verifyCallbackData = {
|
||||
const verifyCallbackData = {
|
||||
status: dto.State,
|
||||
refNum: dto.RefNum,
|
||||
authority: dto.Token,
|
||||
|
||||
@@ -165,7 +165,6 @@ class PaymentService {
|
||||
const session = await startSession();
|
||||
session.startTransaction();
|
||||
try {
|
||||
|
||||
// For SEP gateway, check if refNum has already been used as transaction_id
|
||||
if (gateway === GatewayProvider.SEP && refNum) {
|
||||
const existingPayment = await this.cartPaymentRepo.model.findOne({ transaction_id: refNum }).session(session);
|
||||
@@ -185,17 +184,14 @@ class PaymentService {
|
||||
|
||||
// Handle unsuccessful payment status
|
||||
if (status !== "OK") {
|
||||
return this.buildPaymentResponse(status ?? 'Nok', PaymentMessage.PaymentNotSuccessful, paymentData._id.toString(), order._id);
|
||||
return this.buildPaymentResponse(status ?? "Nok", PaymentMessage.PaymentNotSuccessful, paymentData._id.toString(), order._id);
|
||||
}
|
||||
|
||||
// Verify payment through gateway
|
||||
const verifyData =
|
||||
gateway === GatewayProvider.SEP
|
||||
? { refNum, amount: paymentData.totalPrice }
|
||||
: { authority, amount: paymentData.totalPrice };
|
||||
gateway === GatewayProvider.SEP ? { refNum, amount: paymentData.totalPrice } : { authority, amount: paymentData.totalPrice };
|
||||
const data = await this.paymentGateway.verifyPayment(gateway, verifyData);
|
||||
|
||||
|
||||
if (data.code === 100 || data.code === 101) {
|
||||
await this.handleSellerNotify(order._id, user, session);
|
||||
// For SEP gateway, save refNum as transaction_id; for Zarinpal, save ref_id
|
||||
@@ -397,8 +393,17 @@ class PaymentService {
|
||||
}
|
||||
//##################################################################
|
||||
//##################################################################
|
||||
private async updatePaymentStatusAndReference(paymentId: string, transactionId: string | null, status: PaymentStatus, session: ClientSession) {
|
||||
return await this.cartPaymentRepo.model.findByIdAndUpdate(paymentId, { paymentStatus: status, transaction_id: transactionId }, { session });
|
||||
private async updatePaymentStatusAndReference(
|
||||
paymentId: string,
|
||||
transactionId: string | null,
|
||||
status: PaymentStatus,
|
||||
session: ClientSession,
|
||||
) {
|
||||
return await this.cartPaymentRepo.model.findByIdAndUpdate(
|
||||
paymentId,
|
||||
{ paymentStatus: status, transaction_id: transactionId },
|
||||
{ session },
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -130,7 +130,6 @@ export class ProductVariantRepository extends BaseRepository<IProductVariant> {
|
||||
},
|
||||
},
|
||||
|
||||
|
||||
{
|
||||
$addFields: {
|
||||
product_id: "$product._id",
|
||||
|
||||
@@ -22,7 +22,7 @@ export interface IProductVariant {
|
||||
dimensions: IDimensions;
|
||||
sellerSpecialCode: string;
|
||||
themeValue: Types.ObjectId;
|
||||
deleted: boolean
|
||||
deleted: boolean;
|
||||
}
|
||||
|
||||
export interface IPrice {
|
||||
|
||||
@@ -1105,7 +1105,7 @@ class ProductService {
|
||||
|
||||
async softDeleteProductVariant(variantId: string) {
|
||||
await this.productVariantRepo.model.findOneAndUpdate({ _id: variantId }, { deleted: true });
|
||||
return { message: 'Deleted successfully' }
|
||||
return { message: "Deleted successfully" };
|
||||
}
|
||||
|
||||
//################################################################
|
||||
|
||||
Reference in New Issue
Block a user