Sana Assistant (online)
Table of Contents

PaymentExtension reference

This article provides reference material about PaymentExtension class. All payment extensions have to be inherited from the PaymentExtension which, in turn, inherits from the core Sana Commerce Cloud Extension class.

There is also generic class PaymentExtension<TMethodSettings> inherited from PaymentExtension which provides payment extensions with possibility to have configurable payment methods. It takes type inherited from PaymentMethodSettings as a generic type parameter.

PaymentExtension inheritance

Methods

PaymentExtension

CreateAdditionalCustomerDataModel

Returns an instance of AdditionalCustomerDataModel that will be used as a view model for "Information for payment provider" checkout step. In base implementation returns null.

For example, when payment provider needs additional customer data, extension developer has to create class inherited from AdditionalCustomerDataModel. Instance of this class has to be returned by CreateAdditionalCustomerDataModel method to inform Sana Commerce Cloud to render it as a view model for "Information for payment provider" checkout step.

public override AdditionalCustomerDataModel CreateAdditionalCustomerDataModel(AdditionalCustomerDataModelCreationContext context)
{
    // Assuming that 'MyCustomerDataModel' is the model that needs to be
    // rendered on "Information for payment provider" checkout step
    return new MyCustomerDataModel();
}

CreateExtraCheckoutStepModel

Returns an instance of ExtraCheckoutStepPageModel that will be used as an extra checkout step. In base implementation returns null.

public override ExtraCheckoutStepPageModel CreateExtraCheckoutStepModel(ExtraCheckoutStepModelContext context)
{
    // Assuming that 'ExtraStepModel' is extra checkout step model
    // that needs to be inserted to the current checkout process
    return new ExtraStepModel(Api);
}

FinalizePayment

This method is called when the user is done with the payment on the PSP payment page. Sana Commerce Cloud calls Finalize method to finalize the current payment process. Finalizing means that the current payment status of the order will be updated to the actual status from PSP and the payment transaction will be set as finalized so that we do not start it again accidentally.

public override void FinalizePayment(PaymentContext context)
{
    var result = context.HttpContext.Request.Form["RESULT"];
    if (result == "0")
        context.State.PaymentStatus = PaymentStatus.Paid;
    else
        context.State.PaymentStatus = PaymentStatus.Cancelled;
}

GetAllowedCallbackIpRange

Returns the IPv4 address filter where payment callbacks are allowed from. This method is called by Sana Commerce Cloud before processing payment callback request. If origin IP address of the payment callback request is in the list of allowed IP address ranges then Sana Commerce Cloud will continue execution flow, otherwise request will be blocked and corresponding exception will be thrown.

The filter can be in format of a single IP address (127.0.0.1), an address range (127.0.0.1-127.0.0.256) or an address mask (127.0.*.*). Multiple allowed filters can be combined with a vertical pipe character '|'.

public override string GetAllowedCallbackIpRange()
{
    // Assuming that IP range has been entered on configuration page in Sana Admin
    return Configuration?.IpRange;
}

GetSupportedCurrencies

Gets the list of all currencies identifiers supported by the current payment provider according to the ISO 4217 standard. In base implementation returns a single item with the value "*" means that all currencies are supported.

In case when payment provider does not support all currencies, extension developer has possibility to override this method.

public override IEnumerable<string> GetSupportedCurrencies()
{
    return new string[] { "EUR", "USD", "UAH" };
}

GetTransactionId

Gets the payment transaction identifier from incoming callback request. This method is called by Sana Commerce Cloud when payment transaction identifier is sent in callback request body.

In base implementation this method gets value of "id" parameter from Request.Query or Request.Form collection.

If your payment service provider sends callback data in different way (for example as a JSON object) in request body then you should override this method to parse JSON object and return transaction identifier.

In order to get access to incoming request body extension developer has to read Request.Body property of context parameter.

Note

Before accessing Body property do not forget to call Seek() method in order to set stream pointer to the beginning of stream.

public override string GetTransactionId(IHttpContext context)
{
    var data = GetRequestParameters(context.Request);

    return data["transaction_id"];
}

private Dictionary<string, string> GetRequestParameters(IHttpRequest request)
{
    request.Body.Seek(0, SeekOrigin.Begin);
    using (var readStream = new StreamReader(request.Body, request.ContentEncoding))
    {
        var data = readStream.ReadToEnd();
        return Json.Decode<Dictionary<string, string>>(data);
    }
}

ProcessCallback

This method is called when a callback request is received from the payment service provider. This typically happens when payment service provider finally gets the payment transaction confirmed by a bank and makes a call to the webstore to update the payment status of the order to the actual payment status. Usually returns NextAction.None object.

For example, payment extension can make a call to payment service provider to get updated payment status for transaction.

public override NextAction ProcessCallback(PaymentContext context)
{
    // Assuming that PspWebService is your instance of PSP gateway service
    var updatedStatus = PspWebService.GetPaymentStatus(context.TransactionId);

    switch (updatedStatus)
    {
        case "paid":
        case "captured":
            context.State.PaymentStatus = PaymentStatus.Paid;
        case "cancelled":
        case "failed":
            context.State.PaymentStatus = PaymentStatus.Cancelled:
        default:
            context.State.PaymentStatus =  PaymentStatus.InProgress;
    }

    return NextAction.None;
}

HandleUnknownTransaction

This method is called when a callback request is received from the payment service provider and Sana Commerce Cloud can identify payment module, but then either transaction ID is not returned from GetTransactionId or the returned transaction ID is not registered within Sana Commerce Cloud.

You can override this method in case your PSP notifies Sana Commerce Cloud about transaction created by somebody else (e.g. manually created payment link for a customer) and requires certain response to be returned (instead of default empty HTTP 200 response) to prevent the same transaction being sent repeatedly.

public override NextAction HandleUnknownTransaction(UnknownTransactionPaymentContext context)
{
    // You can send some e-mail for such transactions or add necessary info to the payment log.
    return NextAction.Content("[Success]");
}

StartPayment

This method initiates a payment process with payment service provider. Usually this means that we need to request some sort of a key or a hash string from the payment service provider web service so that it opens a secure payment session to us for this particular payment.

public override NextAction StartPayment(PaymentStartContext context)
{
    var parameters = GetFormPostParameters(context);

    return NextAction.HttpPost(GetPaymentPageUrl(context), parameters);
}

IDictionary<string, string> GetFormPostParameters(PaymentStartContext context)
{
    // returns parameters required to make a call to the PSP gateway
}

string GetPaymentPageUrl(PaymentContext context)
{
    // returns the URL to the PSP payment page
}

GetPaymentStatus

Gets the payment status of transaction from payment service.

Warning

This method must return null if payment service has no information about specified order, exception must not be thrown in this case.

In base implementation this method returns null.

public override PaymentStatus? GetPaymentStatus(PaymentStatusSyncContext syncContext)
{
    var url = $"https://somedomain.com/{syncContext.TransactionId}/status";
    var data = SubmitRequest<StatusResponseModel>(url, WebRequestMethods.Http.Get);
    if (!data.status_code.Equals("200", StringComparison.OrdinalIgnoreCase))
        return null;
    ValidateSignatureKey(syncContext, data);
    return GetPaymentStatus(data);
}

Use case: This method is used to determine if order already was processed. If it returns status PaymentStatus.InProgress or PaymentStatus.Paid, system knows that payment for this document already was started. In case payment service provider you are integrating doesn't support required functionality or sends payment callback immediately, implementing of this method is not necessary.