feat: 增加了 openapi spec
diff --git a/build.gradle b/build.gradle
index 0e2feff..7187f27 100644
--- a/build.gradle
+++ b/build.gradle
@@ -9,6 +9,7 @@
     id 'org.jetbrains.kotlin.plugin.jpa' version '1.3.31' apply false
     id "com.palantir.git-version" version "0.12.2"
     id 'com.palantir.docker' version '0.22.2' apply false
+    id "org.openapi.generator" version "4.2.2" apply false
 }
 
 bootJar {
@@ -101,6 +102,9 @@
             springSocialVersion = '1.1.6.RELEASE'
             springKafkaVersion = '2.2.8.RELEASE'
             postgresVersion = '42.2.5'
+            openapitoolsVersion = '0.1.0'
+            swaggerVersion = '1.6.0'
+            springfoxVersion = '2.9.2'
             multiTenantLibVersion = '1.3.2'
         }
         implementation "org.jetbrains.kotlin:kotlin-reflect"
diff --git a/payapi-common/build.gradle b/payapi-common/build.gradle
index ee97798..ba8d004 100644
--- a/payapi-common/build.gradle
+++ b/payapi-common/build.gradle
@@ -32,6 +32,11 @@
     implementation "commons-beanutils:commons-beanutils:${beanutilsVersion}"
     implementation "commons-codec:commons-codec:${codecVersion}"
     implementation "org.apache.commons:commons-lang3:${lang3Version}"
+    implementation "org.springframework:spring-web"
+    implementation group: 'io.swagger', name: 'swagger-annotations', version: swaggerVersion
+
+    implementation "org.openapitools:jackson-databind-nullable:${openapitoolsVersion}"
+
 
     compileOnly "org.projectlombok:lombok:${lombokVersion}"
     annotationProcessor "org.projectlombok:lombok:${lombokVersion}"
diff --git a/payapi-common/src/main/java/com/supwisdom/dlpay/payapi/model/QrcodeFormat.java b/payapi-common/src/main/java/com/supwisdom/dlpay/payapi/model/QrcodeFormat.java
new file mode 100644
index 0000000..1f0adda
--- /dev/null
+++ b/payapi-common/src/main/java/com/supwisdom/dlpay/payapi/model/QrcodeFormat.java
@@ -0,0 +1,44 @@
+package com.supwisdom.dlpay.payapi.model;
+
+import java.util.Objects;
+import com.fasterxml.jackson.annotation.JsonValue;
+import org.openapitools.jackson.nullable.JsonNullable;
+import javax.validation.Valid;
+import javax.validation.constraints.*;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+
+/**
+ * Gets or Sets QrcodeFormat
+ */
+public enum QrcodeFormat {
+  
+  PLAIN("plain"),
+  
+  BASE64("base64"),
+  
+  HEX("hex");
+
+  private String value;
+
+  QrcodeFormat(String value) {
+    this.value = value;
+  }
+
+  @Override
+  @JsonValue
+  public String toString() {
+    return String.valueOf(value);
+  }
+
+  @JsonCreator
+  public static QrcodeFormat fromValue(String value) {
+    for (QrcodeFormat b : QrcodeFormat.values()) {
+      if (b.value.equals(value)) {
+        return b;
+      }
+    }
+    throw new IllegalArgumentException("Unexpected value '" + value + "'");
+  }
+}
+
diff --git a/payapi-common/src/main/java/com/supwisdom/dlpay/payapi/model/QrcodePayConfirmRequest.java b/payapi-common/src/main/java/com/supwisdom/dlpay/payapi/model/QrcodePayConfirmRequest.java
new file mode 100644
index 0000000..51685c4
--- /dev/null
+++ b/payapi-common/src/main/java/com/supwisdom/dlpay/payapi/model/QrcodePayConfirmRequest.java
@@ -0,0 +1,307 @@
+package com.supwisdom.dlpay.payapi.model;
+
+import java.util.Objects;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.supwisdom.dlpay.payapi.model.QrcodeFormat;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import org.openapitools.jackson.nullable.JsonNullable;
+import javax.validation.Valid;
+import javax.validation.constraints.*;
+
+/**
+ * QrcodePayConfirmRequest
+ */
+@javax.annotation.Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2020-03-12T16:09:45.966+08:00[Asia/Shanghai]")
+
+public class QrcodePayConfirmRequest   {
+  @JsonProperty("billno")
+  private String billno;
+
+  @JsonProperty("shopaccno")
+  private String shopaccno;
+
+  @JsonProperty("transDate")
+  private String transDate;
+
+  @JsonProperty("transTime")
+  private String transTime;
+
+  @JsonProperty("dlttype")
+  private String dlttype;
+
+  @JsonProperty("amount")
+  private Integer amount;
+
+  @JsonProperty("userid")
+  private String userid;
+
+  @JsonProperty("anonymous")
+  private Boolean anonymous;
+
+  @JsonProperty("qrcode")
+  private String qrcode;
+
+  @JsonProperty("qrcodeFormat")
+  private QrcodeFormat qrcodeFormat;
+
+  public QrcodePayConfirmRequest billno(String billno) {
+    this.billno = billno;
+    return this;
+  }
+
+  /**
+   * Get billno
+   * @return billno
+  */
+  @ApiModelProperty(value = "")
+
+@Pattern(regexp="[0-9]*") @Size(min=16) 
+  public String getBillno() {
+    return billno;
+  }
+
+  public void setBillno(String billno) {
+    this.billno = billno;
+  }
+
+  public QrcodePayConfirmRequest shopaccno(String shopaccno) {
+    this.shopaccno = shopaccno;
+    return this;
+  }
+
+  /**
+   * Get shopaccno
+   * @return shopaccno
+  */
+  @ApiModelProperty(value = "")
+
+@Pattern(regexp="[0-9]*") 
+  public String getShopaccno() {
+    return shopaccno;
+  }
+
+  public void setShopaccno(String shopaccno) {
+    this.shopaccno = shopaccno;
+  }
+
+  public QrcodePayConfirmRequest transDate(String transDate) {
+    this.transDate = transDate;
+    return this;
+  }
+
+  /**
+   * Get transDate
+   * @return transDate
+  */
+  @ApiModelProperty(value = "")
+
+@Pattern(regexp="[0-9]*") @Size(min=8,max=8) 
+  public String getTransDate() {
+    return transDate;
+  }
+
+  public void setTransDate(String transDate) {
+    this.transDate = transDate;
+  }
+
+  public QrcodePayConfirmRequest transTime(String transTime) {
+    this.transTime = transTime;
+    return this;
+  }
+
+  /**
+   * Get transTime
+   * @return transTime
+  */
+  @ApiModelProperty(value = "")
+
+@Pattern(regexp="[0-9]*") @Size(min=6,max=6) 
+  public String getTransTime() {
+    return transTime;
+  }
+
+  public void setTransTime(String transTime) {
+    this.transTime = transTime;
+  }
+
+  public QrcodePayConfirmRequest dlttype(String dlttype) {
+    this.dlttype = dlttype;
+    return this;
+  }
+
+  /**
+   * Get dlttype
+   * @return dlttype
+  */
+  @ApiModelProperty(value = "")
+
+
+  public String getDlttype() {
+    return dlttype;
+  }
+
+  public void setDlttype(String dlttype) {
+    this.dlttype = dlttype;
+  }
+
+  public QrcodePayConfirmRequest amount(Integer amount) {
+    this.amount = amount;
+    return this;
+  }
+
+  /**
+   * Get amount
+   * @return amount
+  */
+  @ApiModelProperty(value = "")
+
+
+  public Integer getAmount() {
+    return amount;
+  }
+
+  public void setAmount(Integer amount) {
+    this.amount = amount;
+  }
+
+  public QrcodePayConfirmRequest userid(String userid) {
+    this.userid = userid;
+    return this;
+  }
+
+  /**
+   * Get userid
+   * @return userid
+  */
+  @ApiModelProperty(value = "")
+
+
+  public String getUserid() {
+    return userid;
+  }
+
+  public void setUserid(String userid) {
+    this.userid = userid;
+  }
+
+  public QrcodePayConfirmRequest anonymous(Boolean anonymous) {
+    this.anonymous = anonymous;
+    return this;
+  }
+
+  /**
+   * Get anonymous
+   * @return anonymous
+  */
+  @ApiModelProperty(value = "")
+
+
+  public Boolean getAnonymous() {
+    return anonymous;
+  }
+
+  public void setAnonymous(Boolean anonymous) {
+    this.anonymous = anonymous;
+  }
+
+  public QrcodePayConfirmRequest qrcode(String qrcode) {
+    this.qrcode = qrcode;
+    return this;
+  }
+
+  /**
+   * Get qrcode
+   * @return qrcode
+  */
+  @ApiModelProperty(value = "")
+
+
+  public String getQrcode() {
+    return qrcode;
+  }
+
+  public void setQrcode(String qrcode) {
+    this.qrcode = qrcode;
+  }
+
+  public QrcodePayConfirmRequest qrcodeFormat(QrcodeFormat qrcodeFormat) {
+    this.qrcodeFormat = qrcodeFormat;
+    return this;
+  }
+
+  /**
+   * Get qrcodeFormat
+   * @return qrcodeFormat
+  */
+  @ApiModelProperty(value = "")
+
+  @Valid
+
+  public QrcodeFormat getQrcodeFormat() {
+    return qrcodeFormat;
+  }
+
+  public void setQrcodeFormat(QrcodeFormat qrcodeFormat) {
+    this.qrcodeFormat = qrcodeFormat;
+  }
+
+
+  @Override
+  public boolean equals(java.lang.Object o) {
+    if (this == o) {
+      return true;
+    }
+    if (o == null || getClass() != o.getClass()) {
+      return false;
+    }
+    QrcodePayConfirmRequest qrcodePayConfirmRequest = (QrcodePayConfirmRequest) o;
+    return Objects.equals(this.billno, qrcodePayConfirmRequest.billno) &&
+        Objects.equals(this.shopaccno, qrcodePayConfirmRequest.shopaccno) &&
+        Objects.equals(this.transDate, qrcodePayConfirmRequest.transDate) &&
+        Objects.equals(this.transTime, qrcodePayConfirmRequest.transTime) &&
+        Objects.equals(this.dlttype, qrcodePayConfirmRequest.dlttype) &&
+        Objects.equals(this.amount, qrcodePayConfirmRequest.amount) &&
+        Objects.equals(this.userid, qrcodePayConfirmRequest.userid) &&
+        Objects.equals(this.anonymous, qrcodePayConfirmRequest.anonymous) &&
+        Objects.equals(this.qrcode, qrcodePayConfirmRequest.qrcode) &&
+        Objects.equals(this.qrcodeFormat, qrcodePayConfirmRequest.qrcodeFormat);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(billno, shopaccno, transDate, transTime, dlttype, amount, userid, anonymous, qrcode, qrcodeFormat);
+  }
+
+  @Override
+  public String toString() {
+    StringBuilder sb = new StringBuilder();
+    sb.append("class QrcodePayConfirmRequest {\n");
+    
+    sb.append("    billno: ").append(toIndentedString(billno)).append("\n");
+    sb.append("    shopaccno: ").append(toIndentedString(shopaccno)).append("\n");
+    sb.append("    transDate: ").append(toIndentedString(transDate)).append("\n");
+    sb.append("    transTime: ").append(toIndentedString(transTime)).append("\n");
+    sb.append("    dlttype: ").append(toIndentedString(dlttype)).append("\n");
+    sb.append("    amount: ").append(toIndentedString(amount)).append("\n");
+    sb.append("    userid: ").append(toIndentedString(userid)).append("\n");
+    sb.append("    anonymous: ").append(toIndentedString(anonymous)).append("\n");
+    sb.append("    qrcode: ").append(toIndentedString(qrcode)).append("\n");
+    sb.append("    qrcodeFormat: ").append(toIndentedString(qrcodeFormat)).append("\n");
+    sb.append("}");
+    return sb.toString();
+  }
+
+  /**
+   * Convert the given object to string with each line indented by 4 spaces
+   * (except the first line).
+   */
+  private String toIndentedString(java.lang.Object o) {
+    if (o == null) {
+      return "null";
+    }
+    return o.toString().replace("\n", "\n    ");
+  }
+}
+
diff --git a/payapi-common/src/main/java/com/supwisdom/dlpay/payapi/model/QrcodePayInitRequest.java b/payapi-common/src/main/java/com/supwisdom/dlpay/payapi/model/QrcodePayInitRequest.java
new file mode 100644
index 0000000..cdb426d
--- /dev/null
+++ b/payapi-common/src/main/java/com/supwisdom/dlpay/payapi/model/QrcodePayInitRequest.java
@@ -0,0 +1,212 @@
+package com.supwisdom.dlpay.payapi.model;
+
+import java.util.Objects;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.supwisdom.dlpay.payapi.model.QrcodeFormat;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import org.openapitools.jackson.nullable.JsonNullable;
+import javax.validation.Valid;
+import javax.validation.constraints.*;
+
+/**
+ * QrcodePayInitRequest
+ */
+@javax.annotation.Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2020-03-12T16:09:45.966+08:00[Asia/Shanghai]")
+
+public class QrcodePayInitRequest   {
+  @JsonProperty("qrcode")
+  private String qrcode;
+
+  @JsonProperty("transDate")
+  private String transDate;
+
+  @JsonProperty("transTime")
+  private String transTime;
+
+  @JsonProperty("shopaccno")
+  private String shopaccno;
+
+  @JsonProperty("billno")
+  private String billno;
+
+  @JsonProperty("qrcodeFormat")
+  private QrcodeFormat qrcodeFormat;
+
+  public QrcodePayInitRequest qrcode(String qrcode) {
+    this.qrcode = qrcode;
+    return this;
+  }
+
+  /**
+   * Get qrcode
+   * @return qrcode
+  */
+  @ApiModelProperty(required = true, value = "")
+  @NotNull
+
+
+  public String getQrcode() {
+    return qrcode;
+  }
+
+  public void setQrcode(String qrcode) {
+    this.qrcode = qrcode;
+  }
+
+  public QrcodePayInitRequest transDate(String transDate) {
+    this.transDate = transDate;
+    return this;
+  }
+
+  /**
+   * Get transDate
+   * @return transDate
+  */
+  @ApiModelProperty(required = true, value = "")
+  @NotNull
+
+@Pattern(regexp="[0-9]*") @Size(min=8,max=8) 
+  public String getTransDate() {
+    return transDate;
+  }
+
+  public void setTransDate(String transDate) {
+    this.transDate = transDate;
+  }
+
+  public QrcodePayInitRequest transTime(String transTime) {
+    this.transTime = transTime;
+    return this;
+  }
+
+  /**
+   * Get transTime
+   * @return transTime
+  */
+  @ApiModelProperty(required = true, value = "")
+  @NotNull
+
+@Pattern(regexp="[0-9]*") @Size(min=6,max=6) 
+  public String getTransTime() {
+    return transTime;
+  }
+
+  public void setTransTime(String transTime) {
+    this.transTime = transTime;
+  }
+
+  public QrcodePayInitRequest shopaccno(String shopaccno) {
+    this.shopaccno = shopaccno;
+    return this;
+  }
+
+  /**
+   * Get shopaccno
+   * @return shopaccno
+  */
+  @ApiModelProperty(required = true, value = "")
+  @NotNull
+
+@Pattern(regexp="[0-9]*") 
+  public String getShopaccno() {
+    return shopaccno;
+  }
+
+  public void setShopaccno(String shopaccno) {
+    this.shopaccno = shopaccno;
+  }
+
+  public QrcodePayInitRequest billno(String billno) {
+    this.billno = billno;
+    return this;
+  }
+
+  /**
+   * Get billno
+   * @return billno
+  */
+  @ApiModelProperty(required = true, value = "")
+  @NotNull
+
+@Pattern(regexp="[0-9]*") @Size(min=16) 
+  public String getBillno() {
+    return billno;
+  }
+
+  public void setBillno(String billno) {
+    this.billno = billno;
+  }
+
+  public QrcodePayInitRequest qrcodeFormat(QrcodeFormat qrcodeFormat) {
+    this.qrcodeFormat = qrcodeFormat;
+    return this;
+  }
+
+  /**
+   * Get qrcodeFormat
+   * @return qrcodeFormat
+  */
+  @ApiModelProperty(value = "")
+
+  @Valid
+
+  public QrcodeFormat getQrcodeFormat() {
+    return qrcodeFormat;
+  }
+
+  public void setQrcodeFormat(QrcodeFormat qrcodeFormat) {
+    this.qrcodeFormat = qrcodeFormat;
+  }
+
+
+  @Override
+  public boolean equals(java.lang.Object o) {
+    if (this == o) {
+      return true;
+    }
+    if (o == null || getClass() != o.getClass()) {
+      return false;
+    }
+    QrcodePayInitRequest qrcodePayInitRequest = (QrcodePayInitRequest) o;
+    return Objects.equals(this.qrcode, qrcodePayInitRequest.qrcode) &&
+        Objects.equals(this.transDate, qrcodePayInitRequest.transDate) &&
+        Objects.equals(this.transTime, qrcodePayInitRequest.transTime) &&
+        Objects.equals(this.shopaccno, qrcodePayInitRequest.shopaccno) &&
+        Objects.equals(this.billno, qrcodePayInitRequest.billno) &&
+        Objects.equals(this.qrcodeFormat, qrcodePayInitRequest.qrcodeFormat);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(qrcode, transDate, transTime, shopaccno, billno, qrcodeFormat);
+  }
+
+  @Override
+  public String toString() {
+    StringBuilder sb = new StringBuilder();
+    sb.append("class QrcodePayInitRequest {\n");
+    
+    sb.append("    qrcode: ").append(toIndentedString(qrcode)).append("\n");
+    sb.append("    transDate: ").append(toIndentedString(transDate)).append("\n");
+    sb.append("    transTime: ").append(toIndentedString(transTime)).append("\n");
+    sb.append("    shopaccno: ").append(toIndentedString(shopaccno)).append("\n");
+    sb.append("    billno: ").append(toIndentedString(billno)).append("\n");
+    sb.append("    qrcodeFormat: ").append(toIndentedString(qrcodeFormat)).append("\n");
+    sb.append("}");
+    return sb.toString();
+  }
+
+  /**
+   * Convert the given object to string with each line indented by 4 spaces
+   * (except the first line).
+   */
+  private String toIndentedString(java.lang.Object o) {
+    if (o == null) {
+      return "null";
+    }
+    return o.toString().replace("\n", "\n    ");
+  }
+}
+
diff --git a/payapi-spec/build.gradle b/payapi-spec/build.gradle
new file mode 100644
index 0000000..c4bc7c9
--- /dev/null
+++ b/payapi-spec/build.gradle
@@ -0,0 +1,54 @@
+plugins {
+    id 'java-library'
+    id 'org.springframework.boot'
+    id "org.openapi.generator"
+}
+
+apply plugin: 'io.spring.dependency-management'
+
+
+dependencies {
+    implementation "org.springframework.boot:spring-boot-autoconfigure"
+}
+
+bootJar {
+    enabled = false
+}
+
+openApiGenerate {
+    generatorName = "spring"
+    inputSpec = "$projectDir/payapi-spec.yaml".toString()
+    outputDir = "$buildDir/generated".toString()
+    apiPackage = "com.supwisdom.dlpay.api"
+    invokerPackage = "com.supwisdom.dlpay.payapi"
+    modelPackage = "com.supwisdom.dlpay.payapi.model"
+
+    configOptions = [
+            dateLibrary             : "java8",
+            artifactDescription     : "PayAPI API",
+            developerName           : "Supwisdom",
+            developerEmail          : "admin@supwisdom.com",
+            developerOrganization   : "Supwisdom Tech Inc.",
+            developerOrganizationUrl: "http://www.supwisdom.com",
+            java8                   : "true",
+            interfaceOnly           : "true",
+            generateAliasAsModel    : "false"
+    ]
+}
+
+task copyApiSrc(type: Copy) {
+    from("$buildDir/generated/src/main/java/com/supwisdom/dlpay/api") {
+        include "**/*.java"
+    }
+    into "$rootProject.projectDir/payapi/src/main/java/com/supwisdom/dlpay/api"
+
+
+}
+task copyModelSrc(type: Copy) {
+    from("$buildDir/generated/src/main/java/com/supwisdom/dlpay/payapi/model") {
+        include "**/*.java"
+    }
+    into "$rootProject.projectDir/payapi-common/src/main/java/com/supwisdom/dlpay/payapi/model"
+}
+
+//copySrc.dependsOn openApiGenerate
\ No newline at end of file
diff --git a/payapi-spec/consumeapi.yaml b/payapi-spec/consumeapi.yaml
new file mode 100644
index 0000000..4d62dd5
--- /dev/null
+++ b/payapi-spec/consumeapi.yaml
@@ -0,0 +1,188 @@
+components:
+  schemas:
+    ErrorResponse:
+      type: object
+      title: 请求错误
+      required:
+        - message
+      properties:
+        code:
+          type: string
+          title: 错误码
+        message:
+          type: string
+          title: 错误信息
+    QrcodePayInitRequest:
+      type: object
+      title: QrCode请求初始化
+      required:
+        - qrcode
+        - transDate
+        - transTime
+        - shopaccno
+        - billno
+      properties:
+        qrcode:
+          type: string
+          title: QrCode
+        transDate:
+          $ref: 'definitions.yaml#/components/schemas/TransDate'
+        transTime:
+          $ref: 'definitions.yaml#/components/schemas/TransTime'
+        shopaccno:
+          $ref: 'definitions.yaml#/components/schemas/ShopAccNo'
+        billno:
+          $ref: 'definitions.yaml#/components/schemas/Refno'
+        qrcodeFormat:
+          $ref: 'definitions.yaml#/components/schemas/QrcodeFormat'
+
+    QrcodePayInitResponse:
+      type: object
+      title: QrCode请求初始化应答
+      properties:
+        anonymous:
+          title: 是否匿名用户
+          type: boolean
+        userid:
+          title: 用户ID(非匿名)
+          type: string
+        username:
+          title: 用户名(非匿名)
+          type: string
+        sourcetype:
+          title: 支付方式
+          type: string
+    QrcodePayConfirmRequest:
+      type: object
+      title: Qrcode消费确认请求
+      properties:
+        billno:
+          $ref: 'definitions.yaml#/components/schemas/Refno'
+        shopaccno:
+          $ref: 'definitions.yaml#/components/schemas/ShopAccNo'
+        transDate:
+          $ref: 'definitions.yaml#/components/schemas/TransDate'
+        transTime:
+          $ref: 'definitions.yaml#/components/schemas/TransTime'
+        dlttype:
+          type: string
+          title: 交易类型
+        amount:
+          type: integer
+          format: int32
+          title: 交易金额
+        userid:
+          type: string
+          title: 用户ID(非匿名)
+        anonymous:
+          type: boolean
+          title: 是否匿名
+        qrcode:
+          type: string
+          title: QrCode
+        qrcodeFormat:
+          $ref: 'definitions.yaml#/components/schemas/QrcodeFormat'
+    QrcodePayConfirmResponse:
+      type: object
+      title: QrCode消费确认返回
+      properties:
+        refno:
+          $ref: 'definitions.yaml#/components/schemas/Refno'
+        hostDate:
+          $ref: 'definitions.yaml#/components/schemas/TransDate'
+        hostTime:
+          $ref: 'definitions.yaml#/components/schemas/TransTime'
+        description:
+          type: string
+          title: 交易描述
+        result:
+          title: 交易结果
+          type: string
+          enum:
+            - success
+            - require_query
+            - already_success
+            - failed
+
+  paths:
+    qrcodePayInit:
+      parameters:
+        - $ref: definitions.yaml#/components/headers/TenantId
+      post:
+        operationId: qrcodePayInit
+        tags:
+          - pos
+        requestBody:
+          description: QrCode 初始化
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/QrcodePayInitRequest'
+
+        responses:
+          '200':
+            description: 初始化成功
+            content:
+              application/json:
+                $ref: '#/components/schemas/QrcodePayInitResponse'
+          'default':
+            description: 请求错误
+            content:
+              application/json:
+                $ref: '#/components/schemas/ErrorResponse'
+    qrcodePayConfirm:
+      parameters:
+        - $ref: definitions.yaml#/components/headers/TenantId
+      post:
+        operationId: qrcodePayConfirm
+        tags:
+          - pos
+        requestBody:
+          description: Qrcode确认
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/QrcodePayConfirmRequest'
+        responses:
+          '200':
+            description: 交易成功
+            content:
+              application/json:
+                $ref: '#/components/schemas/QrcodePayConfirmResponse'
+          '409':
+            description: 交易正忙,稍后重试
+            content:
+              application/json:
+                $ref: '#/components/schemas/ErrorResponse'
+          'default':
+            description: 交易失败
+            content:
+              application/json:
+                $ref: '#/components/schemas/ErrorResponse'
+    qrcodePayQuery:
+      parameters:
+        - $ref: 'definitions.yaml#/components/headers/TenantId'
+        - name: refno
+          in: path
+          description: 系统交易参考号
+          required: true
+          schema:
+            type: string
+            title: 系统交易参考号
+      get:
+        description: 根据系统交易参考号查询流水状态
+        tags:
+          - pos
+        operationId: qrcodePayQuery
+        responses:
+          '200':
+            description: 查询成功
+            content:
+              application/json:
+                $ref: '#/components/schemas/QrcodePayConfirmResponse'
+          'default':
+            description: 查询失败
+            content:
+              application/json:
+                $ref: '#/components/schemas/ErrorResponse'
+
diff --git a/payapi-spec/definitions.yaml b/payapi-spec/definitions.yaml
new file mode 100644
index 0000000..fea5324
--- /dev/null
+++ b/payapi-spec/definitions.yaml
@@ -0,0 +1,40 @@
+components:
+  headers:
+    TenantId:
+      name: X-Tenant-Id
+      in: header
+      description: 租户ID
+      required: true
+      schema:
+        type: string
+
+
+  schemas:
+    TransDate:
+      type: string
+      title: 交易日期
+      minLength: 8
+      maxLength: 8
+      pattern: '[0-9]*'
+    TransTime:
+      type: string
+      title: 交易时间
+      minLength: 6
+      maxLength: 6
+      pattern: '[0-9]*'
+    ShopAccNo:
+      type: string
+      title: 商户号
+      pattern: '[0-9]*'
+    Refno:
+      type: string
+      title: 交易号
+      minLength: 16
+      pattern: '[0-9]*'
+    QrcodeFormat:
+      type: string
+      title: Qrcode格式
+      enum:
+        - plain
+        - base64
+        - hex
diff --git a/payapi-spec/payapi-spec.yaml b/payapi-spec/payapi-spec.yaml
new file mode 100644
index 0000000..1b432bc
--- /dev/null
+++ b/payapi-spec/payapi-spec.yaml
@@ -0,0 +1,13 @@
+openapi: '3.0.2'
+info:
+  title: API Title
+  version: '1.0'
+servers:
+  - url: https://api.server.test/api
+paths:
+  /consume/qrcode/init:
+    $ref: consumeapi.yaml#/components/paths/qrcodePayInit
+  /consume/qrcode/confirm:
+    $ref: consumeapi.yaml#/components/paths/qrcodePayConfirm
+  /consume/qrcode/query/{refno}:
+    $ref: consumeapi.yaml#/components/paths/qrcodePayQuery
diff --git a/payapi/build.gradle b/payapi/build.gradle
index 3bbc883..656a319 100644
--- a/payapi/build.gradle
+++ b/payapi/build.gradle
@@ -18,10 +18,6 @@
 bootJar {
     enabled = true
     mainClassName = payapiStartClass
-//    def standalone = ""
-//    if (rootProject.hasProperty("no-multi-tenant")) {
-//        standalone = "-stangalone-"
-//
     archiveFileName = "${project.name}-${buildVersion}.${archiveExtension.getOrElse('.jar')}"
     manifest {
         attributes("Payapi-Version": buildVersion,
@@ -101,6 +97,9 @@
 
     implementation "org.thymeleaf.extras:thymeleaf-extras-springsecurity5"
 
+    implementation group: 'io.swagger', name: 'swagger-annotations', version: swaggerVersion
+    implementation group: 'io.springfox', name: 'springfox-swagger2', version: springfoxVersion
+
     implementation "org.apache.commons:commons-lang3:${lang3Version}"
     implementation "net.javacrumbs.shedlock:shedlock-spring:${shedlockVersion}"
     implementation "net.javacrumbs.shedlock:shedlock-provider-redis-spring:${shedlockVersion}"
diff --git a/payapi/src/main/java/com/supwisdom/dlpay/RFC3339DateFormat.java b/payapi/src/main/java/com/supwisdom/dlpay/RFC3339DateFormat.java
new file mode 100644
index 0000000..7ce517a
--- /dev/null
+++ b/payapi/src/main/java/com/supwisdom/dlpay/RFC3339DateFormat.java
@@ -0,0 +1,22 @@
+package com.supwisdom.dlpay;
+
+import com.fasterxml.jackson.databind.util.ISO8601DateFormat;
+import com.fasterxml.jackson.databind.util.ISO8601Utils;
+
+import java.text.FieldPosition;
+import java.util.Date;
+
+
+public class RFC3339DateFormat extends ISO8601DateFormat {
+
+  private static final long serialVersionUID = 1L;
+
+  // Same as ISO8601DateFormat but serializing milliseconds.
+  @Override
+  public StringBuffer format(Date date, StringBuffer toAppendTo, FieldPosition fieldPosition) {
+    String value = ISO8601Utils.format(date, true);
+    toAppendTo.append(value);
+    return toAppendTo;
+  }
+
+}
\ No newline at end of file
diff --git a/payapi/src/main/java/com/supwisdom/dlpay/api/ApiUtil.java b/payapi/src/main/java/com/supwisdom/dlpay/api/ApiUtil.java
new file mode 100644
index 0000000..4a0abe7
--- /dev/null
+++ b/payapi/src/main/java/com/supwisdom/dlpay/api/ApiUtil.java
@@ -0,0 +1,19 @@
+package com.supwisdom.dlpay.api;
+
+import org.springframework.web.context.request.NativeWebRequest;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+public class ApiUtil {
+    public static void setExampleResponse(NativeWebRequest req, String contentType, String example) {
+        try {
+            HttpServletResponse res = req.getNativeResponse(HttpServletResponse.class);
+            res.setCharacterEncoding("UTF-8");
+            res.addHeader("Content-Type", contentType);
+            res.getWriter().print(example);
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+}
diff --git a/payapi/src/main/java/com/supwisdom/dlpay/api/ConsumeApi.java b/payapi/src/main/java/com/supwisdom/dlpay/api/ConsumeApi.java
new file mode 100644
index 0000000..86c57a8
--- /dev/null
+++ b/payapi/src/main/java/com/supwisdom/dlpay/api/ConsumeApi.java
@@ -0,0 +1,81 @@
+/**
+ * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech) (4.2.2).
+ * https://openapi-generator.tech
+ * Do not edit the class manually.
+ */
+package com.supwisdom.dlpay.api;
+
+import com.supwisdom.dlpay.payapi.model.QrcodePayConfirmRequest;
+import com.supwisdom.dlpay.payapi.model.QrcodePayInitRequest;
+import io.swagger.annotations.*;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestHeader;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RequestPart;
+import org.springframework.web.context.request.NativeWebRequest;
+import org.springframework.web.multipart.MultipartFile;
+
+import javax.validation.Valid;
+import javax.validation.constraints.*;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+@javax.annotation.Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2020-03-12T16:17:20.799+08:00[Asia/Shanghai]")
+
+@Validated
+@Api(value = "consume", description = "the consume API")
+public interface ConsumeApi {
+
+    default Optional<NativeWebRequest> getRequest() {
+        return Optional.empty();
+    }
+
+    @ApiOperation(value = "", nickname = "qrcodePayConfirm", notes = "", tags={ "pos", })
+    @ApiResponses(value = { 
+        @ApiResponse(code = 200, message = "交易成功"),
+        @ApiResponse(code = 409, message = "交易正忙,稍后重试"),
+        @ApiResponse(code = 200, message = "交易失败") })
+    @RequestMapping(value = "/consume/qrcode/confirm",
+        produces = { "application/json" }, 
+        consumes = { "application/json" },
+        method = RequestMethod.POST)
+    default ResponseEntity<Void> qrcodePayConfirm(@ApiParam(value = "租户ID" ,required=true) @RequestHeader(value="X-Tenant-Id", required=true) String xTenantId,@ApiParam(value = "Qrcode确认"  )  @Valid @RequestBody QrcodePayConfirmRequest qrcodePayConfirmRequest) {
+        return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
+
+    }
+
+
+    @ApiOperation(value = "", nickname = "qrcodePayInit", notes = "", tags={ "pos", })
+    @ApiResponses(value = { 
+        @ApiResponse(code = 200, message = "初始化成功"),
+        @ApiResponse(code = 200, message = "请求错误") })
+    @RequestMapping(value = "/consume/qrcode/init",
+        produces = { "application/json" }, 
+        consumes = { "application/json" },
+        method = RequestMethod.POST)
+    default ResponseEntity<Void> qrcodePayInit(@ApiParam(value = "租户ID" ,required=true) @RequestHeader(value="X-Tenant-Id", required=true) String xTenantId,@ApiParam(value = "QrCode 初始化"  )  @Valid @RequestBody QrcodePayInitRequest qrcodePayInitRequest) {
+        return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
+
+    }
+
+
+    @ApiOperation(value = "", nickname = "qrcodePayQuery", notes = "根据系统交易参考号查询流水状态", tags={ "pos", })
+    @ApiResponses(value = { 
+        @ApiResponse(code = 200, message = "查询成功"),
+        @ApiResponse(code = 200, message = "查询失败") })
+    @RequestMapping(value = "/consume/qrcode/query/{refno}",
+        produces = { "application/json" }, 
+        method = RequestMethod.GET)
+    default ResponseEntity<Void> qrcodePayQuery(@ApiParam(value = "租户ID" ,required=true) @RequestHeader(value="X-Tenant-Id", required=true) String xTenantId,@ApiParam(value = "系统交易参考号",required=true) @PathVariable("refno") String refno) {
+        return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
+
+    }
+
+}
diff --git a/payapi/src/main/java/com/supwisdom/dlpay/api/ConsumeApiController.java b/payapi/src/main/java/com/supwisdom/dlpay/api/ConsumeApiController.java
new file mode 100644
index 0000000..dc318fe
--- /dev/null
+++ b/payapi/src/main/java/com/supwisdom/dlpay/api/ConsumeApiController.java
@@ -0,0 +1,25 @@
+package com.supwisdom.dlpay.api;
+
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.context.request.NativeWebRequest;
+import java.util.Optional;
+@javax.annotation.Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2020-03-12T16:09:45.966+08:00[Asia/Shanghai]")
+
+@Controller
+@RequestMapping("${openapi.aPITitle.base-path:/api}")
+public class ConsumeApiController implements ConsumeApi {
+
+    private final NativeWebRequest request;
+
+    @org.springframework.beans.factory.annotation.Autowired
+    public ConsumeApiController(NativeWebRequest request) {
+        this.request = request;
+    }
+
+    @Override
+    public Optional<NativeWebRequest> getRequest() {
+        return Optional.ofNullable(request);
+    }
+
+}
diff --git a/payapi/src/main/java/com/supwisdom/dlpay/api/domain/TAccount.java b/payapi/src/main/java/com/supwisdom/dlpay/api/domain/TAccount.java
index 7b04f82..4a06d9b 100644
--- a/payapi/src/main/java/com/supwisdom/dlpay/api/domain/TAccount.java
+++ b/payapi/src/main/java/com/supwisdom/dlpay/api/domain/TAccount.java
@@ -161,11 +161,11 @@
   }
 
   public Double getAvailbal() {
-    return availbal;
+    return balance - frozebal;
   }
 
   public void setAvailbal(Double availbal) {
-    this.availbal = availbal;
+    this.availbal = 0.0;
   }
 
   public Double getFrozebal() {
diff --git a/payapi/src/main/java/org/openapitools/configuration/HomeController.java b/payapi/src/main/java/org/openapitools/configuration/HomeController.java
new file mode 100644
index 0000000..2572783
--- /dev/null
+++ b/payapi/src/main/java/org/openapitools/configuration/HomeController.java
@@ -0,0 +1,19 @@
+package org.openapitools.configuration;
+
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestMapping;
+
+
+/**
+ * Home redirection to OpenAPI api documentation
+ */
+@Controller
+public class HomeController {
+
+    @RequestMapping("/")
+    public String index() {
+        return "redirect:swagger-ui.html";
+    }
+
+
+}
diff --git a/payapi/src/main/java/org/openapitools/configuration/OpenAPIDocumentationConfig.java b/payapi/src/main/java/org/openapitools/configuration/OpenAPIDocumentationConfig.java
new file mode 100644
index 0000000..9754ed5
--- /dev/null
+++ b/payapi/src/main/java/org/openapitools/configuration/OpenAPIDocumentationConfig.java
@@ -0,0 +1,71 @@
+package org.openapitools.configuration;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import org.springframework.web.util.UriComponentsBuilder;
+import springfox.documentation.builders.ApiInfoBuilder;
+import springfox.documentation.builders.RequestHandlerSelectors;
+import springfox.documentation.service.ApiInfo;
+import springfox.documentation.service.Contact;
+import springfox.documentation.spi.DocumentationType;
+import springfox.documentation.spring.web.paths.Paths;
+import springfox.documentation.spring.web.paths.RelativePathProvider;
+import springfox.documentation.spring.web.plugins.Docket;
+import springfox.documentation.swagger2.annotations.EnableSwagger2;
+
+import javax.servlet.ServletContext;
+
+@javax.annotation.Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2020-03-12T16:09:45.966+08:00[Asia/Shanghai]")
+
+@Configuration
+@EnableSwagger2
+public class OpenAPIDocumentationConfig {
+
+    ApiInfo apiInfo() {
+        return new ApiInfoBuilder()
+            .title("API Title")
+            .description("No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)")
+            .license("")
+            .licenseUrl("http://unlicense.org")
+            .termsOfServiceUrl("")
+            .version("1.0")
+            .contact(new Contact("","", ""))
+            .build();
+    }
+
+    @Bean
+    public Docket customImplementation(ServletContext servletContext, @Value("${openapi.aPITitle.base-path:/v1/api}") String basePath) {
+        return new Docket(DocumentationType.SWAGGER_2)
+                .select()
+                    .apis(RequestHandlerSelectors.basePackage("com.supwisdom.dlpay.api"))
+                    .build()
+                .pathProvider(new BasePathAwareRelativePathProvider(servletContext, basePath))
+                .directModelSubstitute(java.time.LocalDate.class, java.sql.Date.class)
+                .directModelSubstitute(java.time.OffsetDateTime.class, java.util.Date.class)
+                .apiInfo(apiInfo());
+    }
+
+    class BasePathAwareRelativePathProvider extends RelativePathProvider {
+        private String basePath;
+
+        public BasePathAwareRelativePathProvider(ServletContext servletContext, String basePath) {
+            super(servletContext);
+            this.basePath = basePath;
+        }
+
+        @Override
+        protected String applicationPath() {
+            return  Paths.removeAdjacentForwardSlashes(UriComponentsBuilder.fromPath(super.applicationPath()).path(basePath).build().toString());
+        }
+
+        @Override
+        public String getOperationPath(String operationPath) {
+            UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromPath("/");
+            return Paths.removeAdjacentForwardSlashes(
+                    uriComponentsBuilder.path(operationPath.replaceFirst("^" + basePath, "")).build().toString());
+        }
+    }
+
+}
diff --git a/payapi/src/main/kotlin/com/supwisdom/dlpay/PayApiApplication.kt b/payapi/src/main/kotlin/com/supwisdom/dlpay/PayApiApplication.kt
index 42ee1e4..be0b03e 100644
--- a/payapi/src/main/kotlin/com/supwisdom/dlpay/PayApiApplication.kt
+++ b/payapi/src/main/kotlin/com/supwisdom/dlpay/PayApiApplication.kt
@@ -28,6 +28,7 @@
 import org.springframework.cache.interceptor.KeyGenerator
 import org.springframework.cloud.client.discovery.EnableDiscoveryClient
 import org.springframework.context.annotation.Bean
+import org.springframework.context.annotation.ComponentScan
 import org.springframework.context.annotation.Configuration
 import org.springframework.context.event.ContextStartedEvent
 import org.springframework.context.event.EventListener
@@ -243,6 +244,7 @@
 @ServletComponentScan
 @EnableAsync
 @EnableRetry
+@ComponentScan(basePackages = ["com.supwisdom.dlpay", "org.openapitools.configuration"])
 class PayApiApplication : SpringBootServletInitializer() {
 
     override fun configure(builder: SpringApplicationBuilder): SpringApplicationBuilder {
diff --git a/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/account_service_impl.kt b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/account_service_impl.kt
index 71c3189..3c03563 100644
--- a/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/account_service_impl.kt
+++ b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/account_service_impl.kt
@@ -65,7 +65,6 @@
                     }
                     dtl.befbal = account.availbal
                     doRecalcAccountBalance(dtl, amount, account)
-                    account.availbal += amount
                     account.balance += amount
 
                     val sameDay = DateUtil.sameDay(account.lasttranstime,
diff --git a/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/transaction_service_impl.kt b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/transaction_service_impl.kt
index b0aefd2..6165d49 100644
--- a/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/transaction_service_impl.kt
+++ b/payapi/src/main/kotlin/com/supwisdom/dlpay/api/service/impl/transaction_service_impl.kt
@@ -90,7 +90,7 @@
                         userid = builder.person().person!!.userid
                         accountNo = builder.person().person!!.accno
                         userName = builder.person().person!!.accname
-                        befbal = builder.person().person!!.availbal
+                        befbal = builder.person().person!!.balance
                     }
                     aftbal = 0.0
                     outtradeno = builder.outtradeno
diff --git a/payapi/src/main/resources/application.properties b/payapi/src/main/resources/application.properties
index 4c93ce7..7565c8c 100644
--- a/payapi/src/main/resources/application.properties
+++ b/payapi/src/main/resources/application.properties
@@ -49,6 +49,11 @@
 ###################################################
 spring.redis.database=0
 ###################################################
+springfox.documentation.swagger.v2.path=/api-docs
+openapi.aPITitle.base-path=/payapi
+spring.jackson.date-format=com.supwisdom.dlpay.payapi.RFC3339DateFormat
+spring.jackson.serialization.WRITE_DATES_AS_TIMESTAMPS=false
+
 multi-tenant.header.key=X-TENANT-ID
 multi-tenant.session.name=tenant-id
 multi-tenant.session.enableSessionScopedBean=false
diff --git a/settings.gradle b/settings.gradle
index c670d5a..25e20df 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -3,5 +3,6 @@
 include 'payapi-sdk'
 include 'payapi-common'
 include 'ynrcc-agent'
+include 'payapi-spec'
 include 'oauth'