集成支付功能

This commit is contained in:
macro
2023-11-08 15:30:35 +08:00
parent ec3b8b10ba
commit d7789660b9
13 changed files with 432 additions and 3 deletions

View File

@@ -25,4 +25,11 @@ logging:
root: info
com.macro.mall: debug
logstash:
host: localhost
host: localhost
alipay:
gatewayUrl: https://openapi-sandbox.dl.alipaydev.com/gateway.do
appId: your appId
alipayPublicKey: your alipayPublicKey
appPrivateKey: your appPrivateKey
returnUrl: http://localhost:8060/#/pages/money/paySuccess
notifyUrl:

View File

@@ -28,4 +28,11 @@ logging:
com.macro.mall: info
logstash:
host: logstash
alipay:
gatewayUrl: https://openapi-sandbox.dl.alipaydev.com/gateway.do
appId: your appId
alipayPublicKey: your alipayPublicKey
appPrivateKey: your appPrivateKey
returnUrl: http://192.168.3.101:8060/#/pages/money/paySuccess
notifyUrl:

View File

@@ -73,6 +73,7 @@ secure:
- "/mall-portal/home/**"
- "/mall-portal/product/**"
- "/mall-portal/brand/**"
- "/mall-portal/alipay/**"
- "/mall-admin/admin/login"
- "/mall-admin/admin/register"
- "/mall-admin/minio/upload"

View File

@@ -53,6 +53,12 @@
<groupId>io.github.openfeign</groupId>
<artifactId>feign-okhttp</artifactId>
</dependency>
<!--支付宝支付Java SDK-->
<dependency>
<groupId>com.alipay.sdk</groupId>
<artifactId>alipay-sdk-java</artifactId>
<version>${alipay-sdk.version}</version>
</dependency>
</dependencies>
<build>

View File

@@ -0,0 +1,21 @@
package com.macro.mall.portal.config;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @auther macrozheng
* @description 支付宝请求客户端配置
* @date 2023/9/8
* @github https://github.com/macrozheng
*/
@Configuration
public class AlipayClientConfig {
@Bean
public AlipayClient alipayClient(AlipayConfig config){
return new DefaultAlipayClient(config.getGatewayUrl(),config.getAppId(),config.getAppPrivateKey(), config.getFormat(),config.getCharset(),config.getAlipayPublicKey(),config.getSignType());
}
}

View File

@@ -0,0 +1,57 @@
package com.macro.mall.portal.config;
import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* @auther macrozheng
* @description 支付宝支付相关配置
* @date 2023/9/8
* @github https://github.com/macrozheng
*/
@Getter
@Setter
@Component
@ConfigurationProperties(prefix = "alipay")
public class AlipayConfig {
/**
* 支付宝网关
*/
private String gatewayUrl;
/**
* 应用ID
*/
private String appId;
/**
* 应用私钥
*/
private String appPrivateKey;
/**
* 支付宝公钥
*/
private String alipayPublicKey;
/**
* 用户确认支付后,支付宝调用的页面返回路径
* 开发环境为http://localhost:8060/#/pages/money/paySuccess
*/
private String returnUrl;
/**
* 支付成功后,支付宝服务器主动通知商户服务器里的异步通知回调(需要公网能访问)
* 开发环境为http://localhost:8085/alipay/notify
*/
private String notifyUrl;
/**
* 参数返回格式只支持JSON
*/
private String format = "JSON";
/**
* 请求使用的编码格式
*/
private String charset = "UTF-8";
/**
* 生成签名字符串所使用的签名算法类型
*/
private String signType = "RSA2";
}

View File

@@ -0,0 +1,74 @@
package com.macro.mall.portal.controller;
import com.macro.mall.common.api.CommonResult;
import com.macro.mall.portal.config.AlipayConfig;
import com.macro.mall.portal.domain.AliPayParam;
import com.macro.mall.portal.service.AlipayService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
/**
* @auther macrozheng
* @description 支付宝支付Controller
* @date 2023/9/8
* @github https://github.com/macrozheng
*/
@Controller
@Api(tags = "AlipayController")
@Tag(name = "AlipayController", description = "支付宝支付相关接口")
@RequestMapping("/alipay")
public class AlipayController {
@Autowired
private AlipayConfig alipayConfig;
@Autowired
private AlipayService alipayService;
@ApiOperation("支付宝电脑网站支付")
@RequestMapping(value = "/pay", method = RequestMethod.GET)
public void pay(AliPayParam aliPayParam, HttpServletResponse response) throws IOException {
response.setContentType("text/html;charset=" + alipayConfig.getCharset());
response.getWriter().write(alipayService.pay(aliPayParam));
response.getWriter().flush();
response.getWriter().close();
}
@ApiOperation("支付宝手机网站支付")
@RequestMapping(value = "/webPay", method = RequestMethod.GET)
public void webPay(AliPayParam aliPayParam, HttpServletResponse response) throws IOException {
response.setContentType("text/html;charset=" + alipayConfig.getCharset());
response.getWriter().write(alipayService.webPay(aliPayParam));
response.getWriter().flush();
response.getWriter().close();
}
@ApiOperation(value = "支付宝异步回调",notes = "必须为POST请求执行成功返回success执行失败返回failure")
@RequestMapping(value = "/notify", method = RequestMethod.POST)
public String notify(HttpServletRequest request){
Map<String, String> params = new HashMap<>();
Map<String, String[]> requestParams = request.getParameterMap();
for (String name : requestParams.keySet()) {
params.put(name, request.getParameter(name));
}
return alipayService.notify(params);
}
@ApiOperation(value = "支付宝统一收单线下交易查询",notes = "订单支付成功返回交易状态TRADE_SUCCESS")
@RequestMapping(value = "/query", method = RequestMethod.GET)
@ResponseBody
public CommonResult<String> query(String outTradeNo, String tradeNo){
return CommonResult.success(alipayService.query(outTradeNo,tradeNo));
}
}

View File

@@ -0,0 +1,27 @@
package com.macro.mall.portal.domain;
import lombok.Data;
import java.math.BigDecimal;
/**
* @auther macrozheng
* @description 支付宝支付请求参数
* @date 2023/9/8
* @github https://github.com/macrozheng
*/
@Data
public class AliPayParam {
/**
* 商户订单号,商家自定义,保持唯一性
*/
private String outTradeNo;
/**
* 商品的标题/交易标题/订单标题/订单关键字等
*/
private String subject;
/**
* 订单总金额,单位为元,精确到小数点后两位
*/
private BigDecimal totalAmount;
}

View File

@@ -0,0 +1,37 @@
package com.macro.mall.portal.service;
import com.macro.mall.portal.domain.AliPayParam;
import java.util.Map;
/**
* @auther macrozheng
* @description 支付宝支付Service
* @date 2023/9/8
* @github https://github.com/macrozheng
*/
public interface AlipayService {
/**
* 根据提交参数生成电脑支付页面
*/
String pay(AliPayParam aliPayParam);
/**
* 支付宝异步回调处理
*/
String notify(Map<String, String> params);
/**
* 查询支付宝交易状态
* @param outTradeNo 商户订单编号
* @param tradeNo 支付宝交易编号
* @return 支付宝交易状态
*/
String query(String outTradeNo, String tradeNo);
/**
* 根据提交参数生成手机支付页面
*/
String webPay(AliPayParam aliPayParam);
}

View File

@@ -16,7 +16,6 @@ import java.util.Map;
public interface OmsPortalOrderService {
/**
* 根据用户购物车信息生成确认单信息
* @param cartIds
*/
ConfirmOrderResult generateConfirmOrder(List<Long> cartIds);
@@ -68,4 +67,10 @@ public interface OmsPortalOrderService {
* 用户根据订单ID删除订单
*/
void deleteOrder(Long orderId);
/**
* 根据orderSn来实现的支付成功逻辑
*/
@Transactional
void paySuccessByOrderSn(String orderSn, Integer payType);
}

View File

@@ -0,0 +1,162 @@
package com.macro.mall.portal.service.impl;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayTradePagePayRequest;
import com.alipay.api.request.AlipayTradeQueryRequest;
import com.alipay.api.request.AlipayTradeWapPayRequest;
import com.alipay.api.response.AlipayTradeQueryResponse;
import com.macro.mall.mapper.OmsOrderMapper;
import com.macro.mall.portal.config.AlipayConfig;
import com.macro.mall.portal.domain.AliPayParam;
import com.macro.mall.portal.service.AlipayService;
import com.macro.mall.portal.service.OmsPortalOrderService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Map;
/**
* @auther macrozheng
* @description 支付宝支付Service实现类
* @date 2023/9/8
* @github https://github.com/macrozheng
*/
@Slf4j
@Service
public class AlipayServiceImpl implements AlipayService {
@Autowired
private AlipayConfig alipayConfig;
@Autowired
private AlipayClient alipayClient;
@Autowired
private OmsOrderMapper orderMapper;
@Autowired
private OmsPortalOrderService portalOrderService;
@Override
public String pay(AliPayParam aliPayParam) {
AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();
if(StrUtil.isNotEmpty(alipayConfig.getNotifyUrl())){
//异步接收地址,公网可访问
request.setNotifyUrl(alipayConfig.getNotifyUrl());
}
if(StrUtil.isNotEmpty(alipayConfig.getReturnUrl())){
//同步跳转地址
request.setReturnUrl(alipayConfig.getReturnUrl());
}
//******必传参数******
JSONObject bizContent = new JSONObject();
//商户订单号,商家自定义,保持唯一性
bizContent.put("out_trade_no", aliPayParam.getOutTradeNo());
//支付金额最小值0.01元
bizContent.put("total_amount", aliPayParam.getTotalAmount());
//订单标题,不可使用特殊符号
bizContent.put("subject", aliPayParam.getSubject());
//电脑网站支付场景固定传值FAST_INSTANT_TRADE_PAY
bizContent.put("product_code", "FAST_INSTANT_TRADE_PAY");
request.setBizContent(bizContent.toString());
String formHtml = null;
try {
formHtml = alipayClient.pageExecute(request).getBody();
} catch (AlipayApiException e) {
e.printStackTrace();
}
return formHtml;
}
@Override
public String notify(Map<String, String> params) {
String result = "failure";
boolean signVerified = false;
try {
//调用SDK验证签名
signVerified = AlipaySignature.rsaCheckV1(params, alipayConfig.getAlipayPublicKey(), alipayConfig.getCharset(), alipayConfig.getSignType());
} catch (AlipayApiException e) {
log.error("支付回调签名校验异常!",e);
e.printStackTrace();
}
if (signVerified) {
String tradeStatus = params.get("trade_status");
if("TRADE_SUCCESS".equals(tradeStatus)){
result = "success";
log.info("notify方法被调用了tradeStatus:{}",tradeStatus);
String outTradeNo = params.get("out_trade_no");
portalOrderService.paySuccessByOrderSn(outTradeNo,1);
}else{
log.warn("订单未支付成功trade_status:{}",tradeStatus);
}
} else {
log.warn("支付回调签名校验失败!");
}
return result;
}
@Override
public String query(String outTradeNo, String tradeNo) {
AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
//******必传参数******
JSONObject bizContent = new JSONObject();
//设置查询参数out_trade_no和trade_no至少传一个
if(StrUtil.isNotEmpty(outTradeNo)){
bizContent.put("out_trade_no",outTradeNo);
}
if(StrUtil.isNotEmpty(tradeNo)){
bizContent.put("trade_no",tradeNo);
}
//交易结算信息: trade_settle_info
String[] queryOptions = {"trade_settle_info"};
bizContent.put("query_options", queryOptions);
request.setBizContent(bizContent.toString());
AlipayTradeQueryResponse response = null;
try {
response = alipayClient.execute(request);
} catch (AlipayApiException e) {
log.error("查询支付宝账单异常!",e);
}
if(response.isSuccess()){
log.info("查询支付宝账单成功!");
if("TRADE_SUCCESS".equals(response.getTradeStatus())){
portalOrderService.paySuccessByOrderSn(outTradeNo,1);
}
} else {
log.error("查询支付宝账单失败!");
}
//交易状态WAIT_BUYER_PAY交易创建等待买家付款、TRADE_CLOSED未付款交易超时关闭或支付完成后全额退款、TRADE_SUCCESS交易支付成功、TRADE_FINISHED交易结束不可退款
return response.getTradeStatus();
}
@Override
public String webPay(AliPayParam aliPayParam) {
AlipayTradeWapPayRequest request = new AlipayTradeWapPayRequest ();
if(StrUtil.isNotEmpty(alipayConfig.getNotifyUrl())){
//异步接收地址,公网可访问
request.setNotifyUrl(alipayConfig.getNotifyUrl());
}
if(StrUtil.isNotEmpty(alipayConfig.getReturnUrl())){
//同步跳转地址
request.setReturnUrl(alipayConfig.getReturnUrl());
}
//******必传参数******
JSONObject bizContent = new JSONObject();
//商户订单号,商家自定义,保持唯一性
bizContent.put("out_trade_no", aliPayParam.getOutTradeNo());
//支付金额最小值0.01元
bizContent.put("total_amount", aliPayParam.getTotalAmount());
//订单标题,不可使用特殊符号
bizContent.put("subject", aliPayParam.getSubject());
//手机网站支付默认传值FAST_INSTANT_TRADE_PAY
bizContent.put("product_code", "QUICK_WAP_WAY");
request.setBizContent(bizContent.toString());
String formHtml = null;
try {
formHtml = alipayClient.pageExecute(request).getBody();
} catch (AlipayApiException e) {
e.printStackTrace();
}
return formHtml;
}
}

View File

@@ -93,6 +93,10 @@ public class OmsPortalOrderServiceImpl implements OmsPortalOrderService {
@Override
public Map<String, Object> generateOrder(OrderParam orderParam) {
List<OmsOrderItem> orderItemList = new ArrayList<>();
//校验收货地址
if(orderParam.getMemberReceiveAddressId()==null){
Asserts.fail("请选择收货地址!");
}
//获取购物车及优惠信息
UmsMember currentMember = memberService.getCurrentMember();
List<CartPromotionItem> cartPromotionItemList = cartItemService.listPromotion(currentMember.getId(), orderParam.getCartIds());
@@ -230,6 +234,9 @@ public class OmsPortalOrderServiceImpl implements OmsPortalOrderService {
//如使用积分需要扣除积分
if (orderParam.getUseIntegration() != null) {
order.setUseIntegration(orderParam.getUseIntegration());
if(currentMember.getIntegration()==null){
currentMember.setIntegration(0);
}
memberService.updateIntegration(currentMember.getId(), currentMember.getIntegration() - orderParam.getUseIntegration());
}
//删除购物车中的下单商品
@@ -412,6 +419,20 @@ public class OmsPortalOrderServiceImpl implements OmsPortalOrderService {
}
}
@Override
public void paySuccessByOrderSn(String orderSn, Integer payType) {
OmsOrderExample example = new OmsOrderExample();
example.createCriteria()
.andOrderSnEqualTo(orderSn)
.andStatusEqualTo(0)
.andDeleteStatusEqualTo(0);
List<OmsOrder> orderList = orderMapper.selectByExample(example);
if(CollUtil.isNotEmpty(orderList)){
OmsOrder order = orderList.get(0);
paySuccess(order.getId(),payType);
}
}
/**
* 生成18位订单编号:8位日期+2位平台号码+2位支付方式+6位以上自增id
*/
@@ -716,7 +737,10 @@ public class OmsPortalOrderServiceImpl implements OmsPortalOrderService {
*/
private boolean hasStock(List<CartPromotionItem> cartPromotionItemList) {
for (CartPromotionItem cartPromotionItem : cartPromotionItemList) {
if (cartPromotionItem.getRealStock()==null||cartPromotionItem.getRealStock() <= 0) {
if (cartPromotionItem.getRealStock()==null //判断真实库存是否为空
||cartPromotionItem.getRealStock() <= 0 //判断真实库存是否小于0
|| cartPromotionItem.getRealStock() < cartPromotionItem.getQuantity()) //判断真实库存是否小于下单的数量
{
return false;
}
}

View File

@@ -49,6 +49,7 @@
<spring-data-commons.version>2.7.0</spring-data-commons.version>
<jjwt.version>0.9.1</jjwt.version>
<aliyun-oss.version>2.5.0</aliyun-oss.version>
<alipay-sdk.version>4.38.61.ALL</alipay-sdk.version>
<logstash-logback.version>5.3</logstash-logback.version>
<admin-starter-server.version>2.7.0</admin-starter-server.version>
<minio.version>8.4.1</minio.version>