Paylink Integrations Paylink .NET Skeleton Example

Libraries

//
//  Provides access to general .NET classes including those
//  providing primitive data types such as Boolean, Int32 and
//  String.
//
using System;

//
//  Provides access to the HttpContext object for the incoming
//  HTTP GET or HTTP POST request.
//
using System.Web;

//
//  Generic collections library to enable construction of the 
//  JSON structured Paylink Transaction Request.
//
using System.Collections.Generic;

//  
//  Provides access to .NET classes that provide support for 
//  HTTP client applications.
//
using System.Net.Http;
using System.Net.Http.Headers;

//
//  JSON framework for .NET to enable deserialisation of JSON data
//  returned by Paylink in response to JSON formatted requests.
//
using Newtonsoft.Json;

Constants

//
//  The following constant values are used in this integration sample.
//
//  Depending on the nature of the Merchant Application, it may be
//  more appropriate to represent them as part of the global configuration
//  for the application so that it can be amended independently of the
//  program code.
//
const String MERCHANT_ID = "<merchant_id>"
const String LICENCE_KEY = "<licence_key>"

Configure the Paylink Transaction

Get the HttpContext, HttpRequest and HttpResponse objects

//
//  Get the HttpContext object for the current HTTP request.
//
HttpContext context = HttpContext.Current;

//
//  Get the HttpRequest object indirectly from the current
//  HttpContext to enable the .NET application to query
//  the runtime environment, and incoming parameters associated
//  with the HTTP request.
//
HttpRequest httpRequest = context.Request;

//
//  Get the HttpResponse object indirectly from the current
//  HttpContext to enable the .NET application to return
//  a status code, or content in response to the HTTP request.
//
HttpResponse httpResponse = context.Response;

Construct the Paylink Transaction Request

Payment Transaction basic configuration

//
//  Construct System.Collections.Generic.Dictionary object to enable
//  the basic information for the Paylink Transaction Request to be
//  formed using an associative array.
//
Dictionary<String, Object> request = new Dictionary<String, Object>();

//
//  Populate the associative array with mandatory fields for the Paylink
//  Transaction Request.
//
request.Add("merchantid", MERCHANT_ID);
request.Add("licenceKey", LICENCE_KEY);
request.Add("test", "true");
request.Add("identifier", "csharp-integration-test");
request.Add("amount", 5000);

Payment Transaction processing configuration

//  
//
//
Uri uri = httpRequest.Url;
String host = uri.Host;
String hostPort = host + (uri.IsDefaultPort ? "" : ":" + uri.Port.ToString());
String applicationEndpoint = hostPost + uri.AbsolutePath;

//
//  Construct a System.Collections.Generic.Dictionary object to enable
//  the configuration information for the Paylink Transaction Request
//  to be formed using an associative array.
//
Dictionary<String, Object> config = new Dictionary<String, Object>();

//
//  Populate the associative array with desirable fields for configuration
//  of the Paylink Transaction Request as follows -
//
//    1.  the Postback policy (postback_policy).
//
//    2.  the Postback RPC endpoint (postback).
//
//        Note
//        ====
//
//        Postback RPC endpoint must be an accessible from an external
//        client. The Postback RPC endpoint cannot, therefore, be a 'localhost'
//        address.
//
if (host.CompareTo("localhost") != 0x00) {
    config.Add("postback_policy", "async");
    config.Add("postback", "http://" + applicationEndpoint + "?postback=true");
} else {
    config.Add("postback_policy", "none");
}

//
//    3.  Specify the success and failure Redirection URLs.
//
config.Add("redirect_success", "http://" + applicationEndpoint + "?success=true");
config.Add("redirect_failure", "http://" + applicationEndpoint + "?failure=true");

//
//  Insert the configuration associative array into the
//  associative array for the request.
//
request.Add("config", config);

Issue the Paylink Transaction Request

//
//  Create System.Net.Http.HttpClient to facilitate sending the HTTP
//  request and receiving the HTTP response from the Paylink server.
//
httpClient = new HttpClient();

//
//  Create a System.Threading.CancellationToken object to supply to
//  of the Wait method of the Task<HttpResponseMessage> object returned
//  by the call to System.Net.Http.HttpClient::PostAsJsonAsync, which
//  is an asynchronous call which returns immediately pending issue of
//  the outgoing HTTP request and receipt of the HTTP response.
//
cancellationTokenSource = new System.Threading.CancellationTokenSource();
cancellationToken = cancellationTokenSource.Token;

//
//  Send the HTTP POST request to the Paylink server, where the
//  associative array given by [request] is serialized to JSON
//  format.
//
taskHttpPostResponse = httpClient.PostAsJsonAsync(
        "https://secure.citypay.com/paylink3/create",
        request
    );

//
//  Wait for the HTTP POST request to complete by a call to the
//  Wait method of the Task<HttpResponseMessage> object returned
//  by the call to System.Net.Http.HttpClient::PostAsJsonAsync.
//
taskHttpPostResponse.Wait(
        System.Threading.Timeout.Infinite,
        cancellationToken
    );

if (!taskHttpPostResponse.IsCompleted) {
    // 
    //  Raise exception indicating that the Paylink Transaction
    //  Request has failed to complete.
    //
}

Process the Paylink Transaction Response

Main control flow

//
//  Interrogate the Task<HttpResponseMessage> object to obtain
//  the result of the HTTP POST request comprising -
//
//    1.  the request status;
//
//    2.  the general, response and entity headers; and
//
//    3.  the message body. 
//
HttpResponseMessage httpPostResponse = taskHttpPostResponse.Result;

//
//  If the HTTP POST request was reported as having been successfully
//  completed by the Paylink server -
//
if (httpPostResponse.Result.StatusCode == 200)
{
    //
    //  Extract the message body from the from the result of the
    //  HTTP POST request.
    //
    String messageBody = httpPostResponse.Content.ReadAsStringAsync().Result;

    //
    //  De-serialize the message body from the result of the HTTP POST request
    //  to form an associative array comprising [string] to [object] mappings.
    //
    //  A number of different approaches used to de-serialised JSON messages;
    //  in the present case, the JSON.NET library from NewtonSoft is used in
    //  conjunction with an associative array. A more advanced approach may
    //  involve the de-serialization of a JSON message to a specific object
    //  structured in accordance with the specification provided for Paylink
    //  Transaction Response.
    // 
    Dictionary<String, Object> jsonResponse =
        JsonConvert.DeserializeObject<Dictionary<String, Object>>(
                messageBody
            );

    //
    //
    //
    //
    //
    Object objResult = jsonResponse["result"];
    if (objResult != null)
    {
        int resultCode = new int();
        bool result = int.TryParse(
                objResult.ToString(),
                out resultCode
            );

        if (result)
        {
            if (resultCode == 0x01)
            {
                Object objUrl = jsonResponse["url"];
                if (objUrl != null)
                {
                    //
                    //  TODO: Redirect Customer Browser to Paylink Payment Form (see below)
                    //
                    //  OR
                    //
                    //  TODO: Embed link to Paylink Payment Form in HTTP response (see below)
                    //
                }
                else
                {
                    //
                    //  TODO: Raise an exception that a URL has not been returned
                    //  in a Paylink Transaction Response that is reported
                    //  as being successful.
                    //
                }
            }
            else
            {
                //
                //  TODO: Handle Payment Transaction Request processing errors (see below)
                //
            }
        }
        else
        {
            //
            //  TODO: Raise an exception that the [result] value returned in
            //  in the Paylink Transaction Response cannot be parsed
            //  as an integer.
            //
        }
    }
    else
    {
        //
        //  TODO: Raise an exception that there is no [result] value provided
        //  provided in the Paylink Transaction Response.
        //
    }
}
else
{
    //
    //    TODO: Handle Payment Transaction Request non-200 HTTP response codes (see below)
    //
}

Redirect or link

Redirect Customer Browser to Paylink Payment Form

String redirectUrl = (String) objUrl.ToString();

//
//  Set the HTTP response status code and status header to a
//  redirect status code and header as appropriate.
//
httpResponse.StatusCode = 303;
httpResponse.Status = "303 See Other";

//
//  Set the HTTP response "Location" header to contain the
//  absolute URI for the Paylink Payment Form as provided
//  in the Paylink Transaction Response.
//
httpResponse.RedirectLocation = redirectUrl;

//
//  Send the buffered HTTP response to the client and
//  end HTTP request processing.
//
httpResponse.End();

Embed link to Paylink Payment Form in HTTP Response

//
//  TODO: Output a link to the Paylink Payment Form in the
//  HTML document generated by the HTTP GET or HTTP POST
//  request to the Merchant Application.
//
httpResponse.Write(
        "<a href=\""
            + ou.ToString()
            + "\">Click here for payment form</a>"
    );

//
//  Send the buffered HTTP response to the client and
//  end HTTP request processing.
//
httpResponse.End();

Handle Payment Transaction Request processing errors

The following code is specific to an integration that deserializes the Paylink Token Response to an associative array derived from System.Collections.Generic.Dictionary<T> using Json.NET, a third party MIT-licensed open source library for serializing and de-serializing data structures to and from the JSON data representation.

//
//  Extract the object containing the JSON representation of the array
//  of errors from the jsonResponse object.
//
Object objErrors = jsonResponse["errors"];

//
//  Ensure that [objErrors] is not null, and is of the type
//  Newtonsoft.Json.Linq.JArray to enable the object to be
//  typecast for assignment and subsequent use.
//
if (objErrors != null
    && objErrors is Newtonsoft.Json.Linq.JArray)
{ 
    //
    //  Typecast [objErrors] to an object of type
    //  Newtonsoft.Json.Linq.JArray to enable access
    //  to the "error" objects contained in the array.  
    //
    Newtonsoft.Json.Linq.JArray arrayError =
       (Newtonsoft.Json.Linq.JArray) objErrors;

    //
    //  Extract the first "error" object (this may alternatively
    //  be structured as a loop to iterate through all of the
    //  validation errors reported by Paylink).
    //
    Object objError = arrayError.First;

    //
    //  For each "error" -
    //
    if (objError != null)
    {
        //
        //  1.  Convert the JSON representation of the "error" to an
        //      associative array to enable access to each member by name.
        //
        String jsonError = objError.ToString();
        Dictionary<String, Object> error =
            JsonConvert.DeserializeObject<Dictionary<string, object>>(
                    jsonError
                );

        //
        //  2.  Extract the error code and error message components of
        //      each "error" and convert them to strings for onward
        //      processing.
        //
        Object objErrorCode = error["code"];
        Object objErrorMessage = error["msg"];
        if (objErrorCode != null
            && objErrorMessage != null)
        {
            String errorCode = objErrorCode.ToString();
            String errorMessage = objErrorMessage.ToString();
 
            //
            //  TODO: Process the error code / error message combination in
            //  some way, either by raising an exception, by logging an error
            //  condition and by reporting an error message back to the
            //  customer through the HTTP response.
            //
            httpResponse.Write(
                 "<p>Error - "
                     + errorCode
                     + " / "
                     + errorMessage
                     + "</p>"
                );
        }
    }
}

Handle Payment Transaction Request non-200 HTTP response codes

//
//  HTTP 1.1 Status codes that may be accepted and handled by
//  the Merchant Application, either specifically if required
//  or generally.
//
switch (httpPostResponse.Result.StatusCode)
{
    //
    //  HTTP 1.1 Client Error status codes.
    //
    case 400:  // "400 Bad Request"
    case 401:  // "401 Unauthorised"
    case 403:  // "403 Forbidden"
    case 404:  // "404 Not Found"
    case 408:  // "408 Request Timeout"
        //
        //  HTTP 1.1 Status codes that may foreseeably be returned by the
        //  Paylink server and which may require specific handling by the
        //  Merchant Application.
        //
        //  TODO: Log error or raise an exception indicating that an
        //  error relating to the availability of the Merchant Application,
        //  or the resources made available by the Paylink server has
        //  occurred. 
        //
        break;

    case 402:  // "402 Payment Required"
    case 405:  // "405 Method Not Allowed"
    case 407:  // "407 Proxy Authentication Required"     
    case 406:  // "406 Not Acceptable"
    case 409:  // "409 Conflict"
    case 410:  // "410 Gone"
    case 411:  // "411 Length Required"
    case 412:  // "412 Precondition Failed"
    case 413:  // "413 Request Entity Too Large"
    case 414:  // "414 Request-URI Too Long"
    case 415:  // "415 Unsupported Media Type"
    case 416:  // "416 Requested Range Not Satisfiable"
    case 417:  // "417 Expectation Failed"
		//
		//  HTTP 1.1 Status codes that are unlikely to be returned by the
        //  Paylink server and which may only require general handling
        //  by the Merchant Application.
        //
        //  TODO: Log error or raise an exception indicating that an
        //  error relating to the availability of the Merchant Application,
        //  or the resources made available by the Paylink server has
        //  occurred. 
        //
        break;

    //
    //  HTTP 1.1 Server Error status codes.
    //
    case 500:  // "500 Internal Server Error"
    case 501:  // "501 Not Implemented"
    case 502:  // "502 Bad Gateway"
    case 503:  // "503 Service Unavailable"
    case 504:  // "504 Gateway Timeout"
		//
		//  HTTP 1.1 Status codes that may foreseeably be returned by the
        //  Paylink server and which may require specific handling by the
        //  Merchant Application.
        //
        //  TODO: Log error or raise an exception indicating that an
        //  error relating to the availability of the Merchant Application,
        //  or the resources made available by the Paylink server has
        //  occurred. 
        //
        break;
}

Handle the Postback RPC  

//
//  Get the message body accompanying the incoming HTTP POST request,
//  using a call to the System.Web.HttpRequest::InputStream method.
//
System.IO.Stream inputStream = httpRequest.InputStream;

//
//  Convert the data available through the System.IO.Stream object
//  associated with the HTTP request to a string as follows -
//
//    1.  determine the length of the data available from the
//        System.IO.Stream object;
//
//    2.  create an array of type Byte to receive the data
//        available from the System.IO.Stream object;
//
//    3.  convert the Byte array to a UTF-8 encoded string
//        using the System.Text.Encoding.UTF8.GetString method. 
//
Int32 intLen, intRead;
intLen = Convert.ToInt32(inputStream.Length);
Byte[] byteArray = new Byte[intLen];
intRead = str.Read(byteArray, 0, intLen);
String strContent = System.Text.Encoding.UTF8.GetString(byteArray);

//
//  De-serialize the JSON formatted message body contents to a 
//  System.Collections.Generic.Dictionary object which provides
//  an associative array comprising [string] to [object] mappings.
//
Dictionary<String, Object> jsonPostback
    = JsonConvert.DeserializeObject<Dictionary<String, Object>>(
		    strContent
	    );

if (jsonPostback == null)
{
    //
	//  Raise a exception: Json.NET was unable to de-serialize the
    //  message body.
    //
}

Object objAuthorised = jsonPostback["authorised"];
if (objAuthorised != null
    && objAuthorised is System.String)
{
	String strAuthorised = (String) objAuthorised;
    Boolean isAuthorised = Boolean.Parse(objAuthorised.ToString()); 

    if (isAuthorised)
    {
        //
        //  TODO: Mark the relevant customer invoice as "authorised".
        //
    }
    else
    {
        //
        //  TODO: Mark the relevant customer invoice as "declined".
        //
    }
}
 
//
//  Conclude processing of the Postback RPC call by responding to the
//  incoming HTTP POST request with a HTTP 200 OK response. 
//
httpResponse.StatusCode = 200;
httpResponse.End();

Handle the Customer Browser Redirection

On success

//
//  1.  Mandatory: check integrity of redirection payload (see below)
//
//  2.  Recommended: compare certain Payment Transaction data provided in the
//      redirection payload with the known state of the Payment Transaction
//      provided to the Merchant Application through the Postback RPC (see above).
//
if (httpRequest.Form.Get("authorised").CompareTo("true") == 0x00)
{
    //
    //  Advise the Customer of any purchase references, or anticipated delivery
    //  date for the product or service purchased.
    //
}
else
{
    //
    //  Raise an exception: there is a mismatch between the reported state of
    //  the Payment Transaction according to the redirection payload and the
    //  redirection endpoint provided to the application by the Paylink server.
    //
}

On failure

//
//  1.  Mandatory: check integrity of redirection payload (see below)
//
//  2.  Recommended: compare certain Payment Transaction data provided in the
//      redirection payload with the known state of the Payment Transaction
//      provided to the Merchant Application through the Postback RPC (see above).
//
if (httpRequest.Form.Get("authorised").CompareTo("true") != 0x00)
{
    //
    //   Advise the Customer that the Payment Transaction was declined and,
    //   if appropriate, give the customer an opportunity to contact the
    //   the Merchant to report any problems or to facilitate manual handling
    //   of the Payment Transaction.
    //
}
else
{
    //
    //  Raise an exception: there is a mismatch between the reported state
    //  of the Payment Transaction according to the redirection payload and
    //  the redirection endpoint provided to the application by the Paylink
    //  server.
    //
}

Checking the integrity of redirection payload

//
//  If the Payment Transaction has been configured to return the parameters
//  of the authorised or otherwise declined Payment Transaction, the parameters
//  are provided in URL encoded form which is presented to .NET handlers as
//  an associative array accessed through the HttpRequest.Form object. The
//  HttpRequest object may be obtained through the HttpContext object associated
//  with the HTTP POST request from the Customer Browser.
//
//  Note
//
//  Verification of the payload forwarded to the Merchant Application via
//  the Customer Browser Redirection is performed by reference to the licence
//  key [licencekey] used for the Payment Transaction which is not transmitted
//  back to the Merchant Application indirectly via the Customer Browser.
//
System.Collections.Specialized.NameValueCollection payload = httpRequest.Form;

//
//  Generate a string containing the text representation of the fields
//  validated by the hash digest.
//
String digestSource = payload.Get("authcode").ToString()
    + payload.Get("amount").ToString()
    + payload.Get("errorcode").ToString()
    + payload.Get("merchantid").ToString()
    + payload.Get("transno").ToString()
    + payload.Get("identifier").ToString()
    + LICENCE_KEY;

//
//  Encode the digest source as a UTF-8 encoded byte array.
//
Byte[] digestSource_utf8 = System.Text.Encoding.UTF8.GetBytes(digestSource);

//
//  Create the SHA256 cryptographic hash processor.
//
System.Security.Cryptography.SHA256 sha256 = System.Security.Cryptography.SHA256CryptoServiceProvider.Create();

//
//  Calculate the SHA256 hash for the UTF-8 encoded digest source
//  to generate a byte array containing the result.
//
Byte[] digest_sha256 = sha256.ComputeHash(digestSource_utf8);

//
// Encode the SHA256 hash value to a Base64 encoded string.
//
String digest_sha256_base64 = Convert.ToBase64String(digest_sha256);

//
//  Compare the Base64 encoded SHA256 hash value calculated by the
//  Merchant Application to the Base64 encoded SHA256 hash value
//  accompanying the Payment Transaction parameters.
//   
if (payload.Get("sha256").CompareTo(digest_sha256_base64) == 0x00)
{
    //
    //  Data subject to validation using a cryptographic hash function
    //  matches the data sent by the Paylink server for indirect delivery
    //  via the Customer Browser. 
    //
}
else
{
    //
    //  Data subject to validation using a cryptographic hash function
    //  does not match the data sent by the Paylink server for indirect
    //  delivery via the Customer Browser. 
    //
}
+44 (0)1534 884000