UNPKG

@unchainedshop/plugins

Version:

Official plugin collection for the Unchained Engine with payment, delivery, and pricing adapters

163 lines (162 loc) 7.95 kB
import { createLogger } from '@unchainedshop/logger'; import { mapOrderDataToGatewayObject, mapUserToGatewayObject } from "./payrexx.js"; import createPayrexxAPI, { GatewayObjectStatus } from "./api/index.js"; import { OrderPricingSheet, PaymentAdapter, PaymentDirector, PaymentError, } from '@unchainedshop/core'; const logger = createLogger('unchained:payrexx'); const Payrexx = { ...PaymentAdapter, key: 'shop.unchained.payment.payrexx', label: 'Payrexx', version: '1.0.0', typeSupported(type) { return type === 'GENERIC'; }, actions: (config, context) => { const { modules } = context; const instance = config.find((c) => c.key === 'instance')?.value; const adapterActions = { ...PaymentAdapter.actions(config, context), configurationError() { if (!process.env.PAYREXX_SECRET) { return PaymentError.INCOMPLETE_CONFIGURATION; } if (!instance) { return PaymentError.INCOMPLETE_CONFIGURATION; } return null; }, isActive: () => { if (adapterActions.configurationError() === null) return true; return false; }, isPayLaterAllowed() { return false; }, sign: async (transactionContext = {}) => { const { orderPayment, userId, order } = context; if (!order) throw new Error('No order in context, can only sign with order context'); const api = createPayrexxAPI(instance, process.env.PAYREXX_SECRET); if (orderPayment) { const pricing = OrderPricingSheet({ calculation: order.calculation, currencyCode: order.currencyCode, }); const gatewayObject = mapOrderDataToGatewayObject({ order, orderPayment, pricing }, transactionContext); const gateway = await api.createGateway(gatewayObject); return JSON.stringify(gateway); } const user = userId && (await modules.users.findUserById(userId)); if (!user) throw new Error('User not found, can only sign registrations with user context'); const emailAddress = modules.users.primaryEmail(user)?.address; const gateway = await api.createGateway(mapUserToGatewayObject({ userId, emailAddress, currencyCode: order?.currencyCode, })); return JSON.stringify(gateway); }, async register() { throw new Error('Not implemented'); return null; }, async confirm() { const { orderPayment } = context; if (!orderPayment?.transactionId) { return false; } const { transactionId } = orderPayment; const api = createPayrexxAPI(instance, process.env.PAYREXX_SECRET); const gatewayObject = await api.getGateway(transactionId); if (!gatewayObject) { return false; } if (gatewayObject.status === GatewayObjectStatus.reserved) { const allTransactions = gatewayObject.invoices?.flatMap((invoice) => invoice.transactions); await Promise.all(allTransactions.map(async (transaction) => api.chargePreAuthorized(transaction.id, { referenceId: '__IGNORE_WEBHOOK__' }))); return true; } if (gatewayObject.status === GatewayObjectStatus.confirmed) { return true; } return false; }, async cancel() { const { orderPayment } = context; if (!orderPayment?.transactionId) { return false; } const { transactionId } = orderPayment; const api = createPayrexxAPI(instance, process.env.PAYREXX_SECRET); const gatewayObject = await api.getGateway(transactionId); if (!gatewayObject) { return false; } if (gatewayObject.status === GatewayObjectStatus.reserved) { const allTransactions = gatewayObject.invoices?.flatMap((invoice) => invoice.transactions); await Promise.all(allTransactions.map(async (transaction) => api.deleteReservation(transaction.id))); return true; } if (gatewayObject.status === GatewayObjectStatus.confirmed) { return false; } return true; }, charge: async ({ gatewayId, paymentCredentials }) => { if (!gatewayId && !paymentCredentials) { throw new Error('You have to provide gatewayId or paymentCredentials'); } const { order, orderPayment } = context; if (!order) throw new Error('No order in context, can only charge with order context'); if (!orderPayment) throw new Error('Order payment not found'); const api = createPayrexxAPI(instance, process.env.PAYREXX_SECRET); const gatewayObject = await api.getGateway(gatewayId); if (!gatewayObject) { throw new Error('Could not load gateway from the Payrexx API'); } const pricing = OrderPricingSheet({ calculation: order.calculation, currencyCode: order.currencyCode, }); const { currencyCode, amount } = pricing.total({ useNetPrice: false }); if (gatewayObject.status === 'reserved' || gatewayObject.status === 'confirmed') { try { if (gatewayObject.currency !== currencyCode.toUpperCase() || gatewayObject.amount !== Math.round(amount)) { throw new Error('The price has changed since the intent has been created'); } if (gatewayObject.referenceId !== orderPayment?._id) { throw new Error('The order payment is different from the initiating intent'); } logger.info(`Mark as charged, status is ${gatewayObject.status}`, { orderPaymentId: gatewayObject.referenceId, }); return { transactionId: gatewayId, gatewayObject, }; } catch (e) { if (gatewayObject.status === GatewayObjectStatus.reserved) { const allTransactions = gatewayObject.invoices?.flatMap((invoice) => invoice.transactions); await Promise.all(allTransactions.map(async (transaction) => api.deleteReservation(transaction.id))); } throw e; } } logger.info('Charge not possible', { orderPaymentId: gatewayObject.referenceId, status: gatewayObject.status, }); throw new Error(`Gateway Status ${gatewayObject.status} does not allow checkout`); }, }; return adapterActions; }, }; PaymentDirector.registerAdapter(Payrexx);