{"record":{"id":"a8b871fdab402316","repo":"GoogleCloudPlatform/microservices-demo","slug":"could-not-charge-the-card-v","errorCode":null,"errorMessage":"could not charge the card: %+v","messagePattern":"could not charge the card: %\\+v","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"src/checkoutservice/main.go","lineNumber":374,"sourceCode":"\treturn out, nil\n}\n\nfunc (cs *checkoutService) convertCurrency(ctx context.Context, from *pb.Money, toCurrency string) (*pb.Money, error) {\n\tresult, err := pb.NewCurrencyServiceClient(cs.currencySvcConn).Convert(context.TODO(), &pb.CurrencyConversionRequest{\n\t\tFrom:   from,\n\t\tToCode: toCurrency})\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to convert currency: %+v\", err)\n\t}\n\treturn result, err\n}\n\nfunc (cs *checkoutService) chargeCard(ctx context.Context, amount *pb.Money, paymentInfo *pb.CreditCardInfo) (string, error) {\n\tpaymentResp, err := pb.NewPaymentServiceClient(cs.paymentSvcConn).Charge(ctx, &pb.ChargeRequest{\n\t\tAmount:     amount,\n\t\tCreditCard: paymentInfo})\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"could not charge the card: %+v\", err)\n\t}\n\treturn paymentResp.GetTransactionId(), nil\n}\n\nfunc (cs *checkoutService) sendOrderConfirmation(ctx context.Context, email string, order *pb.OrderResult) error {\n\t_, err := pb.NewEmailServiceClient(cs.emailSvcConn).SendOrderConfirmation(ctx, &pb.SendOrderConfirmationRequest{\n\t\tEmail: email,\n\t\tOrder: order})\n\treturn err\n}\n\nfunc (cs *checkoutService) shipOrder(ctx context.Context, address *pb.Address, items []*pb.CartItem) (string, error) {\n\tresp, err := pb.NewShippingServiceClient(cs.shippingSvcConn).ShipOrder(ctx, &pb.ShipOrderRequest{\n\t\tAddress: address,\n\t\tItems:   items})\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"shipment failed: %+v\", err)\n\t}","sourceCodeStart":356,"sourceCodeEnd":392,"githubUrl":"https://github.com/GoogleCloudPlatform/microservices-demo/blob/72ba613a05f7fcee51cf1d0badff401b6ae7074d/src/checkoutservice/main.go#L356-L392","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\npaymentResp, err := pb.NewPaymentServiceClient(cs.paymentSvcConn).Charge(ctx, &pb.ChargeRequest{\n\tAmount:     amount,\n\tCreditCard: paymentInfo})\nif err != nil {\n\treturn \"\", fmt.Errorf(\"could not charge the card: %+v\", err)\n}\n// after\nif amount == nil || paymentInfo == nil || paymentInfo.GetCreditCardNumber() == \"\" {\n\treturn \"\", status.Error(codes.InvalidArgument, \"valid payment information is required\")\n}\npaymentResp, err := pb.NewPaymentServiceClient(cs.paymentSvcConn).Charge(ctx, &pb.ChargeRequest{\n\tAmount:     amount,\n\tCreditCard: paymentInfo})\nif err != nil {\n\treturn \"\", fmt.Errorf(\"could not charge the card: %w\", err)\n}","handlingStrategy":"try-catch","validationCode":"if paymentInfo == nil || paymentInfo.GetCreditCardNumber() == \"\" || paymentInfo.GetCreditCardCvv() == 0 {\n\treturn status.Error(codes.InvalidArgument, \"complete credit card details required\")\n}\nif !luhnValid(paymentInfo.GetCreditCardNumber()) {\n\treturn status.Error(codes.InvalidArgument, \"invalid credit card number\")\n}","typeGuard":"func isChargeable(amount *pb.Money, card *pb.CreditCardInfo) bool {\n\treturn amount != nil && amount.GetUnits() > 0 && card != nil && luhnValid(card.GetCreditCardNumber())\n}","tryCatchPattern":"txID, err := paymentClient.Charge(ctx, req)\nif err != nil {\n\tif status.Code(err) == codes.Unavailable {\n\t\treturn status.Error(codes.Unavailable, \"payment service temporarily unavailable\") // do NOT auto-retry charges\n\t}\n\treturn fmt.Errorf(\"could not charge the card: %w\", err)\n}","preventionTips":["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"],"tags":["grpc","payment","checkout","microservices"],"backgroundTag":"payment-charge-declined","analyzedSha":"72ba613a05f7fcee51cf1d0badff401b6ae7074d","analyzedAt":"2026-09-02T01:15:09.673Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-09T06:17:21.866Z"}