I've written some custom code to automatically capture funds for an order after an order fulfillment company updates the order as having been shipped. However, sometimes the capture fails leaving behind an unhelpful error. When I re-run the code to automatically capture the funds a second time it usually works without a hitch, but on occasion may take a third try. The code doesn't change between attempts, so I can't figure out why it fails one time and not the other.
Here are the order notes for when it fails and then when it works.
Code:
8/11/2011
Capture Error for order number 1047: An Unknown Payment Error Occred: Object reference not set to an instance of an object. at BVSoftware.BVC5.Payment.AuthorizeNet.AuthorizeNetProvider.ProcessCard(String chargeType, PaymentData data) in c:\workspaces\cupertino\bvcommerce\Main\App\source\BVSoftware.BVC5.Payment.AuthorizeNet\AuthorizeNetProvider.vb:line 192
Transaction Reference Number: <removed>
Transaction Response Code: 237365
8/12/2011
Capture Success for order number 1047: This transaction has been approved. [AVS - AVS not applicable for this transaction]
Transaction Reference Number: <removed>
Transaction Response Code: 237365
Amount: $0.01
In our version of BV (1.7.4) AuthorizeNetProvider.vb:lines 191-193 look like this:
Code:
191 sb.Append(HttpUtility.UrlEncode(data.StoreOrder.UserEmail))
192 sb.Append("&x_Customer_IP=")
193 sb.Append(HttpUtility.UrlEncode(HttpContext.Current.Request.UserHostAddress))
If "sb" is defined at line 191, I don't see why it wouldn't be defined at line 192. This leads me to believe that the error message isn't going to be entirely accurate/helpful.
Here is the code I'm using to capture the funds. It is almost identical to the code used in BVAdmin_Orders_Default.CaptureOrder()
Code:
' Based On BVAdmin/Orders/Default.aspx.vb.CaptureOrder
Shared Sub Capture(ByVal o As Orders.Order, ByVal debugMode As Boolean)
WebgistixDebugger.DebugMsg("Attempting To Capture Funds For Order: " & o.OrderNumber, "", debugMode)
If (Not o.Bvin.Equals("")) Then
Dim previousPaymentStatus As Orders.OrderPaymentStatus = o.PaymentStatus
If (o.Payments.Count.Equals(0)) Then
Throw New Exception("No payments exist for order.")
End If
For Each op As Orders.OrderPayment In o.Payments
If op.PaymentMethodId = WebAppSettings.PaymentIdCreditCard Then
Dim m As Payment.PaymentMethod = op.FindMethod()
Dim method As Payment.CollectablePaymentMethod = CType(m, Payment.CollectablePaymentMethod)
If method.CaptureIsValid(op) = True Then
Dim d As New Payment.PaymentData
d.OrderPaymentId = op.Bvin
d.StoreOrder = o
d.Amount = op.AmountAuthorized
WebgistixDebugger.DebugMsg("Capture Info For Order: " & o.OrderNumber, _
"Payment Amount: '" & d.Amount & "'", _
debugMode)
method.Capture(d)
' Reload Order Status
o = Orders.Order.FindByBvin(o.Bvin)
Dim context As New BusinessRules.OrderTaskContext
context.Order = o
context.UserId = o.UserID
context.Inputs.Add("bvsoftware", "PreviousPaymentStatus", previousPaymentStatus.ToString())
' Not sure exactly what this is doing. It looks like its just checking to see if the
' OrderPaymentStatus has changed
If Not BusinessRules.Workflow.RunByBvin(context, WebAppSettings.WorkflowIdPaymentChanged) Then
' We'll create a new error message based off of the error message found in the code
' we copied and throw an error with it.
Dim errMsg As String = ""
For Each item As BusinessRules.WorkflowMessage In context.Errors
errMsg += item.Name & ": " & item.Description & vbCrLf
Next
Throw New Exception("Payment Stats wasn't updated: " & vbCrLf & errMsg)
End If
' If we got this far then it should have captured the funds. If it's not paid we need
' to throw an error.
Dim updatedOrder As Orders.Order = Orders.Order.FindByBvin(o.Bvin)
If (Not updatedOrder.PaymentStatus = Orders.OrderPaymentStatus.Paid) Then
Throw New Exception("Capture appears to have succeeded, but payment status is not set to Paid")
End If
Else
Throw New Exception("CaptureIsValid came back false. Check to make sure that the full payment was authorized.")
End If
Else
WebgistixDebugger.DebugMsg("Method Was Not A CC For Order: " & o.OrderNumber, "", debugMode)
End If
Next
End If
End Sub
Even when the capture fails the following line returns true, so I'm not sure why the original code used that as its only error checking.
Code:
BusinessRules.Workflow.RunByBvin(context, WebAppSettings.WorkflowIdPaymentChanged)
The code also doesn't throw any errors (I have a try/catch blow around this), which means that some internal code is catching error, printing to the log and then merrily going about its business without notifying the calling code that an error occurred.
Has anyone had this issue before? My assumption that Authorize.net is returning something different between capture attempts since the code isn't changing.