fix: resolved conflicts

This commit is contained in:
Brian Pooe 2023-06-16 14:02:00 +02:00
commit 1f5699b1cf
9 changed files with 374 additions and 28 deletions

View file

@ -0,0 +1 @@
export type RequiredProperties<T, K extends keyof T> = T & Required<Pick<T, K>>;

View file

@ -5,11 +5,11 @@ export interface PsChargeTransactionRequestModel {
email: string;
authorization_code: string;
reference?: string;
currency: string;
currency?: string;
metadata?: string;
channels?: Array<PsChannelsModel>;
subaccount: string;
transaction_charge: number;
bearer: string;
queue: boolean;
subaccount?: string;
transaction_charge?: number;
bearer?: string;
queue?: boolean;
}

View file

@ -1,4 +1,6 @@
export interface PsExportTransactionResponseModel {
import { PsBaseModel } from './ps-base.model';
export interface PsExportTransactionResponseModel extends PsBaseModel {
data: PsExportTransactionResponseDataModel;
}

View file

@ -1,6 +1,6 @@
import { PsAuthorizationModel, PsCustomerModel } from './';
import { PsAuthorizationModel, PsBaseModel, PsCustomerModel } from './';
export interface PsPartialDebitResponseModel {
export interface PsPartialDebitResponseModel extends PsBaseModel {
data: PsPartialDebitResponseDataModel;
}

View file

@ -1,4 +1,6 @@
export interface PsTransactionTotalsResponseModel {
import { PsBaseModel } from './ps-base.model';
export interface PsTransactionTotalsResponseModel extends PsBaseModel {
data: PsTransactionTotalsResponseDataModel;
}

View file

@ -2,14 +2,26 @@ import { PsTransactionsService } from './ps-transactions.service';
import { HttpService } from '@nestjs/axios';
import { AxiosResponse } from 'axios';
import {
PsChargeTransactionRequestModel,
PsChargeTransactionResponseModel,
PsExportTransactionRequestModel,
PsExportTransactionResponseModel,
PsFetchTransactionResponseModel,
PsInitializeTransactionRequestModel,
PsInitializeTransactionResponseModel
PsInitializeTransactionResponseModel,
PsListTransactionsQueryParamsModel,
PsListTransactionsResponseModel,
PsPartialDebitRequestModel,
PsPartialDebitResponseModel,
PsTransactionTotalsRequestModel,
PsTransactionTotalsResponseModel,
PsVerifyTransactionResponseModel,
PsViewTransactionTimeLineResponseModel
} from '../../models';
import { fromExact, fromPartial } from '@total-typescript/shoehorn';
import { of } from 'rxjs';
import { expectObservable } from '../../helpers/unit-test/unit-test.util';
import { TestBed } from '@automock/jest';
import DoneCallback = jest.DoneCallback;
import { subscribeSpyTo } from '@hirez_io/observer-spy';
describe(PsTransactionsService.name, () => {
let service: PsTransactionsService;
@ -30,7 +42,8 @@ describe(PsTransactionsService.name, () => {
});
describe('initializeTransaction', () => {
it('should return initialize transaction response', (done: DoneCallback) => {
it('should return initialize transaction response', () => {
// Arrange
const input: PsInitializeTransactionRequestModel = {
amount: '20',
email: 'test@gmail.com'
@ -38,6 +51,8 @@ describe(PsTransactionsService.name, () => {
const response: AxiosResponse<PsInitializeTransactionResponseModel> =
fromPartial({
data: fromPartial({
status: true,
message: 'Authorization URL created',
data: fromExact({
authorization_url: 'https://checkout.paystack.com/0peioxfhpn',
access_code: '0peioxfhpn',
@ -47,15 +62,307 @@ describe(PsTransactionsService.name, () => {
});
httpService.post.mockReturnValueOnce(of(response));
expectObservable(
{
actualObservable: service.initializeTransaction(input),
done
},
(resp) => {
expect(resp).toEqual(response.data);
}
// Act
const observerSpy = subscribeSpyTo(service.initializeTransaction(input));
// Assert
expect(observerSpy.getLastValue()).toEqual(response.data);
});
});
describe('verifyTransaction', () => {
it('should return confirmation of the status of a transaction', () => {
// Arrange
const input = 'l5b6sesmko';
const response: AxiosResponse<PsVerifyTransactionResponseModel> =
fromPartial({
data: {
status: true,
message: 'Verification successful',
data: fromPartial({
id: 2009945086,
status: 'success',
reference: 'rd0bz6z2wu',
amount: 2000,
gateway_response: 'Successful'
})
}
});
httpService.get.mockReturnValueOnce(of(response));
// Act
const observerSpy = subscribeSpyTo(service.verifyTransaction(input));
//Assert
expect(observerSpy.getLastValue()).toEqual(response.data);
});
});
describe('listTransactions', () => {
it('should return list transactions carried out', () => {
// Arrange
const input: PsListTransactionsQueryParamsModel = {
page: 1,
perPage: 5
};
const response: AxiosResponse<PsListTransactionsResponseModel> =
fromPartial({
data: {
status: true,
message: 'Transactions retrieved',
data: [
fromPartial({
id: 2009945086,
status: 'success',
reference: 'rd0bz6z2wu',
amount: 2000,
gateway_response: 'Successful'
}),
fromPartial({
id: 2009945086,
status: 'failed',
reference: 'Declined',
amount: 2000,
gateway_response: 'Successful'
})
]
}
});
httpService.get.mockReturnValueOnce(of(response));
// Act
const observerSpy = subscribeSpyTo(service.listTransactions(input));
//Assert
expect(observerSpy.getLastValue()).toEqual(response.data);
});
});
describe('fetchTransaction', () => {
it('should return details of a transaction carried out', () => {
// Arrange
const input = 2836566657;
const response: AxiosResponse<PsFetchTransactionResponseModel> =
fromPartial({
data: {
status: true,
message: 'Transaction retrieved',
data: fromPartial({
id: 2836566657,
status: 'success',
reference: '203520101',
amount: 2000,
gateway_response: 'Successful'
})
}
});
httpService.get.mockReturnValueOnce(of(response));
// Act
const observerSpy = subscribeSpyTo(service.fetchTransaction(input));
//Assert
expect(observerSpy.getLastValue()).toEqual(response.data);
});
});
describe('chargeTransaction', () => {
it('should return charge transaction response', () => {
// Arrange
const input: PsChargeTransactionRequestModel = {
amount: '2000',
email: 'customer@email.com',
authorization_code: 'AUTH_72btv547'
};
const response: AxiosResponse<PsChargeTransactionResponseModel> =
fromPartial({
data: {
status: true,
message: 'Charge attempted',
data: fromPartial({
amount: 2000,
status: 'success',
reference: 'cn65lf4ixmkzvda',
gateway_response: 'Approved',
channel: 'card',
transaction_date: '2020-05-27T11:45:03.000Z'
})
}
});
httpService.post.mockReturnValueOnce(of(response));
// Act
const observerSpy = subscribeSpyTo(service.chargeTransaction(input));
//Assert
expect(observerSpy.getLastValue()).toEqual(response.data);
});
});
describe('viewTransactionTimeline', () => {
it('should return view transaction timeline of a transaction', () => {
// Arrange
const input = 'cn65lf4ixmkzvda';
const response: AxiosResponse<PsViewTransactionTimeLineResponseModel> =
fromPartial({
data: {
status: true,
message: 'Timeline retrieved',
data: fromPartial({
time_spent: 9061,
attempts: 1,
success: false,
errors: 1,
channel: 'card',
history: [
{
type: 'open',
message: 'Opened payment page',
time: 1
},
{
type: 'input',
message:
'Filled these fields: card number, card expiry, card cvc',
time: 39
},
{
type: 'action',
message: 'Attempted to pay',
time: 39
},
{
type: 'error',
message: 'Error: Declined',
time: 48
},
{
type: 'close',
message: 'Page closed',
time: 9061
}
]
})
}
});
httpService.get.mockReturnValueOnce(of(response));
// Act
const observerSpy = subscribeSpyTo(
service.viewTransactionTimeline(input)
);
//Assert
expect(observerSpy.getLastValue()).toEqual(response.data);
});
});
describe('transactionTotals', () => {
it('should return total amount received on your account', () => {
// Arrange
const input: PsTransactionTotalsRequestModel = {
page: 1,
perPage: 5
};
const response: AxiosResponse<PsTransactionTotalsResponseModel> =
fromPartial({
data: {
status: true,
message: 'Transaction totals',
data: {
total_transactions: 10,
unique_customers: 3,
total_volume: 14000,
total_volume_by_currency: [
{
currency: 'ZAR',
amount: 14000
}
],
pending_transfers: 24000,
pending_transfers_by_currency: [
{
currency: 'ZAR',
amount: 24000
}
]
}
}
});
httpService.get.mockReturnValueOnce(of(response));
// Act
const observerSpy = subscribeSpyTo(service.transactionTotals(input));
//Assert
expect(observerSpy.getLastValue()).toEqual(response.data);
});
});
describe('exportTransaction', () => {
it('should return an exported list of transactions carried out on your integration', () => {
// Arrange
const input: PsExportTransactionRequestModel = {
page: 1,
perPage: 5
};
const response: AxiosResponse<PsExportTransactionResponseModel> =
fromPartial({
data: {
status: true,
message: 'Export successful',
data: {
path: 'https://files.paystack.co/exports/100032/1460290758207.csv'
}
}
});
httpService.get.mockReturnValueOnce(of(response));
// Act
const observerSpy = subscribeSpyTo(service.exportTransaction(input));
//Assert
expect(observerSpy.getLastValue()).toEqual(response.data);
});
});
describe('partialDebit', () => {
it('should return part of a payment from a customer', () => {
// Arrange
const input: PsPartialDebitRequestModel = {
amount: '2000',
email: 'customer@email.com',
authorization_code: 'AUTH_72btv547',
currency: 'ZAR'
};
const response: AxiosResponse<PsPartialDebitResponseModel> = fromPartial({
data: {
status: true,
message: 'Charge attempted',
data: fromPartial({
amount: 2000,
status: 'success',
reference: 'cn65lf4ixmkzvda',
gateway_response: 'Approved',
channel: 'card',
transaction_date: '2020-05-27T11:45:03.000Z',
customer: {
id: 37651078,
email: 'customer@email.com',
customer_code: 'CUS_q8vst7djnx3vq6d',
risk_action: 'default',
international_format_phone: null
}
})
}
});
httpService.post.mockReturnValueOnce(of(response));
// Act
const observerSpy = subscribeSpyTo(service.partialDebit(input));
//Assert
expect(observerSpy.getLastValue()).toEqual(response.data);
});
});
});

View file

@ -1,4 +1,4 @@
import { Inject, Injectable } from '@nestjs/common';
import { BadRequestException, Inject, Injectable } from '@nestjs/common';
import {
PsChargeTransactionRequestModel,
PsChargeTransactionResponseModel,
@ -21,6 +21,7 @@ import { HttpService } from '@nestjs/axios';
import { Observable } from 'rxjs';
import { AxiosRequestConfig } from 'axios';
import { handleResponseAndError, MODULE_OPTIONS_TOKEN } from '../../helpers';
import { RequiredProperties } from '../../helpers/type.util.ts/required-properties';
@Injectable()
export class PsTransactionsService {
@ -42,8 +43,14 @@ export class PsTransactionsService {
* @param payload
*/
initializeTransaction(
payload: PsInitializeTransactionRequestModel
payload: RequiredProperties<
PsInitializeTransactionRequestModel,
'amount' | 'email'
>
): Observable<PsInitializeTransactionResponseModel> {
if (!payload?.amount && !payload?.email) {
throw new BadRequestException('amount ');
}
return this.httpService
.post<PsInitializeTransactionResponseModel>(
'transaction/initialize',
@ -73,7 +80,10 @@ export class PsTransactionsService {
* @param queryParamsPayload - query parameters
*/
listTransactions(
queryParamsPayload: PsListTransactionsQueryParamsModel
queryParamsPayload: RequiredProperties<
PsListTransactionsQueryParamsModel,
'perPage' | 'page'
>
): Observable<PsListTransactionsResponseModel> {
return this.httpService
.get<PsListTransactionsResponseModel>('transaction', {
@ -103,7 +113,10 @@ export class PsTransactionsService {
* @param payload
*/
chargeTransaction(
payload: PsChargeTransactionRequestModel
payload: RequiredProperties<
PsChargeTransactionRequestModel,
'amount' | 'email' | 'authorization_code'
>
): Observable<PsChargeTransactionResponseModel> {
return this.httpService
.post<PsInitializeTransactionResponseModel>(
@ -134,7 +147,10 @@ export class PsTransactionsService {
* @param queryParamsPayload
*/
transactionTotals(
queryParamsPayload: PsTransactionTotalsRequestModel
queryParamsPayload: RequiredProperties<
PsTransactionTotalsRequestModel,
'perPage' | 'page'
>
): Observable<PsTransactionTotalsResponseModel> {
return this.httpService
.get<PsTransactionTotalsResponseModel>(`transaction/totals`, {
@ -149,7 +165,10 @@ export class PsTransactionsService {
* @param queryParamsPayload
*/
exportTransaction(
queryParamsPayload: PsExportTransactionRequestModel
queryParamsPayload: RequiredProperties<
PsExportTransactionRequestModel,
'perPage' | 'page'
>
): Observable<PsExportTransactionResponseModel> {
return this.httpService
.get<PsTransactionTotalsResponseModel>(`transaction/export`, {
@ -164,7 +183,10 @@ export class PsTransactionsService {
* @param payload
*/
partialDebit(
payload: PsPartialDebitRequestModel
payload: RequiredProperties<
PsPartialDebitRequestModel,
'authorization_code' | 'currency' | 'amount' | 'email'
>
): Observable<PsPartialDebitResponseModel> {
return this.httpService
.post<PsInitializeTransactionResponseModel>(

11
package-lock.json generated
View file

@ -21,6 +21,7 @@
"devDependencies": {
"@automock/jest": "^1.2.0",
"@golevelup/ts-jest": "^0.3.7",
"@hirez_io/observer-spy": "^2.2.0",
"@nestjs/platform-express": "^9.1.1",
"@nestjs/schematics": "^9.1.0",
"@nestjs/testing": "^9.1.1",
@ -2440,6 +2441,16 @@
"integrity": "sha512-eoRfl9trBEt7BrQ6t3GHY/ZOcDOMBIOgqghd9qkIWMpcqQgWbU5Yf5UeHA6wwnLYpOMIJoKqazJ0cUDM79yr+g==",
"dev": true
},
"node_modules/@hirez_io/observer-spy": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/@hirez_io/observer-spy/-/observer-spy-2.2.0.tgz",
"integrity": "sha512-G9nv87vjRILgB/X1AtKBv1DZX7yXSYAOCXon/f+QULKoXVhVehYUF5Lv0SQ97ebf1sA48Z2CyQ9h2v4Pz6DgaQ==",
"dev": true,
"peerDependencies": {
"rxjs": ">=6.0.0",
"typescript": ">=2.8.1"
}
},
"node_modules/@humanwhocodes/config-array": {
"version": "0.9.5",
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz",

View file

@ -19,6 +19,7 @@
"devDependencies": {
"@automock/jest": "^1.2.0",
"@golevelup/ts-jest": "^0.3.7",
"@hirez_io/observer-spy": "^2.2.0",
"@nestjs/platform-express": "^9.1.1",
"@nestjs/schematics": "^9.1.0",
"@nestjs/testing": "^9.1.1",