优化
增加账户相关交易接口
增加专款账户设计
优化错误返回结果
diff --git a/payapi-sdk/build.gradle b/payapi-sdk/build.gradle
index 53c76f1..ca5c2f1 100644
--- a/payapi-sdk/build.gradle
+++ b/payapi-sdk/build.gradle
@@ -38,6 +38,7 @@
     implementation project(":payapi-common")
     implementation "com.supwisdom:multi-tenant-core:${multiTenantLibVersion}"
     compile "com.supwisdom:multi-tenant-jwt-client:${multiTenantLibVersion}"
+    implementation "com.fasterxml.jackson.core:jackson-databind:${fasterXMLVersion}"
 
     implementation "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
     implementation "org.springframework.cloud:spring-cloud-starter-openfeign"
diff --git a/payapi-sdk/src/main/java/com/supwisdom/dlpay/paysdk/utils/ApiUtils.java b/payapi-sdk/src/main/java/com/supwisdom/dlpay/paysdk/utils/ApiUtils.java
new file mode 100644
index 0000000..a1e59f8
--- /dev/null
+++ b/payapi-sdk/src/main/java/com/supwisdom/dlpay/paysdk/utils/ApiUtils.java
@@ -0,0 +1,23 @@
+package com.supwisdom.dlpay.paysdk.utils;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.supwisdom.dlpay.payapi.model.ErrorResponse;
+import feign.FeignException;
+
+import java.io.IOException;
+
+public class ApiUtils {
+
+  public static ErrorResponse getErrorResponse(FeignException ex) {
+    if (ex.status() > 0) {
+      ObjectMapper objectMapper = new ObjectMapper();
+      try {
+        return objectMapper.readValue(ex.content(), ErrorResponse.class);
+      } catch (IOException e) {
+      }
+    }
+    ErrorResponse errorResponse = new ErrorResponse();
+    errorResponse.setMessage(ex.contentUTF8());
+    return errorResponse;
+  }
+}
diff --git a/payapi-sdk/src/test/java/com/supwisdom/dlpay/paysdktest/CitizenCardPayProxyTest.java b/payapi-sdk/src/test/java/com/supwisdom/dlpay/paysdktest/CitizenCardPayProxyTest.java
index 542a31d..7f1cf75 100644
--- a/payapi-sdk/src/test/java/com/supwisdom/dlpay/paysdktest/CitizenCardPayProxyTest.java
+++ b/payapi-sdk/src/test/java/com/supwisdom/dlpay/paysdktest/CitizenCardPayProxyTest.java
@@ -3,23 +3,28 @@
 
 import com.supwisdom.dlpay.api.bean.*;
 import com.supwisdom.dlpay.api.util.DateUtil;
+import com.supwisdom.dlpay.payapi.model.ErrorResponse;
+import com.supwisdom.dlpay.payapi.model.QrcodeFormat;
+import com.supwisdom.dlpay.payapi.model.QrcodePayInitRequest;
+import com.supwisdom.dlpay.payapi.model.QrcodePayInitResponse;
 import com.supwisdom.dlpay.paysdk.ApiLoginHelper;
+import com.supwisdom.dlpay.paysdk.PayAPISDKConfigure;
 import com.supwisdom.dlpay.paysdk.proxy.*;
+import com.supwisdom.dlpay.paysdk.utils.ApiUtils;
 import com.supwisdom.mutlitenant.client.annotations.EnableTenantJwtClient;
 import com.supwisdom.mutlitenant.client.config.JwtTenantAuthentication;
+import feign.FeignException;
 import org.junit.jupiter.api.Test;
-import org.junit.runner.RunWith;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
 import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration;
 import org.springframework.boot.test.context.SpringBootTest;
 import org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration;
-import org.springframework.cloud.openfeign.EnableFeignClients;
 import org.springframework.cloud.openfeign.FeignAutoConfiguration;
 import org.springframework.cloud.openfeign.ribbon.FeignRibbonClientAutoConfiguration;
-import org.springframework.context.annotation.ComponentScan;
-import org.springframework.test.context.junit4.SpringRunner;
 
+import java.text.SimpleDateFormat;
+import java.util.Date;
 import java.util.List;
 
 import static org.hamcrest.MatcherAssert.assertThat;
@@ -27,22 +32,21 @@
 import static org.hamcrest.Matchers.notNullValue;
 
 
-@RunWith(SpringRunner.class)
 @SpringBootTest(properties = {
     "payapi.url=http://localhost:8080/payapi"},
     classes = CitizenCardPayProxyTest.class)
 @ImportAutoConfiguration({RibbonAutoConfiguration.class,
     FeignRibbonClientAutoConfiguration.class, FeignAutoConfiguration.class,
-    HttpMessageConvertersAutoConfiguration.class, APITestConfig.class})
-@EnableFeignClients(basePackages = "com.supwisdom.dlpay.paysdk")
+    HttpMessageConvertersAutoConfiguration.class, PayAPISDKConfigure.class, APITestConfig.class})
 @EnableTenantJwtClient
-@ComponentScan(basePackages = {"com.supwisdom.dlpay.paysdk"})
 public class CitizenCardPayProxyTest {
   private final static String appid = "700001";
   private final static String secret = "d6dd7f0d4551419d8d11736d0f28df0d";
 
   private final static String operid = "1001";
 
+  private final static String tenantid = "10010";
+
   public final static JwtTenantAuthentication authentication = new JwtTenantAuthentication(appid, secret,
       "{tenantid}");
 
@@ -64,23 +68,27 @@
 
   @Test
   public void citizencardPayinit() {
-    ApiVersionResponse version = apiCommonProxy.apiVersion();
+    try {
+      ApiVersionResponse version = apiCommonProxy.apiVersion();
 
-    assertThat("get version error " + version.getException(),
-        version.getVersion(), notNullValue());
+      assertThat("get version error " + version.getException(),
+          version.getVersion(), notNullValue());
 
-    System.out.println(version.getVersion());
+      System.out.println(version.getVersion());
 
-    CitizenCardPayinitParam initParam = new CitizenCardPayinitParam();
-    initParam.setBillno("20190708172756000001");
-    initParam.setCardNo("20190619001");
-    initParam.setAmount(0);
-    initParam.setTransdate("20190708");
-    initParam.setTranstime("172713");
-    initParam.setShopaccno("2000000038");
-    CitizenPayResponse payInit = citizenCardPayProxy.citizencardPayinit(initParam);
-    assertThat("pay initialized " + payInit.getRetmsg() + payInit.getException(),
-        payInit.getRetcode(), equalTo(0));
+      CitizenCardPayinitParam initParam = new CitizenCardPayinitParam();
+      initParam.setBillno("20190708172756000001");
+      initParam.setCardNo("20190619001");
+      initParam.setAmount(0);
+      initParam.setTransdate("20190708");
+      initParam.setTranstime("172713");
+      initParam.setShopaccno("2000000038");
+      CitizenPayResponse payInit = citizenCardPayProxy.citizencardPayinit(initParam);
+      assertThat("pay initialized " + payInit.getRetmsg() + payInit.getException(),
+          payInit.getRetcode(), equalTo(0));
+    } catch (FeignException ex) {
+      System.out.println("error : " + ex.status());
+    }
   }
 
   @Test
@@ -122,27 +130,29 @@
         response.getRetcode(), equalTo(0));
   }
 
+  private String getBillno() {
+    SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
+    return sdf.format(new Date()); // + "00001";
+  }
+
   @Test
   public void qrcodeInit() {
 
-//    ApiVersionResponse version = apiCommonProxy.apiVersion();
-//
-//    assertThat("get version error " + version.getException(),
-//        version.getVersion(), notNullValue());
-//
-//    QrcodePayParam param = new QrcodePayParam();
-//    param.setAmount(1);
-//    param.setAnonymous(true);
-//    param.setBillno(DateUtil.getNow());
-//    param.setQrcodeFormat("origin");
-//    param.setTransdate(DateUtil.getNow("yyyyMMdd"));
-//    param.setTranstime(DateUtil.getNow("HHmmss"));
-//    param.setTenantid("default");
-//    param.setShopaccno("2000000012");
-//    param.setQrcode("286972142340737770");
-//    QrcodePayResponse response = consumePropxy.qrcodePayInit("111", param);
-//    assertThat("qrcodeInit " + response.getRetmsg() + response.getException(),
-//        response.getRetcode(), equalTo(0));
+    QrcodePayInitRequest request = new QrcodePayInitRequest();
+    request.setBillno(getBillno());
+    request.setQrcode("186972142340737770");
+    request.setShopaccno("2000000012");
+    request.setQrcodeFormat(QrcodeFormat.PLAIN);
+    request.setTransDate(DateUtil.getNow("yyyyMMdd"));
+    request.setTransTime(DateUtil.getNow("HHmmss"));
+
+    try {
+      QrcodePayInitResponse response = consumePropxy.qrcodePayInit(tenantid, request);
+      assertThat("userid must not be empty", response.getUserid(), notNullValue());
+    } catch (FeignException ex) {
+      ErrorResponse err = ApiUtils.getErrorResponse(ex);
+      assertThat("error message must not be empty", err.getMessage(), notNullValue());
+    }
   }
 
   @Test