最近在调试支付宝给提供的二维码脱机认证库,他们给提供了几个文档和 libposoffline.so库文件。
要想在android或linux上做支持支付宝扫码付的应用,必须会调用他们给的二维码脱机认证库。以下是一个在linux调用成功的例子:
如图:
附上代码,其实很简单,就是 linux下调用so动态库的一个例子:
但也遇到一些问题,如库文件找不到,64位和32位不兼容等。
比较常见的undefined reference问题: 1.dl库 undefined reference to ‘dlerror’ undefined reference to ‘dlopen’ undefined reference to ‘dlerror’ 增加-ldl链接选项 (-l是链接选项的前缀)
找不到库的问题:
修改LD_LIBRARY_PATH export LD_LIBRARY_PATH=/where/you/install/lib:$LD_LIBRARY_PATH
出现wrong ELF class: ELFCLASS32 in Unknown on line这种是32位程序与64位库的冲突问题 那个libcpputil.so应该是在32位系统里编译的,所以拿到64位机器上报了个错。 64位系统可能可以编译,链接运行32位的库和程序,比如添加-m32之类的选项
包含 #include <dlfcn.h> 和调用
handle =dlopen("libposoffline.so",RTLD_NOW)加载动态库
init_pos_verify1 = dlsym(handle,"init_pos_verify");
[root@localhost x86]# gcc -m32 demo.c -ldl -lm [root@localhost x86]# ./a.out
[root@localhost x86]# export LD_LIBRARY_PATH=/home/myshare/x86:$LD_LIBRARY_PATH [root@localhost x86]# ldconfig [root@localhost x86]# ./a.out 鍔犺浇妯″潡閿欒 libposoffline.so: wrong ELF class: ELFCLASS32
查看下你的linux的系统情况,库的情况
[root@localhost x86]# uname -a Linux localhost.localdomain 2.6.32-220.el6.x86_64 #1 SMP Tue Dec 6 19:48:22 GMT 2011 x86_64 x86_64 x86_64 GNU/Linux [root@localhost x86]# cat /proc/version Linux version 2.6.32-220.el6.x86_64 (mockbuild@c6b18n3.bsys.dev.centos.org) (gcc version 4.4.6 20110731 (Red Hat 4.4.6-3) (GCC) ) #1 SMP Tue Dec 6 19:48:22 GMT 2011 [root@localhost x86]# file libposoffline.so libposoffline.so: ELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV), dynamically linked, not stripped [root@localhost x86]# ldd libposoffline.so ldd: 警告: 你没有执行权限 `./libposoffline.so' linux-gate.so.1 => (0x00e9c000) libc.so.6 => /lib/libc.so.6 (0x00110000) /lib/ld-linux.so.2 (0x0055e000)
[root@localhost x86]# gcc -m32 -ldl -lm -Llib demo.c [root@localhost x86]# ./a.out ===========准备数据================ ============进行POS初始化============= ===========准备数据结束================ ===========校验二维码开始================ 二维码过期!请提示用户刷新二维码。 二维码校验结束!验证失败,不放行! ===========验证二维码例程 结束================
写成makefile文件如下:
注意程序运行前需要make install 把库拷贝到 usr/lib目录下,否则运行时会找不到动态库
######################################## #makefile ######################################## BINARY= test CC= gcc LD= ld CFLAGS= -Wall -g -m32 #LDSCRIPT= -lposoffline -ldl -lm LDSCRIPT= -ldl -lm LDFLAGS= -m32 -Wl,-V -Llib OBJS= demo.o #CFLAGS=-std=c99 .PHONY: clean all:images images: (BINARY).out(OBJS):%.o:%.c (CC) -c (CFLAGS) < -o (OBJS) (CC) -o (*).out (OBJS) (LDFLAGS)
install:
cp libposoffline.so /usr/lib clean: rm -f *.o
注意链接和编译都不能少了-m32参数项 。-lposoffline这个不需要带上。因为不是静态链接,是动态加载的。如果posoffline这个库是静态库,则需要链接。
代码语言:javascript复制#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( "加载模块错误 %sn", 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: %sn", verify_response.uid);
printf("验证成功后,返还的脱机记录: %sn", verify_response.record);
printf("二维码中的卡类型为: %sn",verify_response.card_type);
printf("二维码中的卡号为: %sn", 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形式):%sn", 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] = '