GoogleCloudPlatform/microservices-demo · error · ExpiredCreditCard

ExpiredCreditCard

Error message

ExpiredCreditCard

What it means

ExpiredCreditCard is thrown by charge() in src/paymentservice/charge.js when the card's expiration (credit_card_expiration_month/year) is not after the current month, computed as currentYear*12+currentMonth > year*12+month. The card may be valid and accepted, but it has lapsed. It is a CreditCardError with code 400 (Invalid Argument) and includes the masked card number and expiry date in the message.

Solutions

  1. Update the request payload to an expiration date in the future, e.g. {credit_card_expiration_month: 12, credit_card_expiration_year: 2030}
  2. Check the machine's system clock (date) — a wrong currentYear makes every card appear expired
  3. Regenerate stale fixtures/secrets that contain hardcoded expiry dates
  4. If a card expiring in the current month should be chargeable, adjust the comparison in charge.js line 80

Example fix

// before
credit_card: { credit_card_number: '4012-8888-8888-1881', credit_card_expiration_month: 1, credit_card_expiration_year: 2020, ... }
// after
credit_card: { credit_card_number: '4012-8888-8888-1881', credit_card_expiration_month: 12, credit_card_expiration_year: 2030, ... }
Defensive patterns

Strategy: validation

Validate before calling

function isCardNotExpired({ credit_card_expiration_month: m, credit_card_expiration_year: y }) {
  if (!Number.isInteger(m) || !Number.isInteger(y) || m < 1 || m > 12) return false;
  const now = new Date();
  return (now.getFullYear() * 12 + (now.getMonth() + 1)) <= (y * 12 + m);
}
if (!isCardNotExpired(creditCard)) {
  return res.status(400).json({ error: 'Card has expired; please use another card.' });
}

Type guard

function isFutureExpiry(month, year) {
  return Number.isInteger(month) && Number.isInteger(year) &&
    (new Date().getFullYear() * 12 + new Date().getMonth() + 1) <= (year * 12 + month);
}

Try / catch

try {
  const result = await paymentClient.Charge(chargeRequest);
} catch (err) {
  if (err.message === 'ExpiredCreditCard' || /expired on/i.test(err.message)) {
    return res.status(400).json({ error: err.message, hint: 'Please update the expiration date or use a different card.' });
  }
  throw err;
}

Prevention

When it happens

Trigger: Calling Charge with credit_card.credit_card_expiration_year/month set to a past date, e.g. {year: 2020, month: 1}; a year-2038-style clock issue on the host making currentYear wrong; off-by-one where the card expires in the current month (current month counts as expired).

Common situations: Hardcoded test payloads with stale expiry dates; test suites failing after time passes (fixtures written years ago); expired sandbox credentials in demo data; server clock skew.

Related errors


AI-assisted analysis of GoogleCloudPlatform/microservices-demo@72ba613a05 (2026-09-02). Data as JSON: /api/errors/7b3cef24199a24a6. Report an issue: GitHub.

Appendix: source

Thrown at src/paymentservice/charge.js:80

  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: uuidv4() };
};

View on GitHub (pinned to 72ba613a05)