UNPKG

@chevre/domain

Version:

Chevre Domain Library for Node.js

416 lines (415 loc) 21.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.start = start; exports.confirm = confirm; exports.cancel = cancel; exports.exportTasksById = exportTasksById; /** * 通貨転送取引サービス */ const moment = require("moment"); const factory = require("../../factory"); const pecorinoapi = require("../../pecorinoapi"); // import type { TransactionNumberRepo } from '../../repo/transactionNumber'; const MoneyTransferService = require("../moneyTransfer"); const potentialActions_1 = require("./moneyTransfer/potentialActions"); /** * 取引開始 * Pecorinoサービスを利用して口座取引を開始する */ function start(params) { return (repos) => __awaiter(this, void 0, void 0, function* () { var _a; const issuedThroughId = fixIssuedThroughId(params); // location.issuedThroughIdからプロダクトをfixする const products = yield repos.product.projectFields({ limit: 1, page: 1, project: { id: { $eq: params.project.id } }, typeOf: { $eq: factory.product.ProductType.PaymentCard }, id: { $eq: issuedThroughId } }, ['availableChannel', 'serviceOutput'] // [] ); const product = products.shift(); if (product === undefined) { throw new factory.errors.NotFound('Product'); } // fromとtoをfix const { fromLocation } = yield fixFromLocation(params, product)(repos); const { toLocation } = yield fixToLocation(params, product)(repos); const amount = fixMonetaryAmount(params, product); // 自動発行廃止(2022-05-17~) const transactionNumber = params.transactionNumber; if (typeof transactionNumber !== 'string' || transactionNumber.length === 0) { throw new factory.errors.ArgumentNull('transactionNumber'); } const transactionType = (_a = params.object.pendingTransaction) === null || _a === void 0 ? void 0 : _a.typeOf; if (typeof transactionType !== 'string') { throw new factory.errors.ArgumentNull('object.pendingTransaction.typeOf'); } // 取引開始 const startParams = { project: params.project, transactionNumber: transactionNumber, typeOf: factory.assetTransactionType.MoneyTransfer, agent: params.agent, recipient: params.recipient, object: Object.assign(Object.assign({ amount, fromLocation, toLocation, pendingTransaction: Object.assign({ typeOf: transactionType, transactionNumber: transactionNumber }, (typeof params.identifier === 'string') ? { identifier: params.identifier } : undefined) }, (typeof params.object.description === 'string') ? { description: params.object.description } : {}), { force: params.object.force === true }), expires: params.expires }; // 取引開始 let transaction; try { transaction = yield repos.assetTransaction.start(startParams); // const pendingTransaction = await authorizeAccount({ transaction })(repos); yield authorizeAccount({ transaction })(repos); // 更新不要(2023-02-20~) // await repos.assetTransaction.findByIdAndUpdate<factory.assetTransactionType.MoneyTransfer>({ // id: transaction.id, // update: { // 'object.pendingTransaction': { // typeOf: pendingTransaction.typeOf, // id: pendingTransaction.id, // transactionNumber: pendingTransaction.transactionNumber, // ...(typeof pendingTransaction.identifier === 'string') // ? { identifier: pendingTransaction.identifier } // : undefined // } // } // }); } catch (error) { throw error; } return transaction; }); } function fixIssuedThroughId(params) { var _a, _b, _c; let issuedThroughId; const pendingTransactionType = (_a = params.object.pendingTransaction) === null || _a === void 0 ? void 0 : _a.typeOf; switch (pendingTransactionType) { case factory.account.transactionType.Deposit: issuedThroughId = String((_b = params.object.toLocation.issuedThrough) === null || _b === void 0 ? void 0 : _b.id); break; case factory.account.transactionType.Transfer: case factory.account.transactionType.Withdraw: issuedThroughId = String((_c = params.object.fromLocation.issuedThrough) === null || _c === void 0 ? void 0 : _c.id); break; default: throw new factory.errors.Argument('AccountTransactionType', `invalid type: ${params.typeOf}`); } if (typeof issuedThroughId !== 'string' || issuedThroughId.length === 0) { throw new factory.errors.ArgumentNull('location.issuedThrough.id'); } return issuedThroughId; } function authorizeAccount(params) { return (repos) => __awaiter(this, void 0, void 0, function* () { var _a, _b, _c; const transaction = params.transaction; const fromLocation = transaction.object.fromLocation; const toLocation = transaction.object.toLocation; if (typeof ((_a = transaction.object.pendingTransaction) === null || _a === void 0 ? void 0 : _a.typeOf) !== 'string') { throw new factory.errors.NotFound('transaction.object.pendingTransaction.typeOf'); } yield MoneyTransferService.authorize({ typeOf: transaction.object.pendingTransaction.typeOf, identifier: transaction.object.pendingTransaction.identifier, transactionNumber: transaction.object.pendingTransaction.transactionNumber, project: { typeOf: transaction.project.typeOf, id: transaction.project.id }, agent: transaction.agent, object: Object.assign({ force: transaction.object.force === true, amount: transaction.object.amount.value, fromLocation: Object.assign({}, (typeof fromLocation.identifier === 'string') ? { identifier: fromLocation.identifier, hasNoPermit: fromLocation.hasNoPermit === true, issuedThrough: { id: (_b = fromLocation.issuedThrough) === null || _b === void 0 ? void 0 : _b.id } } : undefined), toLocation: Object.assign({}, (typeof toLocation.identifier === 'string') ? { identifier: toLocation.identifier, hasNoPermit: toLocation.hasNoPermit === true, issuedThrough: { id: (_c = toLocation.issuedThrough) === null || _c === void 0 ? void 0 : _c.id } } : undefined) }, (typeof transaction.object.description === 'string') ? { description: transaction.object.description } : undefined), recipient: transaction.recipient, purpose: { typeOf: transaction.typeOf, id: transaction.id } })(repos); // return pendingTransaction; }); } function fixMonetaryAmount(params, product) { var _a, _b, _c, _d; // currencyはプロダクトから自動取得 const currency = (_b = (_a = product.serviceOutput) === null || _a === void 0 ? void 0 : _a.amount) === null || _b === void 0 ? void 0 : _b.currency; if (typeof currency !== 'string') { throw new factory.errors.Argument('amount', 'currency unknown'); } // 金額をfix const amountValue = (_d = (_c = params.object) === null || _c === void 0 ? void 0 : _c.amount) === null || _d === void 0 ? void 0 : _d.value; if (typeof amountValue !== 'number') { throw new factory.errors.ArgumentNull('amount.value'); } return { typeOf: 'MonetaryAmount', currency, value: amountValue }; } function fixFromLocation(params, product) { return (__) => __awaiter(this, void 0, void 0, function* () { var _a, _b, _c; const amount = params.object.amount; if (typeof (amount === null || amount === void 0 ? void 0 : amount.value) !== 'number') { throw new factory.errors.ArgumentNull('amount.value'); } const { permitServiceEndpoint, permitServiceAuthorizeServerDomain, permitServiceClientId, permitServiceClientSecret } = createPermitServiceCredentials({ product }); const permitService = new (yield pecorinoapi.loadPecorino()).service.Permit({ endpoint: permitServiceEndpoint, auth: yield pecorinoapi.auth.ClientCredentials.createInstance({ domain: permitServiceAuthorizeServerDomain, clientId: permitServiceClientId, clientSecret: permitServiceClientSecret, scopes: [], state: '' }) }); let fromLocation = params.object.fromLocation; const transactionType = (_a = params.object.pendingTransaction) === null || _a === void 0 ? void 0 : _a.typeOf; switch (transactionType) { case factory.account.transactionType.Withdraw: case factory.account.transactionType.Transfer: const fromLocationObject = fromLocation; // Permitの存在しない注文口座に対応する const hasNoPermit = fromLocationObject.hasNoPermit === true; let serviceOutput; if (!hasNoPermit) { // サービスアウトプット存在確認 serviceOutput = yield permitService.findByIdentifier({ project: { id: params.project.id }, identifier: fromLocationObject.identifier, issuedThrough: { typeOf: factory.product.ProductType.PaymentCard } }); // 出金金額設定を確認 const paymentAmount = serviceOutput.paymentAmount; if (typeof (paymentAmount === null || paymentAmount === void 0 ? void 0 : paymentAmount.minValue) === 'number') { if (amount.value < paymentAmount.minValue) { throw new factory.errors.Argument('fromLocation', `mininum payment amount requirement not satisfied`); } } if (typeof (paymentAmount === null || paymentAmount === void 0 ? void 0 : paymentAmount.maxValue) === 'number') { if (amount.value > paymentAmount.maxValue) { throw new factory.errors.Argument('fromLocation', `maximum payment amount requirement not satisfied`); } } } const accountNumber = (serviceOutput !== undefined) ? String((_b = serviceOutput.paymentAccount) === null || _b === void 0 ? void 0 : _b.accountNumber) : fromLocationObject.identifier; fromLocation = Object.assign(Object.assign({ typeOf: (serviceOutput !== undefined) ? serviceOutput.typeOf : factory.permit.PermitType.Permit, identifier: (serviceOutput !== undefined) ? serviceOutput.identifier : accountNumber }, (hasNoPermit) ? { hasNoPermit } : undefined), { issuedThrough: { id: (_c = fromLocationObject.issuedThrough) === null || _c === void 0 ? void 0 : _c.id } }); break; default: // no op } return { fromLocation }; }); } function fixToLocation(params, product) { return (__) => __awaiter(this, void 0, void 0, function* () { var _a, _b, _c; let toLocation = params.object.toLocation; const amount = params.object.amount; if (typeof (amount === null || amount === void 0 ? void 0 : amount.value) !== 'number') { throw new factory.errors.ArgumentNull('amount.value'); } const { permitServiceEndpoint, permitServiceAuthorizeServerDomain, permitServiceClientId, permitServiceClientSecret } = createPermitServiceCredentials({ product }); const permitService = new (yield pecorinoapi.loadPecorino()).service.Permit({ endpoint: permitServiceEndpoint, auth: yield pecorinoapi.auth.ClientCredentials.createInstance({ domain: permitServiceAuthorizeServerDomain, clientId: permitServiceClientId, clientSecret: permitServiceClientSecret, scopes: [], state: '' }) }); const transactionType = (_a = params.object.pendingTransaction) === null || _a === void 0 ? void 0 : _a.typeOf; switch (transactionType) { case factory.account.transactionType.Deposit: case factory.account.transactionType.Transfer: const toLocationObject = toLocation; // Permitの存在しない注文口座に対応する const hasNoPermit = toLocationObject.hasNoPermit === true; let serviceOutput; if (!hasNoPermit) { // サービスアウトプット存在確認 serviceOutput = yield permitService.findByIdentifier({ project: { id: params.project.id }, identifier: toLocationObject.identifier, issuedThrough: { typeOf: factory.product.ProductType.PaymentCard } }); // 入金金額設定を確認 const depositAmount = serviceOutput.depositAmount; if (typeof (depositAmount === null || depositAmount === void 0 ? void 0 : depositAmount.minValue) === 'number') { if (amount.value < depositAmount.minValue) { throw new factory.errors.Argument('toLocation', `mininum deposit amount requirement not satisfied`); } } if (typeof (depositAmount === null || depositAmount === void 0 ? void 0 : depositAmount.maxValue) === 'number') { if (amount.value > depositAmount.maxValue) { throw new factory.errors.Argument('toLocation', `maximum deposit amount requirement not satisfied`); } } } const accountNumber = (serviceOutput !== undefined) ? String((_b = serviceOutput.paymentAccount) === null || _b === void 0 ? void 0 : _b.accountNumber) : toLocationObject.identifier; toLocation = Object.assign(Object.assign({ typeOf: (serviceOutput !== undefined) ? serviceOutput.typeOf : factory.permit.PermitType.Permit, identifier: (serviceOutput !== undefined) ? serviceOutput.identifier : accountNumber }, (hasNoPermit) ? { hasNoPermit } : undefined), { issuedThrough: { id: (_c = toLocationObject.issuedThrough) === null || _c === void 0 ? void 0 : _c.id } }); break; default: // no op } return { toLocation }; }); } function createPermitServiceCredentials(params) { var _a, _b, _c, _d, _e, _f, _g; const permitServiceEndpoint = (_a = params.product.availableChannel) === null || _a === void 0 ? void 0 : _a.serviceUrl; const permitServiceAuthorizeServerDomain = (_c = (_b = params.product.availableChannel) === null || _b === void 0 ? void 0 : _b.credentials) === null || _c === void 0 ? void 0 : _c.authorizeServerDomain; const permitServiceClientId = (_e = (_d = params.product.availableChannel) === null || _d === void 0 ? void 0 : _d.credentials) === null || _e === void 0 ? void 0 : _e.clientId; const permitServiceClientSecret = (_g = (_f = params.product.availableChannel) === null || _f === void 0 ? void 0 : _f.credentials) === null || _g === void 0 ? void 0 : _g.clientSecret; if (typeof permitServiceEndpoint !== 'string' || permitServiceEndpoint.length === 0 || typeof permitServiceAuthorizeServerDomain !== 'string' || permitServiceAuthorizeServerDomain.length === 0 || typeof permitServiceClientId !== 'string' || permitServiceClientId.length === 0 || typeof permitServiceClientSecret !== 'string' || permitServiceClientSecret.length === 0) { throw new factory.errors.Internal('membershipService availableChannel invalid'); } return { permitServiceEndpoint, permitServiceAuthorizeServerDomain, permitServiceClientId, permitServiceClientSecret }; } /** * 取引確定 */ function confirm(params) { return (repos) => __awaiter(this, void 0, void 0, function* () { let transaction; // 取引存在確認 if (typeof params.id === 'string') { transaction = yield repos.assetTransaction.findById({ typeOf: factory.assetTransactionType.MoneyTransfer, id: params.id }); } else if (typeof params.transactionNumber === 'string') { transaction = yield repos.assetTransaction.findByTransactionNumber({ typeOf: factory.assetTransactionType.MoneyTransfer, transactionNumber: params.transactionNumber }); } else { throw new factory.errors.ArgumentNull('Transaction ID or Transaction Number'); } const potentialActions = yield (0, potentialActions_1.createPotentialActions)({ transaction: transaction }); yield repos.assetTransaction.confirm({ typeOf: factory.assetTransactionType.MoneyTransfer, id: transaction.id, result: {}, potentialActions: potentialActions }); }); } /** * 取引中止 */ function cancel(params) { return (repos) => __awaiter(this, void 0, void 0, function* () { yield repos.assetTransaction.cancel({ typeOf: factory.assetTransactionType.MoneyTransfer, id: params.id, transactionNumber: params.transactionNumber }); }); } /** * 取引のタスク出力 */ function exportTasksById(params) { return (repos) => __awaiter(this, void 0, void 0, function* () { const transaction = yield repos.assetTransaction.findById({ typeOf: factory.assetTransactionType.MoneyTransfer, id: params.id }); const potentialActions = transaction.potentialActions; const taskAttributes = []; // タスク実行日時バッファの指定があれば調整 let taskRunsAt = new Date(); if (typeof params.runsTasksAfterInSeconds === 'number') { taskRunsAt = moment(taskRunsAt) .add(params.runsTasksAfterInSeconds, 'seconds') .toDate(); } switch (transaction.status) { case factory.transactionStatusType.Confirmed: // tslint:disable-next-line:no-single-line-block-comment /* istanbul ignore else */ if (potentialActions !== undefined) { // tslint:disable-next-line:no-single-line-block-comment /* istanbul ignore else */ if (potentialActions.moneyTransfer !== undefined) { taskAttributes.push(...potentialActions.moneyTransfer.map((a) => { return { project: transaction.project, name: factory.taskName.MoneyTransfer, status: factory.taskStatus.Ready, runsAt: taskRunsAt, remainingNumberOfTries: 10, numberOfTried: 0, executionResults: [], data: a }; })); } } break; case factory.transactionStatusType.Canceled: case factory.transactionStatusType.Expired: const cancelMoneyTransferTaskAttributes = { project: { typeOf: transaction.project.typeOf, id: transaction.project.id }, name: factory.taskName.CancelMoneyTransfer, status: factory.taskStatus.Ready, runsAt: taskRunsAt, remainingNumberOfTries: 10, numberOfTried: 0, executionResults: [], data: { purpose: { typeOf: transaction.typeOf, id: transaction.id } } }; taskAttributes.push(cancelMoneyTransferTaskAttributes); break; default: throw new factory.errors.NotImplemented(`Transaction status "${transaction.status}" not implemented.`); } return repos.task.saveMany(taskAttributes, { emitImmediately: true }); }); }