前段时间,公司有个项目,要用Android平板做一个智能点餐系统。与我们常用的APP不同,此项目仅部署在商家的终端(即Android的平板设备),而且由于时间等原因,领导不打算部署服务器。也就是说,要完全使用Android来实现。顾客点餐后,系统发起支付宝当面付请求,生成当面付二维码,顾客支付完成后,订单下发。具体的一些业务不展开细说。
调研当面付在Android平台的使用时,先看了开放平台文档中心的一些文档(地址https://docs.open.alipay.com/194)。下图是截取的扫码支付的接入指引。我们可以看到,其实是有“商家后台”这么个模块。也就是说,我们需要从Android设备将预下单请求发送给服务端,再由服务端发送给支付宝后台。当然,这是最安全最完美的方式。通过看下图,可能很多人会问:我们能不能绕过服务端,直接由Android设备向支付宝发起预下单请求呢?答案是可以的。这也是这篇文章的重点。
既然你点进了我这篇博客,那么我相信你肯定有如下的需求:没有服务端,仅仅想通过写个Android程序来实现当面付。而且,我相信你可能已经遇到了如下问题:
1.我把java的demo下载下来,在eclipse上不能正常跑。
那么你需要了解下当面付签约、公钥、私钥这些问题。
2.我把java的demo导入eclipse中,可以跑,不可以生成二维码。
那么你需要修改写代码中生成二维码部分的文件路径。
3.我在eclipse中可以跑,但是几乎一样的代码,在Androidstudio开发的app上面不能正常使用。
我相信大多数人是遇到了这个问题,恭喜你,你来对地方了。
看了官方论坛的一些博客,也参考了蚂蚁金服java当面付的demo(下载地址https://docs.open.alipay.com/54/104506/)。起初,自己下载下来,导入eclipse使用,可以跑通正常的流程。但是,几乎一样的Java代码,在Android中就是不行。这个问题困扰了我三天,我也尝试去网上寻找答案,但是始终没有一篇文章能解答我的疑惑。
看一下遇到的问题:当我们向支付宝发起预支付请求,以为自己会收到支付宝的二维码信息时,我们懵逼了,没有任何二维码生成,提示“系统异常,预下单状态未知!!!”。预支付请求代码如下(几乎跟java的demo一样):
// 测试当面付2.0生成支付二维码
public void test_trade_precreate() {
String outTradeNo = "tradeprecreate" + System.currentTimeMillis() + (long) (Math.random() * 10000000L);
String subject = "xxx品牌xxx门店当面付扫码消费";
String totalAmount = "0.01";
String storeId = "test_store_id";
String timeoutExpress = "5m";
AlipayTradePrecreateRequestBuilder builder = new AlipayTradePrecreateRequestBuilder()
.setSubject(subject)
.setTotalAmount(totalAmount)
.setOutTradeNo(outTradeNo)
.setStoreId(storeId)
.setTimeoutExpress(timeoutExpress);
AlipayF2FPrecreateResult result = tradeService.tradePrecreate(builder);
switch (result.getTradeStatus()) {
case SUCCESS:
Log.d("TTTT", "支付宝预下单成功: )");
AlipayTradePrecreateResponse response = result.getResponse();
try {
mBitmap = QrUtils.qrToBitmap(response.getQrCode(), 256);
} catch (WriterException e) {
e.printStackTrace();
}
break;
case FAILED:
Log.d("TTTT", "支付宝预下单失败: )");
break;
case UNKNOWN:
Log.d("TTTT", "系统异常,预下单状态未知!!!");
break;
default:
Log.d("TTTT", "不支持的交易状态,交易返回异常!!!");
break;
}
}
看到这个问题,相信大家都是懵逼的,为什么一模一样的java代码,在eclipse中以java程序运行可以,在Android studio中就不行了呢?我们把Androidstudio下logcat切换为error,发现如下log:
其中有个链接,打开这个链接,会告诉你验签出错之类的提示:
于是,我开始一次又一次的验证自己的私钥公钥是否输入错误。最后,我坚信自己绝对没有配置错误。那到底为何报这个问题?于是,我开始以error中的log为关键字百度,在开发者社区查了查,大体知道问题原因了。(地址https://openclub.alipay.com/read.php?tid=2546&fid=55&ant_source=zsearch)。简单来讲,出于安全考虑,https需要验证证书,但是Android端不能验证证书。也就是说,我们要是在Android客户端实现,必须要绕过安全认证。
由于项目时间紧,我也没有采用自己写网络请求的方法。因为我感觉不是那么靠谱。接下来,我开始去研究支付宝sdk的源码(其实我也不知道这是不是源码,反正是java的demo中的jar包。对了,我直接把java项目中所有的jar包全放进了Android项目中)。
源码看了大半天,也可能是一两天,最后找到了问题点。文件名是WebUtils.java。部分源码如下。于是,我试着把return false改为了return true。编译一下,发起请求,成功了。。惊不惊喜?意不意外?困扰了我们很久的问题,其实只需要修改一个地方。。。
static {
try {
ctx = SSLContext.getInstance("TLS");
ctx.init(new KeyManager[0], new TrustManager[] { new DefaultTrustManager() },
new SecureRandom());
ctx.getClientSessionContext().setSessionTimeout(15);
ctx.getClientSessionContext().setSessionCacheSize(1000);
socketFactory = ctx.getSocketFactory();
} catch (Exception e) {
}
verifier = new HostnameVerifier() {
public boolean verify(String hostname, SSLSession session) {
return false;//默认认证不通过,进行证书校验。
}
};
}
在这里注意下,我没有研究过如何把java代码打包成jar包。我们不使用alipay-sdk-java-3.3.0,直接把对应的source文件夹中的源码拷贝出来,放在我们自己的项目src中,删除原有的sdk的jar包。我相信你们比我强,知道我在说什么吧,哈哈,这应该是比较简单的操作,不展开细说。这是我项目的部分结构,以辅助说明我上面的一些话:
(1)libs:保留了alipay-sdk-java-3.3.0以为的jar包
同时,gradle中也注释掉:
(2)src:把sdk中的com.alipay.api单独拿出来放在项目中
原创博客,如转载,请注明出处!谢谢!