调试支付宝脱机认证接口遇到的问题总结

简介: 调试支付宝脱机认证接口遇到的问题总结

通过 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: %s\n", verify_response.uid);
   LOGI("验证成功后,返还的脱机记录: %s\n", verify_response.record);
   LOGI("二维码中的卡类型为: %s\n",verify_response.card_type);
   LOGI("二维码中的卡号为: %s\n", 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形式):%s\n", 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;
}
// }


相关文章
|
Java
钉钉第三方扫码登录提示 code: 403, 没有调用该接口的权限,接口权限申请参考
钉钉第三方扫码登录提示 code: 403, 没有调用该接口的权限,接口权限申请参考 ,但是我明明申请了Contact.User.Read 这个权限
388 1
|
7月前
|
API 开发者
免费邮箱API发送邮件测试调试的方法和步骤
本文介绍了使用免费邮箱API如aoksend、Mailgun、SMTP2GO发送邮件的测试调试步骤:选择合适的API,获取访问密钥,配置邮件参数,编写测试代码,调试和测试,查看发送日志,以及优化改进邮件发送功能,确保其稳定运行。
|
7月前
|
小程序 API
微信小程序登录授权流程及所用API
微信小程序登录授权流程及所用API
314 0
|
存储 小程序 关系型数据库
后台交互-个人中心->小程序登录微信登录接口演示,小程序授权登录理论,小程序授权登录代码演示,微信表情包存储问题
后台交互-个人中心->小程序登录微信登录接口演示,小程序授权登录理论,小程序授权登录代码演示,微信表情包存储问题
123 0
|
存储 网络安全 数据安全/隐私保护
iOS 逆向编程(七)客户端(手机)免密认证登录
iOS 逆向编程(七)客户端(手机)免密认证登录
136 0
|
API PHP
企业微信授权登录服务端API实战开发(2):php程序开发获取访问用户身份
企业微信授权登录服务端API实战开发(2):php程序开发获取访问用户身份
198 0
|
测试技术 API 开发工具
工银e生活开发脱坑日志(6)开户申请API接入申请表
工银e生活开发脱坑日志(6)开户申请API接入申请表
163 0
|
Linux Android开发
支付宝二维码脱机认证库测试(linux_x86平台验证)
支付宝二维码脱机认证库测试(linux_x86平台验证)
拒绝接口裸奔!开放API接口签名验证
接口安全问题 请求身份是否合法? 请求参数是否被篡改? 请求是否唯一?
|
小程序 数据可视化 前端开发
微信开放平台之第三方平台开发,模板小程序如何提交?
微信第三方开发的精妙就在于可以用一套代码,孵化出多个相同功能的小程序,减少开发成本,包括时间和金钱​成本。而这最重要的一步就是将我们开发好的小程序上传到微信服务器,当做模板来使用。
547 0
微信开放平台之第三方平台开发,模板小程序如何提交?