UNPKG

@chevre/domain

Version:

Chevre Domain Library for Node.js

618 lines (617 loc) 31.4 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.authorize = authorize; exports.cancelMoneyTransfer = cancelMoneyTransfer; exports.moneyTransfer = moneyTransfer; /** * 通貨転送サービス */ const moment = require("moment"); const pecorinoapi = require("../pecorinoapi"); const factory = require("../factory"); const errorHandler_1 = require("../errorHandler"); /** * 口座残高差し押さえ */ function authorize(params) { return (repos) => __awaiter(this, void 0, void 0, function* () { const project = yield repos.project.findById({ id: params.project.id, inclusion: ['settings', 'typeOf'] }); const transaction = yield repos.assetTransaction.findById({ typeOf: params.purpose.typeOf, id: params.purpose.id }); // 口座取引開始 // let pendingTransaction: factory.action.transfer.moneyTransfer.IPendingTransaction; try { yield processAccountTransaction({ typeOf: params.typeOf, identifier: params.identifier, transactionNumber: params.transactionNumber, project: { id: project.id, typeOf: project.typeOf }, object: params.object, agent: params.agent, recipient: params.recipient, transaction: transaction })(repos); } catch (error) { // PecorinoAPIのエラーをハンドリング error = (0, errorHandler_1.handlePecorinoError)(error); throw error; } // return pendingTransaction; }); } function processAccountTransaction(params) { return (repos) => __awaiter(this, void 0, void 0, function* () { const transaction = params.transaction; const agent = createAccountTransactionAgent(params); const recipient = createAccountTransactionRecipient(params); const description = (typeof params.object.description === 'string') ? params.object.description : `for transaction ${transaction.id}`; // 最大1ヵ月のオーソリ const expires = moment() .add(1, 'month') .toDate(); const issuedThroughId = getIssuedThroughId(params); const permitServiceCredentials = yield createPermitServiceCredentials({ issuedThrough: { id: issuedThroughId } })(repos); const permitService = new (yield pecorinoapi.loadPecorino()).service.Permit({ endpoint: permitServiceCredentials.permitServiceEndpoint, auth: yield pecorinoapi.auth.ClientCredentials.createInstance({ domain: permitServiceCredentials.permitServiceAuthorizeServerDomain, clientId: permitServiceCredentials.permitServiceClientId, clientSecret: permitServiceCredentials.permitServiceClientSecret, scopes: [], state: '' }) }); switch (params.typeOf) { case factory.account.transactionType.Deposit: yield processDepositTransaction({ identifier: params.identifier, transactionNumber: params.transactionNumber, project: params.project, object: params.object, agent, recipient, expires, description, permitServiceCredentials })({ permit: permitService }); break; case factory.account.transactionType.Transfer: yield processTransferTransaction({ identifier: params.identifier, transactionNumber: params.transactionNumber, project: params.project, object: params.object, agent, recipient, expires, description, permitServiceCredentials })({ permit: permitService }); break; case factory.account.transactionType.Withdraw: yield processWithdrawTransaction({ identifier: params.identifier, transactionNumber: params.transactionNumber, project: params.project, object: params.object, agent, recipient, expires, description, permitServiceCredentials })({ permit: permitService }); break; default: throw new factory.errors.Argument('Object', 'At least one of accounts from and to must be specified'); } // return pendingTransaction; }); } function processDepositTransaction(params) { return (repos) => __awaiter(this, void 0, void 0, function* () { var _a; const accountTransactionService = new (yield pecorinoapi.loadPecorino()).service.AccountTransaction({ endpoint: params.permitServiceCredentials.permitServiceEndpoint, auth: yield pecorinoapi.auth.ClientCredentials.createInstance({ domain: params.permitServiceCredentials.permitServiceAuthorizeServerDomain, clientId: params.permitServiceCredentials.permitServiceClientId, clientSecret: params.permitServiceCredentials.permitServiceClientSecret, scopes: [], state: '' }) }); if (typeof params.object.toLocation.identifier !== 'string') { throw new factory.errors.ArgumentNull('object.toLocation.identifier'); } const permit = yield repos.permit.findByIdentifier({ project: { id: params.project.id }, identifier: params.object.toLocation.identifier, issuedThrough: { typeOf: factory.product.ProductType.PaymentCard } }); return accountTransactionService.start(Object.assign({ transactionNumber: params.transactionNumber, project: { typeOf: params.project.typeOf, id: params.project.id }, typeOf: factory.account.transactionType.Deposit, agent: params.agent, expires: params.expires, recipient: params.recipient, object: { amount: { value: params.object.amount }, description: params.description, toLocation: { accountNumber: String((_a = permit.paymentAccount) === null || _a === void 0 ? void 0 : _a.accountNumber) } } }, (typeof params.identifier === 'string') ? { identifier: params.identifier } : undefined)); }); } function processTransferTransaction(params) { return (repos) => __awaiter(this, void 0, void 0, function* () { var _a, _b; const accountTransactionService = new (yield pecorinoapi.loadPecorino()).service.AccountTransaction({ endpoint: params.permitServiceCredentials.permitServiceEndpoint, auth: yield pecorinoapi.auth.ClientCredentials.createInstance({ domain: params.permitServiceCredentials.permitServiceAuthorizeServerDomain, clientId: params.permitServiceCredentials.permitServiceClientId, clientSecret: params.permitServiceCredentials.permitServiceClientSecret, scopes: [], state: '' }) }); if (typeof params.object.fromLocation.identifier !== 'string') { throw new factory.errors.ArgumentNull('object.fromLocation.identifier'); } if (typeof params.object.toLocation.identifier !== 'string') { throw new factory.errors.ArgumentNull('object.toLocation.identifier'); } let fromAccountNumber = params.object.fromLocation.identifier; if (params.object.fromLocation.hasNoPermit === true) { // no op } else { const fromLocationPermit = yield repos.permit.findByIdentifier({ project: { id: params.project.id }, identifier: params.object.fromLocation.identifier, issuedThrough: { typeOf: factory.product.ProductType.PaymentCard } }); fromAccountNumber = String((_a = fromLocationPermit.paymentAccount) === null || _a === void 0 ? void 0 : _a.accountNumber); } let toAccountNumber = params.object.toLocation.identifier; if (params.object.toLocation.hasNoPermit === true) { // no op } else { const toLocationPermit = yield repos.permit.findByIdentifier({ project: { id: params.project.id }, identifier: params.object.toLocation.identifier, issuedThrough: { typeOf: factory.product.ProductType.PaymentCard } }); toAccountNumber = String((_b = toLocationPermit.paymentAccount) === null || _b === void 0 ? void 0 : _b.accountNumber); } return accountTransactionService.start(Object.assign({ transactionNumber: params.transactionNumber, project: { typeOf: params.project.typeOf, id: params.project.id }, typeOf: factory.account.transactionType.Transfer, agent: params.agent, expires: params.expires, recipient: params.recipient, object: { amount: { value: params.object.amount }, description: params.description, fromLocation: { accountNumber: fromAccountNumber }, toLocation: { accountNumber: toAccountNumber } } }, (typeof params.identifier === 'string') ? { identifier: params.identifier } : undefined)); }); } function processWithdrawTransaction(params) { return (repos) => __awaiter(this, void 0, void 0, function* () { var _a; // 転送先口座が指定されていない場合は、出金取引 const accountTransactionService = new (yield pecorinoapi.loadPecorino()).service.AccountTransaction({ endpoint: params.permitServiceCredentials.permitServiceEndpoint, auth: yield pecorinoapi.auth.ClientCredentials.createInstance({ domain: params.permitServiceCredentials.permitServiceAuthorizeServerDomain, clientId: params.permitServiceCredentials.permitServiceClientId, clientSecret: params.permitServiceCredentials.permitServiceClientSecret, scopes: [], state: '' }) }); if (typeof params.object.fromLocation.identifier !== 'string') { throw new factory.errors.ArgumentNull('object.toLocation.identifier'); } const fromLocationPermit4withdraw = yield repos.permit.findByIdentifier({ project: { id: params.project.id }, identifier: params.object.fromLocation.identifier, issuedThrough: { typeOf: factory.product.ProductType.PaymentCard } }); return accountTransactionService.start(Object.assign({ transactionNumber: params.transactionNumber, project: { typeOf: params.project.typeOf, id: params.project.id }, typeOf: factory.account.transactionType.Withdraw, agent: params.agent, expires: params.expires, recipient: params.recipient, object: { amount: { value: params.object.amount }, description: params.description, fromLocation: { accountNumber: String((_a = fromLocationPermit4withdraw.paymentAccount) === null || _a === void 0 ? void 0 : _a.accountNumber) }, force: params.object.force === true } }, (typeof params.identifier === 'string') ? { identifier: params.identifier } : undefined)); }); } function createAccountTransactionAgent(params) { return { typeOf: params.agent.typeOf, id: String(params.agent.id), name: (typeof params.agent.name === 'string') ? params.agent.name : `${params.transaction.typeOf} Transaction ${params.transaction.id}` }; } function createAccountTransactionRecipient(params) { const transaction = params.transaction; let recipient; const recipientType = params.recipient.typeOf; if (recipientType === factory.organizationType.Corporation) { recipient = { typeOf: recipientType, id: String(params.recipient.id), name: (typeof params.recipient.name === 'string') ? params.recipient.name : `${transaction.typeOf} Transaction ${transaction.id}` }; } else { if (recipientType === factory.creativeWorkType.SoftwareApplication) { throw new factory.errors.NotImplemented(`recipient.typeOf [${recipientType}] not implemented`); } const recipientButSeller = { typeOf: recipientType, id: String(params.recipient.id), name: (typeof params.recipient.name === 'string') ? params.recipient.name : `${transaction.typeOf} Transaction ${transaction.id}` }; recipient = recipientButSeller; } return recipient; } function getIssuedThroughId(params) { var _a, _b; let issuedThroughId; switch (params.typeOf) { case factory.account.transactionType.Deposit: issuedThroughId = String((_a = params.object.toLocation.issuedThrough) === null || _a === void 0 ? void 0 : _a.id); break; case factory.account.transactionType.Transfer: case factory.account.transactionType.Withdraw: issuedThroughId = String((_b = params.object.fromLocation.issuedThrough) === null || _b === void 0 ? void 0 : _b.id); break; default: throw new factory.errors.Argument('AccountTransactionType', `invalid type: ${params.typeOf}`); } return issuedThroughId; } function getIssuedThroughIdByTransaction(params) { var _a, _b, _c; let issuedThroughId; const pendingTransactionType = (_a = params.transaction.object.pendingTransaction) === null || _a === void 0 ? void 0 : _a.typeOf; switch (pendingTransactionType) { case factory.account.transactionType.Deposit: issuedThroughId = String((_b = params.transaction.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.transaction.object.fromLocation.issuedThrough) === null || _c === void 0 ? void 0 : _c.id); break; default: throw new factory.errors.Argument('AccountTransactionType', `invalid type: ${pendingTransactionType}`); } return issuedThroughId; } function getIssuedThroughIdByAction(params) { var _a, _b; let issuedThroughId; const pendingTransactionType = params.pendingTransactionType; switch (pendingTransactionType) { case factory.account.transactionType.Deposit: issuedThroughId = String((_a = params.action.toLocation.issuedThrough) === null || _a === void 0 ? void 0 : _a.id); break; case factory.account.transactionType.Transfer: case factory.account.transactionType.Withdraw: issuedThroughId = String((_b = params.action.fromLocation.issuedThrough) === null || _b === void 0 ? void 0 : _b.id); break; default: throw new factory.errors.Argument('AccountTransactionType', `invalid type: ${pendingTransactionType}`); } return issuedThroughId; } function createPermitServiceCredentials(params) { return (repos) => __awaiter(this, void 0, void 0, function* () { var _a, _b, _c, _d, _e, _f, _g; const paymentCardService = (yield repos.product.projectFields({ limit: 1, page: 1, id: { $eq: params.issuedThrough.id }, typeOf: { $eq: factory.product.ProductType.PaymentCard } }, ['availableChannel'] // [] )).shift(); if (paymentCardService === undefined) { throw new factory.errors.NotFound(factory.product.ProductType.PaymentCard); } const permitServiceEndpoint = (_a = paymentCardService.availableChannel) === null || _a === void 0 ? void 0 : _a.serviceUrl; const permitServiceAuthorizeServerDomain = (_c = (_b = paymentCardService.availableChannel) === null || _b === void 0 ? void 0 : _b.credentials) === null || _c === void 0 ? void 0 : _c.authorizeServerDomain; const permitServiceClientId = (_e = (_d = paymentCardService.availableChannel) === null || _d === void 0 ? void 0 : _d.credentials) === null || _e === void 0 ? void 0 : _e.clientId; const permitServiceClientSecret = (_g = (_f = paymentCardService.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 cancelMoneyTransfer(params) { return (repos) => __awaiter(this, void 0, void 0, function* () { var _a; const transactions = yield repos.assetTransaction.search({ typeOf: factory.assetTransactionType.MoneyTransfer, ids: [params.purpose.id] }); if (transactions.length > 0) { for (const transaction of transactions) { const pendingTransaction = (_a = transaction.object) === null || _a === void 0 ? void 0 : _a.pendingTransaction; if (typeof (pendingTransaction === null || pendingTransaction === void 0 ? void 0 : pendingTransaction.transactionNumber) === 'string') { const issuedThroughId = getIssuedThroughIdByTransaction({ transaction }); const { permitServiceEndpoint, permitServiceAuthorizeServerDomain, permitServiceClientId, permitServiceClientSecret } = yield createPermitServiceCredentials({ issuedThrough: { id: issuedThroughId } })(repos); // 汎用中止サービスを使用(2022-09-27~) const accountTransactionService = new (yield pecorinoapi.loadPecorino()).service.AccountTransaction({ endpoint: permitServiceEndpoint, auth: yield pecorinoapi.auth.ClientCredentials.createInstance({ domain: permitServiceAuthorizeServerDomain, clientId: permitServiceClientId, clientSecret: permitServiceClientSecret, scopes: [], state: '' }) }); // 取引存在検証(2022-10-26~) const searchAccountTransactionsResult = yield accountTransactionService.search({ limit: 1, project: { id: { $eq: transaction.project.id } }, transactionNumber: { $eq: pendingTransaction.transactionNumber } }); if (searchAccountTransactionsResult.data.length > 0) { yield accountTransactionService.cancelSync({ transactionNumber: pendingTransaction.transactionNumber }); } } } } }); } function moneyTransfer(params) { return (repos) => __awaiter(this, void 0, void 0, function* () { const action = yield repos.action.start(params); try { const pendingTransaction = params.object.pendingTransaction; let transactionType = params.object.typeOf; if (pendingTransaction !== undefined) { transactionType = pendingTransaction.typeOf; } const transactionNumber = yield fixAccountTransactionNumber(params)({ transactionNumber: repos.transactionNumber }); const issuedThroughId = getIssuedThroughIdByAction({ action: params, pendingTransactionType: transactionType }); const permitServiceCredentials = yield createPermitServiceCredentials({ issuedThrough: { id: issuedThroughId } })(repos); const accountTransactionService = new (yield pecorinoapi.loadPecorino()).service.AccountTransaction({ endpoint: permitServiceCredentials.permitServiceEndpoint, auth: yield pecorinoapi.auth.ClientCredentials.createInstance({ domain: permitServiceCredentials.permitServiceAuthorizeServerDomain, clientId: permitServiceCredentials.permitServiceClientId, clientSecret: permitServiceCredentials.permitServiceClientSecret, scopes: [], state: '' }) }); // 入金取引の場合、承認済でないケースがある(ポイント付与など) if (transactionType === factory.account.transactionType.Deposit && pendingTransaction === undefined) { yield processDepositFromNow(params, permitServiceCredentials, transactionNumber)({ accountTransactionService }); } else { yield accountTransactionService.confirmSync({ transactionNumber: transactionNumber }); } } catch (error) { try { yield repos.action.giveUp({ typeOf: action.typeOf, id: action.id, error }); } catch (__) { // no op } throw error; } const actionResult = {}; yield repos.action.completeWithVoid({ typeOf: action.typeOf, id: action.id, result: actionResult }); }); } function fixAccountTransactionNumber(params) { return (repos) => __awaiter(this, void 0, void 0, function* () { const pendingTransaction = params.object.pendingTransaction; let transactionNumber = params.object.transactionNumber; if (pendingTransaction !== undefined) { transactionNumber = pendingTransaction.transactionNumber; } // 取引番号指定でなければ発行 if (typeof transactionNumber !== 'string') { const publishTransactionNumberResult = yield repos.transactionNumber.publishByTimestamp({ startDate: new Date() }); transactionNumber = publishTransactionNumberResult.transactionNumber; } return transactionNumber; }); } /** * 新規で入金取引を確定させる * 処理順序は? * 1.Confirmedでない取引があれば中止する * 2.Confirmedの取引があれば再度confirmSync * 3.取引が存在しなければ新たに取引開始してconfirmSync */ function processDepositFromNow(params, permitServiceCredentials, transactionNumber) { return (repos) => __awaiter(this, void 0, void 0, function* () { var _a; const accountTransactionIdentifier = params.purpose.identifier; // すでに入金済かどうか確認 let confirmedAccountTransactionNumber; if (typeof accountTransactionIdentifier === 'string') { // 口座取引で確認する(2022-10-27~) const searchAccountTransactionsResult = yield repos.accountTransactionService.search({ limit: 100, project: { id: { $eq: params.project.id } }, identifier: { $eq: accountTransactionIdentifier } }); const existingAccountTransactions = searchAccountTransactionsResult.data; for (const existingAccountTransaction of existingAccountTransactions) { if (existingAccountTransaction.status === factory.transactionStatusType.Confirmed) { confirmedAccountTransactionNumber = existingAccountTransaction.transactionNumber; } else { yield repos.accountTransactionService.cancelSync({ transactionNumber: existingAccountTransaction.transactionNumber }); } } } if (typeof confirmedAccountTransactionNumber === 'string') { // 念のためconfirm yield repos.accountTransactionService.confirmSync({ transactionNumber: confirmedAccountTransactionNumber }); return; } const agent = createAgent4depositFromNow(params); const recipient = createRecipient4depositFromNow(params); const expires = moment() .add(1, 'minutes') .toDate(); const amount = (typeof params.amount.value === 'number') ? params.amount.value : 0; const description = (typeof params.description === 'string') ? params.description : params.purpose.typeOf; const permitIdentifier = String(params.toLocation.identifier); // Permitの存在確認 const permitService = new (yield pecorinoapi.loadPecorino()).service.Permit({ endpoint: permitServiceCredentials.permitServiceEndpoint, auth: yield pecorinoapi.auth.ClientCredentials.createInstance({ domain: permitServiceCredentials.permitServiceAuthorizeServerDomain, clientId: permitServiceCredentials.permitServiceClientId, clientSecret: permitServiceCredentials.permitServiceClientSecret, scopes: [], state: '' }) }); const permit = yield permitService.findByIdentifier({ project: { id: params.project.id }, identifier: permitIdentifier, issuedThrough: { typeOf: factory.product.ProductType.PaymentCard } }); yield repos.accountTransactionService.start(Object.assign({ transactionNumber, project: { typeOf: params.project.typeOf, id: params.project.id }, typeOf: factory.account.transactionType.Deposit, agent, expires, recipient, object: { amount: { value: amount }, description: description, // fromLocation: params.fromLocation, toLocation: { accountNumber: String((_a = permit.paymentAccount) === null || _a === void 0 ? void 0 : _a.accountNumber) } } }, (typeof accountTransactionIdentifier === 'string') ? { identifier: accountTransactionIdentifier } : undefined)); try { yield repos.accountTransactionService.confirmSync({ transactionNumber }); } catch (error) { yield repos.accountTransactionService.cancelSync({ transactionNumber }); throw error; } }); } function createAgent4depositFromNow(params) { return { typeOf: params.agent.typeOf, id: String(params.agent.id), name: (typeof params.agent.name === 'string') ? params.agent.name : params.fromLocation.typeOf }; } function createRecipient4depositFromNow(params) { var _a, _b; let recipient = { typeOf: factory.personType.Person, id: String(String(params.toLocation.identifier)), name: params.toLocation.typeOf }; if (((_a = params.recipient) === null || _a === void 0 ? void 0 : _a.typeOf) === factory.organizationType.Corporation) { recipient = { typeOf: params.recipient.typeOf, id: String(params.recipient.id), name: (typeof params.recipient.name === 'string') ? params.recipient.name : params.toLocation.typeOf }; } else if (typeof ((_b = params.recipient) === null || _b === void 0 ? void 0 : _b.typeOf) === 'string') { if (params.recipient.typeOf === factory.creativeWorkType.SoftwareApplication) { throw new factory.errors.NotImplemented(`recipient.typeOf [${params.recipient.typeOf}] not implemented`); } const recipientButSeller = { typeOf: params.recipient.typeOf, id: String(params.recipient.id), name: (typeof params.recipient.name === 'string') ? params.recipient.name : params.toLocation.typeOf }; recipient = recipientButSeller; } return recipient; } /** * 返金後のアクション * @param refundActionAttributes 返金アクション属性 */ // function onRefund(refundActionAttributes: factory.action.trade.refund.IAttributes<factory.paymentMethodType>) { // return async (repos: { task: TaskRepo }) => { // const potentialActions = refundActionAttributes.potentialActions; // const now = new Date(); // const taskAttributes: factory.task.IAttributes<factory.taskName>[] = []; // // 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 (Array.isArray(potentialActions.sendEmailMessage)) { // potentialActions.sendEmailMessage.forEach((s) => { // const sendEmailMessageTask: factory.task.IAttributes<factory.taskName.SendEmailMessage> = { // project: s.project, // name: factory.taskName.SendEmailMessage, // status: factory.taskStatus.Ready, // runsAt: now, // なるはやで実行 // remainingNumberOfTries: 3, // numberOfTried: 0, // executionResults: [], // data: { // actionAttributes: s // } // }; // taskAttributes.push(sendEmailMessageTask); // }); // } // } // // タスク保管 // await Promise.all(taskAttributes.map(async (taskAttribute) => { // return repos.task.save(taskAttribute); // })); // }; // }