支付宝二维码脱机认证库测试过程记录(andorid平台adb shell验证)

本文涉及的产品
阿里云百炼推荐规格 ADB PostgreSQL,4核16GB 100GB 1个月
简介: 支付宝二维码脱机认证库测试过程记录(andorid平台adb shell验证)

最近在调试支付宝给提供的二维码脱机认证库,他们给提供了几个文档和 libposoffline.so库文件。


要想在Android或Linux上做支持支付宝扫码付的应用,必须会调用他们给的二维码脱机认证库。以下是一个在Android调用成功的例子:


记录下过程及注意事项。


如图:

以下是过程记录,


makefile文件:


NDK_ROOT=D:/ADT/android-ndk-r13
TOOLCHAINS_ROOT=$(NDK_ROOT)/toolchains/arm-linux-androideabi-4.9/prebuilt/windows
TOOLCHAINS_PREFIX=$(TOOLCHAINS_ROOT)/bin/arm-linux-androideabi
TOOLCHAINS_INCLUDE=$(TOOLCHAINS_ROOT)/lib/gcc/arm-linux-androideabi/4.9.x/include-fixed
PLATFORM_ROOT=$(NDK_ROOT)/platforms/android-12/arch-arm
PLATFORM_INCLUDE=$(PLATFORM_ROOT)/usr/include
PLATFORM_LIB=$(PLATFORM_ROOT)/usr/lib
MODULE_NAME=demo
RM=del
FLAGS=-I$(TOOLCHAINS_INCLUDE) \
  -I$(PLATFORM_INCLUDE) \
  -L$(PLATFORM_LIB) \
  -nostdlib \
  -lgcc \
  -Bdynamic \
  -lc \
  -ldl
OBJS=$(MODULE_NAME).o \
  $(PLATFORM_LIB)/crtbegin_dynamic.o \
  $(PLATFORM_LIB)/crtend_android.o
all:
  $(TOOLCHAINS_PREFIX)-gcc  $(FLAGS) -c $(MODULE_NAME).c 
zhixing:
  $(TOOLCHAINS_PREFIX)-gcc $(FLAGS) $(OBJS) -o $(MODULE_NAME)
clean:
  $(RM) *.o 
install:
  adb push $(MODULE_NAME) /data/local/
  adb shell chmod 755 /data/local/$(MODULE_NAME)




附:demo.c文件


#include <dlfcn.h>
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <string.h>
#include "pos_crypto.h"
#include "demo.h"
//
void *handle = NULL;  
int (*init_pos_verify1)(const char* key_list, const char* card_type_list);
int (*verify_qrcode_v21)(VERIFY_REQUEST_V2* request_v2, 
          VERIFY_RESPONSE_V2* response_v2);
//
char print_buf[1024] = {0};
int hex_string_to_bytes(
  char* hex_string, 
  int hex_string_len, 
  unsigned char* bytes, 
  int bytes_len);
char* bytes_to_hex_string(
  char* print_buf, 
  int print_buf_len, 
  const unsigned char* bytes, 
  int len);
void mock_qrcode(unsigned char* qrcode, int* qrcode_len);
unsigned char hex_of_char(char c);
/**
 * 验证二维码例程
 *
 * 本例程演示了如何使用支付宝离线安全库对二维码进行验证
 * 例程中使用了mock_qrcode函数生成一个合法的二维码,并验证该二维码的有效性
 * 
 * */
void check_qrcode_demo(){
  int ret = 0;
  const char* pos_param = NULL;
  int qrcode_len = 0;
  unsigned char qrcode[512] = {0};
  const char* key_list = KEY_LIST;
  const char* card_type_list = CARD_TYPE_LIST;
  handle =dlopen("libposoffline.so",RTLD_NOW);  
  if (!handle) {  
    printf( "加载模块错误 %s\n", dlerror() );  
    return;  
  }  
  init_pos_verify1 = dlsym(handle,"init_pos_verify");   
  printf("===========准备数据================\n");
  printf("============进行POS初始化=============\n");  
  /**
   * 请在POS启动时执行POS初始化
   * 初始化时请提供
   * 1. 从支付宝处申请得到的秘钥簇 json列表 形式
   * 2. POS机器支持结算的卡类型 json列表 此处以卡类型 【WH000001 ANT00001】 示例
   * 注:支持卡类型【ANT00001】代表支持支付宝公交付款
   */
  ret = init_pos_verify1(key_list, card_type_list);
  if(ret != SUCCESS){
    printf("初始化POS失败!\n");
    switch(ret){
      case ILLEGAL_PARAM:
        printf("初始化参数格式错误!请检查参数是否符合json列表格式且各字段正确。\n");
      break;
      case NO_ENOUGH_MEMORY:
        printf("内存不足,极端错误,请检查程序运行空间是否足够。\n");
      break;
      case SYSTEM_ERROR:
        printf("系统异常!请联系支付宝技术人员。\n");
      break;
      default:
      break;
    }
    return;
  }
  /**
   * mock一个用户传入的二维码数据qrcode
   * 开发者应当从扫码头获取用户离线公交码
   */
  mock_qrcode(qrcode, &qrcode_len);
  /**
   * pos_param中填入商户pos相关信息 至少包括:
   *    - pos_id  (商户下唯一的pos号)
   *    - type    (脱机记录类型,只刷一次闸机计费的场景下,类型为"SINGLE")
   *    - subject (脱机记录标题,建议放入公交路线)
   *    - record_id (记录id,商户下本次脱机记录唯一id号,record_id必须保证商户唯一,建议通过POS,时间等信息拼装)
   * 注意:pos_param的长度不能大于1024字节!
     */
  pos_param = "{\"pos_id\":\"sh001\",\"type\":\"SINGLE\",\"subject\":\"bus192\",\"record_id\":\"sh001_20160514140218_000001\"}";
  printf("===========准备数据结束================\n");
  printf("===========校验二维码开始================\n");
  //拼装验证请求
  VERIFY_REQUEST_V2 verify_request;
  //装入二进制格式的二维码
  verify_request.qrcode = qrcode;
  //装入二进制二维码长度
  verify_request.qrcode_len = qrcode_len;
  //装入pos_param
  verify_request.pos_param = pos_param;
  //装入本次消费金额 如果生成脱机记录时还无法确定消费金额 装入0(单位:分)
  verify_request.amount_cent = AMOUNT_CENT;
  VERIFY_RESPONSE_V2 verify_response;
  verify_response.uid = (char*)malloc(17);
  verify_response.uid_len = 17;
  verify_response.record = (char*)malloc(2048);
  verify_response.record_len = 2048;
  verify_response.card_no = (char*)malloc(32);
  verify_response.card_no_len = 32;
  verify_response.card_data = (unsigned char*)malloc(128);
  verify_response.card_data_len = 128;
  verify_response.card_type = (char*)malloc(16);
  verify_response.card_type_len = 16;
  /**
   * 调用接口验证二维码的有效性
   */
  verify_qrcode_v21 =  dlsym(handle,"verify_qrcode_v2"); 
  ret = verify_qrcode_v21(&verify_request, &verify_response);
  /**
   * 处理返回的结果
   */
  if(ret != SUCCESS){
    switch(ret){
      case MALFORMED_QRCODE:
        printf("二维码格式错误!请提示用户二维码错误。\n");
      break;
      case QRCODE_INFO_EXPIRED:
        printf("二维码过期!请提示用户刷新二维码。\n");
      break;
      case QRCODE_KEY_EXPIRED:
        printf("二维码密钥过期!请提示用户联网后刷新二维码再使用。\n");
      break;
      case POS_PARAM_ERROR:
        printf("商户传入的pos_param错误,请检查传入的pos_param。\n");
      break;
      case QUOTA_EXCEEDED:
        printf("单笔额度超限!请提示用户由于额度限制无法过闸机。\n");
      break;
      case NO_ENOUGH_MEMORY:
        printf("内存不足,极端错误,请检查程序运行空间是否足够。\n");
      break;
      case QRCODE_DUPLICATED:
        printf("二维码重复!验证失败。\n");
      break;
      case SYSTEM_ERROR:
        printf("系统异常!请联系支付宝技术人员。\n");
      break;
      default:
      break;
    }
    printf("二维码校验结束!验证失败,不放行!\n");
    printf("===========验证二维码例程 结束================\n");
    free(verify_response.uid);
    free(verify_response.record);
    free(verify_response.card_no);
    free(verify_response.card_data);
    free(verify_response.card_type);
    return;
  }
  printf("从二维码中获取到的uid: %s\n", verify_response.uid);
  printf("验证成功后,返还的脱机记录: %s\n", verify_response.record);
  printf("二维码中的卡类型为: %s\n",verify_response.card_type);
  printf("二维码中的卡号为: %s\n", verify_response.card_no);
  bytes_to_hex_string(print_buf, sizeof(print_buf), 
            verify_response.card_data, verify_response.card_data_len);
  printf("二维码中的二进制卡数据(hex string形式):%s\n", print_buf);
  /**
   * 1.商户可以根据uid判断是否为同一用户重复交易
   */
  /**
   * 2.商户可以根据qrcode判断是否为重复二维码
   *   此判断也可以放在校验二维码前执行,商户可以自行选择
   */
  /**
   * 3.商户需要根据卡类型、卡号、卡数据 综合判断该卡的合法性、以及是否受理该卡
   * 请商户保留 可受理 的脱机记录
   */
  free(verify_response.uid);
  free(verify_response.record);
  free(verify_response.card_no);
  free(verify_response.card_data);
  free(verify_response.card_type);
  printf("验证成功,请放行!\n");
  printf("===========验证二维码例程 结束================\n");
}
/**
 * mock一个用户传入的二维码数据qrcode
 * 此处是使用QRCODE_HEX_DATA mock出的用户二维码数据 
 * 开发者测试时请使用二维码工具生成一个新的QRCODE_HEX_DATA后
 * 装入宏定义中QRCODE_HEX_DATA,再执行mock
 */
void mock_qrcode(unsigned char* qrcode, int* qrcode_len){
  char qrcode_hex[] = QRCODE_HEX_DATA;
  int qrcode_hex_len = strlen(qrcode_hex);
  *qrcode_len = strlen(qrcode_hex)/2;
  hex_string_to_bytes(qrcode_hex, qrcode_hex_len, qrcode, *qrcode_len);
}
/**
* 字节数组转hex格式字符串
* @param print_buf: 十六进制字符串buffer
* @param print_buf_len: 十六进制字符串buffer长度
* @param bytes: 二进制数据
* @param bytes_len: 二进制数据长度
*/
char* bytes_to_hex_string(
  char* print_buf, 
  int print_buf_len, 
  const unsigned char* bytes, 
  int len) {
  int i = 0;
  /**
  * 入参校验
  */ 
  if(print_buf == NULL || bytes == NULL || (len * 2 + 1) > print_buf_len) {
    return NULL;
  }
  for(i = 0; i < len; i++) {
    print_buf[i * 2] = g_hex_map_table[(bytes[i] >> 4) & 0x0F];
    print_buf[i * 2 + 1] = g_hex_map_table[(bytes[i]) & 0x0F];
  }
  /**
  * 填充字符串结束符
  */
  print_buf[i * 2] = '\0';
  /**
  * 返回目标地址
  */
  return print_buf;
}
/**
* hex格式字符串转字节数组
* @param hex_string: 十六进制字符串
* @param hex_string_len: 十六进制字符串长度
* @param bytes: 二进制数据存储空间
* @param bytes_len: 目标空间长度
*/
int hex_string_to_bytes(
  char* hex_string, 
  int hex_string_len, 
  unsigned char* bytes, 
  int bytes_len) {
  int i = 0;
  /**
  * 校验十六进制字符串长度必须偶数,并且目标存储空间必须足够存放转换后的二进制数据
  */
  if((hex_string_len % 2 != 0) || (bytes_len * 2 < hex_string_len)) {
    return -1;
  }
  for(i = 0; i < hex_string_len; i += 2) {
    bytes[i/2] = ((hex_of_char(hex_string[i]) << 4) & 0xF0) | 
          (hex_of_char(hex_string[i + 1]) & 0x0F);
  }
  return 1;
}
/**
* hex格式char转二进制
*/
unsigned char hex_of_char(char c) {
  unsigned char tmp = 0;
  if(c >= '0' && c <= '9') {
    tmp = (c - '0');
  }
  else if(c >= 'A' && c <= 'F') {
    tmp = (c - 'A' + 10);
  }
  else if(c >= 'a' && c <= 'f') {
    tmp = (c - 'a' + 10);
  }
  return tmp;
}
int main(int argc, char** argv) {
  check_qrcode_demo();
  return 0;
}


相关实践学习
阿里云百炼xAnalyticDB PostgreSQL构建AIGC应用
通过该实验体验在阿里云百炼中构建企业专属知识库构建及应用全流程。同时体验使用ADB-PG向量检索引擎提供专属安全存储,保障企业数据隐私安全。
AnalyticDB PostgreSQL 企业智能数据中台:一站式管理数据服务资产
企业在数据仓库之上可构建丰富的数据服务用以支持数据应用及业务场景;ADB PG推出全新企业智能数据平台,用以帮助用户一站式的管理企业数据服务资产,包括创建, 管理,探索, 监控等; 助力企业在现有平台之上快速构建起数据服务资产体系
相关文章
|
26天前
|
算法 数据安全/隐私保护 计算机视觉
基于FPGA的图像双线性插值算法verilog实现,包括tb测试文件和MATLAB辅助验证
本项目展示了256×256图像通过双线性插值放大至512×512的效果,无水印展示。使用Matlab 2022a和Vivado 2019.2开发,提供完整代码及详细中文注释、操作视频。核心程序实现图像缩放,并在Matlab中验证效果。双线性插值算法通过FPGA高效实现图像缩放,确保质量。
|
2天前
|
Oracle 关系型数据库 MySQL
使用崖山YMP 迁移 Oracle/MySQL 至YashanDB 23.2 验证测试
这篇文章是作者尚雷关于使用崖山YMP迁移Oracle/MySQL至YashanDB 23.2的验证测试分享。介绍了YMP的产品信息,包括架构、版本支持等,还详细阐述了外置库部署、YMP部署、访问YMP、数据源管理、任务管理(创建任务、迁移配置、离线迁移、校验初始化、一致性校验)及MySQL迁移的全过程。
|
3月前
|
算法 数据挖掘 测试技术
犬类癌症检测(CANDiD)研究:使用独立测试集对1000多只犬进行基于高通量测序的多癌种早期检测"液体活检"血液测试的临床验证
这项研究首次在大规模独立测试集上验证了基于NGS的液体活检在犬类多癌种检测中的应用。该方法具有很高的特异性,可以作为一种新的无创癌症筛查和辅助诊断工具。通过早期发现癌症,有望改善犬类癌症的诊断和管理模式。
77 12
|
4月前
|
人工智能 供应链 安全
AI辅助安全测试案例某电商-供应链平台平台安全漏洞
【11月更文挑战第13天】该案例介绍了一家电商供应链平台如何利用AI技术进行全面的安全测试,包括网络、应用和数据安全层面,发现了多个潜在漏洞,并采取了有效的修复措施,提升了平台的整体安全性。
166 4
|
4月前
|
监控 安全 测试技术
构建高效的精准测试平台:设计与实现指南
在软件开发过程中,精准测试是确保产品质量和性能的关键环节。一个精准的测试平台能够自动化测试流程,提高测试效率,缩短测试周期,并提供准确的测试结果。本文将分享如何设计和实现一个精准测试平台,从需求分析到技术选型,再到具体的实现步骤。
253 1
|
5月前
|
人工智能 监控 测试技术
云应用开发平台测试
云应用开发平台测试
111 2
|
4月前
|
监控 安全 测试技术
构建高效精准测试平台:设计与实现全攻略
在软件开发过程中,精准测试是确保产品质量的关键环节。一个高效、精准的测试平台能够自动化测试流程,提高测试覆盖率,缩短测试周期。本文将分享如何设计和实现一个精准测试平台,从需求分析到技术选型,再到具体的实现步骤。
124 0
|
2月前
|
数据可视化 前端开发 测试技术
接口测试新选择:Postman替代方案全解析
在软件开发中,接口测试工具至关重要。Postman长期占据主导地位,但随着国产工具的崛起,越来越多开发者转向更适合中国市场的替代方案——Apifox。它不仅支持中英文切换、完全免费不限人数,还具备强大的可视化操作、自动生成文档和API调试功能,极大简化了开发流程。
|
1月前
|
JSON 前端开发 测试技术
大前端之前端开发接口测试工具postman的使用方法-简单get接口请求测试的使用方法-简单教学一看就会-以实际例子来说明-优雅草卓伊凡
大前端之前端开发接口测试工具postman的使用方法-简单get接口请求测试的使用方法-简单教学一看就会-以实际例子来说明-优雅草卓伊凡
97 10
大前端之前端开发接口测试工具postman的使用方法-简单get接口请求测试的使用方法-简单教学一看就会-以实际例子来说明-优雅草卓伊凡
|
27天前
|
JSON 前端开发 API
以项目登录接口为例-大前端之开发postman请求接口带token的请求测试-前端开发必学之一-如果要学会联调接口而不是纯写静态前端页面-这个是必学-本文以优雅草蜻蜓Q系统API为实践来演示我们如何带token请求接口-优雅草卓伊凡
以项目登录接口为例-大前端之开发postman请求接口带token的请求测试-前端开发必学之一-如果要学会联调接口而不是纯写静态前端页面-这个是必学-本文以优雅草蜻蜓Q系统API为实践来演示我们如何带token请求接口-优雅草卓伊凡
64 5
以项目登录接口为例-大前端之开发postman请求接口带token的请求测试-前端开发必学之一-如果要学会联调接口而不是纯写静态前端页面-这个是必学-本文以优雅草蜻蜓Q系统API为实践来演示我们如何带token请求接口-优雅草卓伊凡

热门文章

最新文章