GoogleCloudPlatform/microservices-demo · critical
could not charge the card: %+v
Error message
could not charge the card: %+v
What it means
chargeCard wraps a failed PaymentService.Charge gRPC call with 'could not charge the card'. The payment was not processed, so the order fails; the underlying error (card declined by the fake payment processor, invalid credit card info, or payment service unavailable) is included via %+v. This is the most customer-impacting failure point in checkout.
Source
Thrown at src/checkoutservice/main.go:374
return out, nil
}
func (cs *checkoutService) convertCurrency(ctx context.Context, from *pb.Money, toCurrency string) (*pb.Money, error) {
result, err := pb.NewCurrencyServiceClient(cs.currencySvcConn).Convert(context.TODO(), &pb.CurrencyConversionRequest{
From: from,
ToCode: toCurrency})
if err != nil {
return nil, fmt.Errorf("failed to convert currency: %+v", err)
}
return result, err
}
func (cs *checkoutService) chargeCard(ctx context.Context, amount *pb.Money, paymentInfo *pb.CreditCardInfo) (string, error) {
paymentResp, err := pb.NewPaymentServiceClient(cs.paymentSvcConn).Charge(ctx, &pb.ChargeRequest{
Amount: amount,
CreditCard: paymentInfo})
if err != nil {
return "", fmt.Errorf("could not charge the card: %+v", err)
}
return paymentResp.GetTransactionId(), nil
}
func (cs *checkoutService) sendOrderConfirmation(ctx context.Context, email string, order *pb.OrderResult) error {
_, err := pb.NewEmailServiceClient(cs.emailSvcConn).SendOrderConfirmation(ctx, &pb.SendOrderConfirmationRequest{
Email: email,
Order: order})
return err
}
func (cs *checkoutService) shipOrder(ctx context.Context, address *pb.Address, items []*pb.CartItem) (string, error) {
resp, err := pb.NewShippingServiceClient(cs.shippingSvcConn).ShipOrder(ctx, &pb.ShipOrderRequest{
Address: address,
Items: items})
if err != nil {
return "", fmt.Errorf("shipment failed: %+v", err)
}View on GitHub (pinned to 72ba613a05)
Solutions
- Inspect the wrapped error: distinguish InvalidArgument (bad card data) from Unavailable (service down).
- Validate credit card number (Luhn check) and required fields (number, CCV, expiry) before calling PlaceOrder.
- Check paymentservice pods/logs and restart if unhealthy.
- Ensure the Charge amount is a valid, non-nil Money from a successful currency conversion.
- Verify TLS credentials and network policy allow checkout->payment gRPC traffic.
Example fix
// before
paymentResp, err := pb.NewPaymentServiceClient(cs.paymentSvcConn).Charge(ctx, &pb.ChargeRequest{
Amount: amount,
CreditCard: paymentInfo})
if err != nil {
return "", fmt.Errorf("could not charge the card: %+v", err)
}
// after
if amount == nil || paymentInfo == nil || paymentInfo.GetCreditCardNumber() == "" {
return "", status.Error(codes.InvalidArgument, "valid payment information is required")
}
paymentResp, err := pb.NewPaymentServiceClient(cs.paymentSvcConn).Charge(ctx, &pb.ChargeRequest{
Amount: amount,
CreditCard: paymentInfo})
if err != nil {
return "", fmt.Errorf("could not charge the card: %w", err)
} Defensive patterns
Strategy: try-catch
Validate before calling
if paymentInfo == nil || paymentInfo.GetCreditCardNumber() == "" || paymentInfo.GetCreditCardCvv() == 0 {
return status.Error(codes.InvalidArgument, "complete credit card details required")
}
if !luhnValid(paymentInfo.GetCreditCardNumber()) {
return status.Error(codes.InvalidArgument, "invalid credit card number")
} Type guard
func isChargeable(amount *pb.Money, card *pb.CreditCardInfo) bool {
return amount != nil && amount.GetUnits() > 0 && card != nil && luhnValid(card.GetCreditCardNumber())
} Try / catch
txID, err := paymentClient.Charge(ctx, req)
if err != nil {
if status.Code(err) == codes.Unavailable {
return status.Error(codes.Unavailable, "payment service temporarily unavailable") // do NOT auto-retry charges
}
return fmt.Errorf("could not charge the card: %w", err)
} Prevention
- Validate card data (Luhn, expiry) client-side before checkout
- Never blindly retry Charge — risk of double charging
- Ensure the charged amount comes from a successful conversion (non-nil Money)
- Monitor paymentservice health and decline rates
When it happens
Trigger: pb.NewPaymentServiceClient(cs.paymentSvcConn).Charge(ctx, &pb.ChargeRequest{Amount, CreditCard}) errors: payment service unavailable, malformed CreditCardInfo (invalid number/CCV), or processor-side decline surfaced as an RPC error.
Common situations: Client submits an invalid/expired card number; paymentservice pod down; amount Money struct empty due to an earlier conversion failure; TLS/network misconfig between checkout and payment services.
Related errors
- failed to prepare order: %+v
- shipment failed: %+v
- InvalidCreditCard
- UnacceptedCreditCard
- ExpiredCreditCard
AI-assisted analysis of GoogleCloudPlatform/microservices-demo@72ba613a05 (2026-09-02).
Data as JSON: /api/errors/a8b871fdab402316.
Report an issue: GitHub.