Offline Transactions via Deferred Authorizations
To accept card payments, your merchant's POS must be connected to the internet for processing and authorizing transactions 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 leads 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 once the internet connection is restored, the merchant can submit the stored Offline Transactions for Deferred Authorization.
Before implementing this feature please contact your account manager, to understand whether the risk introduced by Offline Transactions is acceptable for the kind of merchants you aim to serve.
To use Offline Transactions, your POS must be extended for running and submitting offline transactions and your processes must 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
TransactionProvider
,
TransactionParameters
and
AccessoryParameters
as normal:
TransactionProvider transactionProvider = Mpos.createTransactionProvider(MyActivity.this, ProviderMode.TEST, "MERCHANT_IDENTIFIER", "MERCHANT_SECRET_KEY");
TransactionParameters transactionParameters = new TransactionParameters.Builder()
.charge(new BigDecimal("10"), Currency.EUR)
.subject("my first transaction")
.customIdentifier("yourReferenceForTheTransaction")
.build();
/* When using the Bluetooth Miura, use the following parameters:
AccessoryParameters accessoryParameters = new AccessoryParameters.Builder(AccessoryFamily.MIURA_MPI)
.bluetooth()
.build();
*/
/* When using Verifone readers via WiFi or Ethernet, use the following parameters:
AccessoryParameters accessoryParameters = new AccessoryParameters.Builder(AccessoryFamily.VERIFONE_VIPA)
.tcp("192.168.254.123", 16107)
.build();
*/
Starting Offline Transaction
Running an offline transaction is just as simple as:
[transactionProvider.offlineModule
startTransactionWithParameters:transactionParameters
accessoryParameters:accessoryParameters processParameters:nil registered:^(MPTransactionProcess * transactionProcess, MPTransaction * transaction) {
NSLog(@"Transaction identifier is %@. Store it in your backed so that you can always query its status", transaction.identifier);
} statusChanged:^(MPTransactionProcess * transactionProcess, MPTransaction *transaction, MPTransactionProcessDetails * details) {
NSLog(@"Status changed %@", details.information);
} actionRequired:^(MPTransactionProcess * transactionProcess, MPTransaction * transaction, MPTransactionAction action, MPTransactionActionSupport *support) {
NSLog(@"Action is required!");
switch (action) {
case MPTransactionActionCustomerSignature:
// in a live app, this image comes from your signature screen
[transactionProcess continueWithCustomerSignature:[UIImage new]
verified:YES];
break;
case MPTransactionActionCustomerIdentification:
// always return false here
[transactionProcess continueWithCustomerIdentityVerified:NO];
break;
case MPTransactionActionApplicationSelection: {
// This happens only for readers that don't support application selection on their screen
MPTransactionActionApplicationSelectionSupportWrapper *sw = [MPTransactionActionApplicationSelectionSupportWrapper wrapAround:support];
[transactionProcess continueWithSelectedApplication:sw.applications.firstObject];
} break;
case MPTransactionActionCreditDebitSelection:
[transactionProcess continueCreditDebitSelectionWithDebit];
break;
case MPTransactionActionNone:
//NOOP
break;
}
} completed:^(MPTransactionProcess * transactionProcess, MPTransaction *transaction, MPTransactionProcessDetails * details) {
if (details.state == MPTransactionProcessDetailsStateAccepted) {
// Make sure to store the transactionIdentifier for later reconciliation
NSLog(@"transactionIdentifer for reconciliation: %@", transaction.identifier);
// print the merchant receipt
MPReceipt *merchantReceipt = transaction.merchantReceipt;
// print a signature line if required
if (merchantReceipt.printSignatureLine) {
NSLog(@"\n\n\n------ PLEASE SIGN HERE ------");
}
// ask the merchant, whether the shopper wants to have a receipt
MPReceipt *customerReceipt = transaction.customerReceipt;
// and close the checkout UI
} else {
// Allow your merchant to try another transaction
// Make clear to merchant NOT to hand out the goods
}
}];
But only when the
TransactionProcessDetailsState
equals
ACCEPTED
>, the Offline Transaction was successfully 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
here.
Refunding Offline Transactions
Refunding a previous Offline Transaction is just as simple as:
TransactionParameters refundTransactionParameters = new TransactionParameters.Builder()
.refund("transactionIdentifer")
.build();
transactionProvider.getOfflineModule().amendTransaction(refundTransactionParameters, new BasicTransactionProcessListener() {
@Override
public void onStatusChanged(TransactionProcess transactionProcess, Transaction transaction, TransactionProcessDetails transactionProcessDetails) {
//intermediary update
}
@Override
public void onCompleted(TransactionProcess transactionProcess, Transaction transaction, TransactionProcessDetails transactionProcessDetails) {
if (transaction.getStatus().equals(TransactionStatus.ACCEPTED)) {
//logic for successful offline transaction refund
}
}
});
Information on creating your own receipts can be found
hereNote 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 Offfline 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:
transactionProvider.getOfflineModule().submitTransactionsBatch(new SubmitTransactionsBatchProcessListener(){
@Override
public void onCompleted(SubmitTransactionsBatchProcessDetails submitTransactionsBatchProcessDetails) {
if (submitTransactionsBatchProcessDetails.getError() == null) {
// The Offline Transactions have been submitted successfully
// for later Deferred Authorization.
// You can find the submitted transactions by
List<SubmittedTransaction> submittedTransactions = submitTransactionsBatchProcessDetails.getAllTransactions();
// 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 occured 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.
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:
transactionProvider.getOfflineModule().lookupTransaction("add your transaction id here", new LookupTransactionListener(){
@Override
public void onCompleted(String transactionIdentifier, Transaction transaction, MposError mposError) {
if (mposError == null) {
//apply logic for the found transaction
} else {
//handle error
}
}
});
Querying the stored Offline Transactions is achieved as follows:
boolean includeReceipts = false;
int offset = 0;
int page = 10;
FilterParameters filterParameters = new FilterParameters.Builder().build();
transactionProvider.getOfflineModule().queryTransactions(filterParameters, includeReceipts, offset, page, new QueryTransactionsListener() {
@Override
public void onCompleted(FilterParameters filterParameters, boolean includeReceipts, int offset, int limit, List transactions, MposError error) {
if (error == null) {
//apply logic for the found transactions
} else {
//handle 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:
means that the
ACCEPTED
transaction is still queued for Deferred Authorization, which will result in either
APPROVED
or
DECLINED
eventually. You need to check the status again later.
means that the
ACCEPTED
transaction 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 the
ACCEPTED
transaction. 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 the
transactionIdentifier
of the
ACCEPTED
transaction from your POS:
transactionProvider.getTransactionModule().lookupTransaction("transactionIdentifierOfAcceptedTransaction", new LookupTransactionListener(){
@Override
public void onCompleted(String transactionIdentifier, Transaction transaction, MposError mposError) {
if (mposError == null) {
if(transaction.getStatus() == TransactionStatus.APPROVED) {
// Merchant will receive the money. No further actions are required.
Log.d("mpos", "Final state: APPROVED");
} else if(transaction.getStatus() == TransactionStatus.DECLINED) {
// Merchant will NOT receive money.
Log.d("mpos", "Final state: DECLINED");
} else if(transaction.getStatus() == TransactionStatus.PENDING) {
// Transaction is still queued for Deferred Authorization, try again later
Log.d("mpos", "Still waiting for Deferred Authorization");
} else {
// handle error and try again
}
} else {
// handle error and try again
}
}
}); }
Receiving a
WebHook on your Backend with the final status (
APPROVED
or
DECLINED
) of the transaction. For this you need to match the previously stored
transactionIdentifier
of the
ACCEPTED
transaction with the
transactionIdentifier
of the WebHook.