nopSolutions/nopCommerce · error · NopException
Card details not found
Error message
Card details not found
What it means
Thrown in CreateOrderAsync when a saved payment token (card) exists for the provided cardId but belongs to a different customer. This is a security check preventing one customer from using another's saved payment method.
Source
Thrown at src/Plugins/Nop.Plugin.Payments.PayPalCommerce/Services/PayPalCommerceServiceManager.cs:1641
/// A task that represents the asynchronous operation
/// The task result contains the created order; error message if exists
/// </returns>
public async Task<(Order Order, string Error)> CreateOrderAsync(PayPalCommerceSettings settings,
ButtonPlacement placement, string paymentSource, int? cardId, bool saveCard)
{
return await HandleFunctionAsync(async () =>
{
if (!IsConfigured(settings))
throw new NopException("Plugin not configured");
if (string.IsNullOrEmpty(settings.MerchantId))
throw new NopException("Merchant PayPal ID not set");
var details = await PrepareCartDetailsAsync(placement);
var savedPaymentToken = await _tokenService.GetByIdAsync(cardId ?? 0);
if (savedPaymentToken is not null && savedPaymentToken.CustomerId != details.Customer.Id)
throw new NopException("Card details not found");
var isGuest = await _customerService.IsGuestAsync(details.Customer);
var isRecurring = await _shoppingCartService.ShoppingCartIsRecurringAsync(details.Cart);
if (isRecurring)
{
if (!settings.UseVault)
throw new NopException("Vault disabled");
if (isGuest)
throw new NopException("Anonymous checkout disabled for recurring items");
var (error, cycleLength, cyclePeriod, totalCycles) = await _shoppingCartService.GetRecurringCycleInfoAsync(details.Cart);
if (!string.IsNullOrEmpty(error))
throw new NopException(error);
}
var paymentRequest = await _orderProcessingService.GetProcessPaymentRequestAsync();
var (order, _) = await GetCreatedOrderAsync(settings, paymentRequest, placement, details.ShippingIsRequired, paymentSource);View on GitHub (pinned to 64bdf2ff08)
Solutions
- Ensure the cardId passed to CreateOrderAsync comes from the current customer's list of saved cards only
- Validate that the selected card belongs to the authenticated customer in the UI before submission
- Check for authentication state changes between card selection and order creation
- Audit card selection logic for any path that could pass another customer's cardId
Example fix
// before
var savedPaymentToken = await _tokenService.GetByIdAsync(cardId ?? 0);
if (savedPaymentToken is not null && savedPaymentToken.CustomerId != details.Customer.Id)
throw new NopException("Card details not found");
// after
var savedPaymentToken = cardId.HasValue
? (await _tokenService.GetByCustomerIdAsync(details.Customer.Id))
.FirstOrDefault(t => t.Id == cardId.Value)
: null;
// Token is already scoped to the customer — no cross-customer access possible Defensive patterns
Strategy: validation
Validate before calling
// Scope card lookup to the current customer to prevent cross-customer access
if (cardId.HasValue)
{
var customerTokens = await _tokenService.GetByCustomerIdAsync(details.Customer.Id);
var validToken = customerTokens.FirstOrDefault(t => t.Id == cardId.Value);
if (validToken is null)
return Error("Selected card not found");
} Type guard
static bool TokenBelongsToCustomer(PaymentToken token, int customerId)
=> token is not null && token.CustomerId == customerId; Try / catch
var (order, error) = await manager.CreateOrderAsync(settings, placement, paymentSource, cardId, saveCard);
if (!string.IsNullOrEmpty(error) && error == "Card details not found")
return BadRequest("Invalid card selection"); Prevention
- Only populate card selection UI with the current customer's own saved cards
- Validate cardId ownership on the server before submitting the order
- Treat cross-customer card access attempts as potential security incidents
When it happens
Trigger: cardId is provided, _tokenService.GetByIdAsync returns a non-null token, but savedPaymentToken.CustomerId does not match details.Customer.Id.
Common situations: cardId was tampered with in the client request to reference another customer's card; session or authentication changed between card selection and order creation (e.g., logout/login); a bug in card selection UI passes a stale or wrong cardId; concurrent sessions under different accounts sharing a browser.
Related errors
- Customer billing address not set
- Customer shipping address not set
- Shopping cart total couldn't be calculated now
- No available shipping options
- Selected shipping option is unavailable
AI-assisted analysis of nopSolutions/nopCommerce@64bdf2ff08 (2026-08-13).
Data as JSON: /api/errors/9cb04fedde346187.
Report an issue: GitHub.