forked from Shared/esg
initial commit
This commit is contained in:
165
src/providers/implementations/hamta.provider.ts
Normal file
165
src/providers/implementations/hamta.provider.ts
Normal file
@@ -0,0 +1,165 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import axios, { AxiosError } from 'axios';
|
||||
import { InquiryType } from '../../common/enums/inquiry-type.enum';
|
||||
import { ProviderName } from '../../common/enums/provider-name.enum';
|
||||
import { ProviderEnvConfig } from '../../config/configuration';
|
||||
import { AmitisProvider } from './amitis.provider';
|
||||
import { ProviderExecutionContext } from '../../common/interfaces/inquiry-provider.interface';
|
||||
import {
|
||||
LegacyInquiryPayload,
|
||||
LegacyInquiryResult,
|
||||
LegacyApiProvider,
|
||||
} from '../shared/legacy-api.provider.abstract';
|
||||
|
||||
interface ShahkarInquiryPayload extends LegacyInquiryPayload {
|
||||
nationalCode?: string;
|
||||
nationalCod?: string;
|
||||
NationalCod?: string;
|
||||
mobileNo?: string;
|
||||
MobileNo?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hamta provider — uses shared LegacyApiProvider base.
|
||||
* Supports multiple inquiry types with different authentication methods.
|
||||
*/
|
||||
@Injectable()
|
||||
export class HamtaProvider extends LegacyApiProvider {
|
||||
readonly name = ProviderName.HAMTA;
|
||||
readonly supportedInquiryTypes = [
|
||||
InquiryType.PERSON,
|
||||
InquiryType.REAL_ESTATE,
|
||||
InquiryType.SHEBA,
|
||||
InquiryType.SHAHKAR,
|
||||
InquiryType.POSTAL_CODE,
|
||||
InquiryType.LEGAL_PERSON,
|
||||
];
|
||||
|
||||
private readonly hamtaConfig: ProviderEnvConfig;
|
||||
|
||||
constructor(configService: ConfigService, private readonly amitisProvider: AmitisProvider) {
|
||||
const config = configService.get<ProviderEnvConfig>('hamta')!;
|
||||
super(
|
||||
config,
|
||||
(providerName, inquiryType, username, password) =>
|
||||
amitisProvider.getAccessToken(providerName, inquiryType, username, password),
|
||||
);
|
||||
this.hamtaConfig = config;
|
||||
}
|
||||
|
||||
protected async callProvider(
|
||||
inquiryType: InquiryType,
|
||||
payload: LegacyInquiryPayload,
|
||||
context: ProviderExecutionContext,
|
||||
): Promise<LegacyInquiryResult> {
|
||||
// Shahkar uses SOAP authentication, handled separately
|
||||
if (inquiryType === InquiryType.SHAHKAR) {
|
||||
return this.inquireShahkar(payload as ShahkarInquiryPayload);
|
||||
}
|
||||
|
||||
return super.callProvider(inquiryType, payload, context);
|
||||
}
|
||||
|
||||
private async inquireShahkar(payload: ShahkarInquiryPayload): Promise<{ raw: unknown }> {
|
||||
const nationalCode = this.getRequiredString(
|
||||
payload.nationalCode ?? payload.nationalCod ?? payload.NationalCod,
|
||||
'nationalCode',
|
||||
);
|
||||
const mobileNo = this.getRequiredString(payload.mobileNo ?? payload.MobileNo, 'mobileNo');
|
||||
|
||||
const inquiryConfig = this.getInquiryConfig(InquiryType.SHAHKAR);
|
||||
|
||||
try {
|
||||
const response = await axios.post<string>(
|
||||
inquiryConfig.url,
|
||||
this.buildShahkarEnvelope(
|
||||
nationalCode,
|
||||
mobileNo,
|
||||
inquiryConfig.username,
|
||||
inquiryConfig.password,
|
||||
),
|
||||
{
|
||||
headers: {
|
||||
'Content-Type': 'text/xml; charset=utf-8',
|
||||
SOAPAction: '"http://tempuri.org/IShahkarInq/ShahkarInquery"',
|
||||
},
|
||||
timeout: this.hamtaConfig.timeout,
|
||||
responseType: 'text',
|
||||
},
|
||||
);
|
||||
|
||||
const result = this.extractSoapValue(response.data, 'ShahkarInqueryResult');
|
||||
|
||||
return {
|
||||
raw: {
|
||||
nationalCode,
|
||||
mobileNo,
|
||||
result,
|
||||
soap: response.data,
|
||||
},
|
||||
};
|
||||
} catch (error) {
|
||||
if (error instanceof AxiosError) {
|
||||
throw this.formatProviderError(
|
||||
error.message,
|
||||
String(error.response?.status ?? 'NETWORK_ERROR'),
|
||||
);
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
private buildShahkarEnvelope(
|
||||
nationalCode: string,
|
||||
mobileNo: string,
|
||||
username: string,
|
||||
password: string,
|
||||
): string {
|
||||
return `<?xml version="1.0" encoding="utf-8"?>
|
||||
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
|
||||
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
|
||||
<soap:Body>
|
||||
<ShahkarInquery xmlns="http://tempuri.org/">
|
||||
<NationalCod>${this.escapeXml(nationalCode)}</NationalCod>
|
||||
<MobileNo>${this.escapeXml(mobileNo)}</MobileNo>
|
||||
<Username>${this.escapeXml(username)}</Username>
|
||||
<Password>${this.escapeXml(password)}</Password>
|
||||
</ShahkarInquery>
|
||||
</soap:Body>
|
||||
</soap:Envelope>`;
|
||||
}
|
||||
|
||||
private extractSoapValue(xml: string, tagName: string): string | null {
|
||||
const match = xml.match(new RegExp(`<(?:\\w+:)?${tagName}[^>]*>([\\s\\S]*?)</(?:\\w+:)?${tagName}>`));
|
||||
return match ? this.decodeXml(match[1].trim()) : null;
|
||||
}
|
||||
|
||||
private getRequiredString(value: unknown, fieldName: string): string {
|
||||
if (typeof value !== 'string' || !value.trim()) {
|
||||
throw this.formatProviderError(undefined, undefined, `${fieldName} is required`);
|
||||
}
|
||||
|
||||
return value.trim();
|
||||
}
|
||||
|
||||
private escapeXml(value: string): string {
|
||||
return value
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''');
|
||||
}
|
||||
|
||||
private decodeXml(value: string): string {
|
||||
return value
|
||||
.replace(/'/g, "'")
|
||||
.replace(/"/g, '"')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/</g, '<')
|
||||
.replace(/&/g, '&');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user