UNPKG

@chevre/domain

Version:

Chevre Domain Library for Node.js

428 lines (427 loc) 23.1 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.payPaymentCard = payPaymentCard; exports.refundPaymentCard = refundPaymentCard; exports.voidTransaction = voidTransaction; /** * ペイメントカード決済サービス */ const moment = require("moment-timezone"); const pecorinoapi = require("../../pecorinoapi"); const errorHandler_1 = require("../../errorHandler"); const factory = require("../../factory"); const onPayActionCompleted_1 = require("./any/onPayActionCompleted"); const onRefundActionCompletedOrFailed_1 = require("./any/onRefundActionCompletedOrFailed"); const accountTransactionIdentifier_1 = require("../../factory/accountTransactionIdentifier"); function authorize(params, paymentServiceId) { return (repos) => __awaiter(this, void 0, void 0, function* () { var _a; const transactionNumber = params.transactionNumber; if (typeof transactionNumber !== 'string') { throw new factory.errors.ArgumentNull('transactionNumber'); } // 決済方法検証 const { permit, currency } = yield validatePaymentMethod(params, paymentServiceId)(repos); const accountNumber = (_a = permit.paymentAccount) === null || _a === void 0 ? void 0 : _a.accountNumber; if (typeof accountNumber !== 'string') { throw new factory.errors.ArgumentNull('permit.paymentAccount.accountNumber'); } // 口座取引開始 let pendingTransaction; try { const expires = moment(params.expires) .add(1, 'month') .toDate(); pendingTransaction = yield processAccountTransaction({ transactionNumber, project: { id: params.project.id, typeOf: factory.organizationType.Project }, paymentMethod: params.object.paymentMethod, agent: params.agent, recipient: params.recipient, expires: expires, accountNumber, paymentServiceId })({ product: repos.product }); } catch (error) { // PecorinoAPIのエラーをハンドリング throw (0, errorHandler_1.handlePecorinoError)(error); } return { pendingTransaction, currency, accountNumber }; }); } function validatePaymentMethod(params, paymentServiceId) { return (repos) => __awaiter(this, void 0, void 0, function* () { var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l; const serviceOutputIdentifier = (_a = params.object.paymentMethod) === null || _a === void 0 ? void 0 : _a.accountId; const amount = (_b = params.object.paymentMethod) === null || _b === void 0 ? void 0 : _b.amount; const paymentMethodType = (_c = params.object.paymentMethod) === null || _c === void 0 ? void 0 : _c.identifier; if (typeof serviceOutputIdentifier !== 'string') { throw new factory.errors.ArgumentNull('object.paymentMethod.accountId'); } if (typeof amount !== 'number') { throw new factory.errors.ArgumentNull('object.paymentMethod.amount'); } if (typeof paymentMethodType !== 'string') { throw new factory.errors.ArgumentNull('object.paymentMethod.identifier'); } // プロダクトから通貨区分を取得 const paymentCatdProduct = (yield repos.product.projectFields({ limit: 1, page: 1, id: { $eq: paymentServiceId }, typeOf: { $eq: factory.product.ProductType.PaymentCard } }, ['serviceOutput'] // [] )).shift(); const currency = (_e = (_d = paymentCatdProduct === null || paymentCatdProduct === void 0 ? void 0 : paymentCatdProduct.serviceOutput) === null || _d === void 0 ? void 0 : _d.amount) === null || _e === void 0 ? void 0 : _e.currency; if (typeof currency !== 'string') { throw new factory.errors.NotFound('product.serviceOutput.amount.currency'); } // プロダクトのavailableChannelを取得する const availableChannel = yield repos.product.findAvailableChannel({ project: { id: params.project.id }, typeOf: factory.service.paymentService.PaymentServiceType.PaymentCard, id: paymentServiceId }); const permitService = new (yield pecorinoapi.loadPecorino()).service.Permit({ endpoint: String(availableChannel.serviceUrl), auth: yield pecorinoapi.auth.ClientCredentials.createInstance({ domain: String((_f = availableChannel.credentials) === null || _f === void 0 ? void 0 : _f.authorizeServerDomain), clientId: String((_g = availableChannel.credentials) === null || _g === void 0 ? void 0 : _g.clientId), clientSecret: String((_h = availableChannel.credentials) === null || _h === void 0 ? void 0 : _h.clientSecret), scopes: [], state: '' }) }); // サービスアウトプット存在確認 const serviceOutput = yield permitService.findByIdentifier({ project: { id: params.project.id }, identifier: serviceOutputIdentifier, issuedThrough: { typeOf: factory.product.ProductType.PaymentCard } }); // サービスタイプを確認 if (((_j = serviceOutput.issuedThrough) === null || _j === void 0 ? void 0 : _j.typeOf) !== factory.product.ProductType.PaymentCard) { throw new factory.errors.Argument('object.paymentMethod.accountId', 'serviceOutput.issuedThrough?.typeOf must be PaymentCard'); } if (((_l = (_k = serviceOutput.issuedThrough) === null || _k === void 0 ? void 0 : _k.serviceType) === null || _l === void 0 ? void 0 : _l.codeValue) !== paymentMethodType) { throw new factory.errors.Argument('object.paymentMethod.accountId', 'paymentMethodType not matched'); } // 出金金額設定を確認 const paymentAmount = serviceOutput.paymentAmount; if (typeof (paymentAmount === null || paymentAmount === void 0 ? void 0 : paymentAmount.minValue) === 'number') { if (amount < paymentAmount.minValue) { throw new factory.errors.Argument('object.paymentMethod.amount', `mininum payment amount requirement not satisfied`); } } if (typeof (paymentAmount === null || paymentAmount === void 0 ? void 0 : paymentAmount.maxValue) === 'number') { if (amount > paymentAmount.maxValue) { throw new factory.errors.Argument('object.paymentMethod.amount', `maximum payment amount requirement not satisfied`); } } return { permit: serviceOutput, currency }; }); } function processAccountTransaction(params) { return (repos) => __awaiter(this, void 0, void 0, function* () { var _a, _b, _c, _d, _e, _f, _g, _h; let pendingTransaction; const defaultName = `${factory.assetTransactionType.Pay} Transaction ${params.transactionNumber}`; const agent = { // ...params.agent, name: (typeof params.agent.name === 'string') ? params.agent.name : defaultName, typeOf: params.agent.typeOf }; const recipient = { // ...params.recipient, name: (typeof params.recipient.name === 'string') ? params.recipient.name : defaultName, typeOf: params.recipient.typeOf }; const description = (typeof ((_a = params.paymentMethod) === null || _a === void 0 ? void 0 : _a.description) === 'string') ? (_b = params.paymentMethod) === null || _b === void 0 ? void 0 : _b.description : ''; const accountNumber = params.accountNumber; const accountTransactionIdentifier = (0, accountTransactionIdentifier_1.createPayIdentifier)({ project: { id: params.project.id }, paymentMethodId: params.transactionNumber }); // プロダクトのavailableChannelを取得する const availableChannel = yield repos.product.findAvailableChannel({ project: { id: params.project.id }, typeOf: factory.service.paymentService.PaymentServiceType.PaymentCard, id: params.paymentServiceId }); const accountTransactionService = new (yield pecorinoapi.loadPecorino()).service.AccountTransaction({ endpoint: String(availableChannel.serviceUrl), auth: yield pecorinoapi.auth.ClientCredentials.createInstance({ domain: String((_c = availableChannel.credentials) === null || _c === void 0 ? void 0 : _c.authorizeServerDomain), clientId: String((_d = availableChannel.credentials) === null || _d === void 0 ? void 0 : _d.clientId), clientSecret: String((_e = availableChannel.credentials) === null || _e === void 0 ? void 0 : _e.clientSecret), scopes: [], state: '' }) }); // ひとまず出金取引に限定 pendingTransaction = yield accountTransactionService.start({ transactionNumber: params.transactionNumber, project: { typeOf: params.project.typeOf, id: params.project.id }, typeOf: factory.account.transactionType.Withdraw, agent: agent, expires: params.expires, recipient: recipient, object: { amount: { value: (typeof ((_f = params.paymentMethod) === null || _f === void 0 ? void 0 : _f.amount) === 'number') ? (_g = params.paymentMethod) === null || _g === void 0 ? void 0 : _g.amount : (_h = params.paymentMethod) === null || _h === void 0 ? void 0 : _h.amount.value }, description: description, fromLocation: { accountNumber: accountNumber } }, identifier: accountTransactionIdentifier }); return pendingTransaction; }); } function voidTransaction(params) { return (repos) => __awaiter(this, void 0, void 0, function* () { var _a, _b, _c; const transaction = params.object; // const paymentMethodId = transaction.object.paymentMethod?.paymentMethodId; const paymentMethodId = transaction.object.paymentMethodId; if (typeof paymentMethodId !== 'string') { throw new factory.errors.ArgumentNull('object.paymentMethodId'); } const paymentServiceId = String(transaction.object.id); // アクションステータスに関係なく取消処理実行 // プロダクトのavailableChannelを取得する const availableChannel = yield repos.product.findAvailableChannel({ project: { id: transaction.project.id }, typeOf: factory.service.paymentService.PaymentServiceType.PaymentCard, id: paymentServiceId }); // 汎用中止サービスを使用(2022-09-27~) const accountTransactionService = new (yield pecorinoapi.loadPecorino()).service.AccountTransaction({ endpoint: String(availableChannel.serviceUrl), auth: yield pecorinoapi.auth.ClientCredentials.createInstance({ domain: String((_a = availableChannel.credentials) === null || _a === void 0 ? void 0 : _a.authorizeServerDomain), clientId: String((_b = availableChannel.credentials) === null || _b === void 0 ? void 0 : _b.clientId), clientSecret: String((_c = availableChannel.credentials) === null || _c === void 0 ? void 0 : _c.clientSecret), scopes: [], state: '' }) }); // 取引存在検証(2022-10-26~) const searchAccountTransactionsResult = yield accountTransactionService.search({ limit: 1, project: { id: { $eq: transaction.project.id } }, transactionNumber: { $eq: paymentMethodId } }); if (searchAccountTransactionsResult.data.length > 0) { yield accountTransactionService.cancelSync({ transactionNumber: paymentMethodId }); } }); } function payPaymentCard(params) { return (repos) => __awaiter(this, void 0, void 0, function* () { var _a, _b, _c; const payObject = params.object; const paymentServiceId = payObject[0].id; const action = yield repos.action.start(params); try { const transactionNumber = payObject[0].paymentMethod.paymentMethodId; const availableChannel = yield repos.product.findAvailableChannel({ project: { id: params.project.id }, typeOf: factory.service.paymentService.PaymentServiceType.PaymentCard, id: paymentServiceId }); const accountTransactionService = new (yield pecorinoapi.loadPecorino()).service.AccountTransaction({ endpoint: String(availableChannel.serviceUrl), auth: yield pecorinoapi.auth.ClientCredentials.createInstance({ domain: String((_a = availableChannel.credentials) === null || _a === void 0 ? void 0 : _a.authorizeServerDomain), clientId: String((_b = availableChannel.credentials) === null || _b === void 0 ? void 0 : _b.clientId), clientSecret: String((_c = availableChannel.credentials) === null || _c === void 0 ? void 0 : _c.clientSecret), scopes: [], state: '' }) }); 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 }); yield (0, onPayActionCompleted_1.onPayActionCompleted)({ // actionStatus: factory.actionStatusType.CompletedActionStatus, id: action.id, object: params.object, potentialActions: params.potentialActions, project: params.project, purpose: params.purpose, typeOf: action.typeOf })(repos); // return action; }); } function refundPaymentCard(params) { // tslint:disable-next-line:max-func-body-length return (repos) => __awaiter(this, void 0, void 0, function* () { var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m; const paymentMethodId = (_a = params.object[0]) === null || _a === void 0 ? void 0 : _a.paymentMethod.paymentMethodId; const paymentServiceId = (_b = params.object[0]) === null || _b === void 0 ? void 0 : _b.id; const payTransaction = yield repos.assetTransaction.findByTransactionNumber({ typeOf: factory.assetTransactionType.Pay, transactionNumber: paymentMethodId }); // accountNumberをpendingTransactionから取得する let accountNumber; accountNumber = (_d = (_c = payTransaction.object.pendingTransaction) === null || _c === void 0 ? void 0 : _c.object.fromLocation) === null || _d === void 0 ? void 0 : _d.accountNumber; if (typeof accountNumber !== 'string') { throw new factory.errors.ArgumentNull('serviceOutput.paymentAccount.accountNumber'); } const action = yield repos.action.start(params); let accountTransactionNumber4refund; try { const availableChannel = yield repos.product.findAvailableChannel({ project: { id: params.project.id }, typeOf: factory.service.paymentService.PaymentServiceType.PaymentCard, id: paymentServiceId }); const accountTransactionService = new (yield pecorinoapi.loadPecorino()).service.AccountTransaction({ endpoint: String(availableChannel.serviceUrl), auth: yield pecorinoapi.auth.ClientCredentials.createInstance({ domain: String((_e = availableChannel.credentials) === null || _e === void 0 ? void 0 : _e.authorizeServerDomain), clientId: String((_f = availableChannel.credentials) === null || _f === void 0 ? void 0 : _f.clientId), clientSecret: String((_g = availableChannel.credentials) === null || _g === void 0 ? void 0 : _g.clientSecret), scopes: [], state: '' }) }); // 返金のユニークネスを保証するため識別子を指定する const accountTransactionIdentifier = (0, accountTransactionIdentifier_1.createRefundIdentifier)({ project: { id: params.project.id }, paymentMethodId }); // すでに返金済かどうか確認 let confirmedAccountTransactionNumber; if (typeof accountTransactionIdentifier === 'string') { // 口座取引で確認する(2022-10-27~) const searchAccountTransactionsResult = yield 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 accountTransactionService.cancelSync({ transactionNumber: existingAccountTransaction.transactionNumber }); } } } if (typeof confirmedAccountTransactionNumber === 'string') { // 念のためconfirm yield accountTransactionService.confirmSync({ transactionNumber: confirmedAccountTransactionNumber }); accountTransactionNumber4refund = confirmedAccountTransactionNumber; } else { const { transactionNumber } = yield repos.transactionNumber.publishByTimestamp({ startDate: new Date() }); const expires = moment() .add(1, 'minute') .toDate(); const agent = createAgent4refund(params); const recipient = createRecipient4refund(params); yield accountTransactionService.start({ transactionNumber: transactionNumber, project: { typeOf: params.project.typeOf, id: params.project.id }, typeOf: factory.account.transactionType.Deposit, agent: agent, expires: expires, recipient: recipient, object: { amount: { value: Number((_k = (_j = (_h = payTransaction.object) === null || _h === void 0 ? void 0 : _h.paymentMethod) === null || _j === void 0 ? void 0 : _j.totalPaymentDue) === null || _k === void 0 ? void 0 : _k.value) }, description: `Refund [${(_m = (_l = payTransaction.object) === null || _l === void 0 ? void 0 : _l.paymentMethod) === null || _m === void 0 ? void 0 : _m.description}]`, toLocation: { accountNumber } }, identifier: accountTransactionIdentifier }); try { yield accountTransactionService.confirmSync({ transactionNumber }); accountTransactionNumber4refund = transactionNumber; } catch (error) { yield accountTransactionService.cancelSync({ transactionNumber }); throw error; } } } catch (error) { try { yield repos.action.giveUp({ typeOf: action.typeOf, id: action.id, error }); } catch (__) { // no op } throw error; } const result = { accountTransaction: { transactionNumber: accountTransactionNumber4refund } }; yield repos.action.completeWithVoid({ typeOf: action.typeOf, id: action.id, result }); yield (0, onRefundActionCompletedOrFailed_1.onRefundActionCompletedOrFailed)({ actionStatus: factory.actionStatusType.CompletedActionStatus, id: action.id, object: params.object, potentialActions: params.potentialActions, project: params.project, purpose: params.purpose, typeOf: action.typeOf })(repos); // return action; }); } function createAgent4refund(params) { return { // ...params.agent, name: (typeof params.agent.name === 'string') ? params.agent.name : `${params.agent.typeOf} ${params.agent.id}`, typeOf: params.agent.typeOf }; } function createRecipient4refund(params) { var _a, _b, _c, _d; if (typeof ((_a = params.recipient) === null || _a === void 0 ? void 0 : _a.typeOf) !== 'string') { throw new factory.errors.NotFound('recipient.typeOf'); } return { // ...<factory.person.IPerson | factory.creativeWork.softwareApplication.webApplication.ICreativeWork>params.recipient, name: (typeof ((_b = params.recipient) === null || _b === void 0 ? void 0 : _b.name) === 'string') ? params.recipient.name : `${(_c = params.recipient) === null || _c === void 0 ? void 0 : _c.typeOf} ${(_d = params.recipient) === null || _d === void 0 ? void 0 : _d.id}`, typeOf: params.recipient.typeOf }; }