增加 readme
diff --git a/.gitignore b/.gitignore
index 141515e..ff0cd5c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,3 +4,4 @@
 *.so
 .gradle
 build
+.idea
diff --git a/Makefile b/Makefile
index d030da8..9ecdece 100644
--- a/Makefile
+++ b/Makefile
@@ -1,5 +1,8 @@
 CARGO ?=cargo
 RM ?=rm
+NDK ?= $(ANDROID_NDK)
+ANDROID_ARM64_STRIP ?= $(shell $(NDK)/ndk-which --abi arm64-v8a strip)
+ANDROID_ARM_STRIP ?= $(shell $(NDK)/ndk-which --abi armeabi-v7a strip)
 
 .PHONY: clean
 
@@ -9,7 +12,9 @@
 
 android:
 	$(CARGO) build --target aarch64-linux-android --release
+	$(ANDROID_ARM64_STRIP) target/aarch64-linux-android/release/libdlsmk.so
 	$(CARGO) build --target armv7-linux-androideabi --release
+	$(ANDROID_ARM_STRIP) target/armv7-linux-androideabi/release/libdlsmk.so
 
 
 
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..fd32e02
--- /dev/null
+++ b/README.md
@@ -0,0 +1,40 @@
+# 编译环境
+
+ * Android NDK r17 以上版本
+ * rust 1.4 以上版本
+ * rust-ios-android 脚本
+
+
+编译环境安装成功后需要配置 rust 交叉编译环境
+
+## 配置 rust-ios-android 
+
+* git clone https://github.com/kennytm/rust-ios-android.git
+
+设置环境变量 ANDROID_HOME 指向 Android SDK 目录, android ndk 应该在 $ANDROID_HOME/ndk-bundles 目录下.
+
+脚本配置环境
+
+```bash
+$ ./create-ndk-standalone.sh
+```
+
+支持成功后会在当前目录下生成 NDK 目录。
+
+## 配置 rust 
+修改 $HOME/.cargo/config 文件,增加以下配置
+
+```ini
+[target.aarch64-linux-android]
+ar = "<path-to-your/rust-ios-android/NDK>/arm64/bin/aarch64-linux-android-ar"
+linker = "<path-to-your/rust-ios-android/NDK>/arm64/bin/aarch64-linux-android-clang"
+
+[target.armv7-linux-androideabi]
+ar = "<path-to-your/rust-ios-android/NDK>/arm/bin/arm-linux-androideabi-ar"
+linker = "<path-to-your/rust-ios-android/NDK>/arm/bin/arm-linux-androideabi-clang"
+```
+## 编译 Android 版本
+
+```bash
+$ make android
+```
diff --git a/dlqrcode/src/lib.rs b/dlqrcode/src/lib.rs
index 8a98de0..deb7c96 100644
--- a/dlqrcode/src/lib.rs
+++ b/dlqrcode/src/lib.rs
@@ -62,6 +62,10 @@
         }
     }
 
+    fn field_count() -> usize {
+        5
+    }
+
     fn from_qrcode(qr_fields: &Vec<Vec<u8>>) -> Result<Self> {
         if qr_fields.len() < 6 {
             return Err(DecodeError::new("qrcode fields length must grater than 6."));
@@ -166,6 +170,9 @@
                     qrdata.push(s);
                 }
                 j = i + 1;
+                if qrdata.len() >= DaliQrData::field_count() {
+                    break;
+                }
             }
         }
         if j < qrcode.len() {
@@ -173,6 +180,7 @@
             s.extend_from_slice(&qrcode[j..]);
             qrdata.push(s);
         }
+
         Ok(())
     }
 
@@ -191,6 +199,8 @@
 
         let totp = self.new_totp();
         let time = self.totp_time(secs_offset);
+        let actual = totp.generate(time);
+        debug!("check totp <{}>,<{}>, <{}>", qr_data.totp, time,  actual);
         if totp.check(&qr_data.totp, time) {
             Ok(qr_data)
         } else {
@@ -235,17 +245,22 @@
             .duration_since(SystemTime::UNIX_EPOCH)
             .unwrap()
             .as_secs();
-        debug!("totp system time : {}, offset {}", time, secs_offset);
-        if secs_offset > 0 {
+        let time2 = if secs_offset > 0 {
             time + (secs_offset as u64)
         } else {
             time - (-secs_offset as u64)
-        }
+        };
+        debug!(
+            "totp system time : {}, offset {}, actual {}",
+            time, secs_offset, time2
+        );
+        time2
     }
 
     fn new_totp(&self) -> TOTP<Vec<u8>> {
         let seed = self.totp_seed.clone();
-        TOTP::new(Algorithm::SHA1, 8, self.totp_skew, self.totp_step, seed)
+        debug!("totp seed : {}, skew {}, step {}", hex::encode(seed), self.totp_skew, self.totp_step);
+        TOTP::new(Algorithm::SHA1, 8, self.totp_skew, self.totp_step, self.totp_seed.clone())
     }
 
     fn encode_qrcode(&self, qr_data: &DaliQrData) -> Result<String> {
@@ -284,6 +299,7 @@
         if qr_data.totp.len() == 0 {
             let totp = self.new_totp();
             let time = self.totp_time(secs_offset);
+            debug!("totp system time : {}", time);
             qr_data.totp = totp.generate(time);
         }
         let sign = self.calc_sign(qr_data);
@@ -295,7 +311,8 @@
 
 pub fn transaction_sign(qrdata: &DaliQrData) -> String {
     let sign_str = qrdata.to_qrdata();
-    let key = Aes256Cbc::new_var(MASTER.as_bytes(), &MASTER_IV).unwrap();
+    let key = base64::decode(MASTER.as_bytes()).unwrap();
+    let key = Aes256Cbc::new_var(&key, &MASTER_IV).unwrap();
 
     let cipher = key.encrypt_vec(sign_str.as_bytes());
     base64::encode_config(cipher, base64::URL_SAFE)
@@ -309,7 +326,8 @@
 ) -> Result<String> {
     let sign = unsafe {
         if let Ok(s) = base64::decode_config(sign, base64::URL_SAFE) {
-            let key = Aes256Cbc::new_var(MASTER.as_bytes(), &MASTER_IV).unwrap();
+            let key = base64::decode(MASTER.as_bytes()).unwrap();
+            let key = Aes256Cbc::new_var(&key, &MASTER_IV).unwrap();
             if let Ok(k) = key.decrypt_vec(&s[..]) {
                 String::from_utf8_unchecked(k)
             } else {
@@ -320,19 +338,40 @@
         }
     };
 
+    debug!("transaction sign : {}", sign);
     let fields: Vec<&str> = sign.split(QR_FIELD_DILIMITER).collect();
     if fields.len() < 4 || fields[1] != cardno {
         return Err(DecodeError::new("sign is invalidated"));
     }
 
     let tac_buffer = format!("{}{}{}", amount, term_date_time, "{dlsmk}");
+    debug!("transaction sign data : <{}>", tac_buffer);
     let mut hasher = Sha256::new();
     hasher.update(sign);
     hasher.update(tac_buffer.as_bytes());
-    let tac = base64::encode_config(hasher.finalize(), base64::URL_SAFE);
+    let tac = base64::encode_config(hasher.finalize(), base64::URL_SAFE_NO_PAD);
     Ok(tac)
 }
 
+fn vec_split_once(data: Vec<u8>, delimiter: u8) -> (Option<Vec<u8>>, Option<Vec<u8>>) {
+    let mut pos = 0;
+    while pos < data.len() && data[pos] != delimiter {
+        pos += 1;
+    }
+    if pos == 0 || pos == data.len() {
+        (None, Some(data))
+    } else {
+        let matched : Vec<u8> = data[..pos].to_vec();
+        let n = data.len() - pos;
+        if n == 0 {
+            (Some(matched), None)
+        } else {
+            let left : Vec<u8> = data[pos+1..].to_vec();
+            (Some(matched), Some(left))
+        }
+    }
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;
@@ -346,6 +385,30 @@
     }
 
     #[test]
+    fn test_vec() {
+        let s = String::from("2c9cab94737ad0f401737ad1bd2e0000:00015596:80:32125968:ud:23983dslsdlfsddd");
+        let mut v : Vec<u8> = s.as_bytes().to_vec();
+        let delimiter = QR_FIELD_DILIMITER.as_bytes()[0];
+        let mut result = Vec::new();
+        loop {
+            let (matched, left) = vec_split_once(v, delimiter);
+            if let Some(v) = matched {
+                result.push(v);
+            } else {
+                result.push(left.unwrap());
+                break;
+            }
+            
+            if left != None {
+                v = left.unwrap();
+            } else {
+                break;
+            }
+        }
+        assert_eq!(result.len(), 6);
+    }
+
+    #[test]
     fn aes_test() {
         let mut key = [0u8; KEYLEN];
         let s = decode("Vbb1syh8U1+CdLmTVGdtDiVvKBQ81n4GmgBEO/ohSbU=").unwrap();
diff --git a/dlsmk/src/lib.rs b/dlsmk/src/lib.rs
index 909632f..c8b6cba 100644
--- a/dlsmk/src/lib.rs
+++ b/dlsmk/src/lib.rs
@@ -13,7 +13,7 @@
         let key = "wDp3/3NPEi+R0peokVv010GkDk1mRTp3tUB/lCEVRAA=";
         let key_buffer = base64::decode_config(key, base64::STANDARD).unwrap();
         let s = hex::encode(key_buffer.as_slice());
-        let key_string = hex::decode(format!("{}{:04x}{:02x}", s, 5, 3)).unwrap();
+        let key_string = hex::decode(format!("{}{:04x}{:02x}", s, 5, 10)).unwrap();
         println!(
             "key : {}",
             base64::encode_config(key_string, base64::URL_SAFE)
@@ -115,7 +115,6 @@
             }
         };
 
-
         match decoder.decode(qrcode.as_ptr(), qrcode.len(), offset as i32) {
             Ok(d) => {
                 put_data(&env, &qrdata, "cardno", &d.cardno);
@@ -183,12 +182,7 @@
             return JNI_FALSE;
         }
 
-        match dlqrcode::transaction_tac(
-            &cardno,
-            amount,
-            &termDatetime,
-            &qrsign
-        ) {
+        match dlqrcode::transaction_tac(&cardno, amount, &termDatetime, &qrsign) {
             Ok(tac) => {
                 put_data(&env, &result, "tac", &tac);
                 JNI_TRUE
diff --git a/java/build.gradle b/java/build.gradle
index 8d5987f..15f4636 100644
--- a/java/build.gradle
+++ b/java/build.gradle
@@ -12,7 +12,8 @@
 }
 
 dependencies {
-   implementation 'commons-codec:commons-codec:1.15' 
+    implementation 'commons-codec:commons-codec:1.15' 
+    implementation 'com.eatthepath:java-otp:0.2.0'
 }
 
 application {
diff --git a/java/src/main/java/com/supwisdom/dlsmk/DLSMK.java b/java/src/main/java/com/supwisdom/dlsmk/DLSMK.java
index 419d72f..916d7b2 100644
--- a/java/src/main/java/com/supwisdom/dlsmk/DLSMK.java
+++ b/java/src/main/java/com/supwisdom/dlsmk/DLSMK.java
@@ -1,8 +1,15 @@
 package com.supwisdom.dlsmk;
 
-import java.util.Map;
+import com.eatthepath.otp.TimeBasedOneTimePasswordGenerator;
+
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.time.Duration;
+import java.time.Instant;
 import java.util.HashMap;
-import org.apache.commons.codec.binary.Base64;
+import java.util.Map;
 
 public class DLSMK {
     static byte[] decodeHex(String hex) {
@@ -26,12 +33,14 @@
     public static void main(String[] args) {
         System.setProperty("java.library.path" , ".");
         System.out.println("Version " + DLSMKQrCode.version());
-        String key = "wDp3_3NPEi-R0peokVv010GkDk1mRTp3tUB_lCEVRAAABQM";
+        String key = "wDp3_3NPEi-R0peokVv010GkDk1mRTp3tUB_lCEVRAAABQo=";
 
-        String qrcode = "Ntd0wHly2IiweNP61JiewTmZ27JM3Vs-vjZaz45Ly8FoE5mB8QfvJBNJOGYsDQsFBrJqutQxVnyGfWcCbocIbWe2MwOdnfjZLLjAMZKQe_cDKntkfU2rJIo93IyS4TdJ";
+        long systimeMillis = 1604469519L * 1000;
+        String qrcode = "Szgp1QkGFy0DJ6mPVg4wMuOE5XuSe7eZrocdPDAs_iq_H7xzb10vQJwRfk6hn6-_fa6SRKRKd00gExbAShJOTgAj2urAPuQm_NxATJs26I4u6khR74iK7oubHjY1Z6qg";
 
         Map<Object, Object> result = new HashMap();
-        if (DLSMKQrCode.decode(key, qrcode, 0L, result)) {
+        long curr = System.currentTimeMillis();
+        if (DLSMKQrCode.decode(key, qrcode, systimeMillis / 1000 - curr / 1000 , result)) {
             System.out.println("Decode OK");
             String cardno = result.get("cardno").toString();
             System.out.format("cardno : %s\n", cardno);
@@ -41,7 +50,10 @@
             System.out.println();
 
             result.clear();
-            DLSMKQrCode.genTac(sign, cardno, 100, "20201102164539", result);
+            int amount = 100;
+            String dt = "20201102164539";
+            DLSMKQrCode.genTac(sign, cardno, amount, dt, result);
+            System.out.format("transaction amount <%d> datetime <%s>\n", amount, dt);
             System.out.format("tac : %s", result.get("tac"));
             System.out.println();
         } else {