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.
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)
Solutions
- Update the request payload to an expiration date in the future, e.g. {credit_card_expiration_month: 12, credit_card_expiration_year: 2030}
- Check the machine's system clock (date) — a wrong currentYear makes every card appear expired
- Regenerate stale fixtures/secrets that contain hardcoded expiry dates
- 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
- Check expiry client-side at checkout and prompt for card update
- Avoid hardcoded expiry dates in test data — generate dates several years in the future
- Verify server clock (NTP) so currentYear is correct
- Regenerate fixtures periodically; treat expiry fields as time-sensitive data
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
- InvalidCreditCard
- UnacceptedCreditCard
- could not charge the card: %+v
- FAILED_PRECONDITION
- FAILED_PRECONDITION
AI-assisted analysis of GoogleCloudPlatform/microservices-demo@72ba613a05 (2026-09-02).
Data as JSON: /api/errors/7b3cef24199a24a6.
Report an issue: GitHub.