more validation on credit card info; change smoke test for payment service to have a valid credit card

This commit is contained in:
Jonathan Lui 2018-06-21 12:48:53 -07:00
parent c7e7692057
commit 88ac6d8fc1
5 changed files with 80 additions and 14 deletions

View file

@ -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 }
}

View file

@ -723,6 +723,11 @@
"yargs": "3.32.0" "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": { "string-width": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",

View file

@ -10,6 +10,7 @@
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"@grpc/proto-loader": "^0.1.0", "@grpc/proto-loader": "^0.1.0",
"grpc": "^1.12.3" "grpc": "^1.12.3",
"simple-card-validator": "^1.1.0"
} }
} }

View file

@ -1,6 +1,8 @@
const grpc = require('grpc'); const grpc = require('grpc');
const protoLoader = require('@grpc/proto-loader'); const protoLoader = require('@grpc/proto-loader');
const charge = require('./charge');
class HipsterShopServer { class HipsterShopServer {
constructor(protoFile, port = HipsterShopServer.DEFAULT_PORT) { constructor(protoFile, port = HipsterShopServer.DEFAULT_PORT) {
this.port = port; this.port = port;
@ -16,24 +18,18 @@ class HipsterShopServer {
*/ */
static ChargeServiceHandler(call, callback) { static ChargeServiceHandler(call, callback) {
try { 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); callback(null, response);
} catch (err) { } catch (err) {
console.warn(err);
callback(err); callback(err);
} }
} }
/**
* Charge function
* @param {*} request
* @return transaction_id
*/
static charge(request) {
return { transaction_id: -1 }
}
listen() { listen() {
this.server.bind(`0.0.0.0:${this.port}`, grpc.ServerCredentials.createInsecure()); this.server.bind(`0.0.0.0:${this.port}`, grpc.ServerCredentials.createInsecure());
console.log(`PaymentService grpc server listening on ${this.port}`);
this.server.start(); this.server.start();
} }

View file

@ -167,7 +167,7 @@ func testRecommendationService() error {
} }
func testPaymentService() error { func testPaymentService() error {
addr := os.Getenv("RECOMMENDATION_SERVICE_ADDR") addr := os.Getenv("PAYMENT_SERVICE_ADDR")
conn, err := grpc.Dial(addr, grpc.WithInsecure()) conn, err := grpc.Dial(addr, grpc.WithInsecure())
if err != nil { if err != nil {
return err return err
@ -184,13 +184,13 @@ func testPaymentService() error {
Fractional: 55}, Fractional: 55},
}, },
CreditCard: &pb.CreditCardInfo{ CreditCard: &pb.CreditCardInfo{
CreditCardNumber: "9999-9999-9999-9999", CreditCardNumber: "4444-4530-1092-6639",
CreditCardCvv: 612, CreditCardCvv: 612,
CreditCardExpirationYear: 2022, CreditCardExpirationYear: 2022,
CreditCardExpirationMonth: 10}, CreditCardExpirationMonth: 10},
}) })
if err != nil { if err != nil {
return nil return err
} }
log.Printf("--> resp: %+v", resp) log.Printf("--> resp: %+v", resp)
return nil return nil