Electric Funds Transfer
In this article
Electric Funds Transfer (EFT) is the main API for any operation to the payment entry device (PED). These are the main operations:
- PurchaseAsync
- RefundAsync
- VoidAsync
- GetTransactionAsync and
- GetLastRawTransactionDataAsync
The examples below all demonstrate how to do a purchase through the Mock plugin.
To get the EFT from the EAM, call the function GetEFT()
:
var eft = eam.GetEFT();
This will return an object for any plugin in LS Pay.
The function returns an IEFT
object. To have access to the EFT functions it needs to be type casted to IPED
or IPIS
, depending on the functions needed.
EFT types
You get two different types of EFT from the GetEFT() function:
- IPED
- This interface is used when the payment device is external from the POS system.
- IPIS
- This interface is used when the payment device is integrated in the POS system.
- An example is the AltaPay device A920 on Android where the communication is done with an Intent. LS Pay provides functions to receive an Intent and to parse the response, the POS system will manage the communication with the Activity.
To gain access to the functions, the EFT object (IEFT) type must be known: ask what the type is:
if (eft is IPED)
{
// Here you know that the IEFT you got from the EAM is implementing the IPED interface
paymentResponse = ((IPED)eft).PurchaseAsync(request);
}
else if (eft is IPIS)
{
// The plugin you asked for is implementing the IPIS interface
// the device is integrated in the same hardware as the POS system and communicates // through an intent
var intent = ((IPIS)eft).GetIntent(request, appId, scheme);
StartActivity(intent);
}
IOfflineEFT
Additional functions to handle offline purchase. Currently, only used by Nets. When the terminal cannot access the host, a cashier should have the option to call the offline purchase function. Offline purchase is handled like the PurchaseAsync
function in the POS.
To gain access to the offline functions, use the PurchaseOfflineAsync
in IOfflineEFT interface:
if (eft is IOfflineEFT)
{
// Here you know that the IEFT you got from the EAM is implementing the IOfflineEFT interface
paymentResponse = ((IOfflineEFT)eft).PurchaseOfflineAsync(request);
}
IVoiceAuthentication
Note: Obsolete as of 10/2020. Use IVerification instead.
A response from a purchase function can include the following result in TransactionResponse
:
ResultCode is ResultCode.VoiceAuthenticationNeeded
EftMessage is "VoiceAuthenticationNeeded"
AuthorizationStatus is AuthorizationStatus.WaitingForInput
The POS/ECR should display a message box where an authorization number can be inserted (string value). The authorization number is given by the card issuer, information regarding this should be known to the cashier or an administrator.
When the authorization number has been inserted, call the function ConfirmVoiceAuthenticationAsync
from the interface IVoiceAuthentication. The function returns the TransactionResponse
object with the final result:
if (eft is IVoiceAuthentication)
{
// Here you know that the IEFT you got from the EAM is implementing the IVoiceAuthentication interface
paymentResponse = ((IVoiceAuthentication)eft).ConfirmVoiceAuthenticationAsync(authorizationNumber);
}
ISignatureVerification
Note: Obsolete as of 10/2020. Use IVerification instead.
A response from a purchase function can include the following result in TransactionResponse
:
ResultCode is ResultCode.SignatureVerificationNeeded
EftMessage is "SignatureVerificationNeeded"
AuthorizationStatus is AuthorizationStatus.WaitingForInput
The POS/ECR should display a message box where the signature given by the customer can be accepted or declined.
Call the function VerifySignatureAsync
from the interface ISignatureVerification. The function accepts a boolean value, true if the signature is accepted. It then returns the TransactionResponse
object with the final result:
if (eft is ISignatureVerification)
{
// Here you know that the IEFT you got from the EAM is implementing the ISignatureVerification interface
paymentResponse = ((ISignatureVerification)eft).VerifySignatureAsync(signatureConfirmed);
}
IAmountConfirmation
Note: Obsolete as of 10/2020. Use IVerification instead.
A response from a purchase function can include the following result in TransactionResponse
:
ResultCode is ResultCode.CashBackConfirmationNeeded
EftMessage is "Cashback Confirmation Needed"
AuthorizationStatus is AuthorizationStatus.WaitingForInput
The POS/ECR should display a message box where the cashback amount is confirmed.
Call the function ConfirmAmountAsync
from the interface IAmountConfirmation.
In this example there is a need to confirm the cashback amount (currently supported by Worldpay).
if (eft is IAmountConfirmation)
{
AmountBreakdown amountBreakdown = new AmountBreakdown()
{
CashBackAmount = amountToConfirm
};
// Here you know that the IEFT you got from the EAM is implementing the IAmountConfirmation interface
paymentResponse = ((IAmountConfirmation)eft).ConfirmAmountAsync(amountBreakdown);
}
IVerification
This interface replaces the old verification interfaces: IVoiceAuthentication, ISignatureVerification, IAmountConfirmation, and ICardVerification.
A response from a purchase function can include the following result in TransactionResponse
:
ResultCode is ResultCode.FurtherInformationNeeded
EftMessage is "Verification Required"
AuthorizationStatus is AuthorizationStatus.WaitingForInput
VerificationInfo contains 1 or more Verification objects
When any sort of verification is required after a purchase, the TransactionResponse
includes a list of Verification objects (can be 1 or more objects) that contain information about what type of verification is required and what data is needed.
Example where two types of verifications are required:
response.VerificationInfo =
{
{
DisplayText = "Please verify signature",
DataType = DataType.bool,
VerificationType = VerificationType.Signature,
Value = null // This field will contain the signature verification (true/false)
},
{
DisplayText = "Please verify last 4 digits of card number",
DataType = DataType.string,
VerificationType = VerificationType.CardData,
Value = null // This field will contain the card data verification (last 4 digits of card)
}
};
The POS/ECR should display a message box where the verification can be performed. Call the function VerifyAsync
from the interface IVerification:
if (eft is IVerification)
{
response.VerificationInfo =
{
{
DisplayText = "Please verify signature",
DataType = DataType.bool,
VerificationType = VerificationType.Signature,
Value = boolVerification // Value filled in by POS
},
{
DisplayText = "Please verify last 4 digits of card number",
DataType = DataType.string,
VerificationType = VerificationType.CardData,
Value = stringVerification // Value filled in by POS
}
};
// Here you know that the IEFT you got from the EAM is implementing the IVerification interface
paymentResponse = ((IVerification)eft).VerifyAsync(response.VerificationInfo, response.Ids);
}
Double purchase function
A response from a purchase function can include the following result in TransactionResponse
:
ResultCode is ResultCode.FurtherInformationNeeded
EftMessage is "SurchargeNeeded"
AuthorizationStatus is AuthorizationStatus.WaitingForInput
The POS/ECR now has the opportunity to update the amounts and send again. This is currently only used by Nets.
When an amount has been added (surcharge), call the purchase function again with the updated amounts. The function will proceed and return the TransactionResponse
object with the final result Transaction Identification.
LS Pay v.2 introduced a new object to keep track of unique identifications for each transaction TransactionIdentification
. This object is part of the TransactionResponse
and is used when the IDs are needed in any other functions, like when retrieving a transaction by ID.
- TransactionId
- Each transaction created in the POS/ECR can send a unique ID that is sent with the request. This ID is sometimes stored with the transaction at the PSP or the Acquirer. This identity must be tracked in the POS and stored with each transaction.
- EFTTransactionId
- An EFT transaction ID created by the PSP (Payment Service Porvider) will be received in all responses. This identity must be tracked in the POS and stored with each transaction.
- TransactionDateTime
- The date and time of the transaction being processed is not to be depended on. If provided, it indicates the date and time value of the authorization. If not supplied by the PSP/Acquirer, this will be the date and time when LS Pay gets the response from the PSP/Acquirer.
- AdditionalId
- All other IDs needed for recovery or reversal, not used to identify the transaction in the POS/ECR. This could be a single ID, JSON object as a string or anything that is known in the plugin handle.
- BatchNumber
- In some instances the authorization is known inside a batch. This batch number is created in the Terminal.
Example when sending request to LS Pay:
PurchaseRequest paymentRequest = new PurchaseRequest()
{
TransactionId = "1239BC" // Unique id of the given transaction in POS/ECR
};
Example when receiving a response from LS Pay:
string uniquePOSReference = transactionResponse.Ids.TransactionId;
string uniqueEFTReference = transactionResponse.Ids.EFTTransactionId;
string additionalReference = transactionResponse.Ids.AdditionalId;
string eftTransactionDateTime = transactionResponse.Ids.TransactionDateTime;
string batchNumber = transactionResponse.Ids.BatchNumber;
VoidRequest voidRequest = new VoidRequest()
{
TransactionId = unquePOSTransactionId,
OriginalIds = new TransactionIdentification()
{
TransactionId = uniqueOriginalPOSReference,
EFTTransactionId = uniqueOriginalEFTReference,
AdditionalId = originalAdditionalReference,
TransactionDateTime = originalEFTTransactionDateTime,
BatchNumber = originalBatchNumber
}
}
Purchase
Let's make a .NET console program. Note: This is a demo project for the Mock plugin.
In the Main function, a variable named response has all information needed from the response. The next step is to connect to a real provider, for example Verifone:
using System.Threading.Tasks;
using LSPay.Engine.Domain.EFT;
using LSPay.Engine.Domain.Services;
using LSPay.Engine.Factory;
namespace TestEngineConsole
{
class EngineService
{
public static void Main(string[] args)
{
// TransactionResponse is the object returned from the purchase
TransactionResponse response;
try
{
// Always try and catch calles to LS Pay.
// LS Pay uses throw to return error messages.
response = PurchaseAsync().Result;
}
catch(Exception e)
{
Servicesystem.Console.WriteLine(e.message);
return;
}
System.Console.WriteLine(response.ToString());
}
public static async Task<TransactionResponse> PurchaseAsync()
{
// Pick the payment service provider
var psp = ExternalAccessoryType.Mock;
// Get the correct External accessory management
var eam = EAManagementModelFinder.EAManagementModel(psp);
// Get the EFT from the EAM there you can make the transactions
var eft = eam.GetEFT();
var TransactionResponse = new TransactionResponse()
{
TransactionId = "1239BC",
// The amount breakdown contains a few variables, most import is the TotalAmount, which is the amount to be authorized.
amountBreakdown = new AmountBreakdown()
{
TotalAmount = 20.00M
}
};
// Finally make the transaction
if (eft is IPED)
{
var response = await ((IPED)eft).PurchaseAsync(paymentRequest);
}
else
{
throw new InvalidOperationException(
"Only IPED supports this opperation: PurchaseAsync");
}
// In this response object you can find all the information you need.
return response;
}
}
}
For more information see https://bitbucket.lsretail.com/projects/PAY/repos/pay-documentation/browse/Engine
Receipts: Transaction flow varies between types of available transaction (purchase, refund or void/reverse). This overview shows some of the main responses and the need for receipt printout by the POS/ECR system.
Offline
If a terminal does not handle the online/offline function on the terminal, the POS/ECR system may need to handle it.
Currently, only one PSP does not manage online/offline on the terminal, that is Nets. See details here: https://bitbucket.lsretail.com/projects/PAY/repos/pay-documentation/browse/Engine/Nets/lspay_engine_nets.md
Tip amount
Tip amount is part of a total amount and if PSP supports receiving a suggested tip amount, the amount is added in the purchase request. Adyen, however, does not support sending amount, but it is possible to activate it on the terminal.
To do so, the request needs to indicate it. The default value is false:
var paymentRequest = new PurchaseRequest()
{
AmountBreakdown = amounts,
TransactionId = localTransactionId,
AskGratuity = checkboxAskGratuity.Checked
};
Pre-authorization
Some plugins support pre-authorization functions. This means an authorization is given for a limited time which has to be confirmed within a given time span.
This is an added interface and needs to be checked before its called:
public async Task<TransactionResponse> PreAuth(PurchaseRequest request)
{
if (eft is IPreAuth)
{
return await ((IPreAuth)eft).PreAuthAsync(request);
}
else
{
throw new InvalidOperationException("Only IPreAuth supports this opperation: Pre-authorization");
}
}
When update is needed, call UpdatePreAuthAsync
with updated amount (if needed) and the same EFTTransactionId and TransactionId from the original pre-authorization.
public async Task<TransactionResponse> UpdatePreAuth(PurchaseRequest request)
{
if (eft is IPreAuth)
{
return await ((IPreAuth)eft).UpdatePreAuthAsync(request);
}
else
{
throw new InvalidOperationException("Only IPreAuth supports this opperation: Pre-authorization");
}
}
In the end, the previously given pre-authorization is finalized by calling FinalizePreAuth
, again with the same EFTTransactionId and TransactionId from the original pre-authorization.
public async Task<TransactionResponse> FinalizePreAuth(PurchaseRequest request)
{
if (eft is IPreAuth)
{
return await ((IPreAuth)eft).FinalizePreAuth(request);
}
else
{
throw new InvalidOperationException("Only IPreAuth supports this opperation: Finalize pre-authorization");
}
}
Note: This functionality is still in test mode
Installments
When the TenderType of the purchaseRequest is set to Installments, the total amount of the payment is split into parts, depending on the number of payments.
// Here you know that the IEFT you got from the EAM is supporting installments
if (eft is IInstallments)
{
var purchaseRequest = new PurchaseRequest()
{
AmountBreakdown = amounts,
TenderType = TenderType.Installments
};
if(purchaseRequest.TenderType == TenderType.Installments)
{
purchaseRequest.Installments = installments
}
}
See also