Commit 83c576d0 authored by tangtuo's avatar tangtuo

微信支付

parent 4819f7b0
......@@ -47,41 +47,6 @@ public class RabbitMQConfig {
return new DirectExchange(DEAD_LETTER_DIRECT, true, false, null);
}
/**
* 存放死信的队列,用户监听这个队列
*
* @return
*/
@Bean
public Queue copyrightQueue() {
return new Queue(COPYRIGHT_QUEUE, true);
}
/**
* 定义死信队列
*
* @return
*/
@Bean
public Queue dlQueue() {
return QueueBuilder.durable(DEAD_LETTER_QUEUE)
.ttl(1000 * 60 * 60 * 6)
.ttl(1000 * 60 * 2) // 测试环境
.deadLetterExchange(DEAD_LETTER_DIRECT)
.deadLetterRoutingKey("copyright.notify")
.build();
}
@Bean
public Binding dlBinding() {
return BindingBuilder.bind(dlQueue()).to(copyrightDirect()).with("copyright.apply");
}
@Bean
public Binding copyrightBinding() {
return BindingBuilder.bind(copyrightQueue()).to(dlDirect()).with("copyright.notify");
}
/**
......
package com.fzm.common.entity.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author tangtuo
* @date 2022/1/21 16:56
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class NftPublishMsg {
private String hash;
private Integer id;
private Long tokenId;
}
......@@ -21,6 +21,7 @@ public enum ResultCode implements IErrorCode {
COPYRIGHT_FAILED(420, "版权申请失败"),
SELECT_FAILED(421, "查询失败"),
PAY_FAILED(422, "支付失败"),
REFUND_FAILED(422, "退款失败"),
;
......
......@@ -34,9 +34,9 @@ public class WxPayProperties {
private String privateKeyPath;
@ApiModelProperty("h5支付回调地址")
private String h5PayNotifyUrl;
private String payNotifyUrl;
@ApiModelProperty("h5退款回调地址")
private String h5RefundNotifyUrl;
@ApiModelProperty("退款回调地址")
private String refundNotifyUrl;
}
package com.fzm.common.service;
import cn.hutool.json.JSONObject;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fzm.common.entity.dto.JsapiPayDto;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.Map;
......@@ -34,4 +36,26 @@ public interface WxPayService {
void processOrder(JSONObject jsonObject) throws GeneralSecurityException;
String queryOrder(Long orderId);
/**
* 退款
*
* @param orderId
*/
void refund(Long orderId) throws IOException;
/**
* 退款回调
*
* @param request
* @return
*/
Boolean notifyRefund(HttpServletRequest request);
/**
* 处理退款成功后的订单
*
* @param jsonObject
*/
void processRefund(JSONObject jsonObject);
}
......@@ -14,6 +14,7 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fzm.common.constant.RedisConstant;
import com.fzm.common.constant.SystemConstant;
import com.fzm.common.entity.*;
import com.fzm.common.entity.dto.NftPublishMsg;
import com.fzm.common.entity.vo.CollectionNftVo;
import com.fzm.common.entity.vo.CopyrightVo;
import com.fzm.common.entity.vo.NftListVo;
......@@ -109,7 +110,8 @@ public class NftServiceImpl extends ServiceImpl<NftMapper, Nft> implements NftSe
@Override
public Integer publish(Integer id) {
User user = userService.getUserByToken();
Nft nft = getById(id);
User user = userService.getUserByWallet(nft.getPublishAddress());
if (!AuthStatus.SUCCESS.getStatus().equals(user.getAuthStatus())) {
throw GlobalException.newException(ResultCode.PUBLISH_ERROR, "您还未实名认证,请先实名认证");
}
......@@ -118,7 +120,6 @@ public class NftServiceImpl extends ServiceImpl<NftMapper, Nft> implements NftSe
String privkey = paraChainClient.walletDumpPrivkey(wallet);
// 生产tokenId
long tokenId = getTokenId();
Nft nft = getById(id);
TreeMap<String, String> map = new TreeMap<>();
map.put("hash", nft.getFileHash());
map.put("publishAddress", wallet);
......@@ -138,26 +139,9 @@ public class NftServiceImpl extends ServiceImpl<NftMapper, Nft> implements NftSe
if (StringUtils.isBlank(tradeHash)) {
throw GlobalException.newException(ResultCode.PUBLISH_ERROR, "nft发行失败");
}
// 确认交易结果
TxResult txResult = paraChainClient.cycleConfirmTxWithHash(hash, true, 1000);
if (!TxStatusEnum.SUCCESS.equals(txResult.getStatus())) {
throw GlobalException.newException(ResultCode.PUBLISH_ERROR, txResult.getErrMsg().getValue());
}
String realHash = paraChainClient.getRealTxHashFromGrp(hash);
/*TxResult txResult = paraChainClient.cycleConfirmTxWithHash(realHash, false, 1000);
if (!TxStatusEnum.SUCCESS.equals(txResult.getStatus())) {
throw GlobalException.newException(ResultCode.PUBLISH_ERROR, txResult.getErrMsg().getValue());
}*/
nft.setNftHash(realHash);
nft.setTokenId(tokenId);
nft.setPublishTime(new Date());
updateById(nft);
// 如果用户是第一次发行作品,把用户的isPublish修改成1,并清空用户统计的缓存信息
if (SystemConstant.BOOLEAN_DATA_FALSE.equals(user.getIsPublish())) {
User u = new User().setId(user.getId()).setIsPublish(SystemConstant.BOOLEAN_DATA_TRUE);
userService.updateById(u);
redisUtil.delete("user::statistic");
}
// 给mq发送一条消息,异步确认交易结果
NftPublishMsg nftPublishMsg = new NftPublishMsg(hash, id, tokenId);
rabbitTemplate.convertAndSend("nft-exchange", "nft.publish", nftPublishMsg);
return nft.getId();
}
......
......@@ -13,7 +13,7 @@ import com.fzm.common.enums.*;
import com.fzm.common.exception.GlobalException;
import com.fzm.common.properties.WxPayProperties;
import com.fzm.common.service.*;
import com.fzm.common.utils.JwtUtil;
import com.fzm.common.utils.SnowflakeUtil;
import com.wechat.pay.contrib.apache.httpclient.util.AesUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
......@@ -74,6 +74,9 @@ public class WxPayServiceImpl implements WxPayService {
@Resource
private RabbitTemplate rabbitTemplate;
@Resource
private SnowflakeUtil snowflakeUtil;
public Map<String, Object> payJsapi(JsapiPayDto jsapiPayDto) throws Exception {
RLock lock = redisson.getLock("pay-" + jsapiPayDto.getOrderId());
if (!lock.tryLock(10, TimeUnit.SECONDS)) {
......@@ -94,7 +97,7 @@ public class WxPayServiceImpl implements WxPayService {
rootNode.put("mchid", wxPayProperties.getMchId())
.put("appid", appId)
.put("description", PayScene.getTypeByCode(order.getPayScene()))
.put("notify_url", wxPayProperties.getH5PayNotifyUrl())
.put("notify_url", wxPayProperties.getPayNotifyUrl())
.put("out_trade_no", String.valueOf(out_trade_no));
rootNode.putObject("amount")
.put("total", order.getFee());
......@@ -216,6 +219,74 @@ public class WxPayServiceImpl implements WxPayService {
return null;
}
@Override
public void refund(Long orderId) throws IOException {
Order order = orderService.getById(orderId);
if (order == null || !order.getOrderStatus().equals(OrderStatus.PAYED.getStatus())) {
throw GlobalException.newException(ResultCode.REFUND_FAILED, "当前订单未支付成功");
}
String out_refund_no = "refund-" + snowflakeUtil.snowflakeId();
String url = "https://api.mch.weixin.qq.com/v3/refund/domestic/refunds";
HttpPost httpPost = new HttpPost(url);
httpPost.addHeader("Accept", "application/json");
httpPost.addHeader("Content-type", "application/json; charset=utf-8");
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectMapper objectMapper = new ObjectMapper();
ObjectNode rootNode = objectMapper.createObjectNode();
rootNode.put("mchid", wxPayProperties.getMchId())
.put("appid", wxPayProperties.getAppId())
.put("out_refund_no", out_refund_no)
.put("notify_url", wxPayProperties.getRefundNotifyUrl())
.put("out_trade_no", String.valueOf(orderId));
rootNode.putObject("amount")
.put("refund", order.getFee())
.put("total", order.getFee())
.put("currency", "CNY");
String json = objectMapper.writeValueAsString(rootNode);
log.info("退款接口请求参数: {}", json);
objectMapper.writeValue(bos, rootNode);
httpPost.setEntity(new StringEntity(bos.toString("UTF-8"), "UTF-8"));
CloseableHttpResponse response = httpClient.execute(httpPost);
String bodyAsString = EntityUtils.toString(response.getEntity());
JSONObject jsonObject = JSONUtil.parseObj(bodyAsString);
// 修改订单状态为退款中
orderService.updateOrderStatus(orderId, OrderStatus.REFUNDING);
}
@Override
public Boolean notifyRefund(HttpServletRequest request) {
try {
String requestBodyData = this.getRequestBodyData(request);
log.info("接收微信退款接口回调请求, 请求参数: {}", requestBodyData);
JSONObject jsonObject = JSONUtil.parseObj(requestBodyData);
JSONObject resource = jsonObject.getJSONObject("resource");
String plainText = this.decrypt(resource);
log.info("解密后的明文信息为: {}", plainText);
JSONObject obj = JSONUtil.parseObj(plainText);
processRefund(jsonObject);
return null;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
@Override
public void processRefund(JSONObject jsonObject) {
Long out_trade_no = jsonObject.getLong("out_trade_no");
RLock lock = redisson.getLock("refund-" + out_trade_no);
lock.lock(10, TimeUnit.SECONDS);
try {
// 修改订单未退款成功
orderService.updateOrderStatus(out_trade_no, OrderStatus.REFUNDED);
// 保存流水信息
} finally {
lock.unlock();
}
}
/**
* 解密
*
......
package com.fzm.portal;
import com.fzm.common.entity.dto.OrderProcessMsg;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.scheduling.support.SimpleTriggerContext;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
/**
* @author tangtuo
* @date 2022/1/21 16:28
*/
@Slf4j
@RestController
@RequestMapping("/test")
public class TestController {
@Resource
private RabbitTemplate rabbitTemplate;
@GetMapping("/send")
public String send(){
log.info("发送消息: {}",123);
rabbitTemplate.convertAndSend("nft-exchange", "nft.publish", 123);
return "SUCCESS";
}
}
package com.fzm.portal.controller;
import com.fzm.common.annotation.Authentication;
import com.fzm.common.entity.Order;
import com.fzm.common.entity.dto.OrderDto;
import com.fzm.common.enums.PayScene;
import com.fzm.common.model.ResponseModel;
import com.fzm.common.service.CopyrightApplyService;
import com.fzm.common.service.NftService;
import com.fzm.common.service.OrderService;
import com.fzm.common.utils.SnowflakeUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;
/**
* @author tangtuo
* @date 2022/1/20 18:22
*/
@Authentication
@Api(tags = "作品类别")
@Api(tags = "订单管理")
@RestController
@RequestMapping("/opus/category")
@RequestMapping("/order")
public class OrderController {
@Resource
......@@ -31,13 +28,13 @@ public class OrderController {
@PostMapping("/create")
@ApiOperation("下单")
public ResponseModel<Long> createOrder(@RequestBody OrderDto orderDto) {
public ResponseModel<String> createOrder(@RequestBody OrderDto orderDto) {
Long orderId = orderService.createOrder(orderDto);
return ResponseModel.success(orderId);
return ResponseModel.success(String.valueOf(orderId));
}
@GetMapping("/query-order-status")
@GetMapping("/query-order-status/{orderId}")
@ApiOperation(value = "查询订单状态")
public ResponseModel<Integer> queryOrderStatus(@PathVariable Long orderId) {
return ResponseModel.success(orderService.getById(orderId).getOrderStatus());
......
......@@ -13,6 +13,7 @@ import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
......@@ -52,6 +53,23 @@ public class WxPayController {
return result;
}
@ApiOperation("退款回调通知")
@PostMapping("/notify/refund")
public Map<String, String> notifyRefund(HttpServletRequest request, HttpServletResponse response) {
Map<String, String> result = new HashMap<>();
Boolean refund = wxPayService.notifyRefund(request);
if (refund) {
result.put("code", "SUCCESS");
result.put("message", "成功");
response.setStatus(HttpStatus.HTTP_OK);
} else {
result.put("code", "FAILED");
result.put("message", "系统异常");
response.setStatus(HttpStatus.HTTP_INTERNAL_ERROR);
}
return result;
}
}
package com.fzm.portal.listener;
import cn.fzm.chain.simplesdk.client.ParaChainClient;
import cn.fzm.chain.simplesdk.constant.TxStatusEnum;
import cn.fzm.chain.simplesdk.model.TxResult;
import com.fzm.common.constant.SystemConstant;
import com.fzm.common.entity.Nft;
import com.fzm.common.entity.User;
import com.fzm.common.entity.dto.NftPublishMsg;
import com.fzm.common.enums.ResultCode;
import com.fzm.common.exception.GlobalException;
import com.fzm.common.service.NftService;
import com.fzm.common.service.UserService;
import com.fzm.common.utils.RedisUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.Date;
/**
* @author tangtuo
* @date 2022/1/21 16:49
*/
@Slf4j
@Component
public class NftListener {
@Resource
private ParaChainClient paraChainClient;
@Resource
private NftService nftService;
@Resource
UserService userService;
@Resource
private RedisUtil redisUtil;
/**
* 监听nft发行结果
*
* @param msg
*/
@RabbitListener(queues = "nft.publish.queue")
public void listenNftPublish(NftPublishMsg msg) {
log.info("收到处理确认nft发行结果的消息: {}", msg);
Nft nft = nftService.getById(msg.getId());
User user = userService.getUserByWallet(nft.getPublishAddress());
String hash = msg.getHash();
// 确认交易结果
TxResult txResult = paraChainClient.cycleConfirmTxWithHash(hash, true, 1000);
if (!TxStatusEnum.SUCCESS.equals(txResult.getStatus())) {
throw GlobalException.newException(ResultCode.PUBLISH_ERROR, txResult.getErrMsg().getValue());
}
String realHash = paraChainClient.getRealTxHashFromGrp(hash);
/*TxResult txResult = paraChainClient.cycleConfirmTxWithHash(realHash, false, 1000);
if (!TxStatusEnum.SUCCESS.equals(txResult.getStatus())) {
throw GlobalException.newException(ResultCode.PUBLISH_ERROR, txResult.getErrMsg().getValue());
}*/
nft.setNftHash(realHash);
nft.setTokenId(msg.getTokenId());
nft.setPublishTime(new Date());
nftService.updateById(nft);
// 如果用户是第一次发行作品,把用户的isPublish修改成1,并清空用户统计的缓存信息
if (SystemConstant.BOOLEAN_DATA_FALSE.equals(user.getIsPublish())) {
User u = new User().setId(user.getId()).setIsPublish(SystemConstant.BOOLEAN_DATA_TRUE);
userService.updateById(u);
redisUtil.delete("user::statistic");
}
}
}
......@@ -37,9 +37,4 @@ public class OrderListener {
}
}
@RabbitListener(queues = "nft.publish.queue")
public void listenProcessOrder(Long id) {
log.info("接收到信息: {}", id);
}
}
......@@ -113,5 +113,5 @@ wx-pay:
api-v3-key: D864DA53FEF8ACD41519064967DC10D2
mch-serial-num: 72A62544B0A08A214FAEC780108692EDC6E7D5FA
private-key-path: apiclient_key.pem
h5-pay-notify-url: https://146.56.218.121:12100/wx-pay/notify/h5
h5-refund-notify-url:
pay-notify-url: https://146.56.218.121:12100/wx-pay/notify/jsapi
refund-notify-url: https://146.56.218.121:12100/wx-pay/notify/refund
......@@ -113,5 +113,5 @@ wx-pay:
api-v3-key: D864DA53FEF8ACD41519064967DC10D2
mch-serial-num: 72A62544B0A08A214FAEC780108692EDC6E7D5FA
private-key-path: apiclient_key.pem
h5-pay-notify-url: https://146.56.218.121:12100/wx-pay/notify/h5
h5-refund-notify-url:
\ No newline at end of file
pay-notify-url: https://146.56.218.121:12100/wx-pay/notify/jsapi
refund-notify-url: https://146.56.218.121:12100/wx-pay/notify/refund
\ No newline at end of file
......@@ -126,5 +126,5 @@ wx-pay:
api-v3-key: D864DA53FEF8ACD41519064967DC10D2
mch-serial-num: 72A62544B0A08A214FAEC780108692EDC6E7D5FA
private-key-path: apiclient_key.pem
h5-pay-notify-url: https://test.inmvo.com:8985/proxyApi/wx-pay/notify/h5
h5-refund-notify-url:
pay-notify-url: https://test.inmvo.com:8985/proxyApi/wx-pay/notify/jsapi
refund-notify-url: https://test.inmvo.com:8985/proxyApi/wx-pay/notify/refund
......@@ -115,5 +115,5 @@ wx-pay:
api-v3-key: D864DA53FEF8ACD41519064967DC10D2
mch-serial-num: 72A62544B0A08A214FAEC780108692EDC6E7D5FA
private-key-path: apiclient_key.pem
h5-pay-notify-url: https://nft.inmvo.com/proxyApi/wx-pay/notify/h5
h5-refund-notify-url:
\ No newline at end of file
pay-notify-url: https://nft.inmvo.com/proxyApi/wx-pay/notify/jsapi
refund-notify-url: https://nft.inmvo.com/proxyApi/wx-pay/notify/refund
\ No newline at end of file
spring:
profiles:
active: local
active: nj
application:
name: joying-portal
servlet:
......
package com.fzm.portal;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.web.bind.annotation.RequestParam;
import javax.annotation.Resource;
/**
* @author tangtuo
......@@ -12,4 +17,11 @@ import org.springframework.boot.test.context.SpringBootTest;
public class RabbitTestDemo {
@Resource
private RabbitTemplate rabbitTemplate;
@Test
public void send() {
}
}
......@@ -2,6 +2,7 @@ package com.fzm.portal;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fzm.common.entity.dto.OrderProcessMsg;
import com.fzm.common.properties.WxPayProperties;
import com.fzm.common.service.WxPayService;
import com.fzm.common.utils.SnowflakeUtil;
......@@ -12,6 +13,7 @@ import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.junit.jupiter.api.Test;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.boot.test.context.SpringBootTest;
import javax.annotation.Resource;
......@@ -39,6 +41,9 @@ public class WxPayTest {
@Resource
private SnowflakeUtil snowflakeUtil;
@Resource
private RabbitTemplate rabbitTemplate;
@Test
public void testSnowFlake() {
for (int i = 0; i < 10; i++) {
......@@ -48,9 +53,10 @@ public class WxPayTest {
@Test
public void testH5Pay() throws IOException {
for (int i = 0; i < 10; i++) {
log.info(String.valueOf(snowflakeUtil.snowflakeId()));
}
OrderProcessMsg orderProcessMsg = new OrderProcessMsg(1, 1);
rabbitTemplate.convertAndSend("order-exchange", "order.process", orderProcessMsg);
// for (int i = 0; i < 10; i++) {
// log.info(String.valueOf(snowflakeUtil.snowflakeId()));// }
// String url = "https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi";
// HttpPost httpPost = new HttpPost(url);
// httpPost.addHeader("Accept", "application/json");
......
This diff is collapsed.
This source diff could not be displayed because it is too large. You can view the blob instead.
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