When this interface is implemented, it is possible for the payment provider to send asynchronous status updates to Litium.
This article explains how to implement this interface and how to receive status updates from the payment provider.
The Litium.Foundation.Modules.ECommerce.Plugins.Payments.IPaymentProviderReport interface has a single method, which is called by the Litium API when the payment provider sends status updates of existing orders to Litium.
Not every payment provider has this capability, and the technical name used to call these status updates may vary based on the provider.
To receive the status updates, the following two need to be done:
- The payment provider plugin should implement the IPaymentProviderReport interface.
- The payment provider should be configured (usually through its admin site) with the correct url to reach Litium.
Implementing the IPaymentProviderReport interface
The method to implement has the following signature:
void OnProviderReportCallback(HttpRequestBase request, HttpResponseBase response, SecurityToken token)
Parameters
- request: HTTP request received in Litium. This object should contain the necessary query string (or HTTP Post data) to identify the order and the payment info object. Also, it should contain the status of the payment.
- response: HTTP response that will be sent back to the payment provider. The exact response to send will be specified by the payment provider.
- token: Admin token of the Sales area.
Implement this interface in the same class as the IPaymentProvider interface is implemented in.
Also, you need to reference the assembly System.Web.Absractions so that the System.Web.HttpRequestBase entity is available.
Thread safety
The implementation should be thread safe, because the payment provider may send several calls at the same time for the same order, or Litium might be synchronizing the payment status at the same time the call is received. More information is in the Synchronizing payment status article.
To make the method thread safe, use Litium.Utilities.LogicalSyncLock and the payment info id as the sync root. Do not use the normal lock statement as it will cause only one payment to be processed at a time hugely impacting the performance.
The PaymentInfo object is not set by the API
When this method is called, the API has not initialized the plugin with the PaymentInfo object. Therefore, the PaymentInfo object is null at the begining of this method, and needs to be set via the SetPaymentInfo method call. (The reason why the API does not set this is that the API does not know how to find the order and the payment info relating to the call received. Only the plugin knows how to find the correct order.)
Sample code
The following is a sample implementation: You need to additionally reference the Litium.Utilities and System.Web.Abstractions assemblies.
You may need the following using statements.
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Net.Sockets;
using System.Text;
using Litium.Owin.InversionOfControl;
using Litium.Foundation;
using Litium.Foundation.Log;
using Litium.Foundation.Modules.ECommerce;
using Litium.Foundation.Modules.ECommerce.GUI;
using Litium.Foundation.Modules.ECommerce.Orders;
using Litium.Foundation.Modules.ECommerce.Payments;
using Litium.Foundation.Modules.ECommerce.Plugins.Payments;
using Litium.Foundation.Security;
using Litium.Studio.AddOns.Samples.TestPaymentsProvider.Configuration;
using Litium.Utilities;
/// <summary>
/// Called when payment provider sneds status updates to Litium Studio.
/// </summary>
/// <param name="request">The request.</param>
/// <param name="response">The response.</param>
/// <param name="token">The token.</param>
public void OnProviderReportCallback(System.Web.HttpRequestBase request, System.Web.HttpResponseBase response, SecurityToken token)
{
//obtain the order id and the paymentinfo id from the request.
//query string keys may differ from payment provider to provider.
var orderId = request["orderId"];
var paymentInfoId = request["paymentInfoId"];
var status = request["status"];
//gaurd against raise condition of same call getting called twiced for the same payment.
//do not use ordinary "locks" as this will cause only one payment to be processed at a time.
//instead use the Litium.Utilities.LogicalSyncLock
string syncRoot = paymentInfoId;
try
{
LogicalSyncLock.Enter(syncRoot);
//find the payment info.
PaymentInfo paymentInfo = null;
Order order = ModuleECommerce.Instance.Orders.GetOrder(orderId, token);
if (order != null)
{
foreach (var item in order.PaymentInfo)
{
if (item.ReferenceID == paymentInfoId)
{
paymentInfo = item;
break;
}
}
}
if (paymentInfo != null)
{
//initialize the plugin with current payment info.
SetPaymentInfo(paymentInfo);
//TODO: change the paymentInfo payment status based on the status received from the payment provider
//response written back depends on provider.
response.Write("OK");
}
else
{
//If the payment info is not found, there is a misconfiguration at the payment providers side.
//they are sending the status to the wrong Litium Studio installation.
var msg = string.Format("Corresponding payment not found for HTTP report callback {0}", request.Url.ToString());
Solution.Instance.Log.CreateLogEntry(" Http report callback failure", msg, LogLevels.ERROR);
//response written back depends on provider.
response.Write("FAIL");
}
}
catch (Exception e)
{
//TODO: handle any exceptions and log error message to foundation log.
//response written back depends on provider.
response.Write("FAIL");
}
finally
{
LogicalSyncLock.Exit(syncRoot);
}
}
When the status is received from the payment provider, it might not exactly match the payment status in Litium. The matching has to be done in the plugin and the correct status set. The best way to do this is explained in the Synchronizing payment status article.
Configuring the payment provider to call Litium
The payment provider should be configured to call Litium when a status update is available. The url that should be provided to the payment provider has the following form:
http://yourServerDomain/PaymentProviderResult.axd/Report/PaymentProviderPluginName
For example, if the payment provider Plugin attribute is set as below:
/// <summary>
/// A sample payment provider for testing checkout flows.
/// </summary>
[Plugin(TestPaymentsProvider.ProviderName)]
public class TestPaymentsProvider:PaymentProviderBase, IPaymentProvider, IPaymentProviderReport
{
/// <summary>
/// The payment providers name.
/// </summary>
public const string ProviderName = "TestPaymentsProvider";
}
Then the call report callback url is:
http://yourServerDomain/PaymentProviderResult.axd/Report/TestPaymentsProvider
Note the name "TestPaymentsProvider" at the end of the above url, which is the same name used to identify the plugin.