Payment Gateway – Redirection flow

This article describes the steps needed to implement a Payment Gateway plugin for SDK v2.0 which is using the redirection flow technique.

What is the “Redirection flow”?

Payment gateways usually use one of two different approaches regarding the collection of sensitive data from a user, such as the credit card details. One can be described as “redirection” and the other as “embedded”.

On a “redirection” technique the interworks.cloud platform redirects the user to another website (URL) in which a secure page is hosted by the Payment Gateway provider. This ensures no sensitive data ever gets to the platform because any data are collected on this secure page. As soon as the user completes the necessary information, the gateway’s page redirects the user back to the platform usually with an HTTP/GET request. This request contains in the query parameters any number of key/value data that the platform needs to use in order to complete the transaction securely. Usually, a transaction identifier is issued by the gateway which the plugin developer can use to contact the gateway using a server-to-server connection and complete and/or test the transaction’s result. Some gateways also pass some indicators to check whether the payment was successful or not. Although you can test this indicator to determine the result, it is highly recommended to call the gateway’s API and make sure it is completed successfully.

On an “embedded” technique, the developer gets to integrate directly with the platform’s UI and display any needed elements to the user “inside” or “embedded” in the checkout process UI. This makes for a better user experience because no refresh/reload of the page needs to happen such as in the “redirection” method. Details of how you implement an “embedded” plugin is out of the scope of this article.

Getting familiar with the Payment Gateway plug-in

A plugin for a “redirection” gateway is comprised of two projects, one used during transactions, the “Processor”, and one for the UI of the gateway’s setting, the “Setup Options UI”.

The “Processor” is responsible to “translate” or act as an adapter if you like from the platform’s actions to the gateway’s API. For example, when the platform needs to make a recurring payment, it will invoke the “ExecuteTransaction” method on the Processor passing any required data, such as the previously saved credit card token, for the “Processor” to execute and charge the card by using the gateway’s API.

The “Setup Options UI” lets the developer configure what settings the user needs to provide for the plugin to function properly. Usually, this would be some API keys or any other means of authentication.

The “Processor” project

1. Create a new C# Class Library Project (.NET Framework). Use as a naming convention for the namespace and project name the “Iwcp.PaymentGateways.<Gateway Name>

2. Install interworks.cloud ‘s NuGet package Iwcp.SDK.PaymentGateways.

3. Create the Processor class file. We recommend using a naming convention “<Gateway Name>Processor.cs”.

4. Edit Emulator’s “Storage.json” and add your new gateway. Follow the example of the default “Foo” gateway. Also, configure the Emulator to use your newly added gateway.

5. Extend your class from Iwcp.SDK.PaymentGateways.DefaultProcessor. This will give your Processor some basic functionality, such as logging support via Trace. Decorate the class using a PaymentGateway attribute so that the Processor will be discovered by the SDK.  The name used in the attribute must match the gateway type used in “Storage.json”

[PaymentGateway("my-foo-gateway")]
public class FooProcessor : DefaultProcessor, IRedirection
{
    public FooProcessor()
    {
 
    }
}

6. Implement the interface Iwcp.SDK.PaymentGateways.Interfaces.IRedirection. This is the interface used by the Redirection flow. The interface serves the “on-session” transactions. The “on-session transaction” is a transaction with a user present, usually during a basket checkout procedure. Also, the “on-session” transaction may or may not produce a token for future recurring charges. A redirection flow always has two phases. The “before” (PrepareTransaction) and “after” (VerifyTransaction) redirection.

a. PrepareTransaction - The first phase is the communication with the gateway, like a “handshake”, which is established right before the user’s agent is redirected to the gateway’s website. At this point, the two parties exchange some information about the imminent transaction such as the amount, currency, buyer details, etc. After a successful PrepareTransaction, the user will be redirected to the designated URL by the TransactionRedirectResponse. He will confirm his Credit Card details and commit the transaction. At this point the gateway’s website will redirect the user back at the designated URL by the Processor by the TransactionRedirectResponse or in some cases directly configured on the gateway’s portal, passing a transaction token and/or the transaction result in the query params.

public TransactionRedirectResponse PrepareTransaction(TransactionRedirectRequest request)
{
    // Try to log before and after a request
    Trace("Foo::PrepareTransaction - Request", request);
 
    // Communicate with gateway...
    // Retrieve some "Transaction ID" and the redirect-away URL from the gateway's API...
    // Return the URL in order for the SDK to redirect the user to it
    try {
        var fooResponse = invokeFooGateway(...);
    }
    catch(Exception ex)
    {
        // If anything goes wrong at this phase try to interpret it and throw a more meaningful error to the user
        throw new PaymentGatewayException("Some more human friendly error message. This will be shown in the UI")
    }
 
    Trace("Foo::PrepareTransaction - Response", fooResponse);
 
    return new TransactionRedirectResponse
    {
        // RedirectionUrl is provided by payment gateway.
        RedirectionUrl = fooResponse.RedirectionUrl,
        TransactionId = fooResponse.TransactionId
    };
}

The following are details regarding payment data:

The TransactionRedirectRequest contains the ReturnedURL, which is the Storefront URL + ?bridge=Redirection&m=base (e.g.: https://www.example.com/?bridge=Redirection&m=base)

Inherit from the interface IPaymentDataConsumer:

public class FooProcessor : DefaultProcessor, IRedirection, IPaymentDataConsumer

Set payment data source:

private IPaymentDataSource _paymentDataSource { get; set; }
public void SetPaymentDataSource(IPaymentDataSource paymentDataSource)
{
    _paymentDataSource = paymentDataSource;
}

You can now use the payment data source in the PrepareTransaction, VerifyTransaction and ExecuteTransaction requests as follows:

var paymentOverview = _paymentDataSource.GetPaymentOverview(request.PaymentId);
var userOverview = _paymentDataSource.GetUserOverview(request.PaymentId);
var billingAddressOverview = _paymentDataSource.GetBillingAddressOverview(request.PaymentId);
var contactOverview = _paymentDataSource.GetBillingContactOverview(request.PaymentId);
var accountOverview = _paymentDataSource.GetAccountOverview(request.PaymentId);

b. VerifyTransaction - When redirected-back, the VerifyTransaction will be invoked by the SDK passing the query params in the TransactionStatusRequest. Use the query params to communicate with the gateway’s API, determine the result of the transaction and return a TransactionStatusResponse. At this point, if a token was generated for recurring charges return it so it can be persisted for future use. If not return null in the PaymentProfile.

public override void ValidateSettings(Dictionary<string, object> settings)
{
    if (!settings.ContainsKey("fooUsername") && string.IsNullOrEmpty(settings["fooUsername"].ToString()))
        throw new ArgumentException("Incorrect payment gateway fooUsername");
 
    if (!settings.ContainsKey("fooPassword") && string.IsNullOrEmpty(settings["fooPassword"].ToString()))
        throw new ArgumentException("Incorrect payment gateway fooPassword");           
}
 
public TransactionStatusResponse<CreditCard> VerifyTransaction(TransactionStatusRequest request)
{
    // Try to log before and after a request
    Trace("Foo::VerifyTransaction - Request", request);

    // At this point request.QueryParameters will contain any query params used during the HTTP/GET request when the gateway's
    // page redirected the user back to the platform

    // Communicate with gateway using any data from request.ExtraData
    // Retrieve information about the transaction's result

    var fooResponse = new MockFooTransactionStatusResponse();
	
	Trace("Foo::VerifyTransaction - Response", fooResponse);
	
	// Test if the transaction was cancelled by the user. You should throw the appropriate error defined by the SDK
	if (fooResponse.CancelledByTheUser)
	    throw new PaymentGatewayException(new PaymentGatewayUserCancellationError());

    // Test if the transaction was successful. If not throw an error, otherwise return a TransactionStatusResponse<CreditCard> object
	if (!fooResponse.Success)
	    throw new PaymentGatewayException(fooResponse.GatewayTranscationId, "Some human readable reason why the transaction failed");
		
	return new TransactionStatusResponse<CreditCard>
{
PaymentProfile = new CreditCard // If the gateway has tokenized the credit card return a PaymentProfile so it will be saved for future use, otherwise return null
{
    ExpirationMonth = fooResponse.ExpMonth, // You can leave it empty if not provided by your gateway
    ExpirationYear = fooResponse.ExpYear, // You can leave it empty if not provided by your gateway
    MaskedNumber = fooResponse.MaskedCreditCardNumber, // You can leave it empty if not provided by your gateway
    CardType = fooResponse.CreditCardType, // You can leave it empty if not provided by your gateway
    Token = fooResponse.CreditCardToken // Must always be provided
} 
};
}

7. ExecuteTransaction – If your gateway tokenizes Credit Cards for recurring charges you need to implement the ExecuteTransaction method. This method is part of the “off-session” transaction interface. An “off-session transaction” is considered a transaction that takes place without a user present, usually used for recurring charges from the platform’s Billing engine. When this method is invoked you will have access to the previously generated and persisted token so you can use it to complete the transaction. If your gateway doesn’t tokenize credit cards you can leave the method body empty, it will not be invoked by the SDK.

public TransactionResponse ExecuteTransaction(PaymentRequest request)
{
    // Try to log before and after a request
    Trace("Foo::ExecuteTransaction - Request", request);
 
    // Communicate with gateway using the request.Token
    // Retrieve information about the transaction's result
    // Return wether the credit card got charged
    var fooResponse = new MockFooExecuteResponse();
 
    Trace("Foo::ExecuteTransaction - Response", fooResponse);
 
    // Test if the transaction was successful. If not throw an error, otherwise return a TransactionResponse object
    if (!fooResponse.Success)
        throw new PaymentGatewayException(fooResponse.GatewayTranscationId, "Some human readable reason why the transaction failed");
     
    return new TransactionResponse
    {
        TransactionId = fooResponse.GatewayTranscationId,   // Some identifier for the transcation generated by the gateway, if provided, otherwise leave empty string
        Amount = fooResponse.AmountCharge          // The amount that was charged, if provided, otherwise leave null
    };
}

8. ValidateSettings – For convenience, the SDK will call this method right before invoking any of the aforementioned methods. You can write here any validation logic and throw exceptions if your criteria are not met.

public override void ValidateSettings(Dictionary<string, object> settings)
{
    if (!settings.ContainsKey("fooUsername") && string.IsNullOrEmpty(settings["fooUsername"].ToString()))
        throw new ArgumentException("Incorrect payment gateway fooUsername");
 
    if (!settings.ContainsKey("fooPassword") && string.IsNullOrEmpty(settings["fooPassword"].ToString()))
        throw new ArgumentException("Incorrect payment gateway fooPassword");           
}

If the payment gateway returns some error, you can throw a PaymentGatewayException so that the error to be logged and displayed on the UI.

The “Setup Options UI” project

  1. Create a new C# Class Library Project (.NET Framework). Use as naming convention, for the namespace and project name, the “Iwcp.PaymentGateways.SetupOptions.<Gateway Name>“.
  2. Install interworks.cloud ‘s NuGet package Iwcp.SDK.PaymentGateways.
  3. Create the SetupOptionsUI class file. We recommend using a naming convention “PaymentGatewaySetupOptions<GatewayName>”.
  4. Implement the interface ISetupOptionsUI from Interworks.SDK.PaymentGateways
  5. Implement the OnInit method.
[PaymentGateway("my-foo-gateway")]
public class PaymentGatewaySetupOptionsFoo : ISetupOptionsUI
{
    public void OnInit(SetupOptionsBuilder builder)
    {
        builder
            .AddInputText("foo-option-1", "Foo 1")
            .AddInputText("foo-option-2", "Foo 2")
            .AddInputText("foo-option-3", "Foo 3")
            .SetInstructions("<div style=\"padding:5px\">\r\n" +
                    "<b>Setup Instructions</b><br/>\r\n" +
                    "<br/>\r\n" +
                    "Lorem ipsum...\r\n" +
                    "</div>");
 
  
 
    }
}

Deploying your plug-in (on-premise installations)

The following applies only for on-premise installations. In case you are just building a plug-in to be hosted in interworks.cloud infrastructure you should zip your source code and send it to interworks.cloud for reviewing and deployment

  • Place your .dll inside the “bin” folder of both the BSS and the Storefront v4.0.
  • Add a record of your newly developed gateway type in your DB.
  • Go to BSS → Setup → Administration → System Options → Payment Gateways and setup your new gateway
INSERT INTO tblPaymentGateway([Name], [Type], [IsSupported], [PCICompliant]) VALUES('My Foo Gateway', 'my-foo-gateway', 1, NULL);

Using the Storefront Emulator to Develop the Plug-In

To develop a gateway plugin, or any kind of plugin for the platform, interworks.cloud provides an “Emulator” that you can use in order to test and debug your code as easily as possible. See the page Storefront Emulator to download the Emulator and learn more about it.