Skip to content

Commit

Permalink
wip: save point
Browse files Browse the repository at this point in the history
  • Loading branch information
johnsonogwuru committed Feb 23, 2024
1 parent d4092be commit 4fc438e
Show file tree
Hide file tree
Showing 6 changed files with 126 additions and 34 deletions.
1 change: 1 addition & 0 deletions processor/src/dtos/operations/payment-intents.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ const PaymentModificationSchema = Type.Enum(PaymentModificationStatus);

export const PaymentIntentResponseSchema = Type.Object({
outcome: PaymentModificationSchema,
paymentReference: Type.String(),
});

export type PaymentIntentRequestSchemaDTO = Static<typeof PaymentIntentRequestSchema>;
Expand Down
8 changes: 8 additions & 0 deletions processor/src/dtos/paypal-payment.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,13 @@ export const PaymentRequestSchema = Type.Object({
paymentMethod: Type.Composite([PaypalPaymentMethodSchema]),
});

export const PaymentConfirmationRequestSchema = Type.Object({
details: Type.Object({
pspReference: Type.String(),
paymentReference: Type.String(),
}),
});

export enum PaymentOutcome {
AUTHORIZED = 'Authorized',
REJECTED = 'Rejected',
Expand All @@ -23,4 +30,5 @@ export const PaymentResponseSchema = Type.Object({
});

export type PaymentRequestSchemaDTO = Static<typeof PaymentRequestSchema>;
export type PaymentConfirmRequestSchemaDTO = Static<typeof PaymentConfirmationRequestSchema>;
export type PaymentResponseSchemaDTO = Static<typeof PaymentResponseSchema>;
13 changes: 7 additions & 6 deletions processor/src/routes/paypal-payment.route.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
import { SessionAuthenticationHook } from '@commercetools/connect-payments-sdk';
import { FastifyInstance, FastifyPluginOptions } from 'fastify';
import {
PaymentConfirmRequestSchemaDTO,
PaymentConfirmationRequestSchema,
PaymentRequestSchema,
PaymentRequestSchemaDTO,
PaymentResponseSchema,
PaymentResponseSchemaDTO,
} from '../dtos/paypal-payment.dto';
import { PaypalPaymentService } from '../services/paypal-payment.service';
import { PaymentIntentResponseSchemaDTO, PaymentIntentResponseSchema } from '../dtos/operations/payment-intents.dto';

type PaymentRoutesOptions = {
paymentService: PaypalPaymentService;
sessionAuthHook: SessionAuthenticationHook;
};

// TODO: this would contain the endpoints for creating and confirming a payment intent. IN our case, /payment-intent and /payment-intent/confirm
export const paymentRoutes = async (fastify: FastifyInstance, opts: FastifyPluginOptions & PaymentRoutesOptions) => {
fastify.post<{ Body: PaymentRequestSchemaDTO; Reply: PaymentResponseSchemaDTO }>(
'/payments',
Expand All @@ -35,20 +37,19 @@ export const paymentRoutes = async (fastify: FastifyInstance, opts: FastifyPlugi
},
);

// TODO: implement this, but we can test it without implementing the enabler. To test lets log the paypal response after creating an order, and use the link to approve the payment
fastify.post<{ Body: PaymentRequestSchemaDTO; Reply: PaymentResponseSchemaDTO }>(
fastify.post<{ Body: PaymentConfirmRequestSchemaDTO; Reply: PaymentIntentResponseSchemaDTO }>(
'/payments/confirm',
{
preHandler: [opts.sessionAuthHook.authenticate()],
schema: {
body: PaymentRequestSchema,
body: PaymentConfirmationRequestSchema,
response: {
200: PaymentResponseSchema,
200: PaymentIntentResponseSchema,
},
},
},
async (request, reply) => {
const resp = await opts.paymentService.createPayment({
const resp = await opts.paymentService.confirmPayment({
data: request.body,
});

Expand Down
46 changes: 31 additions & 15 deletions processor/src/services/operation.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export class DefaultOperationService implements OperationService {

const transactionType = this.getPaymentTransactionType(request.action);

const updatedPayment = await this.ctPaymentService.updatePayment({
let updatedPayment = await this.ctPaymentService.updatePayment({
id: ctPayment.id,
transaction: {
type: transactionType,
Expand All @@ -68,22 +68,38 @@ export class DefaultOperationService implements OperationService {
},
});

const res = await this.processPaymentModification(updatedPayment, transactionType, requestAmount);
// TODO: use the methods within the ctPaymentService, to validate the payment modification requests.
// this.ctPaymentService.validatePaymentCharge() ==> example

// TODO: if we throw an error in any of the calls within processPaymentModification, we never get to update this transsaction state to Failure below
await this.ctPaymentService.updatePayment({
id: ctPayment.id,
transaction: {
type: transactionType,
amount: requestAmount,
interactionId: res?.pspReference,
state: res.outcome === PaymentModificationStatus.APPROVED ? 'Success' : 'Failure',
},
});
try {
const res = await this.processPaymentModification(updatedPayment, transactionType, requestAmount);

return {
outcome: res.outcome,
};
updatedPayment = await this.ctPaymentService.updatePayment({
id: ctPayment.id,
transaction: {
type: transactionType,
amount: requestAmount,
interactionId: res?.pspReference,
state: res.outcome === PaymentModificationStatus.APPROVED ? 'Success' : 'Failure',
},
});

return {
outcome: res.outcome,
paymentReference: updatedPayment.id,
};
} catch (e) {
await this.ctPaymentService.updatePayment({
id: ctPayment.id,
transaction: {
type: transactionType,
amount: requestAmount,
state: 'Failure',
},
});

throw e;
}
}

private getPaymentTransactionType(action: string): string {
Expand Down
86 changes: 74 additions & 12 deletions processor/src/services/paypal-payment.service.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
import { CommercetoolsCartService, CommercetoolsPaymentService } from '@commercetools/connect-payments-sdk';
import { PaymentOutcome, PaymentResponseSchemaDTO } from '../dtos/paypal-payment.dto';
import {
CommercetoolsCartService,
CommercetoolsPaymentService,
ErrorGeneral,
} from '@commercetools/connect-payments-sdk';
import { PaymentConfirmRequestSchemaDTO, PaymentOutcome, PaymentResponseSchemaDTO } from '../dtos/paypal-payment.dto';

import { getCartIdFromContext } from '../libs/fastify/context/context';
import { CreatePayment } from './types/paypal-payment.type';
import { ConfirmPayment, CreatePayment } from './types/paypal-payment.type';
import { PaypalPaymentAPI } from './api/api';
import { Address, Cart, Money } from '@commercetools/platform-sdk';
import { Address, Cart, Money, Payment } from '@commercetools/platform-sdk';
import { CreateOrderRequest, PaypalShipping, parseAmount } from './types/paypal-api.type';
import { PaymentModificationStatus } from '../dtos/operations/payment-intents.dto';
import { PaymentIntentResponseSchemaDTO, PaymentModificationStatus } from '../dtos/operations/payment-intents.dto';
import { randomUUID } from 'crypto';

export type PaypalPaymentServiceOptions = {
Expand Down Expand Up @@ -69,18 +73,14 @@ export class PaypalPaymentService {

const resultCode = isAuthorized ? PaymentOutcome.AUTHORIZED : PaymentOutcome.REJECTED;

const pspReference = paypalResponse.pspReference;

const paymentMethodType = paymentMethod.type;

const updatedPayment = await this.ctPaymentService.updatePayment({
id: ctPayment.id,
pspReference: pspReference,
paymentMethod: paymentMethodType,
pspReference: paypalResponse.pspReference,
paymentMethod: paymentMethod.type,
transaction: {
type: 'Authorization',
amount: ctPayment.amountPlanned,
interactionId: pspReference,
interactionId: paypalResponse.pspReference,
state: this.convertPaymentResultCode(resultCode as PaymentOutcome),
},
});
Expand All @@ -91,6 +91,68 @@ export class PaypalPaymentService {
};
}

public async confirmPayment(opts: ConfirmPayment): Promise<PaymentIntentResponseSchemaDTO> {
const ctPayment = await this.ctPaymentService.getPayment({
id: opts.data.details.paymentReference,
});

this.validateInterfaceIdMismatch(ctPayment, opts.data);

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

try {
// Make call to paypal to create payment intent
const paypalResponse = await this.paypalClient.captureOrder(opts.data.details.pspReference);

updatedPayment = await this.ctPaymentService.updatePayment({
id: ctPayment.id,
transaction: {
type: 'Charge',
amount: ctPayment.amountPlanned,
interactionId: paypalResponse.pspReference,
state: paypalResponse.outcome === PaymentModificationStatus.APPROVED ? 'Success' : 'Failure',
},
});

return {
outcome: paypalResponse.outcome,
paymentReference: updatedPayment.id,
};
} catch (e) {
// TODO: create a new method in payment sdk for changing transaction stat. To be used in scenarios, where we expect the txn state to change,
// from initial, to success to failure https://docs.commercetools.com/api/projects/payments#change-transactionstate
await this.ctPaymentService.updatePayment({
id: ctPayment.id,
transaction: {
type: 'Charge',
amount: ctPayment.amountPlanned,
state: 'Failure',
},
});

throw e;
}
}

private validateInterfaceIdMismatch(payment: Payment, data: PaymentConfirmRequestSchemaDTO) {
if (payment.interfaceId !== data.details?.pspReference) {
throw new ErrorGeneral('not able to confirm the payment', {
fields: {
cocoError: 'interface id mismatch',
pspReference: data.details?.pspReference,
paymentReference: payment.id,
},
});
}
}

private convertPaymentResultCode(resultCode: PaymentOutcome): string {
switch (resultCode) {
case PaymentOutcome.AUTHORIZED:
Expand Down
6 changes: 5 additions & 1 deletion processor/src/services/types/paypal-payment.type.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import { PaymentOutcome, PaymentRequestSchemaDTO } from '../../dtos/paypal-payment.dto';
import { PaymentConfirmRequestSchemaDTO, PaymentOutcome, PaymentRequestSchemaDTO } from '../../dtos/paypal-payment.dto';

export type CreatePayment = {
data: PaymentRequestSchemaDTO;
};

export type ConfirmPayment = {
data: PaymentConfirmRequestSchemaDTO;
};

export type CreatePaymentRequest = {
data: PaymentRequestSchemaDTO;
};
Expand Down

0 comments on commit 4fc438e

Please sign in to comment.