微信支付
This commit is contained in:
parent
39482d6b1d
commit
1567647be2
5
pom.xml
5
pom.xml
@ -98,6 +98,11 @@
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.wechatpay-apiv3</groupId>
|
||||
<artifactId>wechatpay-apache-httpclient</artifactId>
|
||||
<version>0.2.1</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
@ -0,0 +1,24 @@
|
||||
package club.joylink.rtss.controller.pay;
|
||||
|
||||
import club.joylink.rtss.services.pay.wechat.WechatPayService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/api/wechatPay")
|
||||
public class WechatPayController {
|
||||
|
||||
@Autowired
|
||||
private WechatPayService wechatPayService;
|
||||
|
||||
@PostMapping("/receive")
|
||||
public void receive(@RequestBody String data) {
|
||||
log.info(String.format("微信回调信息:%s", data));
|
||||
wechatPayService.receive(data);
|
||||
}
|
||||
}
|
@ -53,7 +53,10 @@ public enum BusinessExceptionAssertEnum implements BusinessExceptionAssert {
|
||||
WECHAT_CODE_EXPIRED(40029, "wechat code expired"),
|
||||
INVALID_CLIENT(40031, "invalid client"),
|
||||
INCORRECT_VERIFICATION_CODE(40051, "incorrect verification code"),
|
||||
THIRD_SERVICE_CALL_EXCEPTION(40071, "the third service call exception")
|
||||
THIRD_SERVICE_CALL_EXCEPTION(40071, "the third service call exception"),
|
||||
|
||||
//支付异常
|
||||
PAY_ERROR(50000, "pay error")
|
||||
;
|
||||
|
||||
int code;
|
||||
|
@ -0,0 +1,39 @@
|
||||
package club.joylink.rtss.services.pay.bean;
|
||||
|
||||
import club.joylink.rtss.vo.client.order.OrderDetailVO;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.time.OffsetDateTime;
|
||||
import java.util.List;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public class OrderPay {
|
||||
|
||||
/**
|
||||
* 订单编号*
|
||||
*/
|
||||
private String orderNo;
|
||||
|
||||
/**
|
||||
* 订单描述*
|
||||
*/
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* 订单总价*
|
||||
*/
|
||||
private Integer totalFee;
|
||||
|
||||
/**
|
||||
* 交易结束时间
|
||||
*/
|
||||
private OffsetDateTime timeExpire;
|
||||
|
||||
/**
|
||||
* 订单中包含的商品详情
|
||||
*/
|
||||
private List<OrderDetailVO> orderDetail;
|
||||
|
||||
}
|
@ -0,0 +1,129 @@
|
||||
package club.joylink.rtss.services.pay.wechat;
|
||||
|
||||
import club.joylink.rtss.exception.BusinessExceptionAssertEnum;
|
||||
import club.joylink.rtss.services.IOrderService;
|
||||
import club.joylink.rtss.services.pay.bean.OrderPay;
|
||||
import club.joylink.rtss.services.pay.wechat.bean.WxUnifiedOrder;
|
||||
import club.joylink.rtss.util.JsonUtils;
|
||||
import club.joylink.rtss.vo.client.order.OrderDetailVO;
|
||||
import club.joylink.rtss.websocket.StompMessageService;
|
||||
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
|
||||
import com.wechat.pay.contrib.apache.httpclient.auth.AutoUpdateCertificatesVerifier;
|
||||
import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;
|
||||
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Credentials;
|
||||
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Validator;
|
||||
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.entity.ContentType;
|
||||
import org.apache.http.entity.StringEntity;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.security.PrivateKey;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.util.List;
|
||||
|
||||
import static club.joylink.rtss.services.pay.wechat.constant.WechatConstants.*;
|
||||
|
||||
@Service
|
||||
@Slf4j
|
||||
public class WechatPayService {
|
||||
|
||||
@Autowired
|
||||
private IOrderService iorderService;
|
||||
|
||||
@Autowired
|
||||
private StompMessageService stompMessageService;
|
||||
|
||||
private CloseableHttpClient httpClient;
|
||||
|
||||
/**
|
||||
* 统一下单
|
||||
* @param orderPay
|
||||
* @return 支付二维码url
|
||||
* @throws IOException
|
||||
*/
|
||||
public String unifiedOrder(OrderPay orderPay) throws IOException {
|
||||
CloseableHttpResponse response = null;
|
||||
try {
|
||||
if (httpClient == null)
|
||||
buildHttpClient();
|
||||
//构建并发送请求
|
||||
HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/pay/transactions/native");
|
||||
httpPost.setHeader("Accept", "application/json");
|
||||
WxUnifiedOrder wxUnifiedOrder = new WxUnifiedOrder(orderPay, "http://2i38984j47.qicp.vip/api/wechatPay/receive");
|
||||
String json = JsonUtils.writeValueAsString(wxUnifiedOrder);
|
||||
StringEntity entity = new StringEntity(json, ContentType.APPLICATION_JSON);
|
||||
entity.setContentType("application/json");
|
||||
httpPost.setEntity(entity);
|
||||
response = httpClient.execute(httpPost);
|
||||
//处理请求结果
|
||||
int statusCode = response.getStatusLine().getStatusCode();
|
||||
String result = EntityUtils.toString(response.getEntity());
|
||||
if (statusCode == 200) { //处理成功
|
||||
log.info("微信支付下单成功,return body = " + result);
|
||||
return result;
|
||||
} else if (statusCode == 204) { //处理成功,无返回Body
|
||||
System.out.println("微信支付下单成功");
|
||||
return result;
|
||||
} else {
|
||||
log.error("failed,resp code = " + statusCode+ ",return body = " + result);
|
||||
throw BusinessExceptionAssertEnum.PAY_ERROR.exception();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw BusinessExceptionAssertEnum.PAY_ERROR.exception("微信Native支付异常", e);
|
||||
} finally {
|
||||
if (httpClient != null)
|
||||
httpClient.close();
|
||||
if (response != null)
|
||||
response.close();
|
||||
}
|
||||
}
|
||||
|
||||
private void buildHttpClient() {
|
||||
try {
|
||||
PrivateKey privateKey = PemUtil.loadPrivateKey(new ByteArrayInputStream(MCH_PRIVATE_KEY.getBytes("utf-8")));
|
||||
AutoUpdateCertificatesVerifier verifier = new AutoUpdateCertificatesVerifier(
|
||||
new WechatPay2Credentials(MCH_ID, new PrivateKeySigner(MCH_SERIAL_NO, privateKey)), APIV3_KEY.getBytes("utf-8"));
|
||||
httpClient = WechatPayHttpClientBuilder.create()
|
||||
.withMerchant(MCH_ID, MCH_SERIAL_NO, privateKey)
|
||||
.withValidator(new WechatPay2Validator(verifier))
|
||||
.build();
|
||||
} catch (Exception e) {
|
||||
throw BusinessExceptionAssertEnum.PAY_ERROR.exception("微信支付httpClient构建异常", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
OrderPay pay = new OrderPay();
|
||||
pay.setOrderNo("2018081400001");
|
||||
pay.setDescription("标准1号线ATS现地工作站实操");
|
||||
pay.setTotalFee(1);
|
||||
pay.setTimeExpire(OffsetDateTime.now().plusMinutes(10));
|
||||
OrderDetailVO orderDetailVO = new OrderDetailVO();
|
||||
orderDetailVO.setGoodsId(1L);
|
||||
orderDetailVO.setGoodsName("测试");
|
||||
orderDetailVO.setGoodsAmount(1);
|
||||
orderDetailVO.setPrice(1L);
|
||||
pay.setOrderDetail(List.of(orderDetailVO));
|
||||
WechatPayService wps = new WechatPayService();
|
||||
String result = wps.unifiedOrder(pay);
|
||||
System.out.println(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 支付结果处理
|
||||
*/
|
||||
public String receive(String data) {
|
||||
String result = "支付结果:" + data;
|
||||
log.info(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,355 @@
|
||||
package club.joylink.rtss.services.pay.wechat.bean;
|
||||
|
||||
public class WxPayResult {
|
||||
|
||||
/**
|
||||
* 返回状态码
|
||||
*/
|
||||
private String returnCode;
|
||||
|
||||
/**
|
||||
* 返回信息
|
||||
*/
|
||||
private String returnMsg;
|
||||
|
||||
/**
|
||||
* 公众账号ID*
|
||||
*/
|
||||
private String appId;
|
||||
|
||||
/**
|
||||
* 商户号*
|
||||
*/
|
||||
private String mchId;
|
||||
|
||||
/**
|
||||
* 设备号
|
||||
*/
|
||||
private String deviceInfo;
|
||||
|
||||
/**
|
||||
* 随机字符串*
|
||||
*/
|
||||
private String nonceStr;
|
||||
|
||||
/**
|
||||
* 签名*
|
||||
*/
|
||||
private String sign;
|
||||
|
||||
/**
|
||||
* 签名类型
|
||||
*/
|
||||
private String signType;
|
||||
|
||||
/**
|
||||
* 业务结果
|
||||
*/
|
||||
private String resultCode;
|
||||
|
||||
/**
|
||||
* 错误代码
|
||||
*/
|
||||
private String errCode;
|
||||
|
||||
/**
|
||||
* 错误代码描述
|
||||
*/
|
||||
private String errCodeDes;
|
||||
|
||||
/**
|
||||
* 用户标识
|
||||
*/
|
||||
private String openId;
|
||||
|
||||
/**
|
||||
* 是否关注公众账号
|
||||
*/
|
||||
private String isSubscribe;
|
||||
|
||||
/**
|
||||
* 交易类型*
|
||||
*/
|
||||
private String tradeType;
|
||||
|
||||
/**
|
||||
* 付款银行
|
||||
*/
|
||||
private String bankType;
|
||||
|
||||
/**
|
||||
* 订单金额
|
||||
*/
|
||||
private Integer totalFee;
|
||||
|
||||
/**
|
||||
* 应结订单金额
|
||||
*/
|
||||
private Integer settlementTotalFee;
|
||||
|
||||
/**
|
||||
* 货币种类
|
||||
*/
|
||||
private String feeType;
|
||||
|
||||
/**
|
||||
* 现金支付金额
|
||||
*/
|
||||
private Integer cashFee;
|
||||
|
||||
/**
|
||||
* 现金支付货币类型
|
||||
*/
|
||||
private String cashFeeType;
|
||||
|
||||
/**
|
||||
* 总代金券金额
|
||||
*/
|
||||
private Integer couponFee;
|
||||
|
||||
/**
|
||||
* 代金券使用数量
|
||||
*/
|
||||
private Integer couponCount;
|
||||
|
||||
/**
|
||||
* 微信支付订单号
|
||||
*/
|
||||
private String transactionId;
|
||||
|
||||
/**
|
||||
* 商户订单号
|
||||
*/
|
||||
private String outTradeNo;
|
||||
|
||||
/**
|
||||
* 商家数据包
|
||||
*/
|
||||
private String attach;
|
||||
|
||||
/**
|
||||
* 支付完成时间
|
||||
*/
|
||||
private String timeEnd;
|
||||
|
||||
public String getReturnCode() {
|
||||
return returnCode;
|
||||
}
|
||||
|
||||
public void setReturnCode(String returnCode) {
|
||||
this.returnCode = returnCode;
|
||||
}
|
||||
|
||||
public String getReturnMsg() {
|
||||
return returnMsg;
|
||||
}
|
||||
|
||||
public void setReturnMsg(String returnMsg) {
|
||||
this.returnMsg = returnMsg;
|
||||
}
|
||||
|
||||
public String getAppId() {
|
||||
return appId;
|
||||
}
|
||||
|
||||
public void setAppId(String appId) {
|
||||
this.appId = appId;
|
||||
}
|
||||
|
||||
public String getMchId() {
|
||||
return mchId;
|
||||
}
|
||||
|
||||
public void setMchId(String mchId) {
|
||||
this.mchId = mchId;
|
||||
}
|
||||
|
||||
public String getDeviceInfo() {
|
||||
return deviceInfo;
|
||||
}
|
||||
|
||||
public void setDeviceInfo(String deviceInfo) {
|
||||
this.deviceInfo = deviceInfo;
|
||||
}
|
||||
|
||||
public String getNonceStr() {
|
||||
return nonceStr;
|
||||
}
|
||||
|
||||
public void setNonceStr(String nonceStr) {
|
||||
this.nonceStr = nonceStr;
|
||||
}
|
||||
|
||||
public String getSign() {
|
||||
return sign;
|
||||
}
|
||||
|
||||
public void setSign(String sign) {
|
||||
this.sign = sign;
|
||||
}
|
||||
|
||||
public String getSignType() {
|
||||
return signType;
|
||||
}
|
||||
|
||||
public void setSignType(String signType) {
|
||||
this.signType = signType;
|
||||
}
|
||||
|
||||
public String getResultCode() {
|
||||
return resultCode;
|
||||
}
|
||||
|
||||
public void setResultCode(String resultCode) {
|
||||
this.resultCode = resultCode;
|
||||
}
|
||||
|
||||
public String getErrCode() {
|
||||
return errCode;
|
||||
}
|
||||
|
||||
public void setErrCode(String errCode) {
|
||||
this.errCode = errCode;
|
||||
}
|
||||
|
||||
public String getErrCodeDes() {
|
||||
return errCodeDes;
|
||||
}
|
||||
|
||||
public void setErrCodeDes(String errCodeDes) {
|
||||
this.errCodeDes = errCodeDes;
|
||||
}
|
||||
|
||||
public String getOpenId() {
|
||||
return openId;
|
||||
}
|
||||
|
||||
public void setOpenId(String openId) {
|
||||
this.openId = openId;
|
||||
}
|
||||
|
||||
public String getIsSubscribe() {
|
||||
return isSubscribe;
|
||||
}
|
||||
|
||||
public void setIsSubscribe(String isSubscribe) {
|
||||
this.isSubscribe = isSubscribe;
|
||||
}
|
||||
|
||||
public String getTradeType() {
|
||||
return tradeType;
|
||||
}
|
||||
|
||||
public void setTradeType(String tradeType) {
|
||||
this.tradeType = tradeType;
|
||||
}
|
||||
|
||||
public String getBankType() {
|
||||
return bankType;
|
||||
}
|
||||
|
||||
public void setBankType(String bankType) {
|
||||
this.bankType = bankType;
|
||||
}
|
||||
|
||||
public Integer getTotalFee() {
|
||||
return totalFee;
|
||||
}
|
||||
|
||||
public void setTotalFee(Integer totalFee) {
|
||||
this.totalFee = totalFee;
|
||||
}
|
||||
|
||||
public Integer getSettlementTotalFee() {
|
||||
return settlementTotalFee;
|
||||
}
|
||||
|
||||
public void setSettlementTotalFee(Integer settlementTotalFee) {
|
||||
this.settlementTotalFee = settlementTotalFee;
|
||||
}
|
||||
|
||||
public String getFeeType() {
|
||||
return feeType;
|
||||
}
|
||||
|
||||
public void setFeeType(String feeType) {
|
||||
this.feeType = feeType;
|
||||
}
|
||||
|
||||
public Integer getCashFee() {
|
||||
return cashFee;
|
||||
}
|
||||
|
||||
public void setCashFee(Integer cashFee) {
|
||||
this.cashFee = cashFee;
|
||||
}
|
||||
|
||||
public String getCashFeeType() {
|
||||
return cashFeeType;
|
||||
}
|
||||
|
||||
public void setCashFeeType(String cashFeeType) {
|
||||
this.cashFeeType = cashFeeType;
|
||||
}
|
||||
|
||||
public Integer getCouponFee() {
|
||||
return couponFee;
|
||||
}
|
||||
|
||||
public void setCouponFee(Integer couponFee) {
|
||||
this.couponFee = couponFee;
|
||||
}
|
||||
|
||||
public Integer getCouponCount() {
|
||||
return couponCount;
|
||||
}
|
||||
|
||||
public void setCouponCount(Integer couponCount) {
|
||||
this.couponCount = couponCount;
|
||||
}
|
||||
|
||||
public String getTransactionId() {
|
||||
return transactionId;
|
||||
}
|
||||
|
||||
public void setTransactionId(String transactionId) {
|
||||
this.transactionId = transactionId;
|
||||
}
|
||||
|
||||
public String getOutTradeNo() {
|
||||
return outTradeNo;
|
||||
}
|
||||
|
||||
public void setOutTradeNo(String outTradeNo) {
|
||||
this.outTradeNo = outTradeNo;
|
||||
}
|
||||
|
||||
public String getAttach() {
|
||||
return attach;
|
||||
}
|
||||
|
||||
public void setAttach(String attach) {
|
||||
this.attach = attach;
|
||||
}
|
||||
|
||||
public String getTimeEnd() {
|
||||
return timeEnd;
|
||||
}
|
||||
|
||||
public void setTimeEnd(String timeEnd) {
|
||||
this.timeEnd = timeEnd;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "WxPayResult [returnCode=" + returnCode + ", returnMsg=" + returnMsg + ", appId=" + appId + ", mchId="
|
||||
+ mchId + ", deviceInfo=" + deviceInfo + ", nonceStr=" + nonceStr + ", sign=" + sign + ", signType="
|
||||
+ signType + ", resultCode=" + resultCode + ", errCode=" + errCode + ", errCodeDes=" + errCodeDes
|
||||
+ ", openId=" + openId + ", isSubscribe=" + isSubscribe + ", tradeType=" + tradeType + ", bankType="
|
||||
+ bankType + ", totalFee=" + totalFee + ", settlementTotalFee=" + settlementTotalFee + ", feeType="
|
||||
+ feeType + ", cashFee=" + cashFee + ", cashFeeType=" + cashFeeType + ", couponFee=" + couponFee
|
||||
+ ", couponCount=" + couponCount + ", transactionId=" + transactionId + ", outTradeNo=" + outTradeNo
|
||||
+ ", attach=" + attach + ", timeEnd=" + timeEnd + "]";
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
package club.joylink.rtss.services.pay.wechat.bean;
|
||||
|
||||
public class WxPayResultReply {
|
||||
|
||||
/**
|
||||
* 返回状态码
|
||||
*/
|
||||
private String returnCode;
|
||||
|
||||
/**
|
||||
* 返回信息
|
||||
*/
|
||||
private String returnMsg;
|
||||
|
||||
public String getReturnCode() {
|
||||
return returnCode;
|
||||
}
|
||||
|
||||
public void setReturnCode(String returnCode) {
|
||||
this.returnCode = returnCode;
|
||||
}
|
||||
|
||||
public String getReturnMsg() {
|
||||
return returnMsg;
|
||||
}
|
||||
|
||||
public void setReturnMsg(String returnMsg) {
|
||||
this.returnMsg = returnMsg;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "WxPayResultReply [returnCode=" + returnCode + ", returnMsg=" + returnMsg + "]";
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,221 @@
|
||||
package club.joylink.rtss.services.pay.wechat.bean;
|
||||
|
||||
import club.joylink.rtss.services.pay.bean.OrderPay;
|
||||
import club.joylink.rtss.services.pay.wechat.constant.WechatConstants;
|
||||
import club.joylink.rtss.vo.client.order.OrderDetailVO;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Getter
|
||||
public class WxUnifiedOrder {
|
||||
|
||||
/**
|
||||
* 公众账号ID*
|
||||
*/
|
||||
private String appid;
|
||||
|
||||
/**
|
||||
* 商户号*
|
||||
*/
|
||||
private String mchid;
|
||||
|
||||
/**
|
||||
* 商品描述
|
||||
*/
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* 商户订单号
|
||||
*/
|
||||
private String out_trade_no;
|
||||
|
||||
/**
|
||||
* 交易结束时间
|
||||
*/
|
||||
private String time_expire;
|
||||
|
||||
/**
|
||||
* 附加数据
|
||||
*/
|
||||
private String attach;
|
||||
|
||||
/**
|
||||
* 通知地址
|
||||
*/
|
||||
private String notify_url;
|
||||
|
||||
/**
|
||||
* 订单优惠标记
|
||||
*/
|
||||
private String goods_tag;
|
||||
|
||||
/**
|
||||
* 订单金额信息
|
||||
*/
|
||||
private WxPayOrderAmountInfo amount;
|
||||
|
||||
/**
|
||||
* 优惠功能(没懂为啥这个参数要叫优惠功能)
|
||||
*/
|
||||
private WxPayOrderDetail detail;
|
||||
|
||||
/**
|
||||
* 场景信息,支付场景描述
|
||||
*/
|
||||
private WxPaySceneInfo scene_info;
|
||||
|
||||
public WxUnifiedOrder(OrderPay orderPay, String notify_url) {
|
||||
this.appid = WechatConstants.APP_ID;
|
||||
this.mchid = WechatConstants.MCH_ID;
|
||||
this.description = orderPay.getDescription();
|
||||
this.out_trade_no = orderPay.getOrderNo();
|
||||
if (orderPay.getTimeExpire() != null) {
|
||||
this.time_expire = DateTimeFormatter.ISO_OFFSET_DATE_TIME.format(orderPay.getTimeExpire().withNano(0));
|
||||
}
|
||||
// this.attach =
|
||||
this.notify_url = notify_url;
|
||||
// this.goods_tag =
|
||||
this.amount = new WxPayOrderAmountInfo(orderPay.getTotalFee());
|
||||
// this.detail = new WxPayOrderDetail(out_trade_no, orderPay.getOrderDetail());
|
||||
// this.scene_info = new WxPaySceneInfo();
|
||||
}
|
||||
|
||||
/**
|
||||
* 订单金额信息
|
||||
*/
|
||||
@Getter
|
||||
class WxPayOrderAmountInfo {
|
||||
/**
|
||||
* 订单总金额,单位/分
|
||||
*/
|
||||
private int total;
|
||||
|
||||
/**
|
||||
* 货币类型
|
||||
*/
|
||||
private String currency = "CNY";
|
||||
|
||||
public WxPayOrderAmountInfo(int total) {
|
||||
this.total = total;
|
||||
}
|
||||
}
|
||||
|
||||
@Getter
|
||||
class WxPayOrderDetail {
|
||||
/**
|
||||
* 订单原价
|
||||
* 该字段主要用于防止同一张小票分多次支付,以享受多次优惠的情况,正常支付订单不必上传此参数。
|
||||
*/
|
||||
private int cost_price;
|
||||
|
||||
/**
|
||||
* 商家小票ID
|
||||
*/
|
||||
private String invoice_id;
|
||||
|
||||
/**
|
||||
* 单品列表
|
||||
*/
|
||||
private List<GoodsDetail> goods_detail;
|
||||
|
||||
public WxPayOrderDetail(String invoice_id, List<OrderDetailVO> orderDetailVOS) {
|
||||
this.invoice_id = invoice_id;
|
||||
this.goods_detail = orderDetailVOS.stream().map(GoodsDetail::new).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Getter
|
||||
class GoodsDetail {
|
||||
/**
|
||||
* 商户侧商品编码
|
||||
* 由半角的大小写字母、数字、中划线、下划线中的一种或几种组成。
|
||||
*/
|
||||
private String merchant_goods_id;
|
||||
|
||||
/**
|
||||
* 微信侧商品编码
|
||||
* 微信支付定义的统一商品编号(没有可不传)
|
||||
*/
|
||||
private String wechatpay_goods_id;
|
||||
|
||||
/**
|
||||
* 商品名称
|
||||
*/
|
||||
private String goods_name;
|
||||
|
||||
/**
|
||||
* 商品数量
|
||||
*/
|
||||
private int quantity;
|
||||
|
||||
/**
|
||||
* 商品单价,单位为分
|
||||
*/
|
||||
private int unit_price;
|
||||
|
||||
public GoodsDetail(OrderDetailVO orderDetail) {
|
||||
this.merchant_goods_id = orderDetail.getGoodsId().toString();
|
||||
this.goods_name = orderDetail.getGoodsName();
|
||||
this.quantity = orderDetail.getGoodsAmount();
|
||||
this.unit_price = orderDetail.getPrice().intValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 场景信息
|
||||
*/
|
||||
@Getter
|
||||
class WxPaySceneInfo {
|
||||
/**
|
||||
* 用户终端ip。调用微信支付API的机器IP,支持IPv4和IPv6两种格式的IP地址。
|
||||
*/
|
||||
private String payer_client_ip;
|
||||
|
||||
/**
|
||||
* 商户端设备号。商户端设备号(门店号或收银设备ID)。
|
||||
*/
|
||||
private String device_id;
|
||||
|
||||
/**
|
||||
* 商户门店信息
|
||||
*/
|
||||
private StoreInfo store_info;
|
||||
|
||||
public WxPaySceneInfo() {
|
||||
this.payer_client_ip = WechatConstants.SPBILL_CREATE_IP;
|
||||
this.device_id = "1";
|
||||
this.store_info = new StoreInfo();
|
||||
}
|
||||
|
||||
@Getter
|
||||
class StoreInfo {
|
||||
/**
|
||||
* 门店编号,商户侧门店编号
|
||||
*/
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* 门店名称,商户侧门店名称
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 地区编码,地区编码,详细请见省市区编号对照表。
|
||||
*/
|
||||
private String area_code;
|
||||
|
||||
/**
|
||||
* 详细地址,详细的商户门店地址
|
||||
*/
|
||||
private String address;
|
||||
|
||||
public StoreInfo() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,183 @@
|
||||
package club.joylink.rtss.services.pay.wechat.bean;
|
||||
|
||||
|
||||
public class WxUnifiedOrderResult {
|
||||
|
||||
/**
|
||||
* 返回状态码*
|
||||
*/
|
||||
private String returnCode;
|
||||
|
||||
/**
|
||||
* 返回信息*
|
||||
*/
|
||||
private String returnMsg;
|
||||
|
||||
/**
|
||||
* 公众账号ID*
|
||||
*/
|
||||
private String appId;
|
||||
|
||||
/**
|
||||
* 商户号*
|
||||
*/
|
||||
private String mchId;
|
||||
|
||||
/**
|
||||
* 设备号
|
||||
*/
|
||||
private String deviceInfo;
|
||||
|
||||
/**
|
||||
* 随机字符串*
|
||||
*/
|
||||
private String nonceStr;
|
||||
|
||||
/**
|
||||
* 签名*
|
||||
*/
|
||||
private String sign;
|
||||
|
||||
/**
|
||||
* 业务结果*
|
||||
*/
|
||||
private String resultCode;
|
||||
|
||||
/**
|
||||
* 错误代码
|
||||
*/
|
||||
private String errCode;
|
||||
|
||||
/**
|
||||
* 错误代码描述
|
||||
*/
|
||||
private String errCodeDes;
|
||||
|
||||
/**
|
||||
* 交易类型*
|
||||
*/
|
||||
private String tradeType;
|
||||
|
||||
/**
|
||||
* 预支付交易会话标识*
|
||||
*/
|
||||
private String prepayId;
|
||||
|
||||
/**
|
||||
* 二维码链接
|
||||
*/
|
||||
private String codeUrl;
|
||||
|
||||
public String getReturnCode() {
|
||||
return returnCode;
|
||||
}
|
||||
|
||||
public void setReturnCode(String returnCode) {
|
||||
this.returnCode = returnCode;
|
||||
}
|
||||
|
||||
public String getReturnMsg() {
|
||||
return returnMsg;
|
||||
}
|
||||
|
||||
public void setReturnMsg(String returnMsg) {
|
||||
this.returnMsg = returnMsg;
|
||||
}
|
||||
|
||||
public String getAppId() {
|
||||
return appId;
|
||||
}
|
||||
|
||||
public void setAppId(String appId) {
|
||||
this.appId = appId;
|
||||
}
|
||||
|
||||
public String getMchId() {
|
||||
return mchId;
|
||||
}
|
||||
|
||||
public void setMchId(String mchId) {
|
||||
this.mchId = mchId;
|
||||
}
|
||||
|
||||
public String getDeviceInfo() {
|
||||
return deviceInfo;
|
||||
}
|
||||
|
||||
public void setDeviceInfo(String deviceInfo) {
|
||||
this.deviceInfo = deviceInfo;
|
||||
}
|
||||
|
||||
public String getNonceStr() {
|
||||
return nonceStr;
|
||||
}
|
||||
|
||||
public void setNonceStr(String nonceStr) {
|
||||
this.nonceStr = nonceStr;
|
||||
}
|
||||
|
||||
public String getSign() {
|
||||
return sign;
|
||||
}
|
||||
|
||||
public void setSign(String sign) {
|
||||
this.sign = sign;
|
||||
}
|
||||
|
||||
public String getResultCode() {
|
||||
return resultCode;
|
||||
}
|
||||
|
||||
public void setResultCode(String resultCode) {
|
||||
this.resultCode = resultCode;
|
||||
}
|
||||
|
||||
public String getErrCode() {
|
||||
return errCode;
|
||||
}
|
||||
|
||||
public void setErrCode(String errCode) {
|
||||
this.errCode = errCode;
|
||||
}
|
||||
|
||||
public String getErrCodeDes() {
|
||||
return errCodeDes;
|
||||
}
|
||||
|
||||
public void setErrCodeDes(String errCodeDes) {
|
||||
this.errCodeDes = errCodeDes;
|
||||
}
|
||||
|
||||
public String getTradeType() {
|
||||
return tradeType;
|
||||
}
|
||||
|
||||
public void setTradeType(String tradeType) {
|
||||
this.tradeType = tradeType;
|
||||
}
|
||||
|
||||
public String getPrepayId() {
|
||||
return prepayId;
|
||||
}
|
||||
|
||||
public void setPrepayId(String prepayId) {
|
||||
this.prepayId = prepayId;
|
||||
}
|
||||
|
||||
public String getCodeUrl() {
|
||||
return codeUrl;
|
||||
}
|
||||
|
||||
public void setCodeUrl(String codeUrl) {
|
||||
this.codeUrl = codeUrl;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "WxUnifiedOrderResult [returnCode=" + returnCode + ", returnMsg=" + returnMsg + ", appId=" + appId
|
||||
+ ", mchId=" + mchId + ", deviceInfo=" + deviceInfo + ", nonceStr=" + nonceStr + ", sign=" + sign
|
||||
+ ", resultCode=" + resultCode + ", errCode=" + errCode + ", errCodeDes=" + errCodeDes + ", tradeType="
|
||||
+ tradeType + ", prepayId=" + prepayId + ", codeUrl=" + codeUrl + "]";
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
package club.joylink.rtss.services.pay.wechat.constant;
|
||||
|
||||
public interface WechatConstants {
|
||||
|
||||
public static interface PayUrl {
|
||||
/** 统一下单接口地址 */
|
||||
String UNIFIED_ORDER = "https://api.mch.weixin.qq.com/pay/unifiedorder";
|
||||
}
|
||||
|
||||
String DATE_FORMAT_STR = "yyyyMMddHHmmss";
|
||||
|
||||
/** 公众账号ID */
|
||||
String APP_ID = "wx41cb66db5faf330f";
|
||||
|
||||
/** 商户号 */
|
||||
String MCH_ID = "1511854231";
|
||||
|
||||
/** 机器IP */
|
||||
String SPBILL_CREATE_IP = "58.87.66.250";
|
||||
|
||||
/** 商户平台设置的密钥key(应该已经没用了) */
|
||||
String MCH_KEY = "a6502d608c64fb2f7e6dbcf854cda32a";
|
||||
|
||||
/** 商户证书序列号 */
|
||||
String MCH_SERIAL_NO = "40980EBE9204E4D8956215FE72A1C89A8480FA27";
|
||||
|
||||
/** APIv3密钥,用于解密接口返回的数据(平台证书公钥) */
|
||||
String APIV3_KEY = "c0666cec1cb15d915f802ce5a3df5c2d";
|
||||
|
||||
/** 商户证书私钥 */
|
||||
String MCH_PRIVATE_KEY = "-----BEGIN PRIVATE KEY-----\n" +
|
||||
"MIIEwAIBADANBgkqhkiG9w0BAQEFAASCBKowggSmAgEAAoIBAQDAzmBwXZX7p+bl\n" +
|
||||
"IiSl/5CukfAxOmYTVgCbLQ1SBJMd9c52VsPgWED4o/llkvwWXY9EUTDX09qOBroF\n" +
|
||||
"+iCQBu9iWvStfN2VTmy7ECC3ROv1HzAK2U/aWOsCxv/svpqUoWImqAVaUtOd+kQK\n" +
|
||||
"RtMVvz/kE+BUZO1RFA3YS7dmYPtAuZQOhLXTvg7uS5k/PaSg39Bx+ZNcymUQtHHs\n" +
|
||||
"6nH0RmneY2LBtmLZHw3aeb2VCydEPY9tON/vhBbLXe/+Lq3C8Lx8R2IdURCcR2Wl\n" +
|
||||
"92UlLwR5nBK/kKcgOPA7DjBf4zPGBHgFovytW5gKRY+UscrPgSEHxa9fmRy/SLQS\n" +
|
||||
"k7AjGZC5AgMBAAECggEBALGIFiLA+Y5sbt3DD43OAbHMbSdXB5B8WziHRkGkNraa\n" +
|
||||
"lI5AnEHh4YlQqx7NNdN+OKIGRHwm9ZJbPUStqPgVeqzM5YktdXa6bMHeOtGl48Kk\n" +
|
||||
"Af+rU6zQvSykghjC9OEwrIi9o5gktfg77hSsXEck/7aKWsA64o4KcikcpvXdDNzt\n" +
|
||||
"qrLumHzJ32WLN6q5JClc4zPfpc3CuRUH3SJevDC7b0E+BFth8c1cX1QDbSl1v8kA\n" +
|
||||
"anolAIcHqhFvJHzpY+WjHqGVdN4RbPXl+xsOoo3mXsTHybSWgHd2ZJGAAqtQlaSV\n" +
|
||||
"Y9dRLSU4MQBITNGe5j+rngyRowDkrYqbtAfFqtYQhwUCgYEA5iDlSqu69pr50xf4\n" +
|
||||
"oQmyOk7ep9U8JOTH+C+OOtbVzpS51oLnq95wJ8cu/QV8qhOUVSZ6KKhD37o7XWhZ\n" +
|
||||
"vGjBGrlv/C0anAsZ4S10GmL/gbqpCckSe4ITz4VI03oy/fdRK+VtulgMFvjKEr9F\n" +
|
||||
"cYOkct4IkKAclAECYLhX7VUEKrMCgYEA1ntX1kA4objy7+bgfmxmFkAk50otHJwR\n" +
|
||||
"huCEOMe9MlgAAc9NYk+GEF1tQPzTQVdVL6jovtrZ+L5HP1SXJGQWY6N06XJThN9+\n" +
|
||||
"oks027gQWBoXnTQJ8/wXPQpXq3EIgCThzKNkTLq4fsnGIUgRUH2/W+IjlNEThw26\n" +
|
||||
"KqmuBiXjfOMCgYEAxe6lSIRMWq8RES8c+eWNFfmgKFqPUGw2UpEUlCcT3oqtDIOr\n" +
|
||||
"H3hCnvQCxj1h7CbK/jIJ/846EsPrK3wFMrgm3wV//DYPHQevSq39nnRnrv0NRw1a\n" +
|
||||
"iEBpKaRJ7xq7oRSHDGpY5l20iE2UAGvjHq9LUkEGvN35tpLnqKjld4wX+WECgYEA\n" +
|
||||
"urCwGzDZWoOXCpTHMaP/FD0PIjehnraGVwWUcawClhCdKPYdoIYh5pq734ZyB/0R\n" +
|
||||
"jCOVO5NZibduYsSprqZkCqSbvhuicRTssC2QO/QyXc2QYmiKhVIXlC0tdHA1+vyf\n" +
|
||||
"grVyN4uLzeipygxl7c8Wws7LM9ztB3A+bKY3cOiH5AsCgYEA4XOMKmJeucNOrXTY\n" +
|
||||
"3NdHCb8Yx5JgCYqgdq5uIY7MQ8f3+OlXNwdhh/ftjk8DejWzyY7jpqgj2W3qZFu+\n" +
|
||||
"BxGJZfCDzAqUS9OufMY8tOO/PaIABzSkU4gliBAuxovj9sU6oSSa7aYD+34QW2UU\n" +
|
||||
"L83fUdwBuNbwX6vZM45hIASP6C8=\n" +
|
||||
"-----END PRIVATE KEY-----";
|
||||
|
||||
/**
|
||||
* 支付类型
|
||||
* @author sheng
|
||||
*/
|
||||
public static interface TradeType {
|
||||
/** 扫码支付 */
|
||||
String NATIVE = "NATIVE";
|
||||
/** 公众号支付 */
|
||||
String JSAPI = "JSAPI";
|
||||
/** APP支付 */
|
||||
String APP = "APP";
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
package club.joylink.rtss.services.pay.wechat.controller;
|
||||
|
||||
import club.joylink.rtss.services.pay.wechat.WechatPayService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMethod;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/wechatpay")
|
||||
@Slf4j
|
||||
public class PayResultController {
|
||||
|
||||
@Autowired
|
||||
private WechatPayService wechatPayService;
|
||||
|
||||
@RequestMapping(path="/receive", method= {RequestMethod.POST})
|
||||
public String receive(@RequestBody String data) {
|
||||
log.info("支付结果通知:" + data);
|
||||
return this.wechatPayService.receive(data);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
package club.joylink.rtss.services.pay.wechat.util;
|
||||
|
||||
import club.joylink.rtss.util.EncryptUtil;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public class SignUtil {
|
||||
|
||||
public static String sign(Map<String, String> map, String mchKey) {
|
||||
Set<String> keysSet = map.keySet();
|
||||
Object[] keys = keysSet.toArray();
|
||||
// 排序
|
||||
Arrays.sort(keys);
|
||||
StringBuffer temp = new StringBuffer();
|
||||
boolean first = true;
|
||||
for (Object key : keys) {
|
||||
Object value = map.get(key);
|
||||
if(null != value) {
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
temp.append("&");
|
||||
}
|
||||
temp.append(key).append("=");
|
||||
String valueString = "";
|
||||
if (null != value) {
|
||||
valueString = value.toString();
|
||||
}
|
||||
temp.append(valueString);
|
||||
}
|
||||
}
|
||||
String tempStr = temp.toString()+"&key="+mchKey;
|
||||
System.out.println(tempStr);
|
||||
return EncryptUtil.md5(tempStr).toUpperCase();
|
||||
}
|
||||
|
||||
}
|
@ -31,7 +31,7 @@ public class OrderDetailVO {
|
||||
|
||||
private Integer monthAmount;
|
||||
|
||||
private Float price;
|
||||
private Long price;
|
||||
|
||||
public OrderDetailVO(SaleOrderDetail detail) {
|
||||
id = detail.getId();
|
||||
@ -41,7 +41,7 @@ public class OrderDetailVO {
|
||||
startTime = detail.getStartTime();
|
||||
goodsAmount = detail.getGoodsAmount();
|
||||
monthAmount = detail.getMonthAmount();
|
||||
price = (float) detail.getPrice() / 100;
|
||||
price = detail.getPrice();
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user