Commit 03d5462b authored by 刘帅's avatar 刘帅

1.接入银联支付

parent 86b9122f
...@@ -180,3 +180,11 @@ sms: ...@@ -180,3 +180,11 @@ sms:
sdkAppId: appid sdkAppId: appid
#地域信息默认为 ap-guangzhou 如无特殊改变可不用设置 #地域信息默认为 ap-guangzhou 如无特殊改变可不用设置
territory: 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
...@@ -183,3 +183,11 @@ sms: ...@@ -183,3 +183,11 @@ sms:
sdkAppId: appid sdkAppId: appid
#地域信息默认为 ap-guangzhou 如无特殊改变可不用设置 #地域信息默认为 ap-guangzhou 如无特殊改变可不用设置
territory: 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
...@@ -5,6 +5,7 @@ import java.util.List; ...@@ -5,6 +5,7 @@ import java.util.List;
import java.util.Arrays; import java.util.Arrays;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import com.maintain.business.domain.bo.ErpSettlementMaintainPayBo;
import com.maintain.business.domain.bo.ErpSettlementMaintainRepairFormBo; import com.maintain.business.domain.bo.ErpSettlementMaintainRepairFormBo;
import com.maintain.business.domain.vo.ErpSettlementMaintainRepairFormVo; import com.maintain.business.domain.vo.ErpSettlementMaintainRepairFormVo;
import com.maintain.business.service.IErpRepairFormService; import com.maintain.business.service.IErpRepairFormService;
...@@ -137,4 +138,16 @@ public class ErpSettlementMaintainController extends BaseController { ...@@ -137,4 +138,16 @@ public class ErpSettlementMaintainController extends BaseController {
public R<Void> receipt(@Validated(EditGroup.class) @RequestBody ErpSettlementMaintainBo bo) { public R<Void> receipt(@Validated(EditGroup.class) @RequestBody ErpSettlementMaintainBo bo) {
return toAjax(iErpSettlementMaintainService.receiptByBo(bo)); return toAjax(iErpSettlementMaintainService.receiptByBo(bo));
} }
/**
* 结算维修单回执
*
* @param bo
*/
@Log(title = "结算维修单支付", businessType = BusinessType.UPDATE)
@PutMapping("/pay")
public R<Void> pay(@Validated(EditGroup.class) @RequestBody ErpSettlementMaintainPayBo bo) {
return toAjax(iErpSettlementMaintainService.pay(bo));
}
} }
...@@ -71,9 +71,13 @@ public class ErpSettlementMaintain extends BaseEntity { ...@@ -71,9 +71,13 @@ public class ErpSettlementMaintain extends BaseEntity {
*/ */
private Date settlementTime; private Date settlementTime;
/** /**
* 支付方式(1支付宝 2微信 * 支付方式(1扫码支付 2手动支付
*/ */
private Integer paymentMethod; private Integer paymentMethod;
/**
* 支付单号
*/
private String paymentNumber;
/** /**
* 付款账号 * 付款账号
*/ */
...@@ -111,7 +115,7 @@ public class ErpSettlementMaintain extends BaseEntity { ...@@ -111,7 +115,7 @@ public class ErpSettlementMaintain extends BaseEntity {
*/ */
private String collectionAccount; private String collectionAccount;
/** /**
* 收款流水号 * 交易流水号
*/ */
private String collectionSerialNumber; private String collectionSerialNumber;
/** /**
......
...@@ -90,7 +90,6 @@ public class ErpSettlementMaintainBo extends BaseEntity { ...@@ -90,7 +90,6 @@ public class ErpSettlementMaintainBo extends BaseEntity {
/** /**
* 支付方式(1支付宝 2微信) * 支付方式(1支付宝 2微信)
*/ */
@NotNull(message = "支付方式(1支付宝 2微信)不能为空", groups = { AddGroup.class, EditGroup.class })
private Integer paymentMethod; private Integer paymentMethod;
/** /**
...@@ -106,7 +105,6 @@ public class ErpSettlementMaintainBo extends BaseEntity { ...@@ -106,7 +105,6 @@ public class ErpSettlementMaintainBo extends BaseEntity {
/** /**
* 付款凭证 * 付款凭证
*/ */
@NotBlank(message = "付款凭证不能为空", groups = { AddGroup.class, EditGroup.class })
private String paymentVoucher; private String paymentVoucher;
/** /**
......
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;
}
package com.maintain.business.service; 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.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.domain.PageQuery;
import com.maintain.common.core.page.TableDataInfo;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
...@@ -53,4 +53,11 @@ public interface IErpSettlementMaintainService { ...@@ -53,4 +53,11 @@ public interface IErpSettlementMaintainService {
* @return * @return
*/ */
Boolean receiptByBo(ErpSettlementMaintainBo bo); Boolean receiptByBo(ErpSettlementMaintainBo bo);
/**
* 结算维修单支付
* @param bo
* @return
*/
Boolean pay(ErpSettlementMaintainPayBo bo);
} }
package com.maintain.business.service.impl; package com.maintain.business.service.impl;
import cn.hutool.core.bean.BeanUtil; 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.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.ErpRepairForm;
import com.maintain.business.domain.ErpSettlementMaintain;
import com.maintain.business.domain.ErpSettlementMaintainRepairForm; 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.ErpClientVo;
import com.maintain.business.domain.vo.ErpSettlementMaintainVo;
import com.maintain.business.mapper.ErpClientMapper; import com.maintain.business.mapper.ErpClientMapper;
import com.maintain.business.mapper.ErpRepairFormMapper; import com.maintain.business.mapper.ErpRepairFormMapper;
import com.maintain.business.mapper.ErpSettlementMaintainMapper;
import com.maintain.business.mapper.ErpSettlementMaintainRepairFormMapper; 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.RepairFormSettlementState;
import com.maintain.common.enums.SettlementState; import com.maintain.common.enums.SettlementState;
import com.maintain.common.exception.ServiceException;
import com.maintain.common.helper.LoginHelper; 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.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 com.maintain.common.utils.redis.RedisUtils;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service; 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 org.springframework.transaction.annotation.Transactional;
import java.util.Collection;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Collection;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/** /**
...@@ -49,6 +53,9 @@ public class ErpSettlementMaintainServiceImpl implements IErpSettlementMaintainS ...@@ -49,6 +53,9 @@ public class ErpSettlementMaintainServiceImpl implements IErpSettlementMaintainS
private final ErpSettlementMaintainRepairFormMapper settlementMaintainRepairFormMapper; private final ErpSettlementMaintainRepairFormMapper settlementMaintainRepairFormMapper;
private final ErpRepairFormMapper repairFormMapper; private final ErpRepairFormMapper repairFormMapper;
private final ErpClientMapper clientMapper; private final ErpClientMapper clientMapper;
private final YinLianPayApi yinLianPayApi;
@Value("${pay.merchantCode}")
private String merchantCode;
/** /**
* 查询结算维修单 * 查询结算维修单
...@@ -204,4 +211,39 @@ public class ErpSettlementMaintainServiceImpl implements IErpSettlementMaintainS ...@@ -204,4 +211,39 @@ public class ErpSettlementMaintainServiceImpl implements IErpSettlementMaintainS
} }
return flag; 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;
}
} }
...@@ -10,8 +10,8 @@ import lombok.Getter; ...@@ -10,8 +10,8 @@ import lombok.Getter;
@Getter @Getter
public enum SettlementState { public enum SettlementState {
ONE(1, "待确认"), ONE(1, "待支付"),
TWO(2, "已确认"); TWO(2, "已支付");
private final Integer code; private final Integer code;
private final String info; private final String info;
......
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;
}
}
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<GoodsItem> 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();
}
}
}
}
...@@ -51,3 +51,13 @@ export function receiptSettlementMaintain(data) { ...@@ -51,3 +51,13 @@ export function receiptSettlementMaintain(data) {
data: data data: data
}) })
} }
// 结算维修单回执
export function paySettlementMaintain(data) {
return request({
url: '/business/settlementMaintain/pay',
method: 'put',
data: data,
timeout: 30000
})
}
...@@ -58,6 +58,8 @@ ...@@ -58,6 +58,8 @@
<el-table-column label="证件号" align="center" prop="clientCertificateNumber" width="100px" show-overflow-tooltip/> <el-table-column label="证件号" align="center" prop="clientCertificateNumber" width="100px" show-overflow-tooltip/>
<el-table-column label="工时费优惠" align="center" prop="manHourDiscountMoney" /> <el-table-column label="工时费优惠" align="center" prop="manHourDiscountMoney" />
<el-table-column label="原料费优惠" align="center" prop="materialDiscountMoney" /> <el-table-column label="原料费优惠" align="center" prop="materialDiscountMoney" />
<el-table-column label="应收金额" align="center" prop="receivableMoney" />
<el-table-column label="结算金额" align="center" prop="practicalMoney" />
<el-table-column label="结算时间" align="center" prop="settlementTime" width="120"> <el-table-column label="结算时间" align="center" prop="settlementTime" width="120">
<template slot-scope="scope"> <template slot-scope="scope">
<span>{{ parseTime(scope.row.settlementTime, '{y}-{m}-{d}') }}</span> <span>{{ parseTime(scope.row.settlementTime, '{y}-{m}-{d}') }}</span>
...@@ -281,8 +283,8 @@ ...@@ -281,8 +283,8 @@
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="6"> <el-col :span="6">
<el-form-item label="收款流水号" prop="collectionSerialNumber"> <el-form-item label="交易流水号" prop="collectionSerialNumber">
<el-input v-model="form.collectionSerialNumber" placeholder="请输入收款流水号" /> <el-input v-model="form.collectionSerialNumber" placeholder="请输入交易流水号" />
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row> </el-row>
...@@ -303,11 +305,25 @@ ...@@ -303,11 +305,25 @@
</el-form> </el-form>
</el-scrollbar> </el-scrollbar>
<div slot="footer" class="dialog-footer"> <div slot="footer" class="dialog-footer">
<el-button :loading="buttonLoading" type="primary" @click="qrOpen">扫码支付</el-button>
<el-button :loading="buttonLoading" type="primary" @click="submitForm"> </el-button> <el-button :loading="buttonLoading" type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button> <el-button @click="cancel"> </el-button>
</div> </div>
</el-dialog> </el-dialog>
<!-- 扫码支付对话框 -->
<el-dialog :title="title" :visible.sync="open2" width="500px" append-to-body>
<el-form ref="payForm" :model="payForm" :rules="payRules" label-width="80px">
<el-form-item label="支付码" prop="paymentAccount">
<el-input ref="payCodeInput" v-model="payForm.paymentAccount" placeholder="使用设备扫描条形码" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button :loading="payButtonLoading" type="primary" @click="paySubmitForm">确认支付</el-button>
<el-button :loading="payButtonLoading" @click="open2 = false"> </el-button>
</div>
</el-dialog>
<!-- 详情对话框 --> <!-- 详情对话框 -->
<el-dialog :title="title" :visible.sync="open1" width="80%" append-to-body> <el-dialog :title="title" :visible.sync="open1" width="80%" append-to-body>
<el-scrollbar style="height: 75vh;"> <el-scrollbar style="height: 75vh;">
...@@ -392,7 +408,7 @@ ...@@ -392,7 +408,7 @@
import { import {
delSettlementMaintain, delSettlementMaintain,
getSettlementMaintain, getSettlementMaintain,
listSettlementMaintain, listSettlementMaintain, paySettlementMaintain,
receiptSettlementMaintain receiptSettlementMaintain
} from '@/api/business/settlementMaintain' } from '@/api/business/settlementMaintain'
...@@ -403,6 +419,7 @@ export default { ...@@ -403,6 +419,7 @@ export default {
return { return {
// 按钮loading // 按钮loading
buttonLoading: false, buttonLoading: false,
payButtonLoading: false,
// 遮罩层 // 遮罩层
loading: true, loading: true,
// 选中数组 // 选中数组
...@@ -422,6 +439,7 @@ export default { ...@@ -422,6 +439,7 @@ export default {
// 是否显示弹出层 // 是否显示弹出层
open: false, open: false,
open1: false, open1: false,
open2: false,
// 查询参数 // 查询参数
queryParams: { queryParams: {
pageNum: 1, pageNum: 1,
...@@ -433,6 +451,10 @@ export default { ...@@ -433,6 +451,10 @@ export default {
}, },
// 表单参数 // 表单参数
form: {}, form: {},
payForm: {
id: undefined,
paymentAccount: undefined,
},
// 表单校验 // 表单校验
rules: { rules: {
collectionTime: [ collectionTime: [
...@@ -444,8 +466,13 @@ export default { ...@@ -444,8 +466,13 @@ export default {
collectionSerialNumber: [ collectionSerialNumber: [
{ required: true, message: "收款流水号不能为空", trigger: "blur" } { required: true, message: "收款流水号不能为空", trigger: "blur" }
], ],
collectionVoucher: [ // collectionVoucher: [
{ required: true, message: "收款回执不能为空", trigger: "change" } // { required: true, message: "收款回执不能为空", trigger: "change" }
// ],
},
payRules: {
paymentAccount: [
{ required: true, message: "支付码不能为空", trigger: "blur" }
], ],
} }
}; };
...@@ -526,6 +553,38 @@ export default { ...@@ -526,6 +553,38 @@ export default {
this.title = "结算维修单详情"; this.title = "结算维修单详情";
}); });
}, },
/** 扫码支付按钮操作 */
qrOpen() {
this.payForm = {
id: this.form.id,
paymentAccount: undefined,
}
this.open2 = true;
this.title = "扫码支付";
this.$nextTick(() => {
// 确保输入框存在并获取焦点
if (this.$refs.payCodeInput && this.$refs.payCodeInput.focus) {
this.$refs.payCodeInput.focus()
}
})
},
/** 确认支付按钮操作 */
paySubmitForm() {
this.$refs["payForm"].validate(valid => {
if (valid) {
this.payButtonLoading = true;
paySettlementMaintain(this.payForm).then(response => {
this.$modal.msgSuccess("支付成功");
this.open2 = false;
getSettlementMaintain(this.form.id).then(response => {
this.form = response.data;
});
}).finally(() => {
this.payButtonLoading = false;
});
}
});
},
/** 新增按钮操作 */ /** 新增按钮操作 */
handleAdd() { handleAdd() {
this.reset(); this.reset();
......
...@@ -119,13 +119,13 @@ ...@@ -119,13 +119,13 @@
</el-col> </el-col>
</el-row> </el-row>
<el-row> <el-row>
<el-col :span="6"> <!-- <el-col :span="6">-->
<el-form-item label="支付方式" prop="paymentMethod"> <!-- <el-form-item label="支付方式" prop="paymentMethod">-->
<el-select v-model="form.paymentMethod" placeholder="请选择支付方式" style="width: 100%"> <!-- <el-select v-model="form.paymentMethod" placeholder="请选择支付方式" style="width: 100%">-->
<el-option v-for="item in dict.type.settlement_payment_method" :key="item.value" :value="parseInt(item.value)" :label="item.label" /> <!-- <el-option v-for="item in dict.type.settlement_payment_method" :key="item.value" :value="parseInt(item.value)" :label="item.label" />-->
</el-select> <!-- </el-select>-->
</el-form-item> <!-- </el-form-item>-->
</el-col> <!-- </el-col>-->
<el-col :span="6"> <el-col :span="6">
<el-form-item label="结算时间" prop="settlementTime"> <el-form-item label="结算时间" prop="settlementTime">
<el-date-picker clearable <el-date-picker clearable
...@@ -280,9 +280,9 @@ export default { ...@@ -280,9 +280,9 @@ export default {
paymentAccountName: [ paymentAccountName: [
{ required: true, message: "账号名称不能为空", trigger: "blur" } { required: true, message: "账号名称不能为空", trigger: "blur" }
], ],
paymentVoucher: [ // paymentVoucher: [
{ required: true, message: "付款凭证不能为空", trigger: "blur" } // { required: true, message: "付款凭证不能为空", trigger: "blur" }
], // ],
}, },
}; };
}, },
...@@ -378,7 +378,7 @@ export default { ...@@ -378,7 +378,7 @@ export default {
this.buttonLoading = false; this.buttonLoading = false;
}).finally(() => { }).finally(() => {
this.buttonLoading = false; this.buttonLoading = false;
this.$tab.closeOpenPage({path: '/settlement/settlementMaintain?clientType=1'}) this.$tab.closeOpenPage({path: '/settlement/settlementMaintain?clientType=2'})
}); });
} }
}); });
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment