UNPKG

@chevre/domain

Version:

Chevre Domain Library for Node.js

453 lines (452 loc) 28.7 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.validateTransaction = validateTransaction; exports.validateAcceptedOffers = validateAcceptedOffers; exports.validateOrderedItem = validateOrderedItem; exports.validatePaymentMethods = validatePaymentMethods; exports.validateEventOffers = validateEventOffers; /** * 注文取引バリデーション */ const createDebug = require("debug"); const util_1 = require("util"); const factory = require("../../../../factory"); const validateMovieTicket_1 = require("./validation/validateMovieTicket"); const debug = createDebug('chevre-domain:service:transaction'); /** * 取引が確定可能な状態かどうかをチェックする */ function validateTransaction(transaction, acceptPayActions, authorizePaymentActions, authorizeEventServiceOfferActions, authorizeMoneyTansferActions, authorizeProductOfferActions, eventReservationAcceptedOffers, payTransactions, customer) { // validateProfile(transaction); validateProfile({ customer }); validatePrice(authorizePaymentActions, authorizeEventServiceOfferActions, authorizeProductOfferActions, authorizeMoneyTansferActions); // 利用可能な通貨単位に対して取引検証 validateMonetaryAmount(authorizePaymentActions, authorizeEventServiceOfferActions); // 利用可能なMovieTicketIF決済方法タイプに対して動的にコーディング const movieTicketPaymentMethodTypes = findMovieTicketPaymentMethodTypesFromTransaction(authorizePaymentActions, eventReservationAcceptedOffers); debug('processing validateMovieTicket... movieTicketPaymentMethodTypes:', movieTicketPaymentMethodTypes); if (Array.isArray(movieTicketPaymentMethodTypes) && movieTicketPaymentMethodTypes.length > 0) { movieTicketPaymentMethodTypes.forEach((paymentMethodType) => { try { const authorizeMovieTicketActions = authorizePaymentActions.filter((a) => { var _a; const resultAsInvoice = (Array.isArray(a.result)) // ? a.result.find(({ typeOf }) => typeOf === factory.action.authorize.paymentMethod.any.ResultType.Payment) ? a.result[0] : undefined; return (resultAsInvoice === null || resultAsInvoice === void 0 ? void 0 : resultAsInvoice.issuedThrough.typeOf) === factory.service.paymentService.PaymentServiceType.MovieTicket // 決済方法区分は必ず存在するはず(2023-08-15~) && ((_a = resultAsInvoice.paymentMethodAsObject) === null || _a === void 0 ? void 0 : _a.typeOf) === paymentMethodType; }); // 決済承認アクションobject.movieTicketsではなく、payTransaction.object参照で再実装(2024-06-20~) const authorizedMovieTickets = []; // authorizeMovieTicketActions.forEach((a) => { // authorizedMovieTickets.push(...(Array.isArray(a.object.movieTickets)) ? a.object.movieTickets : []); // }); const payTransactionNumbers = authorizeMovieTicketActions.map(({ instrument }) => instrument.transactionNumber); payTransactions.filter(({ object }) => payTransactionNumbers.includes(object.paymentMethodId)) .forEach(({ object }) => { if (Array.isArray(object.paymentMethod.movieTickets)) { authorizedMovieTickets.push(...object.paymentMethod.movieTickets); } }); debug(authorizedMovieTickets.length, 'movie tickets authorized', 'transaction:', transaction.id); // validateMovieTicket(paymentMethodType, { id: transaction.id }, authorizePaymentActions, eventReservationAcceptedOffers); (0, validateMovieTicket_1.validateMovieTicket)(paymentMethodType, { id: transaction.id }, authorizedMovieTickets, eventReservationAcceptedOffers); } catch (error) { debug('validateMovieTicket throwed an error. transaction:', transaction.id, 'paymentMethodType:', paymentMethodType, 'message:', error.message); throw error; } }); } // 決済URLが発行されている場合、検証 validatePaymentUrl(transaction, acceptPayActions, authorizePaymentActions); } function findMovieTicketPaymentMethodTypesFromTransaction(authorizePaymentActions, eventReservationAcceptedOffers) { const paymentMethodTypes = []; authorizePaymentActions.forEach(({ result }) => { const resultAsInvoice = (Array.isArray(result)) // ? result.find(({ typeOf }) => typeOf === factory.action.authorize.paymentMethod.any.ResultType.Payment) ? result[0] : undefined; if ((resultAsInvoice === null || resultAsInvoice === void 0 ? void 0 : resultAsInvoice.issuedThrough.typeOf) === factory.service.paymentService.PaymentServiceType.MovieTicket) { // 決済方法区分は必ず存在するはず(2023-08-15~) paymentMethodTypes.push(resultAsInvoice.paymentMethodAsObject.typeOf); } }); // MovieTicketオファーを受け付けた予約から抽出 eventReservationAcceptedOffers.forEach(({ priceSpecification }) => { priceSpecification === null || priceSpecification === void 0 ? void 0 : priceSpecification.priceComponent.forEach((component) => { var _a, _b; // MovieTicketTypeChargeSpecificationがあれば検証リストに追加 if (component.typeOf === factory.priceSpecificationType.MovieTicketTypeChargeSpecification && typeof ((_b = (_a = component.appliesToMovieTicket) === null || _a === void 0 ? void 0 : _a.serviceOutput) === null || _b === void 0 ? void 0 : _b.typeOf) === 'string') { paymentMethodTypes.push(component.appliesToMovieTicket.serviceOutput.typeOf); } }); }); // const paymentMethodTypes: string[] = [ // ...authorizedMovieTicketPaymentMethodTypes, // ...requiredMovieTicketPaymentMethodTypes // ]; return [...new Set(paymentMethodTypes)]; } // function validateProfile(transaction: Pick<factory.transaction.placeOrder.ITransaction, 'object'>) { // // object.customerで検証(2022-05-26~) // const profile = transaction.object.customer; // const profileSatisfied = typeof profile?.typeOf === 'string' && profile.typeOf.length > 0 // && typeof profile.email === 'string' && profile.email.length > 0 // && typeof profile.familyName === 'string' && profile.familyName.length > 0 // && typeof profile.givenName === 'string' && profile.givenName.length > 0 // && typeof profile.telephone === 'string' && profile.telephone.length > 0; // if (!profileSatisfied) { // throw new factory.errors.Argument('Transaction', 'Customer Profile Required'); // } // } function validateProfile(params) { const profile = params.customer; const profileSatisfied = typeof (profile === null || profile === void 0 ? void 0 : profile.typeOf) === 'string' && profile.typeOf.length > 0 && typeof profile.email === 'string' && profile.email.length > 0 && typeof profile.familyName === 'string' && profile.familyName.length > 0 && typeof profile.givenName === 'string' && profile.givenName.length > 0 && typeof profile.telephone === 'string' && profile.telephone.length > 0; if (!profileSatisfied) { throw new factory.errors.Argument('Transaction', 'Customer Profile Required'); } // customer.nameが必ず存在するように if (typeof profile.name !== 'string' && profile.name === '') { throw new factory.errors.Argument('Transaction', 'customer name required'); } } function validatePrice(authorizePaymentActions, authorizeEventServiceOfferActions, authorizeProductOfferActions, authorizeMoneyTansferActions) { let priceByAgent = 0; let priceBySeller = 0; // 決済承認を確認 authorizePaymentActions.forEach(({ result }) => { var _a; const resultAsInvoice = (Array.isArray(result)) // ? result.find(({ typeOf }) => typeOf === factory.action.authorize.paymentMethod.any.ResultType.Payment) ? result[0] : undefined; const jpyAmount = (((_a = resultAsInvoice === null || resultAsInvoice === void 0 ? void 0 : resultAsInvoice.totalPaymentDue) === null || _a === void 0 ? void 0 : _a.currency) === factory.priceCurrency.JPY) ? resultAsInvoice.totalPaymentDue.value : 0; priceByAgent += jpyAmount; }); // 販売者が提供するアイテムの発生金額 [ ...authorizeEventServiceOfferActions, ...authorizeProductOfferActions, ...authorizeMoneyTansferActions ].forEach(({ id, result }) => { const priceByAction = result === null || result === void 0 ? void 0 : result.price; if (typeof priceByAction === 'number' && priceByAction >= 0) { priceBySeller += priceByAction; } else { // price未確定(オファー未指定)のオファー承認アクションが存在すれば確定不可能(2023-11-27~) throw new factory.errors.Argument('id', `some actions with no offer specified. [${id}]`); } }); if (priceByAgent !== priceBySeller) { throw new factory.errors.Argument('id', `prices are not matched. [${priceByAgent},${priceBySeller}]`); } } function validatePaymentUrl(transaction, acceptPayActions, authorizePaymentActions) { var _a; // 決済URLが発行されている場合、検証 const paymentMethodId = (_a = transaction.object.paymentMethods) === null || _a === void 0 ? void 0 : _a.paymentMethodId; if (typeof paymentMethodId === 'string' && paymentMethodId.length > 0) { // check existing accept action(2025-02-26~) const acceptActionExists = acceptPayActions.some(({ object, result }) => { return object.transactionNumber === paymentMethodId && (result === null || result === void 0 ? void 0 : result.paymentMethodId) === paymentMethodId && typeof (result === null || result === void 0 ? void 0 : result.paymentUrl) === 'string'; }); if (!acceptActionExists) { throw new factory.errors.NotFound(factory.actionType.AcceptAction); } // 発行された決済URLに対する決済承認を確認する const authorizePaymentAction4paymentUrlExists = authorizePaymentActions.some(({ result }) => { const resultAsInvoice = (Array.isArray(result)) // ? result.find(({ typeOf }) => typeOf === factory.action.authorize.paymentMethod.any.ResultType.Payment) ? result[0] : undefined; return (resultAsInvoice === null || resultAsInvoice === void 0 ? void 0 : resultAsInvoice.paymentMethodId) === paymentMethodId; }); if (!authorizePaymentAction4paymentUrlExists) { throw new factory.errors.Argument('Transaction', 'Payment for published payment URL required'); } } } /** * JPY以外の通貨について取引を検証する */ function validateMonetaryAmount(authorizePaymentActions, authorizeEventServiceOfferActions) { const authorizeMonetaryAmountActions = authorizePaymentActions.filter(({ result }) => { var _a; const resultAsInvoice = (Array.isArray(result)) // ? result.find(({ typeOf }) => typeOf === factory.action.authorize.paymentMethod.any.ResultType.Payment) ? result[0] : undefined; // JPY以外の通貨に対して承認可能なのはPaymentServiceType.PaymentCardのみ return (resultAsInvoice === null || resultAsInvoice === void 0 ? void 0 : resultAsInvoice.issuedThrough.typeOf) === factory.service.paymentService.PaymentServiceType.PaymentCard && ((_a = resultAsInvoice.totalPaymentDue) === null || _a === void 0 ? void 0 : _a.currency) !== factory.priceCurrency.JPY; }); const requiredMonetaryAmountByCurrencyType = []; authorizeEventServiceOfferActions.forEach((a) => { var _a; const amount = (_a = a.result) === null || _a === void 0 ? void 0 : _a.amount; if (Array.isArray(amount)) { amount.forEach((monetaryAmount) => { if (typeof monetaryAmount.value === 'number') { requiredMonetaryAmountByCurrencyType.push({ currency: monetaryAmount.currency, value: monetaryAmount.value }); } }); } }, 0); const requiredCurrencyTypes = [...new Set(requiredMonetaryAmountByCurrencyType.map((m) => m.currency))]; const authorizedCurrencyTypes = [...new Set(authorizeMonetaryAmountActions.map(({ result }) => { var _a; const resultAsInvoice = (Array.isArray(result)) // ? result.find(({ typeOf }) => typeOf === factory.action.authorize.paymentMethod.any.ResultType.Payment) ? result[0] : undefined; return (_a = resultAsInvoice === null || resultAsInvoice === void 0 ? void 0 : resultAsInvoice.totalPaymentDue) === null || _a === void 0 ? void 0 : _a.currency; }))]; if (requiredCurrencyTypes.length !== authorizedCurrencyTypes.length) { throw new factory.errors.Argument('Transaction', 'MonetaryAmount account types not matched'); } const requireMonetaryAmountSatisfied = requiredCurrencyTypes.every((currencyType) => { const requiredMonetaryAmount = requiredMonetaryAmountByCurrencyType .filter((m) => m.currency === currencyType) .reduce((a, b) => a + b.value, 0); let authorizedMonetaryAmount = 0; authorizeMonetaryAmountActions.forEach(({ result }) => { var _a; const resultAsInvoice = (Array.isArray(result)) // ? result.find(({ typeOf }) => typeOf === factory.action.authorize.paymentMethod.any.ResultType.Payment) ? result[0] : undefined; if (((_a = resultAsInvoice === null || resultAsInvoice === void 0 ? void 0 : resultAsInvoice.totalPaymentDue) === null || _a === void 0 ? void 0 : _a.currency) === currencyType) { authorizedMonetaryAmount += resultAsInvoice.totalPaymentDue.value; } }); return requiredMonetaryAmount === authorizedMonetaryAmount; }); if (!requireMonetaryAmountSatisfied) { throw new factory.errors.Argument('Transaction', 'Required MonetaryAmount not satisfied'); } } /** * 注文オファー数検証 */ function validateAcceptedOffers(params) { var _a, _b, _c; if (!Array.isArray(params.acceptedOffers)) { throw new factory.errors.Argument('order.acceptedOffers', 'must be array'); } // 注文アイテム数制限確認 const numAcceptedOffers = params.acceptedOffers.length; const numItemsMaxValue = (_a = params.result.order.numItems) === null || _a === void 0 ? void 0 : _a.maxValue; const numItemsMinValue = (_b = params.result.order.numItems) === null || _b === void 0 ? void 0 : _b.minValue; if (typeof numItemsMaxValue === 'number') { if (numAcceptedOffers > numItemsMaxValue) { throw new factory.errors.Argument('Transaction', (0, util_1.format)('Number of order items must be less than or equal to %s', numItemsMaxValue)); } } if (typeof numItemsMinValue === 'number') { if (numAcceptedOffers < numItemsMinValue) { throw new factory.errors.Argument('Transaction', (0, util_1.format)('Number of order items must be more than equal to %s', numItemsMinValue)); } } // itemOffered.typeOf数検証(2024-01-31~) const itemOfferedTypeOfs = [...new Set(params.acceptedOffers.map(({ itemOffered }) => itemOffered.typeOf))]; if (itemOfferedTypeOfs.length > 1) { throw new factory.errors.Argument('Transaction', `different itemOffered.typeOfs contained. [${itemOfferedTypeOfs.join(',')}]`); } const maxNumCOAReservationNumbers = (_c = params.result.order.numItems) === null || _c === void 0 ? void 0 : _c.maxNumCOAReservationNumbers; if (typeof maxNumCOAReservationNumbers === 'number') { // COA予約数検証(2024-03-12~) let coaSerialNumbers = []; params.acceptedOffers.forEach(({ offeredThrough, serialNumber }) => { if ((offeredThrough === null || offeredThrough === void 0 ? void 0 : offeredThrough.identifier) === factory.service.webAPI.Identifier.COA && typeof serialNumber === 'string') { coaSerialNumbers.push(serialNumber); } }); coaSerialNumbers = [...new Set(coaSerialNumbers)]; if (coaSerialNumbers.length > maxNumCOAReservationNumbers) { throw new factory.errors.Argument('Transaction', (0, util_1.format)('Number of reservationNumbers must be less than or equal to %s', maxNumCOAReservationNumbers)); } } // チケット識別子ユニークネス検証(2024-04-16~) let ticketIdentifiers = []; params.acceptedOffers.forEach(({ itemOffered }) => { if (itemOffered.typeOf === factory.reservationType.BusReservation || itemOffered.typeOf === factory.reservationType.EventReservation) { if (typeof itemOffered.reservedTicket.identifier === 'string') { ticketIdentifiers.push(itemOffered.reservedTicket.identifier); } } }); if (ticketIdentifiers.length > 0) { ticketIdentifiers = [...new Set(ticketIdentifiers)]; debug('ticketIdentifiers unique?', ticketIdentifiers.join(' '), 'numAcceptedOffers:', numAcceptedOffers); // チケット識別子が注文内ユニークなので、注文オファー数に一致するはず if (ticketIdentifiers.length !== numAcceptedOffers) { throw new factory.errors.Argument('Transaction', 'ticketIdentifiers must be unique'); } } // チケット識別子ユニークネス検証(2024-04-15~) let ticketNumbers = []; params.acceptedOffers.forEach(({ itemOffered }) => { if (itemOffered.typeOf === factory.reservationType.BusReservation || itemOffered.typeOf === factory.reservationType.EventReservation) { if (typeof itemOffered.reservedTicket.ticketNumber === 'string') { ticketNumbers.push(itemOffered.reservedTicket.ticketNumber); } } }); if (ticketNumbers.length > 0) { ticketNumbers = [...new Set(ticketNumbers)]; debug('ticketNumbers unique?', ticketNumbers.join(' '), 'numAcceptedOffers:', numAcceptedOffers); // チケット識別子が注文内ユニークなので、注文オファー数に一致するはず if (ticketNumbers.length !== numAcceptedOffers) { throw new factory.errors.Argument('Transaction', 'ticketNumbers must be unique'); } } } function validateOrderedItem(params) { var _a, _b; if (!Array.isArray(params.order.orderedItem)) { throw new factory.errors.Argument('order.orderedItem', 'must be array'); } // 注文アイテム数制限確認 const numOrderedItemMaxValue = (_b = (_a = params.result.order) === null || _a === void 0 ? void 0 : _a.orderedItem) === null || _b === void 0 ? void 0 : _b.maxValue; if (typeof numOrderedItemMaxValue === 'number') { if (params.order.orderedItem.length > numOrderedItemMaxValue) { throw new factory.errors.Argument('Transaction', (0, util_1.format)('Number of orderedItem must be less than or equal to %s', numOrderedItemMaxValue)); } } } function validatePaymentMethods(params, options) { const { maxNumCreditCardPaymentMethod } = options; if (typeof maxNumCreditCardPaymentMethod === 'number') { // CreditCard IFの決済方法の最大値検証 const creditCardPaymentMethodCount = params.order.paymentMethods.filter((p) => { var _a, _b; // カード通貨区分の存在する決済サービスを考慮(2023-08-09~) const serviceOutputAmountCurrency = (_b = (_a = p.paymentMethod) === null || _a === void 0 ? void 0 : _a.amount) === null || _b === void 0 ? void 0 : _b.currency; const isCurrencyJPY = typeof serviceOutputAmountCurrency !== 'string' || serviceOutputAmountCurrency === factory.priceCurrency.JPY; return p.issuedThrough.typeOf === factory.service.paymentService.PaymentServiceType.CreditCard && isCurrencyJPY; }).length; if (creditCardPaymentMethodCount > maxNumCreditCardPaymentMethod) { throw new factory.errors.Argument('Transaction', (0, util_1.format)(`Number of paymentMethods issued through ${factory.service.paymentService.PaymentServiceType.CreditCard} payment service must be less than equal to %s`, maxNumCreditCardPaymentMethod)); } } } /** * 興行オファー適用条件確認 */ // tslint:disable-next-line:max-func-body-length function validateEventOffers(params) { var _a, _b; const { paymentMethods } = params; const firstCreditCardPaymentMethodIdentifier = (_b = (_a = paymentMethods.find((referenceInvoice) => { return referenceInvoice.issuedThrough.typeOf === factory.service.paymentService.PaymentServiceType.CreditCard || referenceInvoice.issuedThrough.typeOf === factory.service.paymentService.PaymentServiceType.FaceToFace; })) === null || _a === void 0 ? void 0 : _a.paymentMethod) === null || _b === void 0 ? void 0 : _b.identifier; const numCreditCardOrFaceToFacePaymentMethod = paymentMethods.filter((referenceInvoice) => { return referenceInvoice.issuedThrough.typeOf === factory.service.paymentService.PaymentServiceType.CreditCard || referenceInvoice.issuedThrough.typeOf === factory.service.paymentService.PaymentServiceType.FaceToFace; }).length; // tslint:disable-next-line:max-func-body-length params.authorizeEventServiceOfferActions.forEach((a) => { var _a; // support result.offers(2024-06-18~) const acceptedOffersInResult = (_a = a.result) === null || _a === void 0 ? void 0 : _a.offers; if (Array.isArray(acceptedOffersInResult) && acceptedOffersInResult.length > 0) { acceptedOffersInResult.forEach((acceptedOfferInResult) => { var _a, _b, _c, _d; const offerId = acceptedOfferInResult.id; const unitPriceSpec = acceptedOfferInResult.priceSpecification; // 適用金額要件を満たしていなければエラー const eligibleTransactionVolumePrice = (_a = unitPriceSpec === null || unitPriceSpec === void 0 ? void 0 : unitPriceSpec.eligibleTransactionVolume) === null || _a === void 0 ? void 0 : _a.price; const eligibleTransactionVolumePriceCurrency = (_b = unitPriceSpec === null || unitPriceSpec === void 0 ? void 0 : unitPriceSpec.eligibleTransactionVolume) === null || _b === void 0 ? void 0 : _b.priceCurrency; if (typeof eligibleTransactionVolumePrice === 'number') { if (params.order.price < eligibleTransactionVolumePrice) { throw new factory.errors.Argument('Transaction', (0, util_1.format)('Transaction volume must be more than or equal to %s %s for offer:%s', eligibleTransactionVolumePrice, eligibleTransactionVolumePriceCurrency, offerId)); } } // 適用数量検証(全興行オファー承認アクションについて)(2023-11-15~) // 適用数量要件を満たしていなければエラー const numAcceptedOffersByOfferId = params.authorizeEventServiceOfferActions.reduce((previousNumOffer, { result }) => { var _a, _b; const numOfferOnCurrentAction = (_b = (_a = result === null || result === void 0 ? void 0 : result.offers) === null || _a === void 0 ? void 0 : _a.find(({ id }) => id === offerId)) === null || _b === void 0 ? void 0 : _b.includesObject.amountOfThisGood; return previousNumOffer + ((typeof numOfferOnCurrentAction === 'number') ? numOfferOnCurrentAction : 0); }, 0); const eligibleQuantityMaxValue = (_c = unitPriceSpec === null || unitPriceSpec === void 0 ? void 0 : unitPriceSpec.eligibleQuantity) === null || _c === void 0 ? void 0 : _c.maxValue; if (typeof eligibleQuantityMaxValue === 'number') { debug('validateEventOffers: numAcceptedOffersByOfferId > eligibleQuantityMaxValue?', numAcceptedOffersByOfferId, eligibleQuantityMaxValue); if (numAcceptedOffersByOfferId > eligibleQuantityMaxValue) { throw new factory.errors.Argument('Transaction', `Number of offer:${offerId} must be less than or equal to ${eligibleQuantityMaxValue}`); } } const eligibleQuantityMinValue = (_d = unitPriceSpec === null || unitPriceSpec === void 0 ? void 0 : unitPriceSpec.eligibleQuantity) === null || _d === void 0 ? void 0 : _d.minValue; if (typeof eligibleQuantityMinValue === 'number') { debug('validateEventOffers: numAcceptedOffersByOfferId < eligibleQuantityMinValue?', numAcceptedOffersByOfferId, eligibleQuantityMinValue); if (numAcceptedOffersByOfferId < eligibleQuantityMinValue) { throw new factory.errors.Argument('Transaction', `Number of offer:${offerId} must be more than or equal to ${eligibleQuantityMinValue}`); } } // 利用可能決済方法検証(2023-11-15~) if (Array.isArray(acceptedOfferInResult.acceptedPaymentMethod)) { const acceptedPaymentMethodTypes = acceptedOfferInResult.acceptedPaymentMethod.map(({ identifier }) => identifier); const satisfyAcceptedPaymentMethod = numCreditCardOrFaceToFacePaymentMethod === 1 && typeof firstCreditCardPaymentMethodIdentifier === 'string' && acceptedPaymentMethodTypes.includes(firstCreditCardPaymentMethodIdentifier); debug('validateEventOffers: satisfyAcceptedPaymentMethod?', JSON.stringify(acceptedPaymentMethodTypes)); if (!satisfyAcceptedPaymentMethod) { throw new factory.errors.Argument('Transaction', `acceptedPaymentMethod requirement not satisfied`); } } }); } }); // check programMembershipUsed(2024-08-15~) const programMembershipRequired = params.authorizeEventServiceOfferActions.reduce((a, b) => { var _a, _b; const programMembershipUsedfromResult = (_b = (_a = b.result) === null || _a === void 0 ? void 0 : _a.itemOffered) === null || _b === void 0 ? void 0 : _b.serviceOutput.programMembershipUsed; if (Array.isArray(programMembershipUsedfromResult)) { a.push(...programMembershipUsedfromResult); } return a; }, []); const satisfyProgramMembershipRequirement = programMembershipRequired.every(({ identifier, issuedThrough }) => { let satisfyThisRequirement = false; switch (issuedThrough.typeOf) { case factory.product.ProductType.MembershipService: // 検証の必要なし satisfyThisRequirement = true; break; case factory.service.paymentService.PaymentServiceType.CreditCard: satisfyThisRequirement = paymentMethods.some((paymentMethod) => paymentMethod.paymentMethodId === identifier && paymentMethod.issuedThrough.id === issuedThrough.id && paymentMethod.issuedThrough.typeOf === issuedThrough.typeOf); break; case factory.service.paymentService.PaymentServiceType.FaceToFace: satisfyThisRequirement = paymentMethods.some((paymentMethod) => paymentMethod.paymentMethodId === identifier && paymentMethod.issuedThrough.typeOf === issuedThrough.typeOf); break; default: // no op } return satisfyThisRequirement; }); if (!satisfyProgramMembershipRequirement) { throw new factory.errors.Argument('Transaction', `programMembershipUsed requirement not satisfied`); } }