Skip to content

Commit

Permalink
Add Payment Modification routes
Browse files Browse the repository at this point in the history
  • Loading branch information
praveenkumarct committed Feb 7, 2024
1 parent 9de2b43 commit 9a48203
Show file tree
Hide file tree
Showing 6 changed files with 267 additions and 5 deletions.
29 changes: 27 additions & 2 deletions processor/src/clients/mockPaymentAPI.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import { v4 as uuid } from 'uuid';
import { CreatePaymentRequest, MockPaymentProviderResponse } from '../services/types/payment.type';
import { PaymentOutcome } from '../dtos/payment.dto';
import {
CreatePaymentRequest, MockPaymentProviderModificationResponse,
MockPaymentProviderResponse
} from '../services/types/payment.type';
import { PaymentModificationStatus, PaymentOutcome } from '../dtos/payment.dto';
import { Payment } from '@commercetools/platform-sdk';

// TODO: Make it class , add interface and provide implementation
export const paymentProviderApi = (): any => {
Expand All @@ -20,9 +24,30 @@ export const paymentProviderApi = (): any => {
};
};

const cancelAuthorisedPaymentByPspReference =
async (pspReference: string, payment: Payment): Promise<MockPaymentProviderModificationResponse> => {

return { outcome: PaymentModificationStatus.RECEIVED, pspReference: pspReference }
}

const captureAuthorisedPayment =
async (pspReference: string, payment: Payment): Promise<MockPaymentProviderModificationResponse> => {

return { outcome: PaymentModificationStatus.RECEIVED, pspReference: pspReference }
}

const refundCapturedPayment =
async (pspReference: string, payment: Payment): Promise<MockPaymentProviderModificationResponse> => {

return { outcome: PaymentModificationStatus.RECEIVED, pspReference: pspReference }
}

const isCreditCardAllowed = (cardNumber: string) => allowedCreditCards.includes(cardNumber);

return {
processPayment,
cancelAuthorisedPaymentByPspReference,
captureAuthorisedPayment,
refundCapturedPayment,
};
};
26 changes: 26 additions & 0 deletions processor/src/dtos/payment.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,31 @@ export const PaymentResponseSchema = Type.Object({
paymentReference: Type.String(),
});

export const CapturePaymentRequestSchema = Type.Object({
amount: Type.Object({
centAmount: Type.Number(),
currencyCode: Type.String(),
}),
});

export const RefundPaymentRequestSchema = Type.Object({
amount: Type.Object({
centAmount: Type.Number(),
currencyCode: Type.String(),
}),
});

export enum PaymentModificationStatus {
RECEIVED = 'received',
}
const PaymentModificationSchema = Type.Enum(PaymentModificationStatus);

export const PaymentModificationResponseSchema = Type.Object({
status: PaymentModificationSchema,
});

export type PaymentRequestSchemaDTO = Static<typeof PaymentRequestSchema>;
export type PaymentResponseSchemaDTO = Static<typeof PaymentResponseSchema>;
export type CapturePaymentRequestDTO = Static<typeof CapturePaymentRequestSchema>;
export type RefundPaymentRequestDTO = Static<typeof RefundPaymentRequestSchema>;
export type PaymentModificationResponseDTO = Static<typeof PaymentModificationResponseSchema>;
53 changes: 53 additions & 0 deletions processor/src/routes/payment-modification.route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { FastifyInstance, FastifyPluginOptions } from 'fastify';
import { PaymentService } from '../services/types/payment.type';
import { CapturePaymentRequestDTO, PaymentModificationResponseDTO, RefundPaymentRequestDTO } from '../dtos/payment.dto';

type PaymentRoutesOptions = {
paymentService: PaymentService;
};

export const paymentModificationRoutes = async (
fastify: FastifyInstance,
opts: FastifyPluginOptions & PaymentRoutesOptions,
) => {
fastify.post<{ Body: any; Reply: PaymentModificationResponseDTO }>(
'/payments/:id/cancels',
{},
async (request, reply) => {
const params = request.params as any;
const resp = await opts.paymentService.cancelPayment({
paymentId: params.id,
});

return reply.status(200).send(resp);
},
);

fastify.post<{ Body: CapturePaymentRequestDTO; Reply: PaymentModificationResponseDTO }>(
'/payments/:id/captures',
{},
async (request, reply) => {
const params = request.params as any;
const resp = await opts.paymentService.capturePayment({
paymentId: params.id,
data: request.body,
});

return reply.status(200).send(resp);
},
);

fastify.post<{ Body: RefundPaymentRequestDTO; Reply: PaymentModificationResponseDTO }>(
'/payments/:id/refunds',
{},
async (request, reply) => {
const params = request.params as any;
const resp = await opts.paymentService.refundPayment({
paymentId: params.id,
data: request.body,
});

return reply.status(200).send(resp);
},
);
};
4 changes: 4 additions & 0 deletions processor/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { configRoutes } from './routes/config.route';
import { paymentRoutes } from './routes/payment.route';
import { statusRoutes } from './routes/status.route';
import { DefaultPaymentService } from './services/payment.service';
import {paymentModificationRoutes} from "./routes/payment-modification.route";

/**
* Setup Fastify server instance
Expand Down Expand Up @@ -54,6 +55,9 @@ export const setupFastify = async () => {
paymentService,
sessionAuthHook: paymentSDK.sessionAuthHookFn,
});
await server.register(paymentModificationRoutes, {
paymentService,
});

return server;
};
128 changes: 126 additions & 2 deletions processor/src/services/payment.service.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,18 @@
import { CommercetoolsCartService, CommercetoolsPaymentService } from '@commercetools/connect-payments-sdk';
import { paymentProviderApi } from '../clients/mockPaymentAPI';
import { CreatePayment, PaymentService, PaymentServiceOptions } from './types/payment.type';
import { PaymentOutcome, PaymentResponseSchemaDTO } from '../dtos/payment.dto';
import {
CancelPayment,
CapturePayment,
CreatePayment,
PaymentService,
PaymentServiceOptions, RefundPayment
} from './types/payment.type';
import {
PaymentModificationResponseDTO,
PaymentModificationStatus,
PaymentOutcome,
PaymentResponseSchemaDTO
} from '../dtos/payment.dto';
import { getCartIdFromContext } from '../libs/fastify/context/context';

export class DefaultPaymentService implements PaymentService {
Expand Down Expand Up @@ -69,6 +80,119 @@ export class DefaultPaymentService implements PaymentService {
};
}

public async cancelPayment(opts: CancelPayment): Promise<PaymentModificationResponseDTO> {
const ctPayment = await this.ctPaymentService.getPayment({
id: opts.paymentId,
});

await this.ctPaymentService.updatePayment({
id: ctPayment.id,
transaction: {
type: 'CancelAuthorization',
amount: ctPayment.amountPlanned,
state: 'Initial',
},
});

const res = await paymentProviderApi().cancelAuthorisedPaymentByPspReference(
ctPayment.interfaceId as string,
ctPayment,
);

await this.ctPaymentService.updatePayment({
id: ctPayment.id,
transaction: {
type: 'CancelAuthorization',
amount: ctPayment.amountPlanned,
interactionId: res.pspReference,
state: 'Pending',
},
});

return {
status: PaymentModificationStatus.RECEIVED,
};
}

public async capturePayment(opts: CapturePayment): Promise<PaymentModificationResponseDTO> {
const ctPayment = await this.ctPaymentService.getPayment({
id: opts.paymentId,
});

const data = { payment: ctPayment, data: opts.data };

await this.ctPaymentService.updatePayment({
id: ctPayment.id,
transaction: {
type: 'Charge',
amount: {
currencyCode: opts.data.amount.currencyCode,
centAmount: opts.data.amount.centAmount,
},
state: 'Initial',
},
});

const res = await paymentProviderApi().captureAuthorisedPayment(ctPayment.interfaceId as string, ctPayment);

await this.ctPaymentService.updatePayment({
id: ctPayment.id,
transaction: {
type: 'Charge',
amount: {
currencyCode: opts.data.amount.currencyCode,
centAmount: opts.data.amount.centAmount,
},
interactionId: res.pspReference,
state: 'Pending',
},
});

return {
status: PaymentModificationStatus.RECEIVED,
};
}

public async refundPayment(opts: RefundPayment): Promise<PaymentModificationResponseDTO> {
const ctPayment = await this.ctPaymentService.getPayment({
id: opts.paymentId,
});

const data = { payment: ctPayment, data: opts.data };

await this.ctPaymentService.updatePayment({
id: ctPayment.id,
transaction: {
type: 'Refund',
amount: {
currencyCode: opts.data.amount.currencyCode,
centAmount: opts.data.amount.centAmount,
},
state: 'Initial',
},
});

const res = await paymentProviderApi().refundCapturedPayment(ctPayment.interfaceId as string, ctPayment);

await this.ctPaymentService.updatePayment({
id: ctPayment.id,
transaction: {
type: 'Refund',
amount: {
currencyCode: opts.data.amount.currencyCode,
centAmount: opts.data.amount.centAmount,
},
interactionId: res.pspReference,
state: 'Pending',
},
});

return {
status: PaymentModificationStatus.RECEIVED,
};
}


private convertPaymentResultCode(resultCode: PaymentOutcome): string {
switch (resultCode) {
case PaymentOutcome.AUTHORIZED:
Expand Down
32 changes: 31 additions & 1 deletion processor/src/services/types/payment.type.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
import { Cart, Payment } from '@commercetools/platform-sdk';
import { CommercetoolsCartService, CommercetoolsPaymentService } from '@commercetools/connect-payments-sdk';
import { PaymentOutcome, PaymentRequestSchemaDTO, PaymentResponseSchemaDTO } from '../../dtos/payment.dto';
import {
CapturePaymentRequestDTO,
PaymentModificationResponseDTO,
PaymentModificationStatus,
PaymentOutcome,
PaymentRequestSchemaDTO,
PaymentResponseSchemaDTO,
RefundPaymentRequestDTO
} from '../../dtos/payment.dto';

export type CreatePayment = {
data: PaymentRequestSchemaDTO;
Expand All @@ -18,8 +26,30 @@ export type MockPaymentProviderResponse = {
paymentMethodType: string;
};

export type MockPaymentProviderModificationResponse = {
outcome: PaymentModificationStatus;
pspReference: string;
};

export type CancelPayment = {
paymentId: string;
};

export type CapturePayment = {
paymentId: string;
data: CapturePaymentRequestDTO;
};

export type RefundPayment = {
paymentId: string;
data: RefundPaymentRequestDTO;
};

export interface PaymentService {
createPayment(opts: CreatePayment): Promise<PaymentResponseSchemaDTO>;
cancelPayment(opts: CancelPayment): Promise<PaymentModificationResponseDTO>;
capturePayment(opts: CapturePayment): Promise<PaymentModificationResponseDTO>;
refundPayment(opts: RefundPayment): Promise<PaymentModificationResponseDTO>;
}

export type PaymentServiceOptions = {
Expand Down

0 comments on commit 9a48203

Please sign in to comment.