From 88ac6d8fc1d3dde20a8298731c487d09c5ca4012 Mon Sep 17 00:00:00 2001 From: Jonathan Lui Date: Thu, 21 Jun 2018 12:48:53 -0700 Subject: [PATCH] more validation on credit card info; change smoke test for payment service to have a valid credit card --- src/paymentservice/charge.js | 64 ++++++++++++++++++++++++++++ src/paymentservice/package-lock.json | 5 +++ src/paymentservice/package.json | 3 +- src/paymentservice/server.js | 16 +++---- src/test-cli/main.go | 6 +-- 5 files changed, 80 insertions(+), 14 deletions(-) create mode 100644 src/paymentservice/charge.js diff --git a/src/paymentservice/charge.js b/src/paymentservice/charge.js new file mode 100644 index 0000000..2a16780 --- /dev/null +++ b/src/paymentservice/charge.js @@ -0,0 +1,64 @@ +const cardValidator = require('simple-card-validator'); + +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 number. + */ +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, + cvv_length: cvvLength, + } = 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); + + console.log(`Transaction processed: ${cardType} ending ${cardNumber.substr(-4)} \ + Amount: ${amount.currency_code}${amount.amount.decimal}.${amount.amount.fractional}`) + + return { transaction_id: -1 } +} + diff --git a/src/paymentservice/package-lock.json b/src/paymentservice/package-lock.json index b037049..6393258 100644 --- a/src/paymentservice/package-lock.json +++ b/src/paymentservice/package-lock.json @@ -723,6 +723,11 @@ "yargs": "3.32.0" } }, + "simple-card-validator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/simple-card-validator/-/simple-card-validator-1.1.0.tgz", + "integrity": "sha1-675uRp/q7Cy7SBX4Qyu+BMccNvk=" + }, "string-width": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", diff --git a/src/paymentservice/package.json b/src/paymentservice/package.json index 9c281d3..ee87b69 100644 --- a/src/paymentservice/package.json +++ b/src/paymentservice/package.json @@ -10,6 +10,7 @@ "license": "ISC", "dependencies": { "@grpc/proto-loader": "^0.1.0", - "grpc": "^1.12.3" + "grpc": "^1.12.3", + "simple-card-validator": "^1.1.0" } } diff --git a/src/paymentservice/server.js b/src/paymentservice/server.js index 42eb6fb..1a3dc04 100644 --- a/src/paymentservice/server.js +++ b/src/paymentservice/server.js @@ -1,6 +1,8 @@ const grpc = require('grpc'); const protoLoader = require('@grpc/proto-loader'); +const charge = require('./charge'); + class HipsterShopServer { constructor(protoFile, port = HipsterShopServer.DEFAULT_PORT) { this.port = port; @@ -16,24 +18,18 @@ class HipsterShopServer { */ static ChargeServiceHandler(call, callback) { try { - const response = this.charge(call.request) + console.log(`PaymentService#Charge invoked with request ${JSON.stringify(call.request)}`) + const response = charge(call.request) callback(null, response); } catch (err) { + console.warn(err); callback(err); } } - /** - * Charge function - * @param {*} request - * @return transaction_id - */ - static charge(request) { - return { transaction_id: -1 } - } - listen() { this.server.bind(`0.0.0.0:${this.port}`, grpc.ServerCredentials.createInsecure()); + console.log(`PaymentService grpc server listening on ${this.port}`); this.server.start(); } diff --git a/src/test-cli/main.go b/src/test-cli/main.go index 13baf5a..1eeed2e 100644 --- a/src/test-cli/main.go +++ b/src/test-cli/main.go @@ -167,7 +167,7 @@ func testRecommendationService() error { } func testPaymentService() error { - addr := os.Getenv("RECOMMENDATION_SERVICE_ADDR") + addr := os.Getenv("PAYMENT_SERVICE_ADDR") conn, err := grpc.Dial(addr, grpc.WithInsecure()) if err != nil { return err @@ -184,13 +184,13 @@ func testPaymentService() error { Fractional: 55}, }, CreditCard: &pb.CreditCardInfo{ - CreditCardNumber: "9999-9999-9999-9999", + CreditCardNumber: "4444-4530-1092-6639", CreditCardCvv: 612, CreditCardExpirationYear: 2022, CreditCardExpirationMonth: 10}, }) if err != nil { - return nil + return err } log.Printf("--> resp: %+v", resp) return nil