@chevre/domain
Version:
Chevre Domain Library for Node.js
416 lines (415 loc) • 21.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.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 });
});
}