diff --git a/lerna.json b/lerna.json index 3e823cc..aaf9e9d 100644 --- a/lerna.json +++ b/lerna.json @@ -1,7 +1,8 @@ { "npmClient": "npm", "packages": [ - "packages/*" + "packages/medusa/*", + "packages/medusa-extender/*" ], "command": { "publish": { @@ -10,8 +11,7 @@ "ignoreChanges": [ "**/*.spec.ts" ], - "message": "chore(release): publish", - "registry": "https://npm.pkg.github.com/" + "message": "chore(release): publish" } }, "version": "independent" diff --git a/package.json b/package.json index c856015..e199cc2 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "medusa-plugins", "version": "1.0.0", - "description": "A collection of awesome plugins for medusa :rocket:", + "description": "A collection of awesome plugins for medusa and medusa-extender :rocket:", "engines": { "node": "14.17.3" }, diff --git a/packages/payments/payment-paytr/README.md b/packages/medusa-extender/.gitignore similarity index 100% rename from packages/payments/payment-paytr/README.md rename to packages/medusa-extender/.gitignore diff --git a/packages/payments/payment-paytr/.gitignore b/packages/medusa/payment-paytr/.gitignore similarity index 100% rename from packages/payments/payment-paytr/.gitignore rename to packages/medusa/payment-paytr/.gitignore diff --git a/packages/payments/payment-paytr/.npmignore b/packages/medusa/payment-paytr/.npmignore similarity index 100% rename from packages/payments/payment-paytr/.npmignore rename to packages/medusa/payment-paytr/.npmignore diff --git a/packages/medusa/payment-paytr/README.md b/packages/medusa/payment-paytr/README.md new file mode 100644 index 0000000..e69de29 diff --git a/packages/payments/payment-paytr/jest.config.js b/packages/medusa/payment-paytr/jest.config.js similarity index 100% rename from packages/payments/payment-paytr/jest.config.js rename to packages/medusa/payment-paytr/jest.config.js diff --git a/packages/payments/payment-paytr/package-lock.json b/packages/medusa/payment-paytr/package-lock.json similarity index 100% rename from packages/payments/payment-paytr/package-lock.json rename to packages/medusa/payment-paytr/package-lock.json diff --git a/packages/payments/payment-paytr/package.json b/packages/medusa/payment-paytr/package.json similarity index 95% rename from packages/payments/payment-paytr/package.json rename to packages/medusa/payment-paytr/package.json index cbc5e65..9001be5 100644 --- a/packages/payments/payment-paytr/package.json +++ b/packages/medusa/payment-paytr/package.json @@ -26,7 +26,7 @@ "author": "Adrien de Peretti ", "license": "MIT", "scripts": { - "clean": "./node_modules/.bin/rimraf services/ api/ utils.* types.* index.*", + "clean": "./node_modules/.bin/rimraf services api/ utils.* types.* index.*", "build": "npm run clean && tsc -p tsconfig.json", "watch": "tsc --watch", "test": "./node_modules/.bin/jest --config jest.config.js" diff --git a/packages/payments/payment-paytr/src/__mock__/cart.ts b/packages/medusa/payment-paytr/src/__mock__/cart.ts similarity index 100% rename from packages/payments/payment-paytr/src/__mock__/cart.ts rename to packages/medusa/payment-paytr/src/__mock__/cart.ts diff --git a/packages/payments/payment-paytr/src/__mock__/customer.ts b/packages/medusa/payment-paytr/src/__mock__/customer.ts similarity index 100% rename from packages/payments/payment-paytr/src/__mock__/customer.ts rename to packages/medusa/payment-paytr/src/__mock__/customer.ts diff --git a/packages/payments/payment-paytr/src/__mock__/eventbus.ts b/packages/medusa/payment-paytr/src/__mock__/eventbus.ts similarity index 100% rename from packages/payments/payment-paytr/src/__mock__/eventbus.ts rename to packages/medusa/payment-paytr/src/__mock__/eventbus.ts diff --git a/packages/payments/payment-paytr/src/__mock__/totals.ts b/packages/medusa/payment-paytr/src/__mock__/totals.ts similarity index 100% rename from packages/payments/payment-paytr/src/__mock__/totals.ts rename to packages/medusa/payment-paytr/src/__mock__/totals.ts diff --git a/packages/payments/payment-paytr/src/index.ts b/packages/medusa/payment-paytr/src/index.ts similarity index 100% rename from packages/payments/payment-paytr/src/index.ts rename to packages/medusa/payment-paytr/src/index.ts diff --git a/packages/payments/payment-paytr/src/tests/utils.spec.ts b/packages/medusa/payment-paytr/src/tests/utils.spec.ts similarity index 100% rename from packages/payments/payment-paytr/src/tests/utils.spec.ts rename to packages/medusa/payment-paytr/src/tests/utils.spec.ts diff --git a/packages/payments/payment-paytr/src/types.ts b/packages/medusa/payment-paytr/src/types.ts similarity index 100% rename from packages/payments/payment-paytr/src/types.ts rename to packages/medusa/payment-paytr/src/types.ts diff --git a/packages/payments/payment-paytr/src/utils.ts b/packages/medusa/payment-paytr/src/utils.ts similarity index 100% rename from packages/payments/payment-paytr/src/utils.ts rename to packages/medusa/payment-paytr/src/utils.ts diff --git a/packages/payments/payment-paytr/tsconfig.json b/packages/medusa/payment-paytr/tsconfig.json similarity index 100% rename from packages/payments/payment-paytr/tsconfig.json rename to packages/medusa/payment-paytr/tsconfig.json diff --git a/packages/payments/payment-paytr/src/api/index.ts b/packages/payments/payment-paytr/src/api/index.ts deleted file mode 100644 index bde5ba6..0000000 --- a/packages/payments/payment-paytr/src/api/index.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { Router } from 'express'; -import routes from './routes'; - -export default (): Router => { - const app = Router(); - - routes(app); - - return app; -}; diff --git a/packages/payments/payment-paytr/src/api/middleware/await-middleware.ts b/packages/payments/payment-paytr/src/api/middleware/await-middleware.ts deleted file mode 100644 index e95bbfd..0000000 --- a/packages/payments/payment-paytr/src/api/middleware/await-middleware.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { NextFunction, Request, Response } from 'express'; - -export default ( - fn: (req: Request, res: Response) => Promise -): ((req: Request, res: Response, next: NextFunction) => Promise) => { - return (req: Request, res: Response, next: NextFunction) => fn(req, res).catch(next); -}; diff --git a/packages/payments/payment-paytr/src/api/middleware/index.ts b/packages/payments/payment-paytr/src/api/middleware/index.ts deleted file mode 100644 index 93a307b..0000000 --- a/packages/payments/payment-paytr/src/api/middleware/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { default as wrap } from './await-middleware'; - -export default { wrap }; diff --git a/packages/payments/payment-paytr/src/api/routes/index.ts b/packages/payments/payment-paytr/src/api/routes/index.ts deleted file mode 100644 index 1461345..0000000 --- a/packages/payments/payment-paytr/src/api/routes/index.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { Response, Router } from 'express'; -import * as bodyParser from 'body-parser'; - -import middlewares from '../middleware'; -import { CustomRequest } from '../../types'; -import PayTRProviderService from '../../services/paytr-provider'; - -const route = Router(); - -export default (app: Router): Router => { - app.use('/pay-tr', route); - - route.post('/callback', bodyParser.json(), bodyParser.urlencoded({ extended: true }), middlewares.wrap(webhook)); - - return app; -}; - -async function webhook(req: CustomRequest, res: Response): Promise { - try { - const data = req.body; - - const payTRProviderService = req.scope.resolve('pp_paytr') as PayTRProviderService; - await payTRProviderService.handleCallback(data); - - res.send('OK'); - } catch (err) { - res.status(400).json({ message: err.message }); - } -} diff --git a/packages/payments/payment-paytr/src/api/store/carts.ts b/packages/payments/payment-paytr/src/api/store/carts.ts deleted file mode 100644 index 2c8222d..0000000 --- a/packages/payments/payment-paytr/src/api/store/carts.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { Response, Router } from 'express'; -import PayTRProviderService from '../../services/paytr-provider'; -import * as bodyParser from 'body-parser'; -import middlewares from '../middleware'; -import { CustomRequest } from '../../types'; - -const router = Router(); - -export default () => { - router.post('/:cart_id/generate-iframe-url', bodyParser.json(), middlewares.wrap(generateToken)); - return router; -}; - -async function generateToken(req: CustomRequest, res: Response): Promise { - const { cart_id } = req.params; - - const payTrProvider = req.scope.resolve('pp_paytr'); - - await payTrProvider - .generateToken(cart_id) - .then((token: string) => { - return res.json({ url: 'https://www.paytr.com/odeme/guvenli/' + token }); - }) - .catch((err) => { - return res.status(400).send({ errorMessage: err.message }); - }); -} diff --git a/packages/payments/payment-paytr/src/services/paytr-provider.ts b/packages/payments/payment-paytr/src/services/paytr-provider.ts deleted file mode 100644 index 9ff08a5..0000000 --- a/packages/payments/payment-paytr/src/services/paytr-provider.ts +++ /dev/null @@ -1,219 +0,0 @@ -import OrderService from '@medusajs/medusa/dist/services/order'; -import TotalsService from '@medusajs/medusa/dist/services/totals'; -import { Cart, Payment, PaymentSession } from '@medusajs/medusa/dist'; -import { CustomerService, RegionService } from '@medusajs/medusa/dist/services'; -import { PaymentService } from 'medusa-interfaces'; -import { PaymentSessionStatus } from '@medusajs/medusa/dist/models/payment-session'; -import { MerchantConfig, PaymentData, PaymentSessionData } from '../types'; -import CartService from '@medusajs/medusa/dist/services/cart'; -import * as nodeBase64 from 'nodejs-base64-converter'; -import { EntityManager } from 'typeorm'; -import { - buildAddressFromCart, - buildOid, - buildPaymentToken, - buildPaytrToken, - findPendingPaymentSession, - getCartIdFromOid, - request, -} from '../utils'; -import { PaymentSessionRepository } from '@medusajs/medusa/dist/repositories/payment-session'; - -export default class PayTRProviderService extends PaymentService { - static identifier = 'paytr'; - - readonly #merchantConfig: MerchantConfig; - - readonly #manager: EntityManager; - readonly #paymentSessionRepository: typeof PaymentSessionRepository; - readonly #orderService: OrderService; - readonly #customerService: CustomerService; - readonly #regionService: RegionService; - readonly #totalsService: TotalsService; - readonly #cartService: CartService; - - constructor( - { manager, paymentSessionRepository, cartService, customerService, totalsService, regionService, orderService }, - options: MerchantConfig - ) { - super(); - - this.#merchantConfig = options; - - this.#manager = manager; - this.#paymentSessionRepository = paymentSessionRepository; - this.#orderService = orderService; - this.#customerService = customerService; - this.#regionService = regionService; - this.#totalsService = totalsService; - this.#cartService = cartService; - } - - async generateToken(cartId: string): Promise { - const cart = await this.retrieveCart(cartId); - const amount = await this.#totalsService.getTotal(cart); - const { currency_code } = await this.#regionService.retrieve(cart.region_id); - const formattedItems = cart.items.map((item) => [item.title, item.unit_price, item.quantity.toString()]); - const cartToken = nodeBase64.encode(JSON.stringify(formattedItems)); - const userIp = cart.context?.ip ?? 'xxx.x.xxx.xxx'; - const merchantOid = buildOid(cart.id.split('_').pop()); - const payTrToken = await buildPaymentToken({ - amount, - orderId: merchantOid, - email: cart.customer?.email, - ip: userIp, - currency_code, - cartToken, - merchantConfig: this.#merchantConfig, - }); - const billingAddress = buildAddressFromCart(cart); - /* eslint-disable @typescript-eslint/no-unused-vars */ - const { token_endpoint, refund_endpoint, ...config } = this.#merchantConfig; - const data = { - ...config, - paytr_token: payTrToken, - no_installment: this.#merchantConfig.no_installment, - max_installment: this.#merchantConfig.max_installment, - payment_amount: amount, - currency: currency_code, - user_name: (cart?.billing_address?.first_name + ' ' + cart?.billing_address?.last_name).trim(), - user_address: billingAddress, - email: cart.customer?.email, - user_phone: cart.billing_address?.phone, - user_ip: userIp, - user_basket: cartToken, - merchant_oid: merchantOid, - lang: cart.customer?.metadata?.lang ?? 'tr', - }; - - return await request(token_endpoint, data) - .then((res: { token: string }) => { - return res.token; - }) - .catch((e) => { - throw new Error(`An error occurred while trying to create the payment.\n${e?.message ?? e}`); - }); - } - - async createPayment(cart: Cart): Promise { - const merchantOid = buildOid(cart.id.split('_').pop()); - return { merchantOid, isPending: true }; - } - - async getStatus(data: PaymentData): Promise { - const { status } = data as { status: string | null }; - - if (!status) { - return PaymentSessionStatus.PENDING; - } - - return status === 'success' ? PaymentSessionStatus.AUTHORIZED : PaymentSessionStatus.ERROR; - } - - async getPaymentData(sessionData: { data: PaymentSessionData }): Promise { - return sessionData.data; - } - - async authorizePayment(paymentSession: PaymentSession): Promise<{ status: string; data: PaymentSessionData }> { - return { status: 'authorized', data: paymentSession.data }; - } - - async updatePayment( - sessionData: { data: PaymentSessionData }, - updateData: PaymentSessionData - ): Promise { - return { - ...sessionData.data, - ...updateData, - }; - } - - async deletePayment(): Promise { - return; - } - - async capturePayment(payment: Payment): Promise { - return payment.data; - } - - async refundPayment(payment: Payment, refundAmount: number): Promise { - const tokenBody = - this.#merchantConfig.merchant_id + - payment.data.merchantOid + - refundAmount + - this.#merchantConfig.merchant_salt; - const token = buildPaytrToken(tokenBody, { merchant_key: this.#merchantConfig.merchant_key }); - - return await request(this.#merchantConfig.refund_endpoint, { - merchant_id: this.#merchantConfig.merchant_id, - merchant_oid: payment.data.merchantOid, - return_amount: refundAmount, - paytr_token: token, - }) - .then(() => { - return payment.data; - }) - .catch((e) => { - throw new Error(`An error occurred while trying to refund a payment.\n${e?.message ?? e}`); - }); - } - - async cancelPayment(payment: Payment): Promise { - return payment.data; - } - - public async handleCallback({ - merchant_oid, - status, - total_amount, - hash, - }: { - merchant_oid: string; - status: 'success' | 'error'; - total_amount: number; - hash: string; - }): Promise { - const tokenBody = merchant_oid + this.#merchantConfig.merchant_salt + status + total_amount; - const token = buildPaytrToken(tokenBody, { merchant_key: this.#merchantConfig.merchant_key }); - - if (token != hash) { - throw new Error('PAYTR notification failed: bad hash'); - } - - const cartId = getCartIdFromOid(merchant_oid); - const cart = await this.retrieveCart(cartId); - const pendingPaymentSession = findPendingPaymentSession(cart.payment_sessions, { - merchantOid: merchant_oid, - }); - if (!pendingPaymentSession) { - 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); - } - - async retrieveCart(cartId: string): Promise { - return this.#cartService.retrieve(cartId, { - select: ['gift_card_total', 'subtotal', 'tax_total', 'shipping_total', 'discount_total', 'total'], - relations: [ - 'items', - 'discounts', - 'discounts.rule', - 'discounts.rule.valid_for', - 'gift_cards', - 'billing_address', - 'shipping_address', - 'region', - 'region.payment_providers', - 'payment_sessions', - 'customer', - ], - }); - } -} diff --git a/packages/payments/payment-paytr/src/services/tests/paytr.provider.spec.ts b/packages/payments/payment-paytr/src/services/tests/paytr.provider.spec.ts deleted file mode 100644 index cc50717..0000000 --- a/packages/payments/payment-paytr/src/services/tests/paytr.provider.spec.ts +++ /dev/null @@ -1,62 +0,0 @@ -import 'core-js/stable'; -import 'regenerator-runtime/runtime'; -import * as dotenv from 'dotenv'; - -try { - dotenv.config({ path: __dirname + '/../../../.env.test' }); -} catch (e) {} - -import { MockManager, MockRepository } from 'medusa-test-utils'; -import PayTRProviderService from '../paytr-provider'; -import { CustomerServiceMock } from '../../__mock__/customer'; -import { TotalsServiceMock } from '../../__mock__/totals'; -import { cartMockData, CartServiceMock } from '../../__mock__/cart'; - -const merchantConfig: any = { - token_endpoint: process.env.TOKEN_ENDPOINT, - refund_endpoint: process.env.REFUND_ENDPOINT, - debug_on: 1, - test_mode: 1, - max_installment: 0, - no_installment: 0, - merchant_fail_url: 'http://localhost:3000/pay-tr/success', - merchant_ok_url: 'http://localhost:3000/pay-tr/fail', - merchant_id: process.env.MERCHANT_ID, - merchant_key: process.env.MERCHANT_KEY, - merchant_salt: process.env.MERCHANT_SALT, - timeout_limit: 30, -}; - -const RegionServiceMock = { - retrieve: jest.fn().mockReturnValue(Promise.resolve({ currency_code: 'TL' })), -}; - -const OrderServiceMock = { - retrieveByCartId: jest.fn().mockReturnValue(Promise.resolve({ id: 'or_dzlkfzengzelkgvnz' })), -}; - -describe('PayTrProvider', () => { - let provider: PayTRProviderService; - - beforeAll(async () => { - jest.clearAllMocks(); - provider = new PayTRProviderService( - { - manager: MockManager, - paymentSessionRepository: MockRepository, - cartService: CartServiceMock, - customerService: CustomerServiceMock, - regionService: RegionServiceMock, - totalsService: TotalsServiceMock, - orderService: OrderServiceMock, - }, - merchantConfig - ); - PayTRProviderService.prototype.retrieveCart = jest.fn().mockReturnValue(Promise.resolve(cartMockData)); - }); - - it('should allow to generate a new token', async () => { - const token = await provider.generateToken(cartMockData.id); - expect(token).toBeDefined(); - }); -});