@chevre/domain
Version:
Chevre Domain Library for Node.js
283 lines (282 loc) • 13.9 kB
JavaScript
"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 });
});
}