Files
psp_api/src/modules/tspProviders/switch/nama/nama-provider.adapter.ts
T

278 lines
9.3 KiB
TypeScript
Raw Normal View History

import {
createEnsureSuccessResponseInterceptor,
createHeaderRequestInterceptor,
FetchRequestInterceptor,
} from '@/common/interceptors/fetch-request.interceptor'
import { HttpClientUtil } from '@/common/utils/http-client.util'
2026-05-04 11:21:49 +03:30
import { TspProviderResponseStatus } from '@/generated/prisma/enums'
import { Injectable, Logger } from '@nestjs/common'
import {
TspProviderCorrectionSendPayloadDto,
TspProviderCorrectionSendResponseDto,
2026-05-27 22:44:01 +03:30
TspProviderGetResponseDto,
TspProviderOriginalResponseDto,
TspProviderOriginalSendPayloadDto,
2026-05-04 11:21:49 +03:30
TspProviderRevokePayloadDto,
TspProviderRevokeResponseDto,
} from '../../dto'
import {
IProviderSwitchAdapter,
TspProviderBulkSendResultDto,
} from '../../dto/provider-switch.dto'
import {
NamaProviderGetResponseDto,
NamaProviderOriginalResponseDto,
NamaProviderResponseStatus,
2026-05-04 11:21:49 +03:30
NamaProviderRevokeRequestDto,
NamaProviderRevokeResponseDto,
} from './dto'
2026-05-04 11:21:49 +03:30
import { NamaProviderUtils } from './nama-provider.util'
@Injectable()
export class NamaProviderSwitchAdapter implements IProviderSwitchAdapter {
readonly code = 'NAMA'
private readonly logger = new Logger(NamaProviderSwitchAdapter.name)
private readonly sandboxBaseUrl =
process.env.NAMA_PROVIDER_SANDBOX_URL || 'https://external-api-dev.namatsp.ir'
private readonly productionBaseUrl =
process.env.NAMA_PROVIDER_PRODUCTION_URL || 'https://api.nama.ir'
private readonly sendPath = '/api/v1/invoices/single-send'
private readonly sendBulkPath = '/api/v1/invoices'
private readonly checkBulkStatus = '/api/v1/invoices/check-by-uuids'
private readonly getPath = '/api/v1/invoices'
2026-05-04 11:21:49 +03:30
constructor(
private readonly httpClient: HttpClientUtil,
private readonly namaProviderUtils: NamaProviderUtils,
) {}
private get baseUrl() {
return this.sandboxBaseUrl
// return process.env.NODE_ENV === 'production'
// ? this.productionBaseUrl
// : this.sandboxBaseUrl
}
private buildSendUrl() {
return `${this.baseUrl}${this.sendPath}`
}
private buildGetUrl(invoiceId: string) {
return `${this.baseUrl}${this.getPath}/${invoiceId}`
}
private createRequestInterceptors(token: string): FetchRequestInterceptor[] {
return [
createHeaderRequestInterceptor({
'Content-Type': 'application/json',
Authorization: token,
}),
createEnsureSuccessResponseInterceptor(),
{
onError: (context, error) => {
this.logger.error(`NAMA request failed: ${context.url}`, error)
return Promise.reject(error)
},
},
]
}
async originalSend(
payload: TspProviderOriginalSendPayloadDto,
2026-05-27 22:44:01 +03:30
): Promise<TspProviderOriginalResponseDto> {
const mappedRequest = this.namaProviderUtils.mapToNamaOriginalRequestDto(payload)
try {
const response = await this.httpClient.request(
this.buildSendUrl(),
{
method: 'POST',
body: JSON.stringify(mappedRequest),
},
this.createRequestInterceptors(payload.token),
)
const providerResponse: NamaProviderOriginalResponseDto = await response.json()
2026-05-27 22:44:01 +03:30
const result: TspProviderOriginalResponseDto = {
invoice_id: payload.id,
provider_request_payload: JSON.parse(JSON.stringify(mappedRequest)),
hasError: !response.ok,
message: providerResponse.message,
received_at: new Date().toISOString(),
provider_response: JSON.parse(JSON.stringify(providerResponse)),
tax_id: providerResponse.tax_id,
error_message: providerResponse.error_message,
fiscal_warnings: providerResponse.tax_api_response,
validation_errors: providerResponse.validation_error,
2026-05-04 11:21:49 +03:30
status: this.namaProviderUtils.mapResponseStatus(
providerResponse.status as NamaProviderResponseStatus,
),
}
return result
} catch (err) {
console.log('err@@@@@@@@@@@@@@@@@@', err);
console.log(JSON.parse(JSON.stringify(mappedRequest)));
2026-05-27 22:44:01 +03:30
const failure: TspProviderOriginalResponseDto = {
invoice_id: payload.id,
provider_request_payload: JSON.parse(JSON.stringify(mappedRequest)),
hasError: true,
message: (err as Error).message,
received_at: new Date().toISOString(),
status: TspProviderResponseStatus.SEND_FAILURE,
}
return failure
}
}
async correctionSend(
payload: TspProviderCorrectionSendPayloadDto,
): Promise<TspProviderCorrectionSendResponseDto> {
const mappedRequest = this.namaProviderUtils.mapToNamaOriginalRequestDto(payload)
try {
const response = await this.httpClient.request(
this.buildSendUrl(),
{
method: 'POST',
body: JSON.stringify(mappedRequest),
},
this.createRequestInterceptors(payload.token),
)
const providerResponse: NamaProviderOriginalResponseDto = await response.json()
this.logger.debug('NAMA provider response', providerResponse)
2026-05-27 22:44:01 +03:30
const result: TspProviderOriginalResponseDto = {
invoice_id: payload.id,
provider_request_payload: JSON.parse(JSON.stringify(mappedRequest)),
hasError: !response.ok,
message: providerResponse.message,
provider_response: JSON.parse(JSON.stringify(providerResponse)),
received_at: new Date().toISOString(),
tax_id: providerResponse.tax_id,
status: this.namaProviderUtils.mapResponseStatus(
providerResponse.status as NamaProviderResponseStatus,
),
}
return result
} catch (err) {
this.logger.error('NAMA send failed', err)
2026-05-27 22:44:01 +03:30
const failure: TspProviderOriginalResponseDto = {
invoice_id: payload.id,
provider_request_payload: JSON.parse(JSON.stringify(mappedRequest)),
hasError: true,
message: (err as Error).message,
received_at: new Date().toISOString(),
tax_id: null,
status: TspProviderResponseStatus.NOT_SEND,
}
return failure
}
}
async sendBulk(
payloads: TspProviderOriginalSendPayloadDto[],
): Promise<TspProviderBulkSendResultDto[]> {
const result: TspProviderBulkSendResultDto[] = []
for (const payload of payloads) {
const itemResults = await this.originalSend(payload)
result.push({
invoice_id: payload.id,
items: [itemResults],
})
}
return result
}
2026-05-27 22:44:01 +03:30
async get(invoiceId: string, token: string): Promise<TspProviderGetResponseDto> {
try {
const response = await this.httpClient.request(
this.buildGetUrl(invoiceId),
{
method: 'GET',
},
2026-05-27 22:44:01 +03:30
this.createRequestInterceptors(token),
)
const providerResponse: NamaProviderGetResponseDto = await response.json()
this.logger.debug('NAMA provider response', providerResponse)
2026-05-27 22:44:01 +03:30
const result: TspProviderGetResponseDto = {
hasError: !response.ok,
2026-05-27 22:44:01 +03:30
provider_response: JSON.parse(JSON.stringify(providerResponse)),
message: providerResponse.message,
received_at: new Date().toISOString(),
2026-05-27 22:44:01 +03:30
error_message: providerResponse.error_message,
fiscal_warnings: providerResponse.tax_api_response,
validation_errors: providerResponse.validation_error,
2026-05-04 11:21:49 +03:30
status: this.namaProviderUtils.mapResponseStatus(
providerResponse.status as NamaProviderResponseStatus,
),
}
return result
} catch (err) {
this.logger.error('NAMA send failed', err)
2026-05-27 22:44:01 +03:30
const failure: TspProviderGetResponseDto = {
hasError: true,
message: (err as Error).message,
received_at: new Date().toISOString(),
}
return failure
}
}
2026-05-04 11:21:49 +03:30
async revoke(
payload: TspProviderRevokePayloadDto,
): Promise<TspProviderRevokeResponseDto> {
const mappedRequest: NamaProviderRevokeRequestDto =
this.namaProviderUtils.mapToNamaRevokeRequestDto(payload)
2026-05-05 10:14:48 +03:30
this.logger.debug('NAMA provider response', mappedRequest)
2026-05-04 11:21:49 +03:30
try {
const response = await this.httpClient.request(
this.buildSendUrl(),
{
method: 'POST',
body: JSON.stringify(mappedRequest),
},
this.createRequestInterceptors(payload.tsp_token),
)
const providerResponse: NamaProviderRevokeResponseDto = await response.json()
this.logger.debug('NAMA provider response', providerResponse)
2026-05-04 11:21:49 +03:30
const result: TspProviderRevokeResponseDto = {
invoice_id: payload.invoice_id,
provider_request_payload: JSON.parse(JSON.stringify(mappedRequest)),
2026-05-04 11:21:49 +03:30
hasError: !response.ok,
message: providerResponse.message,
sent_at: new Date().toISOString(),
provider_response_payload: providerResponse,
received_at: new Date().toISOString(),
2026-05-04 11:21:49 +03:30
tax_id: providerResponse.tax_id,
status: this.namaProviderUtils.mapResponseStatus(
providerResponse.status as NamaProviderResponseStatus,
),
}
2026-05-04 11:21:49 +03:30
return result
} catch (err) {
this.logger.error('NAMA Revoke failed', err)
const failure: TspProviderRevokeResponseDto = {
invoice_id: payload.invoice_id,
provider_request_payload: JSON.parse(JSON.stringify(mappedRequest)),
2026-05-04 11:21:49 +03:30
hasError: true,
message: (err as Error).message,
sent_at: new Date().toISOString(),
received_at: new Date().toISOString(),
tax_id: '',
status: TspProviderResponseStatus.FAILURE,
}
return failure
}
}
}