{"record":{"id":"439034809cbcaf9a","repo":"nopSolutions/nopCommerce","slug":"capture-id-not-set","errorCode":null,"errorMessage":"Capture ID not set","messagePattern":"Capture ID not set","errorType":"exception","errorClass":"NopException","httpStatus":null,"severity":"error","filePath":"src/Plugins/Nop.Plugin.Payments.PayPalCommerce/Services/PayPalCommerceServiceManager.cs","lineNumber":2566,"sourceCode":"    /// <param name=\"nopOrder\">Order</param>\n    /// <param name=\"amount\">Amount to refund; pass null to refund the full captured amount</param>\n    /// <returns>\n    /// A task that represents the asynchronous operation\n    /// The task result contains the refund details; error message if exists\n    /// </returns>\n    public async Task<(Refund Refund, string Error)> RefundAsync(PayPalCommerceSettings settings, NopOrder nopOrder, decimal? amount = null)\n    {\n        return await HandleFunctionAsync(async () =>\n        {\n            if (!IsConfigured(settings))\n                throw new NopException(\"Plugin not configured\");\n\n            var currencyCode = (await _currencyService.GetCurrencyByIdAsync(_currencySettings.PrimaryStoreCurrencyId))?.CurrencyCode;\n            if (string.IsNullOrEmpty(currencyCode))\n                throw new NopException(\"Primary store currency not set\");\n\n            if (string.IsNullOrEmpty(nopOrder.CaptureTransactionId))\n                throw new NopException(\"Capture ID not set\");\n\n            var request = new CreateRefundRequest\n            {\n                CaptureId = nopOrder.CaptureTransactionId,\n                Amount = amount.HasValue ? PrepareMoney(amount.Value, currencyCode) : null\n            };\n            var refund = await _httpClient.RequestAsync<CreateRefundRequest, CreateRefundResponse>(request, settings);\n\n            if (refund.Status?.ToUpper() == RefundStatusType.CANCELLED.ToString())\n                throw new NopException(\"The refund was cancelled\");\n\n            if (refund.Status?.ToUpper() == RefundStatusType.FAILED.ToString())\n                throw new NopException(\"The refund could not be processed\");\n\n            if (refund.Status?.ToUpper() == RefundStatusType.PENDING.ToString())\n                throw new NopException($\"Capture is in {refund.Status} status due to {refund.StatusDetails?.Reason}\");\n\n            //save id to avoid double refund","sourceCodeStart":2548,"sourceCodeEnd":2584,"githubUrl":"https://github.com/nopSolutions/nopCommerce/blob/64bdf2ff08c8b39e65717bcf974fb43dc2ef68f2/src/Plugins/Nop.Plugin.Payments.PayPalCommerce/Services/PayPalCommerceServiceManager.cs#L2548-L2584","documentation":"Thrown by RefundAsync when nopOrder.CaptureTransactionId is null or empty. A refund targets a specific captured payment, so PayPal requires the capture resource id; without it the CreateRefundRequest.CaptureId cannot be set. This guard fires before any API call and is surfaced as the Error string.","triggerScenarios":"Refunding an order that was authorized but never captured; the capture step ran but failed to persist CaptureTransactionId on the order; attempting to refund a manually-created or Authorize-only order.","commonSituations":"Payment mode set to Authorize (not Capture), so there is no capture to refund; capture failed mid-flow and the order was left without a capture id; order was placed through a different payment method and CaptureTransactionId was never set.","solutions":["Confirm the order was actually captured and that order.CaptureTransactionId is populated.","Guard with !string.IsNullOrEmpty(nopOrder.CaptureTransactionId) before calling RefundAsync.","If the order is Authorize-only, void it (VoidAsync) instead of refunding."],"exampleFix":"// before\nvar (refund, err) = await _serviceManager.RefundAsync(settings, order, amount);\n\n// after\nif (string.IsNullOrEmpty(order.CaptureTransactionId))\n{\n    NotifyError(\"Order has no captured payment to refund\");\n    return;\n}\nvar (refund, err) = await _serviceManager.RefundAsync(settings, order, amount);","handlingStrategy":"validation","validationCode":"if (string.IsNullOrEmpty(order.CaptureTransactionId))\n    return; // no capture to refund","typeGuard":"static string CaptureId(NopOrder o) =>\n    string.IsNullOrEmpty(o?.CaptureTransactionId) ? null : o.CaptureTransactionId;","tryCatchPattern":"var (refund, error) = await mgr.RefundAsync(settings, order, amount);\nif (!string.IsNullOrEmpty(error))\n    NotifyError(error);","preventionTips":["Show Refund only when CaptureTransactionId is set; show Void for authorize-only orders.","Confirm the capture step persisted the id before enabling refunds.","Do not refund orders placed via other payment methods."],"tags":["validation","payments","paypal-commerce","refund","capture"],"backgroundTag":null,"analyzedSha":"64bdf2ff08c8b39e65717bcf974fb43dc2ef68f2","analyzedAt":"2026-08-13T21:19:38.062Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}