支付页面
接口文档
@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_infowhere 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: devserver:port:
spring:application:name:cloud:nacos:discovery:server-addr: localhost:8848datasource:type: com.zaxxer.hikari.HikariDataSourcedriver-class-name: com.mysql.cj.jdbc.Driverurl:username:password:data:redis:host: localhostport: 6379
mybatis:config-localtion: classpath:mybatis-config.xmlmapper-location: classpath:mapper/*/*.xml
②、保存支付记录
public interface PaymetInfoService{PaymentInfo savePaymentInfo(String orderNo);
}
@Service
public class PaymentInfoService implements PaymentInfoService{@Autowiredprivate PaymentInfoMapper paymentInfoMapper; @Overridepublic 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_infowhere 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{@Autowiredprivate AlipayProperties alipayProperties;@Beanpublic 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{@Autowiredprivate 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