Offline Transactions via Deferred Authorizations
To accept card payments, your merchant's POS must be connected to the internet for processing and authorizing the transaction online. But what happens if the internet connection or the acquirer is down? Usually the merchant is blocked and cannot accept card payments temporarily. This situation can lead, not only to customer dissatisfaction, but also to a real business challenge.
Offline Transactions allow your merchants to accept payments
at their own risk
when an internet connection is not available. Transactions are queued securely locally and after the internet connection is restored, the merchant can submit the stored Offline Transactions for Deferred Authorization.Before implementing this feature please talk to your account manager, to understand whether the risk introduced by using Offline Transactions is acceptable for the kind of merchants you aim to serve.
To use Offline Transactions, your POS needs to be extended for running and submitting offline transactions and your processes need to ensure that Offline Transactions are reconciled later on, i.e. that it's verified which transactions got approved or declined in the end.
Running and Submitting Offline Transactions
All the offline operations are syntactically similar to the standard transaction operations and are exposed via the new offline module.
To get started, just create the
PosClient
, and log in to the PayServer as normal:IPosClient PosClient = new PosClient("PAYSERVER_ADDRESS", "PAYSERVER_PORT"); PosClient.ConnectAndLogin("MERCHANT_IDENTIFIER", "MERCHANT_SECRET", Environment.TEST, (devices, error) => { //check for error //use devices for interaction Device = devices[0]; }); IOfflineTransactionModule OfflineModule = PosClient.GetOfflineTransactionModule();
Starting Offline Transaction
Running an offline transaction is just as simple as:
var transactionParameters = new TransactionParameters.Builder() .Charge(10.00 m, Currency.EUR) .Subject("Box of chocolates") .CustomIdentifier("yourReferenceForTheTransaction") .Build(); OfflineModule.StartTransaction(device, transactionParameters, transactionProcessParameters, (transaction, transactionProcessDetails, abortable) = >{}, actionType = >{ // in a live app, this image comes from your signature screen // submit digital signature. // signature field takes a byte array (byte[]) InteractiveProcess.SubmitSignature(signature, (error) = >{ // check for error }); // or collect signature on the receipt after transaction InteractiveProcess.SubmitSignatureOnReceipt((error) = >{ // check for error }); }, (transaction, transactionProcessDetails, error) = >{ if (error != null) { // error indicates that there was a error with the transaction processing // check error.Type and error.Message for more information // Allow your merchant to try another transaction // Make clear to merchant to NOT hand out the goods } else { // Inspect the transaction.Status to get the overall result / transaction completed if (transaction.Status == TransactionStatus.ACCEPTED) { Console.WriteLine("ACCEPTED: " + transaction.Identifier); // Make sure to store the transactionIdentifier for later reconciliation // transaction.Identifier // Provide a printed receipt to the merchant and shopper // transaction.MerchantReceipt // transaction.CustomerReceipt } } });
Only when the
TransactionProcessDetailsState
is ACCEPTED
, the Offline Transaction was successful and stored for later submission (and even later authorization). For any other status, make clear to your merchant that they should not hand out the goods.Make sure to store the
transactionIdentifier
for later reconciliation (see below) and to provide compliant merchant and shopper receipts as documented hereRefunding Offline Transactions
Refunding a previous Offline Transaction is just as simple as:
var refundParameters = new TransactionParameters.Builder() .Refund("transactionIdentifier") .Build(); OfflineModule.AmendTransaction(refundParameters, (transaction, transactionProcessDetails, abortable) = >{ // transaction update information }, (transaction, transactionProcessDetails, error) = >{ // check for error if (error != null) { // error indicates that there was a problem refunding the transaction // check error.Type and error.Message for more information } else { // Let the merchant know that they can return the goods to the shopper // You can access details of the refund in transaction.RefundDetails Console.WriteLine("Refund successful"); // Provide a refund receipt: // Get all refundTransactions from the CHARGE transaction List Payworks.PayClient.Transactions.RefundTransaction > refundTransactions = transaction.RefundDetails.RefundTransactions; // Access the last refund transaction (usually there is only one) RefundTransaction lastRefund = refundTransactions.Last(); if (lastRefund != null and lastRefund.Status == TransactionStatus.ACCEPTED) { // Get REFUND transaction includung receipt data OfflineModule.GetTransaction(lastRefund.Identifier, (refundTransaction, err) = >{ if (err == null) { // Provide a printed REFUND receipt to the merchant and shopper // refundTransaction.MerchantReceipt // refundTransaction.CustomerReceipt } }); } } });
Information on creating your own receipts can be found here.
Note that partial refunds are not supported for Offline Transactions.
Submitting Offline Transactions for Deferred Authorization
Once internet connection is restored, you need to make sure that the merchant submits the stored Offline Transactions for Deferred Authorization: We suggest to train your merchants to do this as part of their daily end-of-day/store closing routines.
If the Offline Transactions are not submitted, they will not be processed, hence they remain stored locally and the merchant will not receive the actual money. Do not forget to ask your merchants to submit them as soon as possible.
Submitting the offline transactions will send all stored Offline Transactions to the gateway, which then asynchronously will attempt to run a Deferred Authorization for each of the submitted transactions. Offline Transactions submission can be achieved as follows:
OfflineModule.SubmitTransactionsBatch((transactions, error) = >{ if (error == null) { // The Offline Transactions have been submitted successfully // for later Deferred Authorization. // You can find the submitted transactions by var submittedTransactions = details.transactions; // You can mark them as submitted in your POS as submitted, but not yet APPROVED/DECLINED } else { // error indicates that there was a problem refunding the transaction // check submitTransactionsBatchProcessDetails.getError().getErrorType() // and submitTransactionsBatchProcessDetails.getError().getInfo() for more information // there could be submitted transaction present even if there are errors // as the error may have occurred during submission process( Eg: Internet connectivity issues) // details.getAllTransactions(); // Make your merchants aware that they need to attempt to submit // the Offline Transactions again, until successful } });
In case an error occurs, please make your merchants aware that they need to attempt to submit the Offline Transactions again, until successful.
If the error indicates
ErrorType.SERVER_OFFLINE_BATCH_MALFORMED
then you will find some submitted transactions to be pending manual review. This indicates that these submitted transactions will be reviewed manually by us because there was some discrepancy with the transaction data. Lookup and Querying Offline Transactions
A transaction lookup for any stored Offline Transaction can achieved as follows:
OfflineModule.GetTransaction("transactionId", (transaction, error) = >{ if (error != null) { //logic to apply for the transaction } else { //handle the error } });
Querying the stored Offline Transactions is achieved as follows:
var filterParameters = new FilterParameters.Builder("payClientTesterTransaction").Build(); bool includeReceipts = false; int offset = 0; int limit = 10; OfflineModule.QueryTransactions(filterParameters, includeReceipts, offset, limit, (transactions, error) = >{ if (error == null) { //logic for queried transactions } else { //handle the error } });
The respective response messages in either case contain the stored Offline Transactions which were not yet submitted to the gateway.
Reconciling Offline Transactions
After submitting Offline Transactions it's important that you verify the final transaction outcome for each
ACCEPTED
transaction. To achieve this, your POS or Backend needs to query each
ACCEPTED
transaction and then act according to the status:- PENDINGmeans that theACCEPTEDtransaction is still queued for Deferred Authorization, which will result in eitherAPPROVEDorDECLINEDeventually. You need to check the status again later.
- means that theAPPROVEDACCEPTEDtransaction has successfully been approved and that the merchant will receive the money. No further actions are required for you and your merchant.
- means that the merchant will NOT receive money for theDECLINEDACCEPTEDtransaction. The merchant should either contact the shopper for an alternative method of payment or write off the loss, depending on the payment risk approach of the merchant.
You can get the status of each
ACCEPTED
transaction via either:- Querying thetransactionIdentifierof theACCEPTEDtransaction from your POS:
ITransactionModule TransactionModule = PosClient.GetTransactionModule(); TransasctionModule.lookupTransaction("transactionIdentifierOfAcceptedTransaction", (transaction, error) = >{ if (error != null) { if (transaction.Status == TransactionStatus.APPROVED) { // Merchant will receive the money. No further actions are required. } else if (transaction.Status == TransactionStatus.DECLINED) { // Merchant will NOT receive money. } else if (transaction.Status == TransactionStatus.PENDING) { // Transaction is still queued for Deferred Authorization, try again later } else { // handle error and try again } } else { // handle the error } });
- Receiving a WebHook on your Backend with the final status (APPROVEDorDECLINED) of the transaction. For this you need to match the previously storedtransactionIdentifierof theACCEPTEDtransaction with thetransactionIdentifierof the WebHook.