Skip to content

Commit

Permalink
feat(medusa/payement-paytr): Improve tests and logic
Browse files Browse the repository at this point in the history
  • Loading branch information
adrien2p committed Apr 6, 2022
1 parent 4bafed9 commit 59cea45
Show file tree
Hide file tree
Showing 5 changed files with 58 additions and 32 deletions.
3 changes: 3 additions & 0 deletions packages/medusa/payment-paytr/src/__mock__/cart.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ export const cartMockData = {
quantity: 10,
},
],
payment_session: {
status: 'pending',
},
metadata: {
ip: 'XX.XXX.XXX.XX',
},
Expand Down
22 changes: 8 additions & 14 deletions packages/medusa/payment-paytr/src/services/paytr-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import {
buildOid,
buildPaymentToken,
buildPaytrToken,
findPendingPaymentSession,
getCartIdFromOid,
request,
} from '../utils';
Expand Down Expand Up @@ -52,9 +51,11 @@ export default class PayTRProviderService extends PaymentService {
async generateToken(cartId: string): Promise<string | never> {
const cart = await this.retrieveCart(cartId);
const amount = await this.#totalsService.getTotal(cart);
const region = await this.#regionService.retrieve(cart.region_id, { relations: ['currency']});
const region = await this.#regionService.retrieve(cart.region_id, { relations: ['currency'] });
if (region?.currency?.symbol.toLowerCase() !== 'tl') {
throw new Error('Unable to use the payTr payment provider with currency: ' + region.currency.symbol + '. Expected TL');
throw new Error(
'Unable to use the payTr payment provider with currency: ' + region.currency.symbol + '. Expected TL'
);
}

const formattedItems = cart.items.map((item) => [item.title, item.unit_price, item.quantity.toString()]);
Expand Down Expand Up @@ -101,7 +102,7 @@ export default class PayTRProviderService extends PaymentService {

async createPayment(cart: Cart): Promise<PaymentSessionData> {
const merchantOid = buildOid(cart.id.split('_').pop());
return { merchantOid, isPending: true };
return { merchantOid };
}

async getStatus(data: PaymentData): Promise<PaymentSessionStatus> {
Expand Down Expand Up @@ -186,20 +187,13 @@ export default class PayTRProviderService extends PaymentService {

const cartId = getCartIdFromOid(merchant_oid);
const cart = await this.retrieveCart(cartId);
const pendingPaymentSession = findPendingPaymentSession(cart.payment_sessions, {
merchantOid: merchant_oid,
});
if (!pendingPaymentSession) {
if (!cart.payment_session) {
throw new Error('Unable to complete payment session. The payment session was not found.');
}

const paymentSessionRepo = this.#manager.getCustomRepository(this.#paymentSessionRepository);
pendingPaymentSession.data = {
...pendingPaymentSession.data,
isPending: !(status === 'success'),
status,
};
await paymentSessionRepo.save(pendingPaymentSession);
cart.payment_session.status = PaymentSessionStatus.AUTHORIZED;
await paymentSessionRepo.save(cart.payment_session);
let order = await this.#orderService.retrieveByCartId(cartId);
if (!order) {
order = await this.#orderService.createFromCart(cartId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ import PayTRProviderService from '../paytr-provider';
import { CustomerServiceMock } from '../../__mock__/customer';
import { TotalsServiceMock } from '../../__mock__/totals';
import { cartMockData, CartServiceMock } from '../../__mock__/cart';
import { MerchantConfig } from "../../types";
import { MerchantConfig } from '../../types';
import { Cart } from '@medusajs/medusa/dist';
import { buildOid, buildPaytrToken, getCartIdFromOid } from '../../utils';
import { PaymentSessionStatus } from '@medusajs/medusa/dist/models/payment-session';

const merchantConfig: MerchantConfig = {
token_endpoint: process.env.TOKEN_ENDPOINT,
Expand All @@ -29,13 +32,19 @@ const merchantConfig: MerchantConfig = {
};

const RegionServiceMock = {
retrieve: jest.fn().mockReturnValue(Promise.resolve({ currency_code: 'TL', currency: { symbol: 'TL' }})),
retrieve: jest.fn().mockReturnValue(Promise.resolve({ currency_code: 'TL', currency: { symbol: 'TL' } })),
};

const OrderServiceMock = {
retrieveByCartId: jest.fn().mockReturnValue(Promise.resolve({ id: 'or_dzlkfzengzelkgvnz' })),
retrieveByCartId: jest.fn().mockReturnValue(Promise.resolve()),
createFromCart: jest.fn().mockReturnValue(Promise.resolve({ id: 'or_dzlkfzengzelkgvnz' })),
capturePayment: jest.fn().mockReturnValue(Promise.resolve()),
};

const PaymentMockRepository = MockRepository({
save: jest.fn().mockReturnValue(Promise.resolve()),
});

describe('PayTrProvider', () => {
let provider: PayTRProviderService;

Expand All @@ -44,7 +53,7 @@ describe('PayTrProvider', () => {
provider = new PayTRProviderService(
{
manager: MockManager,
paymentSessionRepository: MockRepository,
paymentSessionRepository: PaymentMockRepository,
cartService: CartServiceMock,
customerService: CustomerServiceMock,
regionService: RegionServiceMock,
Expand All @@ -60,4 +69,37 @@ describe('PayTrProvider', () => {
const token = await provider.generateToken(cartMockData.id);
expect(token).toBeDefined();
});

it('should return the corresponding data on create payment', async () => {
const data = await provider.createPayment(cartMockData as Cart);
expect(data.merchantOid).toBe(buildOid(cartMockData.id.split('_').pop()));
});

it('should return the appropriate status', async () => {
let status = await provider.getStatus({ status: null });
expect(status).toBe(PaymentSessionStatus.PENDING);

status = await provider.getStatus({ status: 'success' });
expect(status).toBe(PaymentSessionStatus.AUTHORIZED);

status = await provider.getStatus({ status: 'rejected' });
expect(status).toBe(PaymentSessionStatus.ERROR);
});

it('should create an order on success callback', async () => {
const merchantOid = buildOid(cartMockData.id.split('_').pop());
const successCallbackData = {
merchant_oid: merchantOid,
status: 'success' as 'success' | 'error',
total_amount: 100,
hash: buildPaytrToken(merchantOid + merchantConfig.merchant_salt + 'success' + 100, {
merchant_key: merchantConfig.merchant_key,
}),
};
await provider.handleCallback(successCallbackData);
const expectedCartId = getCartIdFromOid(merchantOid);
expect(OrderServiceMock.retrieveByCartId).toHaveBeenCalledWith(expectedCartId);
expect(OrderServiceMock.createFromCart).toHaveBeenCalledWith(expectedCartId);
expect(OrderServiceMock.capturePayment).toHaveBeenCalledWith('or_dzlkfzengzelkgvnz');
});
});
1 change: 0 additions & 1 deletion packages/medusa/payment-paytr/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ export type PayTrResponse<TStatus = 'success' | 'failed'> = {
};

export type PaymentSessionData = {
isPending: boolean;
merchantOid: string;
};

Expand Down
14 changes: 1 addition & 13 deletions packages/medusa/payment-paytr/src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as crypto from 'crypto';
import { Cart, PaymentSession } from '@medusajs/medusa/dist';
import { Cart } from '@medusajs/medusa/dist';
import * as FormData from 'form-data';
import { IncomingMessage } from 'http';
import { MerchantConfig, PayTrResponse } from './types';
Expand Down Expand Up @@ -86,18 +86,6 @@ export function convertIpToIpv4(ip: string): string {
return result;
}

export function findPendingPaymentSession(
paymentsSessions: PaymentSession[],
{ merchantOid }: { merchantOid: string }
): PaymentSession {
return paymentsSessions.find(
(session) =>
session.provider_id === 'paytr' &&
session.data.merchantOid === merchantOid &&
session.data.isPending === true
);
}

export function request(endpoint: string, data: Record<string, unknown>): Promise<unknown> {
const formData = new FormData();
Object.entries(data).forEach(([key, value]) => {
Expand Down

0 comments on commit 59cea45

Please sign in to comment.