29.支付

阅读: 评论:0

29.支付

29.支付

支付宝沙箱环境

  • 支付宝加密采用RSA非对称加密,分别在商户端和支付宝端有两对公钥和私钥
  • 在发送订单数据时,直接使用明文,但会使用商户私钥加一个对应的签名,支付宝端会使用商户公钥对签名进行验签,只有数据明文和签名对应的时候才能说明传输正确
  • 支付成功后,支付宝发送支付成功数据之外,还会使用支付宝私钥加一个对应的签名,商户端收到支付成功数据之后也会使用支付宝公钥延签,成功后才能确
     

我们要配置一个支付宝公钥和商户的私钥

配置内网穿透,沙箱环境等

导入依赖

        <dependency><groupId>com.alipay.sdk</groupId><artifactId>alipay-sdk-java</artifactId><version>4.9.28.ALL</version></dependency>

支付工具类(私钥和公钥,appid换成自己的)

package com.fig;@ConfigurationProperties(prefix = "alipay")
@Component
@Data
public class AlipayTemplate {//在支付宝创建的应用的idprivate   String app_id = "2016092200568607";// 商户私钥,您的PKCS8格式RSA2私钥private  String merchant_private_key = "MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCSgX/nTQ0lD+S8ObaM5LGZ1hiz18GXnNpqPLhJCym4xOpn35FNPHrPkDGEoMKrZ5LJeA4cZulckD8AtpvBCpeyIkrj/i1WVmSg10hVX67MlVets4UecCHZv2hKAN0/iId76kozdqrd7Csp/YgXPquN9Np0NFotggTrmiBANk+vcpTF9SCGrDq/isOoCvClfbvVJjApfLLOel3yECe5K/SZ8puiWILVm1NxEXAqJ8z0ipPZVGrXsT6Bo0pEyCPcEL0SqaC9WT0zdWQzdUknCzZV9W2wKjEXBJG9hqxay5kPaKm9leBatSkDAaDxH/N5g36HRfY7BmklwRZsp17lHinxAgMBAAECggEAfnnfck35WBKFc90a9D0F+Xlzr+ZGEV3uzKIIsb46UXFlrzC5HoVkvEWOCiJCjHiIpvbGr8xED43TZgk/IwLC/JxQLM0kVJGWo6fWoSVOIP2YSLNe620APBvaq3BdkFiMJfSYBB+g2J7mkIR39SE8Nvu3j3QWmYzSNJbE2spINnwTzNBL1OPaB5h3hSjyI07KaUcOjhTBF0EZl83NlBDsxmQvy0NmuOIWAcIXXvGoIbwkA774J3LhwL+VS4W2FpQj4FlxvDlPu24GeNWN7oO66T3Jp9bweO120ObhuKwZQosDGkJq0975zVSJX5QtUWHMM/QDPO8Pk24n2AoPcACQcQKBgQDS6kqD+sK8dDBpkmxYopA1gJJATnur0RHFZJb5webOhnEZnePhB1hhhGvKFcrdY2hcYeQiUZkHMsnWItNUe9E9ccp4++m6KKG0iV/BQda7zx1zMTTZUMvSbO282Q31YnQu7Yz6BSk4f/U5Qbu61AK53Tv1ejSAgQhXt1Pwq8KD7QKBgQCx0pkqW4+53tY2o4iPqFGjKYI2yk5bAH5etmOvW51OZ4Slsq/aUJKBVG6fOpRVKkiXulHhrp5csZH0/C7kaj4Hy7TjgUKSWvwlv7i7jgN0dq/bhVJz82y+N9pENWvy5J0I8Kt67XH+6JDEGWjlV58auifMRSx5mRJNn5pM6qrFlQKBgFyZWm/JV1fv1xVyoLjlXlTvBsbO7kMH/jpgqFwtAk1n/x3VEShJ1kayIbTOjotWSopMvCFJG9tqM+0cyxWLatkELXWifAIsNpqRuYWah1FbZD2fu+kxLNtM0a+YyCUUvZeg2cUnIOraWupxbp9e13eMpvdmWMiWXfhM18CRWEwdAoGAUwT0l076EhgUQJwm1JML0jY94eCfpmLbnNJgRe1qysEPr+B1s2IslA7cOqC5we0kyRmmwsuoibQpZYwbRG7JmRAk2pZtgzDRSbpxv7a0rDoBLmbXMOU0Hraqw2+Bf3v2SMc79/9FWnIvrC4EyBYZZPwGOpsNAZRSdEUQX9qrceUCgYB99OOtFFt1ixzyTCyUj3Fuiw7BsPhdI3nuMSoNTPIDNpzRBp/KFXyv/FNJ2CjTAsX3OR3D6KmEYihqUfrYeb0P5zoybcQLMxbXxK+ec6F2o6U2iqFIq0MKwHUqsb9X3pj4qE0ZHbFgRtIHnL2/QGV5PFJdmIZIBKZcvB8fW6ztDA==";// 支付宝公钥,查看地址:.htm 对应APPID下的支付宝公钥。private  String alipay_public_key = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyQQceVUChTJGtF/a8SXufhSxDTKporieTq9NO7yDZSpDlAX1zVPT/nf0KWAlxq1TYappWMIYtyrOABhJyn6flNP6vuSBiM5lYsepHvYrtRHqlFiJruEkiaCgEZBKL5aCfBHYj0oqgQn9MpNV/PEH4cBYAVaiI4+VX8CBUQfeEGjgN6OkpLULZ3X0JUkmSnVvCNJ1m3PD68IIlbOfEZXJUKCqmZhzprGR5VWswjxA+g87cMwvijL4gdkSy/daG62Bz5vApcmmMkuX1k1fMWP4ajZCASVw8HD+MSLRhd8We9F97gd8CW0TavzbdR+mTS5H4yEgO8F9HRAsbkhV9yu0yQIDAQAB";// 服务器[异步通知]页面路径  需格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问// 支付宝会悄悄的给我们发送一个请求,告诉我们支付成功的信息private  String notify_url;// 页面跳转同步通知页面路径 需格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问//同步通知,支付成功,一般跳转到成功页private  String return_url;// 签名方式private  String sign_type = "RSA2";// 字符编码格式private  String charset = "utf-8";// 支付宝网关; .doprivate  String gatewayUrl = ".do";public  String pay(PayVo vo) throws AlipayApiException {//AlipayClient alipayClient = new DefaultAlipayClient(AlipayTemplate.gatewayUrl, AlipayTemplate.app_id, hant_private_key, "json", AlipayTemplate.charset, AlipayTemplate.alipay_public_key, AlipayTemplate.sign_type);//1、根据支付宝的配置生成一个支付客户端AlipayClient alipayClient = new DefaultAlipayClient(gatewayUrl,app_id, merchant_private_key, "json",charset, alipay_public_key, sign_type);//2、创建一个支付请求 //设置请求参数AlipayTradePagePayRequest alipayRequest = new AlipayTradePagePayRequest();alipayRequest.setReturnUrl(return_url);alipayRequest.setNotifyUrl(notify_url);//商户订单号,商户网站订单系统中唯一订单号,必填String out_trade_no = vo.getOut_trade_no();//付款金额,必填String total_amount = vo.getTotal_amount();//订单名称,必填String subject = vo.getSubject();//商品描述,可空String body = vo.getBody();alipayRequest.setBizContent("{"out_trade_no":""+ out_trade_no +"","+ ""total_amount":""+ total_amount +"","+ ""subject":""+ subject +"","+ ""body":""+ body +"","+ ""product_code":"FAST_INSTANT_TRADE_PAY"}");String result = alipayClient.pageExecute(alipayRequest).getBody();//会收到支付宝的响应,响应的是一个页面,只要浏览器显示这个页面,就会自动来到支付宝的收银台页面System.out.println("支付宝的响应:"+result);return result;}
}

支付的vo

@Data
public class PayVo {private String out_trade_no; // 商户订单号 必填private String subject; // 订单名称 必填private String total_amount;  // 付款金额 必填private String body; // 商品描述 可空
}

处理支付请求的controller

package com.der.web;/*** @ Author wuyimin* @ Date 2021/8/30-19:39* @ Description*/
@Controller
public class PayWebController {@AutowiredAlipayTemplate alipayTemplate;@AutowiredOrderService orderService;@ResponseBody//返回数据带有json必须要@GetMapping(value = "/payOrder",produces = "text/html")public String payOrder(@RequestParam("orderSn") String orderSn) throws AlipayApiException {PayVo payVo &#OrderBySn(orderSn);//返回的是一个页面 直接把页面交给浏览器就行String pay = alipayTemplate.pay(payVo);System.out.println(pay);return pay;}
}

getOrderBy方法


package com.der.service.impl;    
@Overridepublic PayVo getOrderBySn(String orderSn) {PayVo payVo = new PayVo();OrderEntity orderEntity = One(new QueryWrapper<OrderEntity>().eq("order_sn", orderSn));List<OrderItemEntity> orderItemEntity = orderItemService.list(new QueryWrapper<OrderItemEntity>().eq("order_sn", orderSn));payVo.(0).getSkuAttrsVals()+"等");//订单备注payVo.setOut_trade_no(orderSn);//订单号payVo.(0).getSkuName()+"等");//订单的标题//支付的金额只能是两位小数,向上取值12.001=12.01payVo.setTotal_PayAmount().setScale(2,BigDecimal.ROUND_UP).toString());//金额return payVo;}

会员服务环境整理

整理会员系统的静态资源,配置springSession和thymeleaf,丢失请求头cookie配置(去订单模块看),将order的拦截器复制到member里,再配置以下这个拦截器

package com.fig;/*** @ Author wuyimin* @ Date 2021/8/31-10:10* @ Description*/
@Configuration
public class MemberWebConfig implements WebMvcConfigurer {@AutowiredLoginUserInterceptor loginUserInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(loginUserInterceptor).addPathPatterns("/**");}
}
package com.ber.interceptor;
/*** @ Author wuyimin* @ Date 2021/8/26-10:52* @ Description 拦截未登录用户*/
@Component //放入容器中
public class LoginUserInterceptor implements HandlerInterceptor {public static ThreadLocal<MemberRespVo> loginUser=new ThreadLocal<>();//方便其他请求拿到@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {String uri&#RequestURI();boolean match = new AntPathMatcher().match("/member/**", uri);if(match){return true;}HttpSession session = Session();//获取sessionMemberRespVo attribute = (MemberRespVo) Attribute(AuthServerConstant.LOGIN_USER);if(attribute!=null){//已经登录loginUser.set(attribute);return true;}else{//给前端用户的提示Session().setAttribute("msg","请先进行登录");//未登录response.sendRedirect(".html");//重定向到登录页return false;}}
}

配置网关

 添加域名映射

 订单列表渲染

远程调用的订单方法,这个方法返回一个订单页,远程方法推荐使用requestBody以json方式传输对象

package com.ller;    
/*** 列表*/@RequestMapping("/listWithItem")//@RequiresPermissions("order:order:list")public R listWithItem(@RequestBody Map<String, Object> params){PageUtils page = orderService.queryPageWithItem(params);return R.ok().put("page", page);}
  package com.der.service.impl;  @Overridepublic PageUtils queryPageWithItem(Map<String, Object> params) {MemberRespVo memberRespVo = ();//拿到会员信息IPage<OrderEntity> page = this.page(new Query<OrderEntity>().getPage(params),new QueryWrapper<OrderEntity>().eq("member_id",Id()).orderByDesc("id")//降序排列);List<OrderEntity> collect = Records().stream().map(order -> {List<OrderItemEntity> order_sn = orderItemService.list(new QueryWrapper<OrderItemEntity>().eq("order_sn", OrderSn()));order.setItemEntities(order_sn);return order;}).List());page.setRecords(collect);return new PageUtils(page);}

 会员服务的方法,r的打印

package com.ber.web;@Controller
public class MemberWebController {@AutowiredOrderFeignService orderFeignService;@GetMapping("/memberOrder.html")public String memberOrderPage(@RequestParam(value = "pageNum",defaultValue = "1") Integer pageNum, Model model) {HashMap<String, Object> hashMap = new HashMap<>();hashMap.put("page",String());R r = orderFeignService.listWithItem(hashMap);model.addAttribute("orders",r);return "orderList";}
}

异步通知

  • 订单支付成功后支付宝会回调商户接口,这个时候需要修改订单状态
  • 由于同步跳转可能由于网络问题失败,所以使用异步通知
  • 支付宝使用的是最大努力通知方案,保障数据一致性,隔一段时间会通知商户支付成功,直到返回success

配置notify_url

 修改穿透地址

 内网穿透软件不是一个浏览器,他确实把请求转给了虚拟机,但是请求头带的是他自己的外网的请求头,交不给订单服务,在f里配置,其原理类似于网关,越精确的地址放在上面

server {listen       80;server_name gulimall *.gulimall 45gr2a.natappfree;location /static {root   /usr/share/nginx/html;}location /payed/ {proxy_pass gulimall;proxy_set_header Host order.gulimall;}#charset koi8-r;#access_log  /var/log/nginx/log/host.access.log  main;location / {proxy_pass gulimall;proxy_set_header Host $host;}
}

 因为我们这里发送的是post请求所以我们拿postman来测试

package com.der.listener;@RestController
public class OrderPayedListener {@PostMapping("/payed/notify")public String handleAlipayed(HttpServletRequest request){Map<String, String[]> parameterMap = ParameterMap();System.out.println("支付宝通知到位"+parameterMap);//收到了支付宝给我们异步的通知必须返回success的json字符串,支付宝收到后就再也不通知了return "success";}
}

 成功返回了页面,但是注意到返回的是登录页面,这是因为我们的拦截器拦截了

 修改order的拦截器

package com.der.interceptor;/*** @ Author wuyimin* @ Date 2021/8/26-10:52* @ Description 拦截未登录用户*/
@Component //放入容器中
public class LoginUserInterceptor implements HandlerInterceptor {public static ThreadLocal<MemberRespVo> loginUser=new ThreadLocal<>();//方便其他请求拿到@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {String uri&#RequestURI();boolean match = new AntPathMatcher().match("/order/order/status/**", uri);boolean match2 = new AntPathMatcher().match("/payed/notify", uri);if(match|match2){return true;}HttpSession session = Session();//获取sessionMemberRespVo attribute = (MemberRespVo) Attribute(AuthServerConstant.LOGIN_USER);if(attribute!=null){//已经登录loginUser.set(attribute);return true;}else{//给前端用户的提示Session().setAttribute("msg","请先进行登录");//未登录response.sendRedirect(".html");//重定向到登录页return false;}}
}

再次发送请求成功返回success

到这里流程就打通了

支付完善流程

中间出了一个date数据格式的错误

支付成功后异步通知会返回给我们的map的参数

收到支付宝异步通知******************
key:gmt_create===========>value:2020-10-18 09:13:26
key:charset===========>value:utf-8
key:gmt_payment===========>value:2020-10-18 09:13:34
key:notify_time===========>value:2020-10-18 09:13:35
key:subject===========>value:华为
key:sign===========>value:aqhKWzgzTLE84Scy5d8i3f+t9f7t7IE5tK/s5iHf3SdFQXPnTt6MEVtbr15ZXmITEo015nCbSXaUFJvLiAhWpvkNEd6ysraa+2dMgotuHPIHnIUFwvdk+U4Ez+2A4DBTJgmwtc5Ay8mYLpHLNR9ASuEmkxxK2F3Ov6MO0d+1DOjw9c/CCRRBWR8NHSJePAy/UxMzULLtpMELQ1KUVHLgZC5yym5TYSuRmltYpLHOuoJhJw8vGkh2+4FngvjtS7SBhEhR1GvJCYm1iXRFTNgP9Fmflw+EjxrDafCIA+r69ZqoJJ2Sk1hb4cBsXgNrFXR2Uj4+rQ1Ec74bIjT98f1KpA==
key:buyer_id===========>value:2088622954825223
key:body===========>value:上市年份:2020;内存:64G
key:invoice_amount===========>value:6300.00
key:version===========>value:1.0
key:notify_id===========>value:2020101800222091334025220507700182
key:fund_bill_list===========>value:[{"amount":"6300.00","fundChannel":"ALIPAYACCOUNT"}]
key:notify_type===========>value:trade_status_sync
key:out_trade_no===========>value:12345523123
key:total_amount===========>value:6300.00
key:trade_status===========>value:TRADE_SUCCESS
key:trade_no===========>value:2020101822001425220501264292
key:auth_app_id===========>value:2016102600763190
key:receipt_amount===========>value:6300.00
key:point_amount===========>value:0.00
key:app_id===========>value:2016102600763190
key:buyer_pay_amount===========>value:6300.00
key:sign_type===========>value:RSA2
key:seller_id===========>value:2088102181115314

 对于这个封装一个vo

package com.der.vo;@ToString
@Data
public class PayAsyncVo {private String gmt_create;private String charset;private String gmt_payment;private String notify_time;private String subject;private String sign;private String buyer_id;//支付者的idprivate String body;//订单的信息private String invoice_amount;//支付金额private String version;private String notify_id;//通知idprivate String fund_bill_list;private String notify_type;//通知类型; trade_status_syncprivate String out_trade_no;//订单号private String total_amount;//支付的总额private String trade_status;//交易状态  TRADE_SUCCESSprivate String trade_no;//流水号private String auth_app_id;//private String receipt_amount;//商家收到的款private String point_amount;//private String app_id;//应用idprivate String buyer_pay_amount;//最终支付的金额private String sign_type;//签名类型private String seller_id;//商家的id
}

监听支付成功的controller ,这里一定要验签

package com.der.listener;
@RestController
public class OrderPayedListener {@AutowiredOrderService orderService;@AutowiredAlipayTemplate alipayTemplate;@PostMapping("/payed/notify")public String handleAlipayed(PayAsyncVo vo, HttpServletRequest request) throws AlipayApiException, UnsupportedEncodingException {//验签--是不是支付宝返回的数据(别人有可能模拟数据)Map<String, String> params = new HashMap<String, String>();Map<String, String[]> requestParams = ParameterMap();for (Iterator<String> iter = requestParams.keySet().iterator(); iter.hasNext(); ) {String name = (String) ();String[] values = (String[]) (name);String valueStr = "";for (int i = 0; i < values.length; i++) {valueStr = (i == values.length - 1) ? valueStr + values[i]: valueStr + values[i] + ",";}params.put(name, valueStr);}boolean signVerified = AlipaySignature.rsaCheckV1(params, Alipay_public_key(), Charset(), Sign_type()); //调用SDK验证签名if (signVerified) {System.out.println("签名验证成功");String res = orderService.handlePayResult(vo);return res;} else {return "error";}}
}

handlePayResult方法,支付宝收到了success就不会再发确认消息了

package com.der.service.impl;
@Overridepublic String handlePayResult(PayAsyncVo vo) {//1.保存交易流水PaymentInfoEntity paymentInfoEntity = new PaymentInfoEntity();paymentInfoEntity.Trade_no());paymentInfoEntity.Out_trade_no());paymentInfoEntity.Trade_status());paymentInfoEntity.Notify_time());paymentInfoService.save(paymentInfoEntity);//2.修改订单的状态信息if (vo.getTrade_status().equals("TRADE_SUCCESS")||vo.getTrade_status().equals("TRADE_FINISHED")){String outTradeNo = vo.getOut_trade_no();this.updateOrderStatus(outTradeNo,Code());}return "success" ;}

updateOrderStatus使用的sql语句

    <update id="updateOrderStatus">update oms_order set status=#{code} where order_sn=#{outTradeNo}</update>

收单

由于可能出现订单已经过期后,库存已经解锁,但支付成功后再修改订单状态的情况,需要设置支付有效时间,只有在有效期内才能进行支付

更改alipayTemplate中pay函数的这一部分

alipayRequest.setBizContent("{"out_trade_no":""+ out_trade_no +"","+ ""total_amount":""+ total_amount +"","+ ""subject":""+ subject +"","+ ""body":""+ body +"","//设置过期时间为1m+""timeout_express":"1m","+ ""product_code":"FAST_INSTANT_TRADE_PAY"}");

 

本文发布于:2024-02-04 07:38:23,感谢您对本站的认可!

本文链接:https://www.4u4v.net/it/170702172053600.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:
留言与评论(共有 0 条评论)
   
验证码:

Copyright ©2019-2022 Comsenz Inc.Powered by ©

网站地图1 网站地图2 网站地图3 网站地图4 网站地图5 网站地图6 网站地图7 网站地图8 网站地图9 网站地图10 网站地图11 网站地图12 网站地图13 网站地图14 网站地图15 网站地图16 网站地图17 网站地图18 网站地图19 网站地图20 网站地图21 网站地图22/a> 网站地图23