mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-09-02 17:23:38 +00:00
fix
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
import { Injectable, Logger } from '@nestjs/common';
|
import { Injectable, Logger } from "@nestjs/common";
|
||||||
import { ConfigService } from '@nestjs/config';
|
import { ConfigService } from "@nestjs/config";
|
||||||
import { HttpService } from '@nestjs/axios';
|
import { HttpService } from "@nestjs/axios";
|
||||||
import {
|
import {
|
||||||
PaymentProvider,
|
PaymentProvider,
|
||||||
ProviderInitiationInput,
|
ProviderInitiationInput,
|
||||||
@@ -8,10 +8,10 @@ import {
|
|||||||
ProviderStatus,
|
ProviderStatus,
|
||||||
ProviderPaymentStatus,
|
ProviderPaymentStatus,
|
||||||
ProviderMethod,
|
ProviderMethod,
|
||||||
} from '@edr/types';
|
} from "@edr/types";
|
||||||
import { AxiosError, AxiosRequestConfig } from 'axios';
|
import { AxiosError, AxiosRequestConfig } from "axios";
|
||||||
import { firstValueFrom } from 'rxjs';
|
import { firstValueFrom } from "rxjs";
|
||||||
import * as crypto from 'node:crypto';
|
import * as crypto from "node:crypto";
|
||||||
|
|
||||||
interface DMoneyAuthResponse {
|
interface DMoneyAuthResponse {
|
||||||
token: string;
|
token: string;
|
||||||
@@ -56,9 +56,11 @@ export class DMoneyProvider implements PaymentProvider {
|
|||||||
constructor(
|
constructor(
|
||||||
private readonly config: ConfigService,
|
private readonly config: ConfigService,
|
||||||
private readonly http: HttpService,
|
private readonly http: HttpService,
|
||||||
) {}
|
) { }
|
||||||
|
|
||||||
async initiate(input: ProviderInitiationInput): Promise<ProviderInitiationResult> {
|
async initiate(
|
||||||
|
input: ProviderInitiationInput,
|
||||||
|
): Promise<ProviderInitiationResult> {
|
||||||
const token = await this.getFabricToken();
|
const token = await this.getFabricToken();
|
||||||
const amount = (input.amountMinor / 100).toFixed(2);
|
const amount = (input.amountMinor / 100).toFixed(2);
|
||||||
const timestamp = new Date().toISOString();
|
const timestamp = new Date().toISOString();
|
||||||
@@ -95,8 +97,11 @@ export class DMoneyProvider implements PaymentProvider {
|
|||||||
return {
|
return {
|
||||||
providerOrderId: response.orderId,
|
providerOrderId: response.orderId,
|
||||||
clientAction: response.checkoutUrl
|
clientAction: response.checkoutUrl
|
||||||
? { type: 'REDIRECT', url: response.checkoutUrl }
|
? { type: "REDIRECT", url: response.checkoutUrl }
|
||||||
: { type: 'REDIRECT', url: `${this.baseUrl}/checkout/${response.orderId}` },
|
: {
|
||||||
|
type: "REDIRECT",
|
||||||
|
url: `${this.baseUrl}/checkout/${response.orderId}`,
|
||||||
|
},
|
||||||
expiresAt,
|
expiresAt,
|
||||||
rawInitiation: {
|
rawInitiation: {
|
||||||
request: this.sanitize(requestBody),
|
request: this.sanitize(requestBody),
|
||||||
@@ -106,7 +111,7 @@ export class DMoneyProvider implements PaymentProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async queryStatus(merchantOrderId: string): Promise<ProviderStatus> {
|
async queryStatus(merchantOrderId: string): Promise<ProviderStatus> {
|
||||||
const token = await this.authenticate();
|
const token = await this.getFabricToken();
|
||||||
const timestamp = new Date().toISOString();
|
const timestamp = new Date().toISOString();
|
||||||
const signature = this.signRequest({
|
const signature = this.signRequest({
|
||||||
merchantId: this.merchantId,
|
merchantId: this.merchantId,
|
||||||
@@ -130,14 +135,15 @@ export class DMoneyProvider implements PaymentProvider {
|
|||||||
return {
|
return {
|
||||||
status: mapped,
|
status: mapped,
|
||||||
providerTxnId: response.transactionId,
|
providerTxnId: response.transactionId,
|
||||||
failureCode: mapped === ProviderPaymentStatus.FAILED ? response.status : undefined,
|
failureCode:
|
||||||
|
mapped === ProviderPaymentStatus.FAILED ? response.status : undefined,
|
||||||
rawResponse: response as unknown as Record<string, unknown>,
|
rawResponse: response as unknown as Record<string, unknown>,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
verifyWebhookSignature(payload: Record<string, unknown>): boolean {
|
verifyWebhookSignature(payload: Record<string, unknown>): boolean {
|
||||||
const { signature, ...data } = payload;
|
const { signature, ...data } = payload;
|
||||||
if (!signature || typeof signature !== 'string') return false;
|
if (!signature || typeof signature !== "string") return false;
|
||||||
|
|
||||||
const expectedSignature = this.signRequest(data);
|
const expectedSignature = this.signRequest(data);
|
||||||
return crypto.timingSafeEqual(
|
return crypto.timingSafeEqual(
|
||||||
@@ -152,17 +158,17 @@ export class DMoneyProvider implements PaymentProvider {
|
|||||||
|
|
||||||
private mapStatus(status: string): ProviderPaymentStatus {
|
private mapStatus(status: string): ProviderPaymentStatus {
|
||||||
switch (status?.toUpperCase()) {
|
switch (status?.toUpperCase()) {
|
||||||
case 'SUCCESS':
|
case "SUCCESS":
|
||||||
case 'COMPLETED':
|
case "COMPLETED":
|
||||||
return ProviderPaymentStatus.SUCCEEDED;
|
return ProviderPaymentStatus.SUCCEEDED;
|
||||||
case 'FAILED':
|
case "FAILED":
|
||||||
case 'REJECTED':
|
case "REJECTED":
|
||||||
case 'EXPIRED':
|
case "EXPIRED":
|
||||||
case 'CANCELLED':
|
case "CANCELLED":
|
||||||
return ProviderPaymentStatus.FAILED;
|
return ProviderPaymentStatus.FAILED;
|
||||||
case 'PENDING':
|
case "PENDING":
|
||||||
return ProviderPaymentStatus.REQUIRES_ACTION;
|
return ProviderPaymentStatus.REQUIRES_ACTION;
|
||||||
case 'PROCESSING':
|
case "PROCESSING":
|
||||||
return ProviderPaymentStatus.PROCESSING;
|
return ProviderPaymentStatus.PROCESSING;
|
||||||
default:
|
default:
|
||||||
return ProviderPaymentStatus.PROCESSING;
|
return ProviderPaymentStatus.PROCESSING;
|
||||||
@@ -178,7 +184,9 @@ export class DMoneyProvider implements PaymentProvider {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (!response.token) {
|
if (!response.token) {
|
||||||
throw new Error(`DMoney authentication failed: ${JSON.stringify(response)}`);
|
throw new Error(
|
||||||
|
`DMoney authentication failed: ${JSON.stringify(response)}`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return response.token;
|
return response.token;
|
||||||
@@ -186,22 +194,24 @@ export class DMoneyProvider implements PaymentProvider {
|
|||||||
|
|
||||||
private signRequest(data: Record<string, unknown>): string {
|
private signRequest(data: Record<string, unknown>): string {
|
||||||
const sortedKeys = Object.keys(data).sort();
|
const sortedKeys = Object.keys(data).sort();
|
||||||
const signString = sortedKeys
|
const signString = sortedKeys.map((key) => `${key}=${data[key]}`).join("&");
|
||||||
.map((key) => `${key}=${data[key]}`)
|
|
||||||
.join('&');
|
|
||||||
|
|
||||||
return crypto
|
return crypto
|
||||||
.createHmac('sha256', this.secretKey)
|
.createHmac("sha256", this.secretKey)
|
||||||
.update(signString)
|
.update(signString)
|
||||||
.digest('hex');
|
.digest("hex");
|
||||||
}
|
}
|
||||||
|
|
||||||
private async postJson<T>(url: string, body: unknown, token?: string): Promise<T> {
|
private async postJson<T>(
|
||||||
|
url: string,
|
||||||
|
body: unknown,
|
||||||
|
token?: string,
|
||||||
|
): Promise<T> {
|
||||||
const headers: Record<string, string> = {
|
const headers: Record<string, string> = {
|
||||||
'Content-Type': 'application/json',
|
"Content-Type": "application/json",
|
||||||
};
|
};
|
||||||
if (token) {
|
if (token) {
|
||||||
headers['Authorization'] = `Bearer ${token}`;
|
headers["Authorization"] = `Bearer ${token}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const config: AxiosRequestConfig = {
|
const config: AxiosRequestConfig = {
|
||||||
@@ -212,7 +222,9 @@ export class DMoneyProvider implements PaymentProvider {
|
|||||||
const started = Date.now();
|
const started = Date.now();
|
||||||
try {
|
try {
|
||||||
const res = await firstValueFrom(this.http.post<T>(url, body, config));
|
const res = await firstValueFrom(this.http.post<T>(url, body, config));
|
||||||
this.logger.debug(`DMoney POST ${url} status=${res.status} latency=${Date.now() - started}ms`);
|
this.logger.debug(
|
||||||
|
`DMoney POST ${url} status=${res.status} latency=${Date.now() - started}ms`,
|
||||||
|
);
|
||||||
return res.data;
|
return res.data;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err instanceof AxiosError) {
|
if (err instanceof AxiosError) {
|
||||||
@@ -220,7 +232,9 @@ export class DMoneyProvider implements PaymentProvider {
|
|||||||
`DMoney POST ${url} failed: status=${err.response?.status} body=${JSON.stringify(err.response?.data)}`,
|
`DMoney POST ${url} failed: status=${err.response?.status} body=${JSON.stringify(err.response?.data)}`,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
this.logger.error(`DMoney POST ${url} threw: ${err instanceof Error ? err.message : err}`);
|
this.logger.error(
|
||||||
|
`DMoney POST ${url} threw: ${err instanceof Error ? err.message : err}`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
@@ -232,21 +246,21 @@ export class DMoneyProvider implements PaymentProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private get baseUrl(): string {
|
private get baseUrl(): string {
|
||||||
return this.config.get<string>('dmoney.baseUrl') ?? '';
|
return this.config.get<string>("dmoney.baseUrl") ?? "";
|
||||||
}
|
}
|
||||||
private get merchantId(): string {
|
private get merchantId(): string {
|
||||||
return this.config.get<string>('dmoney.merchantId') ?? '';
|
return this.config.get<string>("dmoney.merchantId") ?? "";
|
||||||
}
|
}
|
||||||
private get appSecret(): string {
|
private get appSecret(): string {
|
||||||
return this.config.get<string>('dmoney.appSecret') ?? '';
|
return this.config.get<string>("dmoney.appSecret") ?? "";
|
||||||
}
|
}
|
||||||
private get secretKey(): string {
|
private get secretKey(): string {
|
||||||
return this.config.get<string>('dmoney.secretKey') ?? '';
|
return this.config.get<string>("dmoney.secretKey") ?? "";
|
||||||
}
|
}
|
||||||
private get notifyUrl(): string {
|
private get notifyUrl(): string {
|
||||||
return this.config.get<string>('dmoney.notifyUrl') ?? '';
|
return this.config.get<string>("dmoney.notifyUrl") ?? "";
|
||||||
}
|
}
|
||||||
private get returnUrl(): string {
|
private get returnUrl(): string {
|
||||||
return this.config.get<string>('dmoney.returnUrl') ?? '';
|
return this.config.get<string>("dmoney.returnUrl") ?? "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user