diff --git a/maintain-admin/src/main/resources/application-dev.yml b/maintain-admin/src/main/resources/application-dev.yml index db50ffbb8f4b4e7b3def45561386864c374a778f..f6ede95bd41ba604a492ecf17e53ffbb5330d6d2 100644 --- a/maintain-admin/src/main/resources/application-dev.yml +++ b/maintain-admin/src/main/resources/application-dev.yml @@ -180,3 +180,11 @@ sms: sdkAppId: appid #地域信息默认为 ap-guangzhou 如无特殊改变可不用设置 territory: ap-guangzhou +pay: + appid: 10037e6f72b586db0172b6ea24ec0000 + appkey: 6f7a00170935445fbe25e67a5ce1be48 + url: https://test-api-open.chinaums.com/v6/poslink/transaction/pay + merchantCode: 123456789111115 + terminalCode: gxrb0001 + deviceType: 11 + payMode: CODE_SCAN diff --git a/maintain-admin/src/main/resources/application-prod.yml b/maintain-admin/src/main/resources/application-prod.yml index d57a9f6344e51e90a90c2138830d6a8f00248c6a..4244908f74f2114cef2317b052d5a2c68251b82f 100644 --- a/maintain-admin/src/main/resources/application-prod.yml +++ b/maintain-admin/src/main/resources/application-prod.yml @@ -183,3 +183,11 @@ sms: sdkAppId: appid #地域信息默认为 ap-guangzhou 如无特殊改变可不用设置 territory: ap-guangzhou +pay: + appid: 10037e6f72b586db0172b6ea24ec0000 + appkey: 6f7a00170935445fbe25e67a5ce1be48 + url: https://test-api-open.chinaums.com/v6/poslink/transaction/pay + merchantCode: 123456789111115 + terminalCode: gxrb0001 + deviceType: 11 + payMode: CODE_SCAN diff --git a/maintain-business/src/main/java/com/maintain/business/controller/ErpSettlementMaintainController.java b/maintain-business/src/main/java/com/maintain/business/controller/ErpSettlementMaintainController.java index 8f22e63734bf536d4868322b40f55c67964a1908..6bc85a14fa60703dc78072df677285cd9d88c299 100644 --- a/maintain-business/src/main/java/com/maintain/business/controller/ErpSettlementMaintainController.java +++ b/maintain-business/src/main/java/com/maintain/business/controller/ErpSettlementMaintainController.java @@ -5,6 +5,7 @@ import java.util.List; import java.util.Arrays; import java.util.stream.Collectors; +import com.maintain.business.domain.bo.ErpSettlementMaintainPayBo; import com.maintain.business.domain.bo.ErpSettlementMaintainRepairFormBo; import com.maintain.business.domain.vo.ErpSettlementMaintainRepairFormVo; import com.maintain.business.service.IErpRepairFormService; @@ -137,4 +138,16 @@ public class ErpSettlementMaintainController extends BaseController { public R receipt(@Validated(EditGroup.class) @RequestBody ErpSettlementMaintainBo bo) { return toAjax(iErpSettlementMaintainService.receiptByBo(bo)); } + + /** + * 结算维修单回执 + * + * @param bo + */ + @Log(title = "结算维修单支付", businessType = BusinessType.UPDATE) + @PutMapping("/pay") + public R pay(@Validated(EditGroup.class) @RequestBody ErpSettlementMaintainPayBo bo) { + return toAjax(iErpSettlementMaintainService.pay(bo)); + } + } diff --git a/maintain-business/src/main/java/com/maintain/business/domain/ErpSettlementMaintain.java b/maintain-business/src/main/java/com/maintain/business/domain/ErpSettlementMaintain.java index 8b770de586ab0c448b1ecf7cbcfcc7c034335412..a5ce1a975f8456b7a07cd9a9d0a4a45edf7c18f4 100644 --- a/maintain-business/src/main/java/com/maintain/business/domain/ErpSettlementMaintain.java +++ b/maintain-business/src/main/java/com/maintain/business/domain/ErpSettlementMaintain.java @@ -71,9 +71,13 @@ public class ErpSettlementMaintain extends BaseEntity { */ private Date settlementTime; /** - * 支付方式(1支付宝 2微信) + * 支付方式(1扫码支付 2手动支付) */ private Integer paymentMethod; + /** + * 支付单号 + */ + private String paymentNumber; /** * 付款账号 */ @@ -111,7 +115,7 @@ public class ErpSettlementMaintain extends BaseEntity { */ private String collectionAccount; /** - * 收款流水号 + * 交易流水号 */ private String collectionSerialNumber; /** diff --git a/maintain-business/src/main/java/com/maintain/business/domain/bo/ErpSettlementMaintainBo.java b/maintain-business/src/main/java/com/maintain/business/domain/bo/ErpSettlementMaintainBo.java index 2242dcd13ed443b62aa1e7f78cc27196e51487c8..07b39a2e60762dae26ed4220f3eb18c3b16f4a2b 100644 --- a/maintain-business/src/main/java/com/maintain/business/domain/bo/ErpSettlementMaintainBo.java +++ b/maintain-business/src/main/java/com/maintain/business/domain/bo/ErpSettlementMaintainBo.java @@ -90,7 +90,6 @@ public class ErpSettlementMaintainBo extends BaseEntity { /** * 支付方式(1支付宝 2微信) */ - @NotNull(message = "支付方式(1支付宝 2微信)不能为空", groups = { AddGroup.class, EditGroup.class }) private Integer paymentMethod; /** @@ -106,7 +105,6 @@ public class ErpSettlementMaintainBo extends BaseEntity { /** * 付款凭证 */ - @NotBlank(message = "付款凭证不能为空", groups = { AddGroup.class, EditGroup.class }) private String paymentVoucher; /** diff --git a/maintain-business/src/main/java/com/maintain/business/domain/bo/ErpSettlementMaintainPayBo.java b/maintain-business/src/main/java/com/maintain/business/domain/bo/ErpSettlementMaintainPayBo.java new file mode 100644 index 0000000000000000000000000000000000000000..3d779173a82982f560a8ee407990833a7d5fc9ae --- /dev/null +++ b/maintain-business/src/main/java/com/maintain/business/domain/bo/ErpSettlementMaintainPayBo.java @@ -0,0 +1,34 @@ +package com.maintain.business.domain.bo; + +import com.maintain.common.core.domain.BaseEntity; +import com.maintain.common.core.validate.AddGroup; +import com.maintain.common.core.validate.EditGroup; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; + +/** + * 结算维修单业务对象 erp_settlement_maintain + * + * @author liushuai + * @date 2025-04-03 + */ + +@Data +@EqualsAndHashCode(callSuper = true) +public class ErpSettlementMaintainPayBo extends BaseEntity { + + /** + * 结算单ID + */ + @NotNull(message = "结算单ID不能为空", groups = { AddGroup.class, EditGroup.class }) + private Long id; + + /** + * 付款码 + */ + @NotBlank(message = "付款码不能为空", groups = { AddGroup.class, EditGroup.class }) + private String paymentAccount; +} diff --git a/maintain-business/src/main/java/com/maintain/business/service/IErpSettlementMaintainService.java b/maintain-business/src/main/java/com/maintain/business/service/IErpSettlementMaintainService.java index 4cfec9f48ed26aebe06740180bda501655019bbd..fb13e1f22083a904c254f762e5f48982ffa30934 100644 --- a/maintain-business/src/main/java/com/maintain/business/service/IErpSettlementMaintainService.java +++ b/maintain-business/src/main/java/com/maintain/business/service/IErpSettlementMaintainService.java @@ -1,10 +1,10 @@ package com.maintain.business.service; -import com.maintain.business.domain.ErpSettlementMaintain; -import com.maintain.business.domain.vo.ErpSettlementMaintainVo; import com.maintain.business.domain.bo.ErpSettlementMaintainBo; -import com.maintain.common.core.page.TableDataInfo; +import com.maintain.business.domain.bo.ErpSettlementMaintainPayBo; +import com.maintain.business.domain.vo.ErpSettlementMaintainVo; import com.maintain.common.core.domain.PageQuery; +import com.maintain.common.core.page.TableDataInfo; import java.util.Collection; import java.util.List; @@ -53,4 +53,11 @@ public interface IErpSettlementMaintainService { * @return */ Boolean receiptByBo(ErpSettlementMaintainBo bo); + + /** + * 结算维修单支付 + * @param bo + * @return + */ + Boolean pay(ErpSettlementMaintainPayBo bo); } diff --git a/maintain-business/src/main/java/com/maintain/business/service/impl/ErpSettlementMaintainServiceImpl.java b/maintain-business/src/main/java/com/maintain/business/service/impl/ErpSettlementMaintainServiceImpl.java index 76592729d5368958b7fabe7d4da2f0a57990f848..3172de73e81da46bedc7b1d7faa93da3a94e0344 100644 --- a/maintain-business/src/main/java/com/maintain/business/service/impl/ErpSettlementMaintainServiceImpl.java +++ b/maintain-business/src/main/java/com/maintain/business/service/impl/ErpSettlementMaintainServiceImpl.java @@ -1,38 +1,42 @@ package com.maintain.business.service.impl; import cn.hutool.core.bean.BeanUtil; -import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.maintain.business.domain.ErpRepairForm; +import com.maintain.business.domain.ErpSettlementMaintain; import com.maintain.business.domain.ErpSettlementMaintainRepairForm; -import com.maintain.business.domain.bo.ErpSettlementMaintainRepairFormBo; +import com.maintain.business.domain.bo.ErpSettlementMaintainBo; +import com.maintain.business.domain.bo.ErpSettlementMaintainPayBo; import com.maintain.business.domain.vo.ErpClientVo; +import com.maintain.business.domain.vo.ErpSettlementMaintainVo; import com.maintain.business.mapper.ErpClientMapper; import com.maintain.business.mapper.ErpRepairFormMapper; +import com.maintain.business.mapper.ErpSettlementMaintainMapper; import com.maintain.business.mapper.ErpSettlementMaintainRepairFormMapper; +import com.maintain.business.service.IErpSettlementMaintainService; +import com.maintain.common.core.domain.PageQuery; +import com.maintain.common.core.page.TableDataInfo; import com.maintain.common.enums.RepairFormSettlementState; import com.maintain.common.enums.SettlementState; +import com.maintain.common.exception.ServiceException; import com.maintain.common.helper.LoginHelper; +import com.maintain.common.pay.PayResponse; +import com.maintain.common.pay.YinLianPayApi; import com.maintain.common.utils.StringUtils; -import com.maintain.common.core.page.TableDataInfo; -import com.maintain.common.core.domain.PageQuery; -import com.baomidou.mybatisplus.extension.plugins.pagination.Page; -import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; -import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.maintain.common.utils.redis.RedisUtils; import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; -import com.maintain.business.domain.bo.ErpSettlementMaintainBo; -import com.maintain.business.domain.vo.ErpSettlementMaintainVo; -import com.maintain.business.domain.ErpSettlementMaintain; -import com.maintain.business.mapper.ErpSettlementMaintainMapper; -import com.maintain.business.service.IErpSettlementMaintainService; import org.springframework.transaction.annotation.Transactional; +import java.util.Collection; import java.util.Date; import java.util.List; import java.util.Map; -import java.util.Collection; import java.util.stream.Collectors; /** @@ -49,6 +53,9 @@ public class ErpSettlementMaintainServiceImpl implements IErpSettlementMaintainS private final ErpSettlementMaintainRepairFormMapper settlementMaintainRepairFormMapper; private final ErpRepairFormMapper repairFormMapper; private final ErpClientMapper clientMapper; + private final YinLianPayApi yinLianPayApi; + @Value("${pay.merchantCode}") + private String merchantCode; /** * 查询结算维修单 @@ -204,4 +211,39 @@ public class ErpSettlementMaintainServiceImpl implements IErpSettlementMaintainS } return flag; } + + /** + * 结算维修单支付 + * + * @param bo + * @return + */ + @Transactional(rollbackFor = Exception.class) + @Override + public Boolean pay(ErpSettlementMaintainPayBo bo) { + ErpSettlementMaintain maintain = baseMapper.selectById(bo.getId()); + + if (ObjectUtil.isEmpty(maintain)) { + throw new ServiceException("当前支付订单不存在,请刷新后重试!"); + } + if (maintain.getState().equals(SettlementState.TWO.getCode())) { + throw new ServiceException("当前结算单已支付,不能重复支付!"); + } + // 生成一个时间戳单号 + String orderNo = String.valueOf(System.currentTimeMillis()); + int money = maintain.getPracticalMoney().movePointRight(2).intValue(); + PayResponse response = yinLianPayApi.pay(orderNo, money, bo.getPaymentAccount()); + if (!response.getErrCode().equals("00")) { + throw new ServiceException("结算单支付失败:"+ response.getErrInfo()); + } + maintain.setCollectionAccount(merchantCode); + maintain.setCollectionSerialNumber(response.getOrderId()); + maintain.setCollectionTime(new Date()); + maintain.setState(SettlementState.TWO.getCode()); + maintain.setPaymentMethod(1); + maintain.setPaymentAccount(bo.getPaymentAccount()); + maintain.setPaymentNumber(orderNo); + baseMapper.updateById(maintain); + return true; + } } diff --git a/maintain-common/src/main/java/com/maintain/common/enums/SettlementState.java b/maintain-common/src/main/java/com/maintain/common/enums/SettlementState.java index 9d15573e716d2b477f514b416c903fcdedccb898..c83219d13da7e56c20cd60425288fd66505b89d7 100644 --- a/maintain-common/src/main/java/com/maintain/common/enums/SettlementState.java +++ b/maintain-common/src/main/java/com/maintain/common/enums/SettlementState.java @@ -10,8 +10,8 @@ import lombok.Getter; @Getter public enum SettlementState { - ONE(1, "待确认"), - TWO(2, "已确认"); + ONE(1, "待支付"), + TWO(2, "已支付"); private final Integer code; private final String info; diff --git a/maintain-common/src/main/java/com/maintain/common/pay/PayResponse.java b/maintain-common/src/main/java/com/maintain/common/pay/PayResponse.java new file mode 100644 index 0000000000000000000000000000000000000000..f077c2596a986849978f9aedd5b40d9b0acc8c78 --- /dev/null +++ b/maintain-common/src/main/java/com/maintain/common/pay/PayResponse.java @@ -0,0 +1,255 @@ +package com.maintain.common.pay; + +/** + * PayResponse + * + * @author Liu Shuai + * @date 2025/10/28 14:30 + */ +public class PayResponse { + + + /** + * errCode : 00 + * errInfo : 10000成功响应码 + * transactionTime : 140747 + * transactionDate : 1028 + * settlementDate : 1028 + * transactionDateWithYear : 20251028 + * settlementDateWithYear : 20251028 + * retrievalRefNum : 01479200019N + * transactionAmount : 1 + * actualTransactionAmount : 1 + * amount : 1 + * orderId : 20251028140747440012928716 + * thirdPartyDiscountInstrution : 微信钱包支付0.01元 + * thirdPartyDiscountInstruction : 微信钱包支付0.01元 + * thirdPartyName : 微信钱包 + * userId : oUpF8uMv_IaPg4f03FtZSQ7TiAiM + * thirdPartyBuyerId : oUpF8uMv_IaPg4f03FtZSQ7TiAiM + * thirdPartyOrderId : 4200002861202510289915715464 + * thirdPartyPayInformation : 现金:1 + * cardAttr : 91 + * mchntName : 测试商户 + * chnlType : WXPay + * promotionAmt : 0 + * systemTraceNum : 002841 + */ + + private String errCode; + private String errInfo; + private String transactionTime; + private String transactionDate; + private String settlementDate; + private String transactionDateWithYear; + private String settlementDateWithYear; + private String retrievalRefNum; + private int transactionAmount; + private int actualTransactionAmount; + private int amount; + private String orderId; + private String thirdPartyDiscountInstrution; + private String thirdPartyDiscountInstruction; + private String thirdPartyName; + private String userId; + private String thirdPartyBuyerId; + private String thirdPartyOrderId; + private String thirdPartyPayInformation; + private String cardAttr; + private String mchntName; + private String chnlType; + private String promotionAmt; + private String systemTraceNum; + + public String getErrCode() { + return errCode; + } + + public void setErrCode(String errCode) { + this.errCode = errCode; + } + + public String getErrInfo() { + return errInfo; + } + + public void setErrInfo(String errInfo) { + this.errInfo = errInfo; + } + + public String getTransactionTime() { + return transactionTime; + } + + public void setTransactionTime(String transactionTime) { + this.transactionTime = transactionTime; + } + + public String getTransactionDate() { + return transactionDate; + } + + public void setTransactionDate(String transactionDate) { + this.transactionDate = transactionDate; + } + + public String getSettlementDate() { + return settlementDate; + } + + public void setSettlementDate(String settlementDate) { + this.settlementDate = settlementDate; + } + + public String getTransactionDateWithYear() { + return transactionDateWithYear; + } + + public void setTransactionDateWithYear(String transactionDateWithYear) { + this.transactionDateWithYear = transactionDateWithYear; + } + + public String getSettlementDateWithYear() { + return settlementDateWithYear; + } + + public void setSettlementDateWithYear(String settlementDateWithYear) { + this.settlementDateWithYear = settlementDateWithYear; + } + + public String getRetrievalRefNum() { + return retrievalRefNum; + } + + public void setRetrievalRefNum(String retrievalRefNum) { + this.retrievalRefNum = retrievalRefNum; + } + + public int getTransactionAmount() { + return transactionAmount; + } + + public void setTransactionAmount(int transactionAmount) { + this.transactionAmount = transactionAmount; + } + + public int getActualTransactionAmount() { + return actualTransactionAmount; + } + + public void setActualTransactionAmount(int actualTransactionAmount) { + this.actualTransactionAmount = actualTransactionAmount; + } + + public int getAmount() { + return amount; + } + + public void setAmount(int amount) { + this.amount = amount; + } + + public String getOrderId() { + return orderId; + } + + public void setOrderId(String orderId) { + this.orderId = orderId; + } + + public String getThirdPartyDiscountInstrution() { + return thirdPartyDiscountInstrution; + } + + public void setThirdPartyDiscountInstrution(String thirdPartyDiscountInstrution) { + this.thirdPartyDiscountInstrution = thirdPartyDiscountInstrution; + } + + public String getThirdPartyDiscountInstruction() { + return thirdPartyDiscountInstruction; + } + + public void setThirdPartyDiscountInstruction(String thirdPartyDiscountInstruction) { + this.thirdPartyDiscountInstruction = thirdPartyDiscountInstruction; + } + + public String getThirdPartyName() { + return thirdPartyName; + } + + public void setThirdPartyName(String thirdPartyName) { + this.thirdPartyName = thirdPartyName; + } + + public String getUserId() { + return userId; + } + + public void setUserId(String userId) { + this.userId = userId; + } + + public String getThirdPartyBuyerId() { + return thirdPartyBuyerId; + } + + public void setThirdPartyBuyerId(String thirdPartyBuyerId) { + this.thirdPartyBuyerId = thirdPartyBuyerId; + } + + public String getThirdPartyOrderId() { + return thirdPartyOrderId; + } + + public void setThirdPartyOrderId(String thirdPartyOrderId) { + this.thirdPartyOrderId = thirdPartyOrderId; + } + + public String getThirdPartyPayInformation() { + return thirdPartyPayInformation; + } + + public void setThirdPartyPayInformation(String thirdPartyPayInformation) { + this.thirdPartyPayInformation = thirdPartyPayInformation; + } + + public String getCardAttr() { + return cardAttr; + } + + public void setCardAttr(String cardAttr) { + this.cardAttr = cardAttr; + } + + public String getMchntName() { + return mchntName; + } + + public void setMchntName(String mchntName) { + this.mchntName = mchntName; + } + + public String getChnlType() { + return chnlType; + } + + public void setChnlType(String chnlType) { + this.chnlType = chnlType; + } + + public String getPromotionAmt() { + return promotionAmt; + } + + public void setPromotionAmt(String promotionAmt) { + this.promotionAmt = promotionAmt; + } + + public String getSystemTraceNum() { + return systemTraceNum; + } + + public void setSystemTraceNum(String systemTraceNum) { + this.systemTraceNum = systemTraceNum; + } +} diff --git a/maintain-common/src/main/java/com/maintain/common/pay/YinLianPayApi.java b/maintain-common/src/main/java/com/maintain/common/pay/YinLianPayApi.java new file mode 100644 index 0000000000000000000000000000000000000000..d74af4c8f9f219295914062335d4ea8f9f5ff8b0 --- /dev/null +++ b/maintain-common/src/main/java/com/maintain/common/pay/YinLianPayApi.java @@ -0,0 +1,410 @@ +package com.maintain.common.pay; + +import cn.hutool.json.JSONUtil; +import org.apache.commons.codec.binary.Base64; +import org.apache.commons.codec.digest.DigestUtils; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; +import java.io.*; +import java.net.HttpURLConnection; +import java.net.URL; +import java.net.URLConnection; +import java.text.SimpleDateFormat; +import java.util.*; + +/** + * 银联支付API + */ +@Component +public class YinLianPayApi { + + @Value("${pay.appid}") + private String appid; + @Value("${pay.appkey}") + private String appkey; + @Value("${pay.url}") + private String url; + @Value("${pay.merchantCode}") + private String merchantCode; + @Value("${pay.terminalCode}") + private String terminalCode; + @Value("${pay.deviceType}") + private String deviceType; + @Value("${pay.payMode}") + private String payMode; + + public static void main(String[] args) throws Exception { + // 1. 组建请求报文 + PayBody reqBody = new PayBody(); + reqBody.merchantCode = "123456789111115"; + reqBody.terminalCode = "gxrb0001"; + reqBody.transactionAmount = 1; + reqBody.transactionCurrencyCode = "156"; + reqBody.merchantOrderId = "121423534643"; + reqBody.merchantRemark = "remark"; + reqBody.payMode = "CODE_SCAN"; + reqBody.payCode = "134183932097088249"; + reqBody.deviceType = "11"; + + String requestBody = reqBody.toString(); + System.out.println("请求参数:\n" + requestBody); + + // 2. 获取认证报文 + String timestamp = getCurrentTimestamp14(); + //2. 获取认证报文,timestamp为当前日期,老旧日期无法请求成功 + String authorization = getAuthorization("10037e6f72b586db0172b6ea24ec0000","6f7a00170935445fbe25e67a5ce1be48",timestamp,"nonce",reqBody.toString()); + System.out.println("认证报文:\n"+authorization); + + //3. 发送http请求,并解析返回信息 + String response = request("https://test-api-open.chinaums.com/v6/poslink/transaction/pay",authorization,reqBody.toString()); + System.out.println("响应参数:\n"+response); + PayResponse payResponse = JSONUtil.toBean(response, PayResponse.class); + System.out.println(payResponse); + } + + /** + * 发起支付 + * @param merchantOrderId 订单号 + * @param transactionAmount 交易金额 + * @param payCode 支付码 + * @return 支付结果 + */ + public PayResponse pay(String merchantOrderId,int transactionAmount, String payCode) { + // 组建请求报文 + PayBody reqBody = new PayBody(); + reqBody.merchantCode = merchantCode; + reqBody.terminalCode = terminalCode; + // 交易金额 + reqBody.transactionAmount = transactionAmount; + // 交易币种 + reqBody.transactionCurrencyCode = "156"; + // 订单号 + reqBody.merchantOrderId = merchantOrderId; + reqBody.merchantRemark = "remark"; + // 支付方式(E_CASH – 电子现金、SOUNDWAVE – 声波、NFC – NFC、CODE_SCAN – 扫码、MANUAL – 手输、FACE_SCAN – 扫脸) + reqBody.payMode = payMode; + // 支付码 + reqBody.payCode = payCode; + // 设备类型固定11 + reqBody.deviceType = deviceType; + String requestBody = reqBody.toString(); + System.out.println("请求参数:\n" + requestBody); + // 获取认证报文,timestamp为当前日期 + String timestamp = getCurrentTimestamp14(); + String authorization; + try { + authorization = getAuthorization(appid,appkey,timestamp,"nonce",reqBody.toString()); + System.out.println("认证报文:\n"+authorization); + }catch (Exception e){ + throw new RuntimeException("获取认证报文失败,请检查!"); + } + // 发送http请求,并解析返回信息 + String response = request(url,authorization,reqBody.toString()); + System.out.println("响应参数:\n"+response); + return JSONUtil.toBean(response, PayResponse.class); + } + + /** + * 获取14位时间戳 (yyyyMMddHHmmss) + */ + static String getCurrentTimestamp14() { + SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss"); + sdf.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai")); // 设置为北京时区 + return sdf.format(new Date()); + } + + /** + * 发送http请求 - 增强错误处理 + */ + static String request(String url, String authorization, String reqBody) { + StringBuilder response = new StringBuilder(); + PrintWriter out = null; + BufferedReader in = null; + HttpURLConnection httpUrlConnection = null; + + try { + URL realUrl = new URL(url); + httpUrlConnection = (HttpURLConnection) realUrl.openConnection(); + + // 设置请求参数 + httpUrlConnection.setRequestMethod("POST"); + httpUrlConnection.setRequestProperty("Content-Type", "application/json; charset=utf-8"); + httpUrlConnection.setRequestProperty("authorization", authorization); + httpUrlConnection.setRequestProperty("User-Agent", "Mozilla/5.0"); + httpUrlConnection.setConnectTimeout(30000); + httpUrlConnection.setReadTimeout(30000); + httpUrlConnection.setDoOutput(true); + httpUrlConnection.setDoInput(true); + + // 发送请求体 + out = new PrintWriter(new OutputStreamWriter(httpUrlConnection.getOutputStream(), "UTF-8")); + out.write(reqBody); + out.flush(); + + // 获取响应状态 + int responseCode = httpUrlConnection.getResponseCode(); + System.out.println("HTTP响应码: " + responseCode); + + // 读取响应 + if (responseCode == HttpURLConnection.HTTP_OK) { + in = new BufferedReader(new InputStreamReader(httpUrlConnection.getInputStream(), "UTF-8")); + } else { + // 读取错误流 - 这是关键! + in = new BufferedReader(new InputStreamReader(httpUrlConnection.getErrorStream(), "UTF-8")); + } + + String line; + while ((line = in.readLine()) != null) { + response.append(line); + } + + // 记录错误详情 + if (responseCode != HttpURLConnection.HTTP_OK) { + System.err.println("HTTP错误: " + responseCode); + System.err.println("错误响应内容: " + response.toString()); + } + + } catch (Exception e) { + System.err.println("请求异常: " + e.getMessage()); + e.printStackTrace(); + return "HTTP请求异常: " + e.getMessage(); + } finally { + try { + if (out != null) out.close(); + if (in != null) in.close(); + if (httpUrlConnection != null) httpUrlConnection.disconnect(); + } catch (Exception ex) { + ex.printStackTrace(); + } + } + + return response.toString(); + } + + /** + * 获取签名头 + * @param appid + * @param appkey + * @param timestamp 格式:"yyyyMMddHHmmss" + * @param nonce 随机字符串, + * @param body 请求体 + * @return authorization 认证报文 + * @throws Exception + */ + static String getAuthorization(String appid, String appkey, String timestamp, String nonce, String body) throws Exception { + System.out.println("appid:\n"+appid); + System.out.println("appkey:\n"+appkey); + byte[] data = body.getBytes("utf-8"); + InputStream is = new ByteArrayInputStream(data); + String testSH = DigestUtils.sha256Hex(is); + String s1 = appid+timestamp+nonce+testSH; + Mac mac = Mac.getInstance("HmacSHA256"); + mac.init(new SecretKeySpec(appkey.getBytes("utf-8"),"HmacSHA256")); + byte[] bytes = s1.getBytes("utf-8"); + byte[] localSignature = mac.doFinal(bytes); + String localSignatureStr = Base64.encodeBase64String(localSignature); + return "OPEN-BODY-SIG AppId="+"\""+appid+"\""+", Timestamp="+"\""+timestamp+"\""+", Nonce="+"\""+nonce+"\""+", Signature="+"\""+localSignatureStr+"\""; + } + + static class PayBody { + // 商户号 + String merchantCode; + // 终端号 + String terminalCode; + // 交易金额 + int transactionAmount; + // 交易币种 + String transactionCurrencyCode; + // 商户订单号 + String merchantOrderId; + // 商户备注 + String merchantRemark; + // 支付方式 + String payMode; + // 支付码 + String payCode; + // 设备类型 + String deviceType; + // 终端硬件序列号 + String serialNum; + // 加密随机因子 + String encryptRandNum; + // 硬件序列号密文数据 + String secretText; + // 经度 + String longitude; + // 纬度 + String latitude; + // 商品信息 + List goods; + // 商户冗余信息 + String srcReserved; + // 门店号 + String storeId; + // 是否限制信用卡 + String limitCreditCard; + // 操作员编号 + String operatorId; + // 业务标识 + String bizIdentifier; + // 商品标识 + String goodsTag; + + String toJson() { + StringBuilder sb = new StringBuilder(); + sb.append("{"); + + appendField(sb, "merchantCode", merchantCode); + appendField(sb, "terminalCode", terminalCode); + appendField(sb, "transactionAmount", transactionAmount, transactionAmount != 0); + appendField(sb, "transactionCurrencyCode", transactionCurrencyCode); + appendField(sb, "merchantOrderId", merchantOrderId); + appendField(sb, "merchantRemark", merchantRemark); + appendField(sb, "payMode", payMode); + appendField(sb, "payCode", payCode); + appendField(sb, "deviceType", deviceType); + appendField(sb, "serialNum", serialNum); + appendField(sb, "encryptRandNum", encryptRandNum); + appendField(sb, "secretText", secretText); + appendField(sb, "longitude", longitude); + appendField(sb, "latitude", latitude); + + // 处理商品列表 + if (goods != null && !goods.isEmpty()) { + if (sb.charAt(sb.length() - 1) != '{') { + sb.append(","); + } + sb.append("\"goods\":["); + for (int i = 0; i < goods.size(); i++) { + if (i > 0) { + sb.append(","); + } + sb.append(goods.get(i).toJson()); + } + sb.append("]"); + } + + appendField(sb, "srcReserved", srcReserved); + appendField(sb, "storeId", storeId); + appendField(sb, "limitCreditCard", limitCreditCard); + appendField(sb, "operatorId", operatorId); + appendField(sb, "bizIdentifier", bizIdentifier); + appendField(sb, "goodsTag", goodsTag); + + // 移除末尾多余的逗号 + if (sb.charAt(sb.length() - 1) == ',') { + sb.deleteCharAt(sb.length() - 1); + } + + sb.append("}"); + return sb.toString(); + } + + // 辅助方法:添加字符串字段 + private void appendField(StringBuilder sb, String fieldName, String value) { + appendField(sb, fieldName, value, value != null); + } + + // 辅助方法:添加整数字段 + private void appendField(StringBuilder sb, String fieldName, int value, boolean condition) { + if (condition) { + if (sb.charAt(sb.length() - 1) != '{') { + sb.append(","); + } + sb.append("\"").append(fieldName).append("\":").append(value); + } + } + + // 辅助方法:添加字符串字段(带条件) + private void appendField(StringBuilder sb, String fieldName, String value, boolean condition) { + if (condition) { + if (sb.charAt(sb.length() - 1) != '{') { + sb.append(","); + } + sb.append("\"").append(fieldName).append("\":\"").append(escapeJson(value)).append("\""); + } + } + + // 转义JSON特殊字符 + private String escapeJson(String value) { + if (value == null) return ""; + return value.replace("\\", "\\\\") + .replace("\"", "\\\"") + .replace("\b", "\\b") + .replace("\f", "\\f") + .replace("\n", "\\n") + .replace("\r", "\\r") + .replace("\t", "\\t"); + } + + public String toString() { + return this.toJson(); + } + + static class GoodsItem { + // 商品ID + String goodsId; + // 商品名称 + String goodsName; + // 商品数量 + String quantity; + // 商品单价(分) + String price; + // 商品分类 + String goodsCategory; + // 商品说明 + String body; + // 商品折扣 + String discount; + + String toJson() { + StringBuilder sb = new StringBuilder(); + sb.append("{"); + + appendGoodsField(sb, "goodsId", goodsId); + appendGoodsField(sb, "goodsName", goodsName); + appendGoodsField(sb, "quantity", quantity); + appendGoodsField(sb, "price", price); + appendGoodsField(sb, "goodsCategory", goodsCategory); + appendGoodsField(sb, "body", body); + appendGoodsField(sb, "discount", discount); + + // 移除末尾多余的逗号 + if (sb.charAt(sb.length() - 1) == ',') { + sb.deleteCharAt(sb.length() - 1); + } + + sb.append("}"); + return sb.toString(); + } + + private void appendGoodsField(StringBuilder sb, String fieldName, String value) { + if (value != null) { + if (sb.charAt(sb.length() - 1) != '{') { + sb.append(","); + } + sb.append("\"").append(fieldName).append("\":\"").append(escapeJson(value)).append("\""); + } + } + + // 转义JSON特殊字符 + private String escapeJson(String value) { + if (value == null) return ""; + return value.replace("\\", "\\\\") + .replace("\"", "\\\"") + .replace("\b", "\\b") + .replace("\f", "\\f") + .replace("\n", "\\n") + .replace("\r", "\\r") + .replace("\t", "\\t"); + } + + public String toString() { + return this.toJson(); + } + } + } +} diff --git a/maintain-ui/src/api/business/settlementMaintain.js b/maintain-ui/src/api/business/settlementMaintain.js index 331e05777fce4c9a0569a0f66cba76dbdf2b97fa..a2116c6554c80f636753b54c3850469beebe3285 100644 --- a/maintain-ui/src/api/business/settlementMaintain.js +++ b/maintain-ui/src/api/business/settlementMaintain.js @@ -51,3 +51,13 @@ export function receiptSettlementMaintain(data) { data: data }) } + +// 结算维修单回执 +export function paySettlementMaintain(data) { + return request({ + url: '/business/settlementMaintain/pay', + method: 'put', + data: data, + timeout: 30000 + }) +} diff --git a/maintain-ui/src/views/business/settlementConfirm/index.vue b/maintain-ui/src/views/business/settlementConfirm/index.vue index 712e16429105e21df4f024182364337b5664ff90..f8162eb4fd3f2decde4a36360485a287910da84a 100644 --- a/maintain-ui/src/views/business/settlementConfirm/index.vue +++ b/maintain-ui/src/views/business/settlementConfirm/index.vue @@ -58,6 +58,8 @@ + +