最近在调试支付宝给提供的二维码脱机认证库,他们给提供了几个文档和 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 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 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; }