调试支付宝脱机认证接口遇到的问题总结
时间:2022-07-22
本文章向大家介绍调试支付宝脱机认证接口遇到的问题总结,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
通过 android的JNI调用支付宝脱机认证库本地接口时,我欲返回一个类的实例,但是却报了几个错,最后查出来了原因。在此总结下。
错误一 :E/dalvikvm﹕JNI ERROR (app bug): accessed stale local reference
,jclass is an invalid local reference AllocObject
原因是 android的垃圾回收机制问题,新版本已经不允许全局使用findclass出来的局部引用了,
会被GC回收掉。所以必须new成全局引用才行。
需要注意的地方是:
static jclass myClass;
,一,需要把jclass声明为静态的 。二,注意这个地方,用NewGlobalRef把引用设为全局的。写在
JNIEXPORT jint JNI_OnLoad(JavaVM* vm,void *reserved)
中。
jclass tmp;
tmp = (*env)->FindClass(env,"com/example/ndktest/VerifyResponse");
if( tmp == NULL )
{
ALOGE("%d..Can't find class %s!n",__LINE__, "com/example/ndktest/VerifyResponse");
return -1;
}
myClass = (jclass)(*env)->NewGlobalRef(env,tmp);
错误二: E/dalvikvm﹕ JNI ERROR (app bug): accessed stale global reference 0x474e386e
需要注意的地方是:
(*env)->SetObjectField(env,obj,cardNo,"12345678");
这样的写法是错误的,正确应该是:
(*env)->SetObjectField(env,obj,cardNo,(jstring)(*env)->NewSringUTF(env,"1234567890"));
给欲返回的字符串赋值,需要用(*env)->NewSringUTF(env,"1234567890"
//========================================================================
以下为我欲通过JNI返回给java层调用的支付宝脱机校验的结果,类
VerifyResponse
的定义:
package com.example.ndktest;
/**
* Created by yang on 2017/7/8.
*/
public class VerifyResponse {
byte[] cardData;
int cardDataLength;
String cardNo="";
String cardType;
int errorCode;
String record;
String uid;
public byte[] getCardData()
{
return this.cardData;
}
public int getCardDataLength()
{
return this.cardDataLength;
}
public String getCardNo()
{
return this.cardNo;
}
public String getCardType()
{
return this.cardType;
}
public int getErrorCode()
{
return this.errorCode;
}
public String getRecord()
{
return this.record;
}
public String getUid()
{
return this.uid;
}
public void setCardData(byte[] paramArrayOfByte)
{
this.cardData = paramArrayOfByte;
}
public void setCardDataLength(int paramInt)
{
this.cardDataLength = paramInt;
}
public void setCardNo(String paramString)
{
this.cardNo = paramString;
}
public void setCardType(String paramString)
{
this.cardType = paramString;
}
public void setErrorCode(int paramInt)
{
this.errorCode = paramInt;
}
public void setRecord(String paramString)
{
this.record = paramString;
}
public void setUid(String paramString)
{
this.uid = paramString;
}
public String toString()
{
return "uid:" + getUid() + "," + "record:" + getRecord() + "," + "cardType:" + getCardType() + "," + "cardNo:" + getCardNo() + "," + "cardData:" + getCardData() + "," + "cardDataLength:" + getCardDataLength() + "," + "errorCode:" + new Integer(getErrorCode()).toString();
}
}
在android的应用层,声明如下:
public static native VerifyResponse verifyQrcode(byte[] qrCodeOfByte, String paramPos, int amount_cent);
在用c写的JNI 层,写法如下:
jobject JNICALL jni_verify_pos_qrcode(JNIEnv *env,jobject thiz,jbyteArray qrCodeOfByte, jstring paramPos, jint amount_cent)
{
int ret = 0;
jboolean b;
jsize qrcode_len;
const unsigned char* qrcode= NULL;
const char* pos_param = NULL;
LOGD("%s>>> ..%s..%d..enter",LOG_TAG,__FUNCTION__,__LINE__);
// jclass clazz = (*env)->FindClass(env,"com/example/ndktest/VerifyResponse");
// if(clazz == 0){
// return (void*)-100;
// }
jobject obj = (*env)->AllocObject(env,myClass);
jfieldID errorCode = (*env)->GetFieldID(env,myClass,"errorCode","I");
if(errorCode == 0){
return (void*)-101;
}
jfieldID cardNo = (*env)->GetFieldID(env,myClass,"cardNo","Ljava/lang/String;");
if(cardNo == 0){
ret = -104;
goto END;
}
jfieldID uid = (*env)->GetFieldID(env,myClass,"uid","Ljava/lang/String;");
if( uid == 0){
ret = -105;
goto END;
}
pos_param= (char*)((*env)->GetStringUTFChars(env,paramPos, &b));
qrcode = (unsigned char*)(*env)->GetByteArrayElements(env,qrCodeOfByte, NULL);
qrcode_len = (*env)->GetArrayLength(env,qrCodeOfByte);
if(qrcode_len > 512){
ret = -103;
goto END;
}
LOGI("===========校验二维码开始================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;
/**
* 调用接口验证二维码的有效性
*/
ret = verify_qrcode_v2(&verify_request, &verify_response);
/**
* 处理返回的结果
*/
if(ret != SUCCESS){
switch(ret){
case MALFORMED_QRCODE:
LOGI("二维码格式错误!请提示用户二维码错误。n");
break;
case QRCODE_INFO_EXPIRED:
LOGI("二维码过期!请提示用户刷新二维码。n");
break;
case QRCODE_KEY_EXPIRED:
LOGI("二维码密钥过期!请提示用户联网后刷新二维码再使用。n");
break;
case POS_PARAM_ERROR:
LOGI("商户传入的pos_param错误,请检查传入的pos_param。n");
break;
case QUOTA_EXCEEDED:
LOGI("单笔额度超限!请提示用户由于额度限制无法过闸机。n");
break;
case NO_ENOUGH_MEMORY:
LOGI("内存不足,极端错误,请检查程序运行空间是否足够。n");
break;
case QRCODE_DUPLICATED:
LOGI("二维码重复!验证失败。n");
break;
case SYSTEM_ERROR:
LOGI("系统异常!请联系支付宝技术人员。n");
break;
default:
break;
}
LOGI("二维码校验结束!验证失败,不放行!n");
LOGI("===========验证二维码 结束================n");
goto END;
}
LOGI("从二维码中获取到的uid: %sn", verify_response.uid);
LOGI("验证成功后,返还的脱机记录: %sn", verify_response.record);
LOGI("二维码中的卡类型为: %sn",verify_response.card_type);
LOGI("二维码中的卡号为: %sn", verify_response.card_no);
bytes_to_hex_string(print_buf, sizeof(print_buf),
verify_response.card_data, verify_response.card_data_len);
LOGI("二维码中的二进制卡数据(hex string形式):%sn", print_buf);
/**
* 1.商户可以根据uid判断是否为同一用户重复交易
*/
/**
* 2.商户可以根据qrcode判断是否为重复二维码
* 此判断也可以放在校验二维码前执行,商户可以自行选择
*/
/**
* 3.商户需要根据卡类型、卡号、卡数据 综合判断该卡的合法性、以及是否受理该卡
* 请商户保留 可受理 的脱机记录
*/
LOGI("验证成功,请放行!n");
LOGI("response ok!n");
(*env)->SetObjectField(env,obj,cardNo,"12345678");
//(*env)->SetObjectField(env,obj,cardNo,(jstring)(*env)->NewSringUTF(env,"1234567890"));
LOGI("response ok1!n");
END:
free(verify_response.uid);
free(verify_response.record);
free(verify_response.card_no);
free(verify_response.card_data);
free(verify_response.card_type);
(*env)->SetIntField(env,obj,errorCode,ret);
LOGI("response ok2!n");
//(*env)->SetObjectField(env,obj,cardNo,"12345678");
(*env)->SetObjectField(env,obj,cardNo,(jstring)(*env)->NewStringUTF(env,"12345678"));
LOGI("response ok3!n");
(*env)->ReleaseStringUTFChars(env, paramPos, pos_param);
(*env)->ReleaseByteArrayElements(env,qrCodeOfByte,(jbyte*)qrcode,0);
return obj;
}
//定义批量注册的数组,是注册的关键部分
static const JNINativeMethod gMethods[] = {
{"qrcode_test", /* func2是在java中声明的native函数名 */
"()I", /* "()V"是函数的签名,可以通过javah获取。*/
(void*)jni_check_qrcode_test
},
{
"initPosVerify",
"(Ljava/lang/String;Ljava/lang/String;)I",
(void*)jni_init_pos_verify
},
{
"verifyQrcode",
"([BLjava/lang/String;I)Lcom/example/ndktest/VerifyResponse;",
(void*)jni_verify_pos_qrcode
}
};
// extern "C" {
JNIEXPORT jint JNI_OnLoad(JavaVM* vm,void *reserved)
{
JNIEnv *env =NULL;
jint result = -1;
static const char* kClassName="com/example/ndktest/Test";
jclass clazz,tmp;
LOGD("%s>>> ..%s..%d..enter",LOG_TAG,__FUNCTION__,__LINE__);
if( (*vm)->GetEnv(vm,(void**)&env,JNI_VERSION_1_4) != JNI_OK )
{
return result;
}
clazz = (*env)->FindClass(env,kClassName);
if( clazz == NULL )
{
ALOGE("%d..Can't find class %s!n",__LINE__, kClassName);
return -1;
}
tmp = (*env)->FindClass(env,"com/example/ndktest/VerifyResponse");
if( tmp == NULL )
{
ALOGE("%d..Can't find class %s!n",__LINE__, "com/example/ndktest/VerifyResponse");
return -1;
}
myClass = (jclass)(*env)->NewGlobalRef(env,tmp);
if( (*env)->RegisterNatives( env,clazz, gMethods, sizeof(gMethods)/sizeof(gMethods[0]) ) != JNI_OK )
{
ALOGE("Failed registering methods for %s!n", kClassName);
return -1;
}
LOGD(">>> ..%s..%d.exit",__FUNCTION__,__LINE__);
return JNI_VERSION_1_4;
}
// }
- UC Berkeley提出新型分布式执行框架Ray:有望取代Spark
- 卡奇话爬虫使用方法以及下载地址
- flash读取XML 背景自动适应大小
- 记录一个发邮件的cs文件
- ASP.NET MVC5+EF6+EasyUI 后台管理系统(56)-插件---单文件上传与easyui使用fancybox
- xml-rpc(2)-first demo_v2
- xml-rpc(1)-first demo
- ASP.NET MVC5+EF6+EasyUI 后台管理系统(55)-工作流设计-表单布局
- 网站源文件被注入了iframe代码—ARP欺骗的木马病毒攻击
- ASP.NET MVC5+EF6+EasyUI 后台管理系统--工作流演示截图
- 基于CPPN与GAN+VAE生成高分辨率图像
- ASP.NET MVC5+EF6+EasyUI 后台管理系统(54)-工作流设计-所有流程监控
- (收藏)搭建.NET Framework 3.0开发环境 及SharePoint 2007/WSS 3环境
- WCF技术剖析之八:ClientBase<T>中对ChannelFactory<T>的缓存机制
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- K8S 生态周报| NGINX Ingress Controller又添新特性
- 网络安全实验室平台(脚本关)
- shell 多线程及线程数控制实现
- CTF考点总结-sql注入篇
- dotnet OpenXML 为什么资源使用 Relationship 引用
- k8s部署Kafka集群
- 彻底搞懂 etcd 系列文章(六):etcd 核心 API v3
- Open3d学习计划—7(RGBD测程法)
- 一次信息泄露引发的越权
- MySQL 调优 | OPTIMIZER_TRACE 详解
- 这次被坑惨了,MySQL的隐式转换导致了一个线上BUG
- iOS开发之UIMenu
- mysql必会技能-基本操作
- 移动端跨平台技术之下的变与不变
- java-JDBC操作Mysql