UNPKG

@chevre/domain

Version:

Chevre Domain Library for Node.js

623 lines (622 loc) 31.2 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.publishPaymentUrl = publishPaymentUrl; exports.check = check; exports.start = start; exports.confirm = confirm; exports.cancel = cancel; exports.exportTasksById = exportTasksById; exports.searchGMOTrade = searchGMOTrade; const moment = require("moment"); const factory = require("../../factory"); const CreditCardPayment = require("../payment/creditCard"); const MovieTicketPayment = require("../payment/movieTicket"); const PaymentCardPayment = require("../payment/paymentCard"); const fixInformAction_1 = require("./fixInformAction"); const validation_1 = require("./pay/account/validation"); const factory_1 = require("./pay/factory"); function publishPaymentUrlResult2recipe(params) { const { project, result } = params; return { project: { id: project.id, typeOf: factory.organizationType.Project }, typeOf: 'Recipe', recipeCategory: factory.recipe.RecipeCategory.publishPaymentUrl, step: [{ typeOf: 'HowToSection', itemListElement: [ { typeOf: 'HowToStep', identifier: factory.recipe.StepIdentifier.entryTran, itemListElement: [{ typeOf: 'HowToDirection', beforeMedia: result.entryTranArgs, afterMedia: result.entryTranResult // text: 'entryTran' }] }, { typeOf: 'HowToStep', identifier: factory.recipe.StepIdentifier.execTran, itemListElement: [{ typeOf: 'HowToDirection', beforeMedia: result.execTranArgs, afterMedia: result.execTranResult // text: 'execTran' }] } ] }] }; } /** * 決済ロケーション発行 */ // tslint:disable-next-line:max-func-body-length function publishPaymentUrl(params, options) { // tslint:disable-next-line:max-func-body-length return (repos, settings) => __awaiter(this, void 0, void 0, function* () { var _a, _b, _c, _d, _e, _f; const paymentServiceType = (_a = params.object) === null || _a === void 0 ? void 0 : _a.typeOf; // 金額をfix const amount = (_b = params.object.paymentMethod) === null || _b === void 0 ? void 0 : _b.amount; if (typeof amount !== 'number') { throw new factory.errors.ArgumentNull('object.paymentMethod.amount'); } const transactionNumber = params.transactionNumber; if (typeof transactionNumber !== 'string' || transactionNumber.length === 0) { throw new factory.errors.ArgumentNull('transactionNumber'); } yield validateSeller(params)(repos); // 決済サービス確認 const paymentServiceId = getPaymentServiceId(params); // 決済受入アクション生成(2024-03-28~) const taskId = (_c = options === null || options === void 0 ? void 0 : options.executor) === null || _c === void 0 ? void 0 : _c.id; const actionAttributes = Object.assign(Object.assign(Object.assign({ project: params.project, typeOf: factory.actionType.AcceptAction, agent: params.agent, object: { object: params.object, transactionNumber, typeOf: params.typeOf }, purpose: { typeOf: factory.transactionType.PlaceOrder, id: options.purposeAsTransaction.id } }, (typeof taskId === 'string') ? { sameAs: { id: taskId, typeOf: 'Task' } } // タスク関連付け(2024-05-22~) : undefined), (typeof ((_d = params.location) === null || _d === void 0 ? void 0 : _d.typeOf) === 'string') ? { location: params.location } : undefined), (typeof params.identifier === 'string') ? { identifier: params.identifier } : undefined); const action = yield repos.action.start(actionAttributes); let result; let recipe; try { switch (paymentServiceType) { case factory.service.paymentService.PaymentServiceType.CreditCard: const authorizeResult = yield CreditCardPayment.authorize(Object.assign(Object.assign({}, params), { id: '' // 決済URL発行プロセスにおいては決済取引IDは使用しないので空文字でok }), paymentServiceId, { processPublishPaymentUrl: true, executor: (typeof ((_e = options.executor) === null || _e === void 0 ? void 0 : _e.id) === 'string') ? { id: options.executor.id } : {} // タスク関連付け(2024-05-22~) })(repos, settings); let paymentUrl; // 3DS拡張(2024-01-02~) const retUrl = (_f = params.object.paymentMethod.creditCard) === null || _f === void 0 ? void 0 : _f.retUrl; if (typeof retUrl === 'string' && retUrl.length > 0) { paymentUrl = authorizeResult.execTranResult.redirectUrl; } else { paymentUrl = authorizeResult.execTranResult.acsUrl; } if (typeof paymentUrl !== 'string' || paymentUrl.length === 0) { throw new factory.errors.Internal(`Payment URL unable to publish. [retUrl: ${retUrl}]`); } result = { paymentMethodId: transactionNumber, paymentUrl, entryTranArgs: authorizeResult.entryTranArgs, entryTranResult: authorizeResult.entryTranResult, execTranArgs: authorizeResult.execTranArgs, execTranResult: authorizeResult.execTranResult }; // create recipe(2024-06-02~) recipe = publishPaymentUrlResult2recipe({ project: { id: params.project.id }, result }); break; default: throw new factory.errors.NotImplemented(`Payment service '${paymentServiceType}' not implemented`); } } catch (error) { try { yield repos.action.giveUp({ typeOf: actionAttributes.typeOf, id: action.id, error }); } catch (__) { // 失敗したら仕方ない } throw error; } const actionResult = { paymentMethodId: result.paymentMethodId, paymentUrl: result.paymentUrl }; yield repos.action.completeWithVoid({ typeOf: actionAttributes.typeOf, id: action.id, result: actionResult, recipe }); return result; }); } /** * 決済方法認証 */ function check(params) { return (repos, settings) => __awaiter(this, void 0, void 0, function* () { var _a; let action; const paymentServiceType = (_a = params.object[0]) === null || _a === void 0 ? void 0 : _a.typeOf; try { switch (paymentServiceType) { case factory.service.paymentService.PaymentServiceType.MovieTicket: action = yield MovieTicketPayment.checkMovieTicket(params)(repos, settings); break; default: throw new factory.errors.NotImplemented(`Payment service '${paymentServiceType}' not implemented`); } } catch (error) { throw error; } return action; }); } /** * 取引開始 */ function start(params, options) { return (repos, settings) => __awaiter(this, void 0, void 0, function* () { var _a, _b, _c, _d; // 金額をfix const amount = (_a = params.object.paymentMethod) === null || _a === void 0 ? void 0 : _a.amount; if (typeof amount !== 'number') { throw new factory.errors.ArgumentNull('object.paymentMethod.amount'); } const transactionNumber = params.transactionNumber; if (typeof transactionNumber !== 'string' || transactionNumber.length === 0) { throw new factory.errors.ArgumentNull('transactionNumber'); } yield validateSeller(params)(repos); // 決済サービス確認 const paymentServiceType = (_b = params.object) === null || _b === void 0 ? void 0 : _b.typeOf; const paymentService = yield fixPaymentService(params)(repos); const informActions = yield (0, fixInformAction_1.fixInformAction)({ paymentService, project: { id: params.project.id } })(repos); // 取引開始 let transaction; const startParams = (0, factory_1.createStartParams)(Object.assign(Object.assign({}, params), { transactionNumber, paymentServiceType, amount, paymentService, informActions }), { checkedAction: options.checkedAction } // 連携(2024-12-13~) ); transaction = yield repos.assetTransaction.start(startParams); switch (paymentServiceType) { case factory.service.paymentService.PaymentServiceType.FaceToFace: // 対面決済は特に何もしない break; case factory.service.paymentService.PaymentServiceType.PaymentCard: transaction = yield processAuthorizeAccount(params, transaction, String(paymentService === null || paymentService === void 0 ? void 0 : paymentService.id))(repos); break; case factory.service.paymentService.PaymentServiceType.CreditCard: // transaction = yield processAuthorizeCreditCard(params, transaction, String(paymentService === null || paymentService === void 0 ? void 0 : paymentService.id), Object.assign({ executor: (typeof ((_c = options.executor) === null || _c === void 0 ? void 0 : _c.id) === 'string') ? { id: options.executor.id } : {} }, (options.pendingPaymentAgencyTransaction !== undefined) ? { pendingPaymentAgencyTransaction: options.pendingPaymentAgencyTransaction } : undefined))(repos, settings); break; case factory.service.paymentService.PaymentServiceType.MovieTicket: transaction = yield processAuthorizeMovieTicket(params, transaction, String(paymentService === null || paymentService === void 0 ? void 0 : paymentService.id), // options.useCheckByIdentifierIfNotYet, { executor: (typeof ((_d = options.executor) === null || _d === void 0 ? void 0 : _d.id) === 'string') ? { id: options.executor.id } : {}, purpose: options.purpose, checkedAction: options.checkedAction })(repos, settings); break; default: throw new factory.errors.NotImplemented(`Payment service '${paymentServiceType}' not implemented`); } return transaction; }); } function getPaymentServiceId(params) { var _a; let paymentServiceId = ''; const paymentServiceType = (_a = params.object) === null || _a === void 0 ? void 0 : _a.typeOf; if (typeof params.object.id === 'string' && params.object.id.length > 0) { paymentServiceId = params.object.id; } else { switch (paymentServiceType) { case factory.service.paymentService.PaymentServiceType.FaceToFace: // 対面決済は特に何もしない break; case factory.service.paymentService.PaymentServiceType.PaymentCard: case factory.service.paymentService.PaymentServiceType.CreditCard: case factory.service.paymentService.PaymentServiceType.MovieTicket: // リクエストでの指定を必須化(2022-04-12~) throw new factory.errors.ArgumentNull('object.id'); default: throw new factory.errors.NotImplemented(`Payment service '${paymentServiceType}' not implemented`); } } return paymentServiceId; } function fixPaymentService(params) { return (repos) => __awaiter(this, void 0, void 0, function* () { var _a; const paymentServiceType = (_a = params.object) === null || _a === void 0 ? void 0 : _a.typeOf; let paymentService; switch (paymentServiceType) { case factory.service.paymentService.PaymentServiceType.FaceToFace: // no op // FaceToFaceの場合プロダクトは存在しない break; // PaymentCardの場合、プロダクト検索 case factory.service.paymentService.PaymentServiceType.PaymentCard: paymentService = (yield repos.product.projectFields({ limit: 1, page: 1, project: { id: { $eq: params.project.id } }, typeOf: { $eq: factory.product.ProductType.PaymentCard }, id: { $eq: getPaymentServiceId(params) } }, ['availableChannel', 'serviceOutput', 'serviceType', 'potentialAction'] // [] )).shift(); if (paymentService === undefined) { throw new factory.errors.NotFound('PaymentService'); } break; default: paymentService = (yield repos.paymentService.projectFields({ limit: 1, page: 1, project: { id: { $eq: params.project.id } }, typeOf: { $eq: paymentServiceType }, id: { $eq: getPaymentServiceId(params) } }, ['availableChannel', 'serviceOutput', 'serviceType', 'potentialAction'])).shift(); if (paymentService === undefined) { throw new factory.errors.NotFound('PaymentService'); } } return paymentService; }); } function validateSeller(params) { return (repos) => __awaiter(this, void 0, void 0, function* () { var _a, _b; const sellerId = (_a = params.recipient) === null || _a === void 0 ? void 0 : _a.id; if (typeof sellerId !== 'string') { throw new factory.errors.ArgumentNull('recipient.id'); } const paymentMethodType = (_b = params.object.paymentMethod) === null || _b === void 0 ? void 0 : _b.identifier; if (typeof paymentMethodType !== 'string') { throw new factory.errors.ArgumentNull('object.paymentMethod.identifier'); } // FaceToFaceの場合、決済方法区分未指定に対応(2023-08-29~) if (params.object.typeOf === factory.service.paymentService.PaymentServiceType.FaceToFace) { if (paymentMethodType.length > 0) { // 販売者の対応決済方法かどうか確認 // const paymentAccepted = seller.paymentAccepted?.some((a) => a.paymentMethodType === paymentMethodType); const paymentAccepted = yield repos.paymentAccepted.isAcceptedBySeller({ seller: { id: sellerId }, codeValue: paymentMethodType }); if (paymentAccepted !== true) { throw new factory.errors.Argument('object.paymentMethod.identifier', `payment not accepted [${paymentMethodType}]`); } } } else { // 販売者の対応決済方法かどうか確認 // const paymentAccepted = seller.paymentAccepted?.some((a) => a.paymentMethodType === paymentMethodType); const paymentAccepted = yield repos.paymentAccepted.isAcceptedBySeller({ seller: { id: sellerId }, codeValue: paymentMethodType }); if (paymentAccepted !== true) { throw new factory.errors.Argument('object.paymentMethod.identifier', `payment not accepted [${paymentMethodType}]`); } } }); } function processAuthorizeAccount(params, transaction, paymentServiceId) { return (repos) => __awaiter(this, void 0, void 0, function* () { var _a; const amount = (_a = params.object.paymentMethod) === null || _a === void 0 ? void 0 : _a.amount; if (typeof amount !== 'number') { throw new factory.errors.ArgumentNull('object.paymentMethod?.amount'); } yield (0, validation_1.validateAccount)(params)(repos); const { pendingTransaction, currency, accountNumber } = yield PaymentCardPayment.authorize(params, paymentServiceId)(repos); const totalPaymentDue = { typeOf: 'MonetaryAmount', currency, value: amount }; const savingPendingTransaction = { typeOf: pendingTransaction.typeOf, id: pendingTransaction.id, transactionNumber: pendingTransaction.transactionNumber, object: { fromLocation: { accountNumber } } }; return saveAuthorizeResult({ id: transaction.id, update: { 'object.paymentMethod.totalPaymentDue': totalPaymentDue, 'object.pendingTransaction': savingPendingTransaction } })(repos); }); } function processAuthorizeCreditCard(params, transaction, paymentServiceId, options) { return (repos, settings) => __awaiter(this, void 0, void 0, function* () { var _a; yield CreditCardPayment.authorize(Object.assign(Object.assign({}, params), { id: transaction.id }), paymentServiceId, Object.assign({ processPublishPaymentUrl: false, executor: (typeof ((_a = options.executor) === null || _a === void 0 ? void 0 : _a.id) === 'string') ? { id: options.executor.id } : {} }, (options.pendingPaymentAgencyTransaction !== undefined) ? { pendingPaymentAgencyTransaction: options.pendingPaymentAgencyTransaction } : undefined))(repos, settings); // ↓discontinue(2024-06-14~) // return saveAuthorizeResult({ // id: transaction.id, // update: { // 'object.accountId': authorizeResult.accountId, // 'object.paymentMethod.accountId': authorizeResult.accountId, // 'object.paymentMethod.paymentMethodId': authorizeResult.paymentMethodId, // 'object.entryTranArgs': authorizeResult.entryTranArgs, // 'object.entryTranResult': authorizeResult.entryTranResult, // 'object.execTranArgs': authorizeResult.execTranArgs, // 'object.execTranResult': authorizeResult.execTranResult, // ...(authorizeResult.secureTran2Result !== undefined) // ? { 'object.secureTran2Result': authorizeResult.secureTran2Result } // : undefined // } // })(repos); }); } function processAuthorizeMovieTicket(params, transaction, paymentServiceId, // useCheckByIdentifierIfNotYet: boolean, options) { return (repos, settings) => __awaiter(this, void 0, void 0, function* () { // const { accountsReceivablesByServiceType } = await MovieTicketPayment.authorize( // params, transaction, paymentServiceId, options // )(repos, settings); yield MovieTicketPayment.authorize(params, transaction, paymentServiceId, options)(repos, settings); return transaction; // discontinue(2024-12-17~) // return saveAuthorizeResult({ // id: transaction.id, // update: { // 'object.accountsReceivablesByServiceType': accountsReceivablesByServiceType // 認証レスポンスより計上金額を保管(2023-05-15~) // } // })(repos); }); } function saveAuthorizeResult(params) { return (repos) => __awaiter(this, void 0, void 0, function* () { return repos.assetTransaction.findByIdAndUpdateInProgress(params); }); } /** * 取引確定 */ function confirm(params) { return (repos) => __awaiter(this, void 0, void 0, function* () { var _a, _b, _c, _d, _e, _f, _g, _h; let transaction; // 取引存在確認 if (typeof params.id === 'string') { transaction = yield repos.assetTransaction.findById({ typeOf: factory.assetTransactionType.Pay, id: params.id }); } else if (typeof params.transactionNumber === 'string') { transaction = yield repos.assetTransaction.findByTransactionNumber({ typeOf: factory.assetTransactionType.Pay, transactionNumber: params.transactionNumber }); } else { throw new factory.errors.ArgumentNull('Transaction ID or Transaction Number'); } let overwritingPaymentMethodIdentifier; if (transaction.object.typeOf === factory.service.paymentService.PaymentServiceType.FaceToFace) { const specifiedPaymentMethodIdentifire = (_b = (_a = params.object) === null || _a === void 0 ? void 0 : _a.paymentMethod) === null || _b === void 0 ? void 0 : _b.identifier; if (typeof specifiedPaymentMethodIdentifire === 'string' && specifiedPaymentMethodIdentifire.length > 0) { overwritingPaymentMethodIdentifier = specifiedPaymentMethodIdentifire; transaction.object.paymentMethod.identifier = overwritingPaymentMethodIdentifier; } } const confirmationNumber = (_e = (_d = (_c = params.potentialActions) === null || _c === void 0 ? void 0 : _c.pay) === null || _d === void 0 ? void 0 : _d.purpose) === null || _e === void 0 ? void 0 : _e.confirmationNumber; const orderNumber = (_h = (_g = (_f = params.potentialActions) === null || _f === void 0 ? void 0 : _f.pay) === null || _g === void 0 ? void 0 : _g.purpose) === null || _h === void 0 ? void 0 : _h.orderNumber; if (typeof confirmationNumber !== 'string' || confirmationNumber.length === 0 || typeof orderNumber !== 'string' || orderNumber.length === 0) { throw new factory.errors.ArgumentNull('potentialActions.pay.purpose'); } let potentialActions; // 注文検証 const existingOrders = yield repos.order.projectFields({ limit: 1, page: 1, project: { id: { $eq: transaction.project.id } }, confirmationNumbers: [confirmationNumber], orderNumbers: [orderNumber] }, { inclusion: ['orderNumber'] }); if (existingOrders.length === 0) { throw new factory.errors.NotFound(factory.order.OrderType.Order); } const payActionData4order = { object: { paymentMethod: { paymentMethodId: transaction.transactionNumber } }, purpose: { confirmationNumber, orderNumber, typeOf: factory.order.OrderType.Order } }; potentialActions = { pay: [payActionData4order] }; yield repos.assetTransaction.confirm(Object.assign({ typeOf: factory.assetTransactionType.Pay, id: transaction.id, result: {}, potentialActions: potentialActions }, (typeof overwritingPaymentMethodIdentifier === 'string') ? { object: { paymentMethod: { identifier: overwritingPaymentMethodIdentifier } } } : undefined)); }); } /** * 取引中止 */ function cancel(params) { return (repos) => __awaiter(this, void 0, void 0, function* () { yield repos.assetTransaction.cancel({ typeOf: factory.assetTransactionType.Pay, id: params.id, transactionNumber: params.transactionNumber }); }); } /** * 取引のタスク出力 */ function exportTasksById(params) { // tslint:disable-next-line:max-func-body-length return (repos) => __awaiter(this, void 0, void 0, function* () { const transaction = yield repos.assetTransaction.findById({ typeOf: factory.assetTransactionType.Pay, id: params.id }); const potentialActions = transaction.potentialActions; if (transaction.status === factory.transactionStatusType.InProgress) { throw new factory.errors.NotImplemented(`Transaction status "${transaction.status}" not implemented.`); } const taskAttributes = []; // タスク実行日時バッファの指定があれば調整 let taskRunsAt = new Date(); if (typeof params.runsTasksAfterInSeconds === 'number') { taskRunsAt = moment(taskRunsAt) .add(params.runsTasksAfterInSeconds, 'seconds') .toDate(); } const payTransactionAsObject = { project: transaction.project, typeOf: transaction.typeOf, id: transaction.id, transactionNumber: transaction.transactionNumber, object: transaction.object, recipient: transaction.recipient }; const voidPaymentTasks = { project: transaction.project, name: factory.taskName.VoidPayment, status: factory.taskStatus.Ready, runsAt: taskRunsAt, remainingNumberOfTries: 10, numberOfTried: 0, executionResults: [], data: { object: payTransactionAsObject } }; // OnAssetTransactionStatusChangedを追加(2023-08-30~) const onAssetTransactionStatusChangedTaskData = { project: transaction.project, object: { typeOf: factory.assetTransactionType.Pay, transactionNumber: transaction.transactionNumber, status: transaction.status }, purpose: { confirmationNumber: '', orderNumber: '', typeOf: factory.order.OrderType.Order }, useOnOrderStatusChanged: true }; const onAssetTransactionStatusChangedTask = { project: transaction.project, name: factory.taskName.OnAssetTransactionStatusChanged, status: factory.taskStatus.Ready, runsAt: taskRunsAt, remainingNumberOfTries: 10, numberOfTried: 0, executionResults: [], data: onAssetTransactionStatusChangedTaskData }; switch (transaction.status) { case factory.transactionStatusType.Confirmed: const payActions = potentialActions === null || potentialActions === void 0 ? void 0 : potentialActions.pay; if (Array.isArray(payActions) && payActions.length > 0) { const payTasks = payActions.map(({ object, purpose }) => { const data = { object, purpose }; return { project: transaction.project, name: factory.taskName.Pay, status: factory.taskStatus.Ready, runsAt: taskRunsAt, remainingNumberOfTries: 10, numberOfTried: 0, executionResults: [], data }; }); taskAttributes.push(...payTasks); } break; case factory.transactionStatusType.Canceled: taskAttributes.push(voidPaymentTasks, onAssetTransactionStatusChangedTask); break; case factory.transactionStatusType.Expired: taskAttributes.push(voidPaymentTasks, onAssetTransactionStatusChangedTask); break; default: throw new factory.errors.NotImplemented(`Transaction status "${transaction.status}" not implemented.`); } return repos.task.saveMany(taskAttributes, { emitImmediately: true }); }); } /** * 決済代行取引参照 */ function searchGMOTrade(params) { return (repos, settings) => __awaiter(this, void 0, void 0, function* () { var _a, _b; const assetTransaction = yield repos.assetTransaction.findByTransactionNumber({ typeOf: factory.assetTransactionType.Pay, transactionNumber: params.transactionNumber }); if (assetTransaction.object.typeOf !== factory.service.paymentService.PaymentServiceType.CreditCard) { throw new factory.errors.Argument('transactionNumber', 'must be CreditCard payment service'); } // if (assetTransaction.status !== factory.transactionStatusType.Confirmed) { // throw new factory.errors.Argument('transactionNumber', 'must be confirmed'); // } // CreditCard系統の決済方法タイプは動的 const paymentMethodType = (_a = assetTransaction.object.paymentMethod) === null || _a === void 0 ? void 0 : _a.identifier; if (typeof paymentMethodType !== 'string') { throw new factory.errors.ArgumentNull('object.paymentMethod.identifier'); } const paymentServiceId = String(assetTransaction.object.id); const availableChannel = yield repos.paymentService.findAvailableChannelCreditCard({ project: assetTransaction.project, // typeOf: factory.service.paymentService.PaymentServiceType.CreditCard, id: paymentServiceId }); const sellerId = (_b = assetTransaction.recipient) === null || _b === void 0 ? void 0 : _b.id; if (typeof sellerId !== 'string') { throw new factory.errors.ArgumentNull('recipient.id'); } const { shopId, shopPass } = yield CreditCardPayment.getGMOInfoFromSeller({ paymentMethodType, seller: { id: sellerId }, paymentServiceId, requirePaymentAccepted: false })(repos); return CreditCardPayment.searchGMOTrade({ availableChannel, paymentMethodId: String(assetTransaction.transactionNumber), shopId, shopPass }, settings); }); }