// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. const cardValidator = require('simple-card-validator'); const uuid = require('uuid/v4'); const pino = require('pino'); const logger = pino({ name: 'paymentservice-charge', messageKey: 'message', changeLevelName: 'severity', useLevelLabels: true, timestamp: pino.stdTimeFunctions.unixTime }); class CreditCardError extends Error { constructor (message) { super(message); this.code = 400; // Invalid argument error } } class InvalidCreditCard extends CreditCardError { constructor (cardType) { super(`Credit card info is invalid`); } } class UnacceptedCreditCard extends CreditCardError { constructor (cardType) { super(`Sorry, we cannot process ${cardType} credit cards. Only VISA or MasterCard is accepted.`); } } class ExpiredCreditCard extends CreditCardError { constructor (number, month, year) { super(`Your credit card (ending ${number.substr(-4)}) expired on ${month}/${year}`); } } /** * Verifies the credit card number and (pretend) charges the card. * * @param {*} request * @return transaction_id - a random uuid v4. */ module.exports = function charge (request) { const { amount, credit_card: creditCard } = request; const cardNumber = creditCard.credit_card_number; const cardInfo = cardValidator(cardNumber); const { card_type: cardType, valid } = cardInfo.getCardDetails(); if (!valid) { throw new InvalidCreditCard(); } // Only VISA and mastercard is accepted, other card types (AMEX, dinersclub) will // throw UnacceptedCreditCard error. if (!(cardType === 'visa' || cardType === 'mastercard')) { throw new UnacceptedCreditCard(cardType); } // Also validate expiration is > today. const currentMonth = new Date().getMonth() + 1; const currentYear = new Date().getFullYear(); const { credit_card_expiration_year: year, credit_card_expiration_month: month } = creditCard; if ((currentYear * 12 + currentMonth) > (year * 12 + month)) { throw new ExpiredCreditCard(cardNumber.replace('-', ''), month, year); } logger.info(`Transaction processed: ${cardType} ending ${cardNumber.substr(-4)} \ Amount: ${amount.currency_code}${amount.units}.${amount.nanos}`); return { transaction_id: uuid() }; };