电商中的订单支付(内网穿透)

发布于:2025-04-16 ⋅ 阅读:(21) ⋅ 点赞:(0)

支付页面

在这里插入图片描述

接口文档

在这里插入图片描述
在这里插入图片描述

@Operation(summary="获取订单信息")
@GetMapping("auth/{orderId}")
public Reuslt<OrderInfo> getOrderInfo(@Parameter(name="orderId",description="订单id",required=true) 
									  @PathVaariable Long orderId){
	
	OrderInfo orderInfo = orderInfoService.getOrderInfo(orderId);
	return Reuslt.build(orderInfo,ResultCode.SUCCESS);
}
@Override
public OrderInfo getOrderInfo(Long orderId){
	
	return orderInfoMapper.getById(orderId);
}
//mapper
OrderInfo getById(Long orderId);
<select id="getById"resultType="com.xxx.OrderInfo" >
	select *
	from order_info
	where id=#{orderId}
	
</select>

不经过购物车,直接购买

@Operation(summary="立即购买")
@GetMapping("auth/buy/{skuId}")
public Result buy(@PathVariable Long skuId){
	
	TradeVo tradeVo = orderInfoService.buy(skuId);
	return Result.build(tredeVo,ResultCodeEnum.SUCCESS);
}
@Override
public TradeVo buy(Long skuId){
	
	List<OrderItem> orderItemList = new ArrayList<>();
	ProductSku productSku = productFeignClient.getBySkuId(skuId);
	OrderIntem orderItem = new OrderItem();
	orderItem.setSkuId(skuId);
	orderItem.setSkuName(productSku.getSkuName());
	orderItem.setSkuNum(1);
	orderItem.setSkuPrice(productSku.getSalePrice());
	orderItem.setThumbImg(productSku.getThumbImg());

	orderItemList.add(orderItem);
	
	TradeVo tradeVo = new TradeVo();
	trade.setOrderItemList(orderItemList);
	trade.setTotalAmount(productSku.getSalePrice());
	
	return tradeVo;
}

查询订单
在这里插入图片描述

@Operation(summary="获取订单分页列表")
@GetMapping("auth/{page}/{limit}")
public Result<PageInfo<OrderInfo>> list(
	@Parameter(name="page",description="当前页码",required=true)
	@PathVariable Integer page,

	@Parameter(name="limit",description="每页记录数",required=true)
	@PathVariable Integer limit,

	@Parameter(name="orderStatus",description="订单状态",required=false)
	@RequestParam(required=false,defaultValue="") Integer orderStatus){
	
	PageInfo<OrderInfo> pageInfo = orderInfoService.findUserPage(page,limit,orderStatus);
	
	return Result.build(pageInfo,ResultCodeEnum.SUCCESS);
}
@Override
public PageInfo<OrderInfo> findOrderPage(Integer page,Integer limit,Integer orderStatus){
	
	PageHelper.startPage(page,limit);

	//查询订单信息
	Long userId = AuthContextUtil.getUserInfo().getId();
	List<OrderItem> orderInfoList = orderInfoMapper.findUserPage(userId,orderStatus);
	
	//查询订单里所有的订单项
	orderInfoList.forEach(orderInfo -> {
		//订单id查询订单里面的订单项
		List<OrderItem> orderItemList = orderItemMapper.findByOrderId(orderInfo.getId());
		
		orderInfo.setOrderItemList(orderItemList);
	});
	return new PageInfo<>(orderInfoList);
}

在这里插入图片描述

在这里插入图片描述

===================================================================================

支付宝支付

在这里插入图片描述

在这里插入图片描述
通过营业执照进行开通

获取的参数
在这里插入图片描述

在这里插入图片描述

①、创建模块service-pay

配置文件、启动类……

spring:
	profiles:
		active: dev

server:
	port:
spring:
	application:
		name:
	cloud:
		nacos:
			discovery:
				server-addr: localhost:8848
	datasource:
		type: com.zaxxer.hikari.HikariDataSource
		driver-class-name: com.mysql.cj.jdbc.Driver
		url:
		username:
		password:
	data:
		redis:
			host: localhost
			port: 6379
mybatis:
	config-localtion: classpath:mybatis-config.xml
	mapper-location: classpath:mapper/*/*.xml

②、保存支付记录

public interface PaymetInfoService{
	PaymentInfo savePaymentInfo(String orderNo);
}
@Service
public class PaymentInfoService implements PaymentInfoService{
	
	@Autowired
	private PaymentInfoMapper paymentInfoMapper; 
	
	@Override
	public PaymentInfo savePaymentInfo(String orderNo){
		
		PaymentInfo paymentInfo = paymentInfoMapper.getByOrderNo(orderNo);
		
		if(paymentInfo==null){
			//如果支付记录不存在,则远程调用订单信息
			OrderInfo orderInfo = orderFeignClient.getOrderInfoByOrderNo(orderNo).getData();
			paymentInfo = new PaymentInfo();
			paymentInfo.setUserId(orderInfo.getUserId());
			paymentInfo.setPayType(orderInfo.getPayType());
			String content = "";
			for(OrderItem item:orderInfo.getOrderItemList()){
				content += item.getSkuName() + "";
			}
			paymentInfo.setContent(content);
			paymentInfo.setAmount(orderInfo.getTotalAmount());
			paymentInfo.setOrderNo(orderNo);
			
			paymentInfoMapper.save(paymentInfo);
			
		}
		return paymentInfo;
	}
}
@Mapper
public interface PaymentInfoMapper{
	
	PaymentInfo getByOrderNo(String orderNo);

	void save(PaymentInfo paymentInfo);
}
<sql id="columns">
	id,user_id,order_no,pay_type,out_trade_no,amount,content,payment_status,callback_time,callback_content
</sql>

<select id="getByOrderNo" resultMap="paymentInfoMap">
	select <include refid="colums"></include>
	from payment_info
	where order_no = #{orderNo}
</select>

<insert id="save" useGeneratedKeys="true" keyProperty="id">
	insert into payment_info(
		id,
		user_id,
		order_no,
		pay_type,
		out_tread_no,
		amount,
		coutent,
		payment_status,
	)
</insert>

在这里插入图片描述

在这里插入图片描述

③、支付接口

Ⅰ、service-pay模块中添加alipay-sdk依赖

Ⅱ、application-alipay.yml文件,添加配置
在这里插入图片描述
Ⅲ、application-dev.yml配置文件需要引入application-alipay.yml

spring:
	config:
		import: application-alipay.yml

Ⅳ、读取配置文件

@Data
@Configuration(prefix="spzx.alipay")
public class AlipayProperties{
	
	//属性名必需和配置文件中的保持一致
	private String alipayUrl;
	private String appPrivateKey;
	public String alipayPublicKey;
	private String appId;
	public String returnPaymetUrl;
	public String notifyPaymentUrl;

	public final static String format="json";
	public final static String charset="utf-8";
	public final static String sign_type="RAS2";
}

Ⅴ、启动类添加注解@EnableConfigurationProperties(value={AlipayProperties.class})

④、配置发送请求的核心对象AlipayClient

@Configuration
public class AlipayConfiguration{
	
	@Autowired
	private AlipayProperties alipayProperties;

	@Bean
	public AlipayClient alipayClient(){
		
		AlipayClient alipayClient = new DefaultAlipayClient(
			alipayProperties.getAlipayUrl(),
			alipayProperties.getAppPrivateKey(),
			alipayProperties.format,
			alipayProperties.charset,
			alipayProperties.getAlipayPublicKey(),
			alipayProperties.sign_type
		);
		return alipayClient;
	}
}

⑤、返回支付宝支付页面

@Controller
@RequestMapping()
public class AlipayController{
	
	@Autowired
	private AlipayService alipayService;

	@Operation(summary="支付宝下单")
	@GetMapping("submitAlipay/{orderNo}")
	public Result<String> submitAliPay(@Parameter(name="orderNo",description="订单号",required=true),
										@PathVariable(value="orderNo") String orderNo){
		String form = aliapyService.submitAlipay(orderNo);//返回表单页面
		return Result.build(form,ResultCodeEnum.SUCCESS);
	}
}
@Override
public String submitAlipay(String orderNo){
	
	//保存支付记录
	PaymentInfo payementInfo = paymentInfoService.savePaymentInfo(orderNo);

	//调用支付宝服务接口封装参数
	AlipayTradeWapPayRequest alipayRequest = new AlipayTradeWapPayRequest();
	//同步回调
	alipayRequest.setReturnUrl(alipayProperties.getReturnPaymentUrl());
	//异步回调
	alipayRequest.setNotifyUrl(alipayProperties.getNotifyPaymentUrl());
	
	//准备请求参数
	HashMap<String,Object> map = new HashMap<>();
	map.put("out_trade_no",paymentInfo.getOrderNo());
	map.put("product_code","QUICK_WAP_WAY");
	map.put("total_amount",paymentInfo.getAmount());
	//map.put("total_amount",new BigDecimal("0.01")); 为了便于测试
	map.put("subject",paymentInfo.getContent());

	alipayRequest.setBizContent(JSON.toJSONString(map));

	try{
		//发送请求
		AlipayTradeWapPayResponse response = alipayClient.pageExecute(alipayRequest);
		if(response.isSuccess()){
			log.info("调用成功");
			String form = response.getBody();//返回支付页面
			return form;
		}else{
			log.info("调用失败");
			throw new GuiguException(ResultCodeEnum.DATA_ERROR);
		}
		
	}catch(AlipayApiException e){
		throw new RuntimeException(e);
	}
	
}

在这里插入图片描述

⑥、完成支付

  • 内网穿透,方便用来开发测试

在这里插入图片描述

  • 支付成功之后到达的页面:修改自定义的页面
  • 本地的接口路径:修改为内网穿透绑定到后端网关接口赠送的域名地址
    在这里插入图片描述

在这里插入图片描述

@Operation(summary="支付宝异步回调")
@ReqeustMapping("callback/notify")//该路径和配置文件中的notify_payment_url
@ResponseBody
public String alipayNotify(@RequestParam Map<String,String> paramMap,HttpServletRequest request){
	
	log.info("AlipayController...alipayNotify方法执行了");
	boolean signVerified = false;//调用SDK验证签名(验证是否是支付宝传递过来)
	try{
		signVerified = AlipaySignature.rsaCheckVl(paramMap,
										alipayProperties.getAlipayPublicKey(),
										AlipayProperties.charset,
										AlipayProperties.sign_type);
										
	}catch(AlipayApiException e){
		e.printStackTrace();
	}
	//交易状态
	String trade_status = paramMap.get("trade_status");

	if(signVerified){
		//进行二次校验
		if("TRADE_SUCCESS".equals(trade_status)||"TRADE_FINISHED".equals(trade_status)){
			//正常的支付成功,更新交易记录状态
			paymentInfoService.updatePaymentStatus(paraMap,2);
			return "success";
		}
	}else{
		//验签失败,记录异常日志
		return "failture";
	}
}
//支付状态的更新
@Override
public void updatePaymentStatus(Map<String,String> map){

	//根据订单编号查询支付记录信息
	PaymentInfo paymentInfo = paymentInfoMapper.getByOrderNo(map.get("out_trade_no"));
	
	//如果支付记录已经完成,不需要更新
	if(paymentInfo.getPaymentStatus()==1){
		return;
	}

	//没有完成才更新
	paymentInfo.setPaymentStatus(1);
	paymentInfo.setOutTradeNo(map.get("trade_no"));
	paymentInfo.setCallbackTime(new Date());
	paymentInfo.set.CallbackContent(JSON.toJSONString(map));

	paymentInfoMapper.updatePaymentInfo(paymentInfo);
	
	//远程调用,更新订单模块(根据订单编号修改)
	orderFeignClient.updateOorderStatus(paymentInfo.getOrderNo());

	//更新sku商品销售
}

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

配置内网穿透

  • 本质上就是因为无法直接调用本地服务,所以通过内网穿透便于第三方服务通过域名来调用本地服务
  • ngrok工具:https://ngrok.cc/login/register 注册用户
  • 完成实名认证
  • 开通隧道管理
  • 绑定本地服务开发者的service-gateway网关模块,获取赠送的域名
  • 复制隧道id,下载客户端工具windows_amd64zip

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述