UNPKG

@chevre/domain

Version:

Chevre Domain Library for Node.js

283 lines (282 loc) 13.9 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.call = call; const moment = require("moment"); const factory = require("../../factory"); const action_1 = require("../../repo/action"); const assetTransaction_1 = require("../../repo/assetTransaction"); const order_1 = require("../../repo/order"); const paymentService_1 = require("../../repo/paymentService"); const potentialAction_1 = require("../../repo/potentialAction"); const product_1 = require("../../repo/product"); const task_1 = require("../../repo/task"); const transaction_1 = require("../../repo/transaction"); const transactionNumber_1 = require("../../repo/transactionNumber"); const RefundTransactionService = require("../assetTransaction/refund"); /** * タスク実行関数 */ function call(params) { return (_a) => __awaiter(this, [_a], void 0, function* ({ redisClient, connection }) { if (redisClient === undefined) { throw new factory.errors.Argument('settings', 'redisClient required'); } yield returnPayTransaction(Object.assign(Object.assign({}, params.data), { sameAs: { id: params.id } }))({ action: new action_1.ActionRepo(connection), assetTransaction: new assetTransaction_1.AssetTransactionRepo(connection), order: new order_1.OrderRepo(connection), paymentService: new paymentService_1.PaymentServiceRepo(connection), potentialAction: new potentialAction_1.PotentialActionRepo(connection), product: new product_1.ProductRepo(connection), task: new task_1.TaskRepo(connection), transaction: new transaction_1.TransactionRepo(connection), transactionNumber: new transactionNumber_1.TransactionNumberRepo({ redisClient, connection }) }); }); } function task2actionAttributes(params) { const { taskData, transactionNumber, order } = params; const { object, potentialActions, sameAs } = taskData; const agent = order.project; const recipient = { typeOf: order.customer.typeOf, id: order.customer.id, name: String(order.customer.name) }; const purpose = { typeOf: order.typeOf, orderNumber: order.orderNumber, // price: order.price, // priceCurrency: order.priceCurrency, orderDate: order.orderDate }; return Object.assign(Object.assign(Object.assign({ project: order.project, typeOf: factory.actionType.ReturnAction, agent, object, recipient, purpose }, (potentialActions !== undefined) ? { potentialActions } : undefined), (typeof transactionNumber === 'string') ? { instrument: { transactionNumber, typeOf: factory.assetTransactionType.Refund } } // add instrument(2024-06-17~) : undefined), (typeof (sameAs === null || sameAs === void 0 ? void 0 : sameAs.id) === 'string') ? { sameAs: { id: sameAs.id, typeOf: 'Task' } } : undefined); } function fixOrderAndTransaction(params) { return (repos) => __awaiter(this, void 0, void 0, function* () { const paymentMethodId = params.object.paymentMethodId; // objectから注文を特定する(2024-06-19~) const orderByPaymentMethodId = (yield repos.order.projectFields({ limit: 1, page: 1, paymentMethods: { paymentMethodIds: [paymentMethodId] } }, { inclusion: ['orderNumber'] })).shift(); if (orderByPaymentMethodId === undefined) { throw new factory.errors.NotFound(factory.order.OrderType.Order); } // const orderNumber = params.purpose.orderNumber; const orderNumber = orderByPaymentMethodId.orderNumber; const returnOrderTransaction = (yield repos.transaction.projectFields({ limit: 1, page: 1, typeOf: factory.transactionType.ReturnOrder, object: { order: { orderNumbers: [orderNumber] } }, inclusion: ['object', 'project'] })).shift(); if (returnOrderTransaction === undefined) { throw new factory.errors.NotFound(factory.transactionType.ReturnOrder); } const order = yield repos.order.projectFieldsByOrderNumber({ orderNumber, project: { id: returnOrderTransaction.project.id }, inclusion: ['seller', 'project', 'dateReturned', 'typeOf', 'price', 'priceCurrency', 'orderNumber', 'orderDate', 'customer'] }); return { order, returnOrderTransaction }; }); } /** * 決済取引返却 */ function returnPayTransaction(taskData) { return (repos) => __awaiter(this, void 0, void 0, function* () { var _a; const { order, returnOrderTransaction } = yield fixOrderAndTransaction(taskData)(repos); const paymentServiceType = (_a = taskData.object.issuedThrough) === null || _a === void 0 ? void 0 : _a.typeOf; if (typeof paymentServiceType !== 'string' || paymentServiceType.length === 0) { throw new factory.errors.ArgumentNull('object.issuedThrough.typeOf'); } let transactionNumber; // 返金取引番号 // すでに返金取引が存在すれば何もしない const alreadyExists = yield refundTransactionAlreadyExists({ project: { id: order.project.id }, object: { paymentMethodId: taskData.object.paymentMethodId } })({ assetTransaction: repos.assetTransaction }); if (!alreadyExists) { transactionNumber = (yield repos.transactionNumber.publishByTimestamp({ startDate: new Date() })).transactionNumber; } const refundActionAttributes = task2actionAttributes({ taskData, transactionNumber, order }); const action = yield repos.action.start(refundActionAttributes); // let refundTransaction: Pick<factory.assetTransaction.refund.ITransaction, 'id' | 'transactionNumber' | 'typeOf'> | undefined; try { if (typeof transactionNumber === 'string') { const refundFee = transaction2refundFee({ returnOrderTransaction }); // 返品手数料 const startRefundTransactionParams = createStartRefundTransactionParams(Object.assign(Object.assign({}, refundActionAttributes), { id: action.id, transactionNumber, paymentServiceType, refundFee, seller: order.seller })); const refundPurpose = createRefundPurpose(refundActionAttributes, order); yield RefundTransactionService.start(startRefundTransactionParams)({ action: repos.action, paymentService: repos.paymentService, potentialAction: repos.potentialAction, product: repos.product, assetTransaction: repos.assetTransaction }); // refundTransaction = { id, transactionNumber, typeOf }; yield RefundTransactionService.confirm({ transactionNumber, potentialActions: { refund: { purpose: refundPurpose } } })({ assetTransaction: repos.assetTransaction }); } } catch (error) { try { yield repos.action.giveUp({ typeOf: action.typeOf, id: action.id, error }); } catch (__) { // no op } throw error; } const result = { // ...(typeof refundTransaction?.id === 'string') ? { refundTransaction } : undefined // migrate into instrument(2024-06-17~) }; // optimize(2024-04-27~) yield repos.action.completeWithVoid({ typeOf: action.typeOf, id: action.id, result: result }); yield onRefund({ refundActionAttributes, order })({ task: repos.task }); }); } function createRefundPurpose(params, order) { return { typeOf: factory.actionType.ReturnAction, object: Object.assign(Object.assign({}, params.purpose), { dateReturned: order.dateReturned }) }; } function refundTransactionAlreadyExists(params) { return (repos) => __awaiter(this, void 0, void 0, function* () { const refundTransactions = yield repos.assetTransaction.search({ limit: 1, page: 1, project: { id: { $eq: params.project.id } }, typeOf: factory.assetTransactionType.Refund, statuses: [factory.transactionStatusType.Confirmed], object: { paymentMethodId: { $eq: params.object.paymentMethodId } } }, ['_id'], []); return refundTransactions.length > 0; }); } function transaction2refundFee(params) { var _a; let refundFee = 0; const returnPolicy = params.returnOrderTransaction.object.returnPolicy; // 返品ポリシーに返品手数料が定義されていれば、プロジェクト設定から適用する if (returnPolicy.returnFees === factory.merchantReturnPolicy.ReturnFeesEnumeration.RestockingFees) { if (typeof returnPolicy.restockingFee === 'number') { throw new factory.errors.NotImplemented('restockingFee in type of number not implemented'); } else { const returnFeeByPolicy = (_a = returnPolicy.restockingFee) === null || _a === void 0 ? void 0 : _a.value; if (typeof returnFeeByPolicy !== 'number') { throw new factory.errors.NotFound('appliedReturnPolicy.restockingFee.value'); } refundFee = returnFeeByPolicy; } } return refundFee; } function createStartRefundTransactionParams(params) { var _a, _b; const paymentMethodType = (_a = params.object.paymentMethod) === null || _a === void 0 ? void 0 : _a.identifier; if (typeof paymentMethodType !== 'string') { throw new factory.errors.NotFound('object.paymentMethod.identifier'); } const agent = { typeOf: params.seller.typeOf, id: params.seller.id, name: params.seller.name }; const instrument = [ { id: params.id, typeOf: params.typeOf }, { orderNumber: params.purpose.orderNumber, typeOf: factory.order.OrderType.Order } ]; return { project: { id: params.project.id, typeOf: factory.organizationType.Project }, typeOf: factory.assetTransactionType.Refund, transactionNumber: params.transactionNumber, agent, // tslint:disable-next-line:no-object-literal-type-assertion recipient: { typeOf: params.recipient.typeOf, name: params.recipient.name }, object: { typeOf: params.paymentServiceType, id: (typeof ((_b = params.object.issuedThrough) === null || _b === void 0 ? void 0 : _b.id) === 'string') ? params.object.issuedThrough.id : '', paymentMethod: { additionalProperty: params.object.additionalProperty, name: params.object.name, typeOf: paymentMethodType, paymentMethodId: params.object.paymentMethodId }, refundFee: params.refundFee }, expires: moment() .add(1, 'minutes') .toDate(), instrument // add(2025-02-18~) }; } /** * 返金後のアクション */ function onRefund(params) { return (repos) => __awaiter(this, void 0, void 0, function* () { const { refundActionAttributes, order } = params; const potentialActions = refundActionAttributes.potentialActions; const now = new Date(); const taskAttributes = []; const sendEmailMessageByPotentialActions = potentialActions === null || potentialActions === void 0 ? void 0 : potentialActions.sendEmailMessage; if (Array.isArray(sendEmailMessageByPotentialActions)) { const simpleOrder = { typeOf: order.typeOf, orderNumber: order.orderNumber, // price: order.price, // priceCurrency: order.priceCurrency, orderDate: order.orderDate }; sendEmailMessageByPotentialActions.forEach((s) => { const actionAttributes = { project: refundActionAttributes.project, typeOf: factory.actionType.SendAction, object: s.object, agent: refundActionAttributes.project, recipient: { typeOf: order.customer.typeOf, id: order.customer.id }, purpose: simpleOrder }; const sendEmailMessageTask = { project: refundActionAttributes.project, name: factory.taskName.SendEmailMessage, status: factory.taskStatus.Ready, runsAt: now, remainingNumberOfTries: 3, numberOfTried: 0, executionResults: [], data: { actionAttributes } }; taskAttributes.push(sendEmailMessageTask); }); } // タスク保管 yield repos.task.saveMany(taskAttributes, { emitImmediately: true }); }); }