POS机项目原理分析

智能POS机收银app开发分析

Posted by JovenHe on January 22, 2019

开发本质

智能POS机本质上就是一个Android手机,我们要开发的支付软件从本质上也是一个普通的app,只不过这个Android手机是根据指定商家进行配置的,所以我们开发的app也只能给特定POS机使用,POS机厂家通过审核商家材料定制出商家POS机,商家就可以在POS机的应用商店下载并使用商家自己的app进行支付收银操作。

商家现在的操作场景是在PC系统收银调起POS机app,这就是在云端使用消息推送把支付相关信息推送给POS机,POS机后台服务接收到支付信息后,调起app并显示相关信息,确认后进行支付,支付行为完成后,根据支付结果进行响应并传给PC系统

   
易通POS.png

开发方式

支付功能开发的过程大部分POS机只有两种:

1.集成支付相关的接口

这种方式就是我们自己做一个收银app,集成了POS机厂商的SDK,这种方式我们要做收银,查询订单,撤销订单这些收银app都有的功能,同时在搭建项目的时候也要引入二维码扫描与生成库等第三方库。这种方式过于费时费力。

2.调用POS机自带的收银app

POS机上自带厂家或者代理商的收银APP,通常这个app是允许其他应用调用的。开发的app在需要收银时通过Intent调用收银APP的收银Activity,并传入相关的支付参数,待支付成功或失败后会携带相关参数返回给我们的app,我们再返回给系统后台进行数据保存。我们现在用的就是这种方式

绑定POS机

首先app可能安装在多个POS机上,那在PC系统上开单或者充值时,如何确定让哪一个POS机来进行支付行为。这就需要在POS机安装app后进行绑定POS机,当然也会存在解绑后再重新绑定的情况,所以需要在app的启动页把POS机相关信息传给后台,让后台可以绑定这个POS机给系统店铺。

POS机相关信息就包括SN码与友盟token码

友盟token码

是友盟在其推送消息时的唯一标识,它就相当于设备的唯一码。当然其它推送平台也会有类似token的唯一码。

集成好友盟推送SDK后,在打开app时,就会通过调用注册方法获取token

获取POS机sn码 (Serial Number 产品序列号)

POS机硬件信息.jpg

SN码是POS机的唯一标识,起码同一个厂家里SN码是不同的,可以直接在设备背面看到。

不同的POS机厂商有不同的SN码配置,代码获取的话,有的直接获取Android SN码就可以,有的却需要通过厂商Jar文件调用相关服务获取

1.设备sn码直接获取 (易通的联迪POS)

Aandroid设备系统信息里就有SN码,可以通过android.os.Build.SERIAL来获取sn码

2.厂商自定义设备SN码 (新大陆星POS)

通过Build.SERIAL获取的sn码与设备背面明显不一致,所以推测通过相关厂商接口调用才能得到。

新大陆星POS是引入厂商jar文件,调用相关接口得到

原理是通过绑定系统服务,使用AIDL得到相关参数

bindService(intent,mConnection, Context.BIND_AUTO_CREATE); 绑定系统服务

  • 第一个参数是一个明确指定了要绑定的service的Intent.通过给的service的Action字符串得出
  • 第二个参数是ServiceConnection对象.
  • 第三个参数是一个标志,它表明绑定中的操作.它一般应是BIND_AUTO_CREATE,这样就会在service不存在时创建一个.其它可选的值是BIND_DEBUG_UNBIND和BIND_NOT_FOREGROUND,不想指定时设为0即可.
private ServiceConnection serviceConnection = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        //绑定成功
        aidlDeviceService = AidlDeviceService.Stub.asInterface(service);
        AidlSystem aidlSystem = AidlSystem.Stub.asInterface(aidlDeviceService.getSystemService());
            aidlSystem.setAppSwitchKeyEnabled(false);
            LogUtil.e(TAG, "getSerialNo: "+aidlSystem.getSerialNo() );
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {
        //解除绑定
        aidlDeviceService = null;
    }
};

POS机接收支付消息

在POS机使用中,经常是我们的app未被打开,在系统发送支付信息时也能把消息发送到App,这就是使用了Android中的消息推送。

后端与app端都集成了友盟推送SDK,并通过Appkey等参数配置,确保后台调起的app就是POS机中的app。

原理:app通过集成友盟推送SDK,POS机系统后台有一个Service服务在与友盟进行通信,后端通过集成的友盟推送SDK推送支付信息到POS机上,POS机的Service服务接收到信息后打开app支付信息页面,并显示支付信息

支付功能的实现

在App的支付信息显示页面使用startActivityForResult方法能够跳转到POS机自带的收银APP的指定页面,该方法可以携带支付方式、支付金额等参数调起了收银app的收银页面,收银页面根据支付信息进行收银,支付失败或者成功都会把结果数据回调给app,在app支付信息显示页面的onActivityResult方法中对返回的intent实例的携带参数进行分析,得到支付结果数据,并传给后台

//设置要跳转的收银app的包名与收银页面的类名
ComponentName component = new ComponentName("com.landicorp.android.sdectonsale", 
                    "com.landicorp.android.sdectonsale.MainActivity");
            Intent intent = new Intent();
            intent.setComponent(component);
            //支付方式
            intent.putExtra("transName", "微信支付");
            //支付金额
            intent.putExtra("amount", "1");
            //订单号
            intent.putExtra("orderInfo", "87533700744");
            startActivityForResult(intent, REQUEST_PAY);
@Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode == REQUEST_PAY) {
            Bundle bundle = data.getExtras();
            if (bundle != null) {
                switch (resultCode) {
                    case Activity.RESULT_OK:
                        /**
                         *    金额 amount-value-000000000001
                         *    卡号(银行卡支付才会有值,否则为空) cardNo-value-null
                         *    终端号 terminalId-value-56066311
                         *    凭证号 traceNo-value-000015
                         *    条形码,商户订单号 barcode-value-201901210741837381
                         *    批次号 batchNo-value-000002
                         *    商户编号 merchantId-value-870451073990352
                         *    参考号 referenceNo-value-210741837381
                         *    日期 date-value-0121
                         *    时间 time-value-154251
                         *    发卡行(银行卡支付才会有值,否则为空) issue-value-null
                         *    商户名 merchantName-value-个体户王长举
                         */
                        Toast.makeText(mContext, "交易成功", Toast.LENGTH_SHORT).show();
                        break;
                    case Activity.RESULT_CANCELED:
                        //用户取消
                        //45 请使用芯片
                        String reason = data.getStringExtra(Constants.BUNDLE_REASON);
                        LogUtil.e(TAG, "onActivityResult--fail-" + reason);
                        if (reason != null) {
                            Toast.makeText(mContext, "交易失败", Toast.LENGTH_SHORT).show();
                        }
                        break;
                    default:
                        finish();
                        break;
                }
            }
        }
        super.onActivityResult(requestCode, resultCode, data);
    }

App上线

我们做好的app要能让客户使用,就要上传到POS机上的应用商店,而根据设备商代理层次的不同,分为两种上线流程

1.开发者通过厂商提供的开发者平台上线app

我们需要在开发者平台网站注册商户账号,提供资质证明,审核通过后提交app,进行审核,审核成功后可以在定制POS的应用市场中查询到app,并下载使用

2.设备商上线app

开发者把app提供给设备商,设备商拿到app后,进行签名上传等操作,相当于他们是开发者,我们使用他们的定制应用市场得到我们应用的下载使用权

在我看来,智能POS机只是一个低版本的Android机,有的是高级定制,比如新大陆的POS机,整个系统都是定制过的,各种界面都是新大陆自己在原生基础上进行改动,删除的删除,替换的替换,有的是低级定制,比如易通的迪联POS机,看上去仅仅像是自己做了一个Launcher,然后只显示自己要显示的应用,其他系统设置都是原生界面,有的可以跳转,有的不给显示,所以无从跳转。