hamcrestVersion = '2.1'
mockkVersion = '1.9.3'
commonNetVersion = '3.6'
+ fasterXMLVersion = '2.9.8'
kaptchaVersion = '2.3.2'
jerseyClientVersion = '1.19'
javaxWSRSVersion = '2.1.1'
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"
--- /dev/null
+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;
+ }
+}
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;
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}");
@Test
public void citizencardPayinit() {
- ApiVersionResponse version = apiCommonProxy.apiVersion();
-
- assertThat("get version error " + version.getException(),
- version.getVersion(), notNullValue());
-
- 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));
+ try {
+ ApiVersionResponse version = apiCommonProxy.apiVersion();
+
+ assertThat("get version error " + version.getException(),
+ version.getVersion(), notNullValue());
+
+ 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));
+ } catch (FeignException ex) {
+ System.out.println("error : " + ex.status());
+ }
}
@Test
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
title: 子账户列表
type: array
items:
+ description: |
+ 需要查询的子账户名,如果查询所有子账户可以用 ['*'] 来查询
type: string
title: 子账户ID
AccountQueryResponse:
title: 预授权交易请求
required:
- userid
- - accountType
- preAuthorizedAmount
- shopaccno
- transDate
userid:
title: 用户ID
type: string
- accountType:
- title: 账户类型
+ accountName:
+ title: 账户名
type: string
preAuthorizedAmount:
title: 预授权金额(分)
type: integer
AccountPayRequest:
type: object
- title: 账户余额扣款交易
+ title: 主账户余额扣款交易
required:
- billno
- shopaccno
amount:
title: 扣款金额(分)
type: integer
+ accountName:
+ title: 账户名
+ type: string
description:
title: 交易描述
type: string
AccountPayResponse:
type: object
title: 账户余额扣款交易应答
+ required:
+ - refno
properties:
refno:
title: 系统交易参考号
aftbal:
title: 账户余额(分)
type: integer
-
+ realAmount:
+ title: 实际扣款金额(分)
+ type: integer
paths:
accountQuery:
schema:
$ref: 'definitions.yaml#/components/schemas/ErrorResponse'
default:
- description: 查询错误
- content:
- application/json:
- schema:
- $ref: 'definitions.yaml#/components/schemas/ErrorResponse'
+ $ref: 'definitions.yaml#/components/responses/CommonError'
accountPay:
parameters:
- $ref: definitions.yaml#/components/headers/TenantId
tags:
- pos
requestBody:
- description: 账户余额消费
+ description: 主账户余额消费
content:
application/json:
schema:
application/json:
schema:
$ref: '#/components/schemas/AccountPayResponse'
- default:
- description: 消费失败
+ '400':
+ description: |
+ 消费失败, code 用于定义错误信息
+ * short_of_balance 余额不足
+ * account_status_error 账户状态异常
content:
application/json:
schema:
$ref: 'definitions.yaml#/components/schemas/ErrorResponse'
+ default:
+ $ref: 'definitions.yaml#/components/responses/CommonError'
accountPreAuthorizedDebit:
parameters:
- $ref: definitions.yaml#/components/headers/TenantId
application/json:
schema:
$ref: '#/components/schemas/PreAuthorizedDebitResponse'
+ default:
+ $ref: 'definitions.yaml#/components/responses/CommonError'
accountPreAuthorizedSettle:
parameters:
- $ref: definitions.yaml#/components/headers/TenantId
application/json:
schema:
$ref: '#/components/schemas/PreAuthorizedSettleRequest'
-
responses:
'200':
description: 预授权交易清算成功
application/json:
schema:
$ref: '#/components/schemas/PreAuthorizedSettleResponse'
+ default:
+ $ref: 'definitions.yaml#/components/responses/CommonError'
interfaceOnly : "true",
generateAliasAsModel : "false"
]
+
+ systemProperties = [
+ "hideGenerationTimestamp": true
+ ]
}
task copyApiSrc(type: Copy) {
application/json:
schema:
$ref: '#/components/schemas/QrcodePayInitResponse'
- 'default':
- description: 请求错误
- content:
- application/json:
- schema:
- $ref: 'definitions.yaml#/components/schemas/ErrorResponse'
+ default:
+ $ref: 'definitions.yaml#/components/responses/CommonError'
qrcodePayConfirm:
parameters:
- $ref: definitions.yaml#/components/headers/TenantId
application/json:
schema:
$ref: 'definitions.yaml#/components/schemas/ErrorResponse'
- 'default':
- description: 交易失败
- content:
- application/json:
- schema:
- $ref: 'definitions.yaml#/components/schemas/ErrorResponse'
+ default:
+ $ref: 'definitions.yaml#/components/responses/CommonError'
qrcodePayQuery:
parameters:
- $ref: 'definitions.yaml#/components/headers/TenantId'
application/json:
schema:
$ref: '#/components/schemas/QrcodePayConfirmResponse'
- 'default':
- description: 查询失败
- content:
- application/json:
- schema:
- $ref: 'definitions.yaml#/components/schemas/ErrorResponse'
+ default:
+ $ref: 'definitions.yaml#/components/responses/CommonError'
refund:
parameters:
- $ref: 'definitions.yaml#/components/headers/TenantId'
schema:
$ref: '#/components/schemas/RefundResponse'
'409':
- description: 退款申请失败,订单号已存在或正在退款中
+ description: |
+ 退款申请失败,订单号已存在或正在退款中
content:
application/json:
schema:
$ref: 'definitions.yaml#/components/schemas/ErrorResponse'
default:
- description: 退款申请失败
- content:
- application/json:
- schema:
- $ref: 'definitions.yaml#/components/schemas/ErrorResponse'
+ $ref: 'definitions.yaml#/components/responses/CommonError'
refundQuery:
parameters:
- $ref: 'definitions.yaml#/components/headers/TenantId'
schema:
$ref: 'definitions.yaml#/components/schemas/ErrorResponse'
default:
- description: 退款申请失败
- content:
- application/json:
- schema:
- $ref: 'definitions.yaml#/components/schemas/ErrorResponse'
+ $ref: 'definitions.yaml#/components/responses/CommonError'
schema:
type: string
+ responses:
+ CommonError:
+ description: 请求失败
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/ErrorResponse'
+
schemas:
ErrorResponse:
- failed
- already_success
- require_query
+ UserStatus:
+ type: string
+ description: |
+ * normal 正常
+ * frozen 冻结
+ * canceled 注销
+ * closed 关闭
+ * notexists 不存在
+ enum:
+ - normal
+ - fronzen
+ - canceled
+ - closed
+ - notexists
+ SubAccountMetadata:
+ type: object
+ required:
+ - name
+ - status
+ properties:
+ name:
+ type: string
+ title: 子账户名
+ status:
+ title: 子账户状态
+ $ref: '#/components/schemas/UserStatus'
+ balance:
+ type: integer
+ title: 子账户余额
+ expire:
+ type: string
+ title: 余额有效期
+ format: date-time
$ref: consumeapi.yaml#/components/paths/refund
/consume/refund/query/{billno}:
$ref: consumeapi.yaml#/components/paths/refundQuery
+ /account/query:
+ $ref: accountapi.yaml#/components/paths/accountQuery
+ /account/apy:
+ $ref: accountapi.yaml#/components/paths/accountPay
+ /account/preAuthorizedDebit:
+ $ref: accountapi.yaml#/components/paths/accountPreAuthorizedDebit
+ /account/preAuthorizedSettle:
+ $ref: accountapi.yaml#/components/paths/accountPreAuthorizedSettle
@Column(name = "ACCNO", length = 32)
private String accountNo;
+ @Column(name = "accname", length = 32)
+ private String accountName; // 账户名
+
@Column(name = "USERNAME", length = 200)
private String userName;
@NotNull
private String status = TradeDict.DTL_STATUS_NONE;
+ /**
+ * @deprecated
+ */
@Column(name = "BEFBAL", precision = 9, scale = 2)
private Double befbal;
+ /**
+ * @since 1.1
+ */
@Column(name = "AFTBAL", precision = 9, scale = 2)
private Double aftbal;
this.status = status;
}
+ /**
+ * @deprecated
+ */
public Double getBefbal() {
return befbal;
}
+ /**
+ * @deprecated
+ */
public void setBefbal(Double befbal) {
this.befbal = befbal;
}
public void setAftbal(Double aftbal) {
this.aftbal = aftbal;
}
+
+ public String getAccountName() {
+ return accountName;
+ }
+
+ public void setAccountName(String accountName) {
+ this.accountName = accountName;
+ }
}
--- /dev/null
+package com.supwisdom.dlpay.api.domain;
+
+import org.hibernate.annotations.GenericGenerator;
+
+import javax.persistence.*;
+import javax.validation.constraints.NotNull;
+import java.sql.Timestamp;
+
+
+/**
+ * 子账户表
+ */
+@Entity
+@Table(name = "TB_SUBACCOUNT",
+ indexes = {@Index(name = "subacc_idx1", columnList = "userid, name", unique = true),
+ @Index(name = "subacc_idx2", columnList = "name, status")})
+@GenericGenerator(name = "subaccount_seq", strategy = "org.hibernate.id.UUIDGenerator")
+public class TSubAccount {
+ @Id
+ @Column(name = "id", length = 32)
+ @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "subaccount_seq")
+ private String id;
+
+ @Column(name = "userid", length = 32)
+ @NotNull
+ private String userid;
+
+ @Column(name = "subjno", length = 16)
+ private String subjno;
+ /**
+ * 子账户名
+ */
+ @Column(name = "name", length = 30)
+ @NotNull
+ private String accountName;
+
+ @Column(name = "status", length = 12)
+ @NotNull
+ private String status;
+
+ @Column(name = "creditlimit", precision = 10, scale = 2)
+ @NotNull
+ private Double creditLimit;
+
+ @Column(name = "limitcycle", length = 30)
+ @NotNull
+ private String limitCycle;
+
+ @Column(name = "expired", length = 8)
+ @NotNull
+ private String expired;
+
+ @Version
+ @Column(name = "lastupdate")
+ private Timestamp lastUpdate;
+
+ @Column(name = "cyclepolicy", length = 30)
+ @NotNull
+ private String cyclePolicy;
+
+ @Column(name = "tenantid", length = 32)
+ @NotNull
+ private String tenantid;
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getUserid() {
+ return userid;
+ }
+
+ public void setUserid(String userid) {
+ this.userid = userid;
+ }
+
+ public String getAccountName() {
+ return accountName;
+ }
+
+ public void setAccountName(String accountName) {
+ this.accountName = accountName;
+ }
+
+ public String getStatus() {
+ return status;
+ }
+
+ public void setStatus(String status) {
+ this.status = status;
+ }
+
+ public Double getCreditLimit() {
+ return creditLimit;
+ }
+
+ public void setCreditLimit(Double creditLimit) {
+ this.creditLimit = creditLimit;
+ }
+
+ public String getLimitCycle() {
+ return limitCycle;
+ }
+
+ public void setLimitCycle(String limitCycle) {
+ this.limitCycle = limitCycle;
+ }
+
+ public String getExpired() {
+ return expired;
+ }
+
+ public void setExpired(String expired) {
+ this.expired = expired;
+ }
+
+ public Timestamp getLastUpdate() {
+ return lastUpdate;
+ }
+
+ public void setLastUpdate(Timestamp lastUpdate) {
+ this.lastUpdate = lastUpdate;
+ }
+
+ public String getCyclePolicy() {
+ return cyclePolicy;
+ }
+
+ public void setCyclePolicy(String cyclePolicy) {
+ this.cyclePolicy = cyclePolicy;
+ }
+
+ public String getTenantid() {
+ return tenantid;
+ }
+
+ public void setTenantid(String tenantid) {
+ this.tenantid = tenantid;
+ }
+}
import com.supwisdom.dlpay.exception.TransactionException
import com.supwisdom.dlpay.framework.ResponseBodyBuilder
import com.supwisdom.dlpay.framework.service.CommonService
+import com.supwisdom.dlpay.payapi.model.ErrorResponse
import mu.KotlinLogging
import org.aspectj.lang.ProceedingJoinPoint
import org.aspectj.lang.annotation.Around
val msg = ex.bindingResult.fieldErrors.joinToString(",") {
it?.defaultMessage ?: ""
}
- return ResponseEntity.ok(ResponseBodyBuilder.create()
- .fail(300001, msg))
-
+ val response = ErrorResponse().apply {
+ code = "300001"
+ message = msg
+ }
+ return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(response)
}
@ExceptionHandler(TransactionSystemException::class)
}
cause = cause.cause
}
- return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE).body(e)
+ return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(e)
}
}