Hello! Today we want to share our experience with peripheral devices on the Android platform.
You have come to the ski rental for skis and you need to pay with a card and get an offer agreement for the provision of services and a check. It would seem, what could be easier?
In reality, everything is much longer, and you yourself know very well why.
In one place you choose the inventory, then go to the cashier, where all the racks from all rental racks flow, you pay for it, return to the distributor, get the inventory from him and are finally ready to meet the mountain!
And during peak hours, this story inevitably turns into hell.
This is how the speed of service from one and a half minutes stretched out to all 20, and at this time someone already cuts the morning velvet of the snow slope!
Why not to equip each rack cashier? Yes, because such a problem is not constant. Peak loads occur regularly, but by and large not so often - morning weekends and holidays, and this is only if the weather is catal. Therefore, to invest in cash equipment from the point of view of the owners, the costs are clearly not super justified. This task is solved by the appearance of an additional mobile employee who once, twice, and helped the queue to dissolve.
In a case with a ski, it would be enough to buy a boxed solution for integrating a smartphone with a cash printer and a card reader, which is enough on the market. They are used by couriers who deliver orders and girls who sell aeroexpress tickets.
We could also enter the amount and get paid, but we do not have rental equipment!
In the customer's system with which we worked, the letter of the law required the issuance of the client not just a check for a certain amount, but also a receipt with a detailed description of the service and a unique number assigned to it. That is, we have to make a payment, register it on the server, get a unique transaction number, and then print the receipt, which displays this number, a full description of the service and a barcode with the coded information.
The task is set, we proceed to the implementation.
To begin with, we decided that we need to use the complex with a smartphone, which allows the user to configure the service parameters and interact with the server. The question of choosing smartphones for the system was solved by itself. The customer chose "workhorses" - smartphones based on Android. They were Huawei Honor 5C. Convenient inexpensive devices from the Chinese manufacturer. The main thing is that Bluetooth is and works. But then it was necessary to solve the problem more difficult. So that all operations with the sale of services were carried out legally, we needed a fiscal registrar. This is a printer with a memory, which records the history of operations on sales.
Mobile fiscal printers (and we remember that our solution must be mobile!) Are produced by a number of Russian companies Atol, Incotex, Accountsmash, Shtrikh-M. We studied their proposals, but in our solution the printer had to print on a wide ribbon (3 ”instead of 2”), since we needed to place on the check detailed data about the service provided. The wide ribbon was found only in one of the mobile barcode printer MTRILE-PTK included in the state register.
We need to take not only cash payments from customers, but also to debit money from the card. So, we need a card reader and provider of such a solution in order to make payments and take into account all the funds spent in this way. And there are enough such solutions in our market. They differ, perhaps, in the types of reader models, the percentage of the commission for acquiring, and ... integrations with fiscal printers! It was then that we came across a solution from iBox Pro , using a chip-n-pin model card reader with a keyboard, and, fortunately, integrated with the SHTRIH-MOBILE-PTK mobile fiscal recorder.
The company provided us with test devices, and we set to work ...
With the iBox Pro payment system, the mobile application can be integrated in a simple way (via an intent-call) and in a complicated way (through the SDK). We have chosen a difficult path, but not at all because we like to overcome difficulties. And for another important reason. There will need a lyrical digression.
In the case of an intent call, the algorithm is as follows:
Our algorithm looks different. We can not just take, drive in the amount for the service and print the check. We need to send data to the server, get the cost of the service, then receive payment from the client, and then contact the server and get a unique code and then be sure to reflect this code in the check.
Therefore, we have developed such an algorithm:
As a result, using the iBox SDK (the latest version of their evolving SDK can be downloaded from GitHUB ) we have embedded calls to the iBox in our mobile application. With the bonus, we got a more user-friendly single interface - at the payment stage, a person does not have to switch to a third-party iBox interface, all processes occur only within our mobile application.
// PaymentController- : // . PaymentController.getInstance().setSingleStepEMV(true); // PaymentController.getInstance().setCredentials(loginInfo.userName, loginInfo.userPassword); // , // . // PaymentCotroller-. ReaderBluetoothInfo readerBluetoothInfo = settingsService.getReaderBluetoothInfo(); List<BluetoothDevice> devices = PaymentController.getInstance().getBluetoothDevices(getView().getContext()); for (int i = 0; i < devices.size(); ++i) { if (devices.get(i).getAddress().equals(readerBluetoothInfo.readerAddres) && devices.get(i).getName().equals(readerBluetoothInfo.readerName)) { PaymentController.getInstance().setReaderType( getView().getContext(), PaymentController.ReaderType.WISEPAD, i, null); } } } ... PaymentDialog paymentDialog = new PaymentDialog(); // : // , ibox. , . PaymentController.getInstance().setPaymentControllerListener(paymentDialog); PaymentController.getInstance().enable(); // PaymentController.getInstance().startPayment(getContext(), mPaymentContext); ... // PaymentController . public void onReaderEvent(PaymentController.ReaderEvent event) { switch (event) { case CONNECTED : case START_INIT : lblState.setText(R.string.reader_state_init); break; case DISCONNECTED : stopProgress(); lblState.setText(R.string.reader_state_disconnected); break; case SWIPE_CARD : case TRANSACTION_STARTED : startProgress(); break; ... case EJECT_CARD : stopProgress(); lblState.setText(R.string.reader_state_eject); break; case BAD_SWIPE : Toast.makeText(mActivity, R.string.reader_bad_swipe, Toast.LENGTH_LONG).show(); break; case LOW_BATTERY : Toast.makeText(mActivity, R.string.reader_low_battery, Toast.LENGTH_LONG).show(); break; default : break; } }
After fixing the fact of sale, it is necessary to issue a check to the client, and sometimes additional documents - an offer contract, order form or letter of guarantee. The question “what are we going to print on?” - if the user has a mobile printer on his belt - let him print on it. Therefore, we forced the printer to print other documents besides fiscal checks.
You can work with the printer through the official library from Shtrikh-M with the documentation they created for the convenience of working with JPOS , as well as using a small example library from enthusiasts.
When printing documents on a mobile printer, we are faced with two cases where the matrix failed:
In combination with some smartphones that we used for testing, the printer’s speed dropped dramatically. It was very disturbing to us, because according to the standard of service, the customer required us to meet 1.5 minutes with the whole process from the registration of the service to the issuance of the check. Fortunately, the target device printed via Bluetooth without problems, and we decided to leave this mystery unsolved.
ShtrihFiscalPrinter printer = new ShtrihFiscalPrinter(new FiscalPrinter()); PrinterBarcode printerBarcode = new PrinterBarcode(); printerBarcode.setText(boardingPass.barcodeText); //, . printerBarcode.setLabel(""); // , . // - . . printerBarcode.setBarWidth(2); // 2 - small width printerBarcode.setType(PrinterBarcode.SM_BARCODE_PDF417); Map<EncodeHintType, Object> parameters = new HashMap<>(); // . // . parameters.put(EncodeHintType.PDF417_DIMENSIONS, new Dimensions(5, 5, 2, 60)); printerBarcode.addParameter(parameters); printer.printBarcode(printerBarcode);
By default, the fiscal printer issues a receipt from the payment system with a minimum of information. It did not suit us. First, we needed a beautifully designed check that would look similar to checks approved in other customer systems. Secondly, we needed to combine the receipt with a receipt for the service and detailed information about it. Total - with the help of the printer SDK, we made a beautiful check-receipt document. And then, long and painfully, one after another, the checks were printed on tests, changing the parameters, until finally the real piece of paper did not look the same on all printers and as needed.
private void printTicket(PrinterInfo printerInfo, boolean isRefund, String agentId) throws Exception { ShtrihFiscalPrinter printer = new ShtrihFiscalPrinter(new FiscalPrinter()); // final String NO_TAX = "0"; final String TEN_PERCENT_TAX = "1000"; printer.setVatValue(1, NO_TAX); printer.setVatValue(2, TEN_PERCENT_TAX); // 10% printer.setVatValue(3, NO_TAX); printer.setVatValue(4, NO_TAX); printer.setVatTable(); printer.setHeaderLine(1, StringTools.appendStrings("", "*", LINE_LENGTH), false); printer.setHeaderLine(2, getHeader(" \" \""), false); printer.setHeaderLine(3, getHeader(" "), false); printer.setHeaderLine(4, getHeader(" , \n "), false); printer.setHeaderLine(5, getHeader("+7(XXX)XXX-XX-XX"), false); printer.setHeaderLine(6, StringTools.appendStrings("", "*", LINE_LENGTH), false); if (isRefund) { printer.setFiscalReceiptType(jpos.FiscalPrinterConst.FPTR_RT_REFUND); } else { printer.setFiscalReceiptType(jpos.FiscalPrinterConst.FPTR_RT_SALES); } printer.beginFiscalReceipt(true); printLine(); double priceSum = 0; //PrinterEmdData . for (PrinterEmdData printerEmdData : printerInfo.getPrinterEmdDatas()) { String description = getDescription(printerEmdData, printerInfo.isCashierFormat()); int tax = 0; final int TEN_PERCENT_NDS = 10; final int SECOND_TAX_SLOT = 2; if (printerEmdData.taxValue == TEN_PERCENT_TAX) { tax = SECOND_TAX_SLOT; // , 10% ( 2- ) } priceSum += printerEmdData.price; if (isRefund) { printer.printRecItemRefund(description, 0, 0, tax, (long) printerEmdData.price, ""); } else { printer.printRecItem(description, 0, 0, tax, (long) printerEmdData.price, ""); } } printLine(); if (printerInfo.isCard()) { printer.printRecTotal((long) priceSum, (long) priceSum, "1"); } else { long cashIn = (long) priceSum; if (printerInfo.getCashIn() > 0) { cashIn = (long) printerInfo.getCashIn(); } printer.printRecTotal((long) priceSum, cashIn, ""); } printer.endFiscalReceipt(true); }
Of course, it is not enough just to make integration into the system; it is important to make this system reliable. For us it was important to provide mechanisms for rolling back from any stage. For example, the payment service confirmed the withdrawal to the application, the application requested a unique service number from the service reservation service, and then the connection with the server was lost. And at this stage you need to make a Fallback mechanism that will allow you to repeat the request to the server or return the money to the card.
There were many variants of such mistakes: when withdrawing money, they pulled out the card early, entered the pin-code incorrectly, the cash register tape ran out while trying to print the check, the printer was dead and so on. The boundaries of required resiliency at each stage were determined during test sessions with the participation of experienced tellers. As a result, the necessary Fallback mechanisms were developed for each error.
We were convinced by our own example that it is possible to solve the problem of accepting payments using mobile devices, even if on the server side it is necessary to keep strict records of each operation. Maybe, after reading the article, someone will see an opportunity to save people from losing time in queues and in some other area.
Give more comfort to customers that we can be with you!
Source: https://habr.com/ru/post/325950/
All Articles