尚筹网17订单、支付
订单部分建模回顾
实体类之间关系
创建OrderVO实体类
public class OrderVO implements Serializable { private static final long serialVersionUID = 1L; // 主键 private Integer id; // 订单号 private String orderNum; // 支付宝流水单号 private String payOrderNum; // 订单金额 private Double orderAmount; // 是否开发票 private Integer invoice; // 发票抬头 private String invoiceTitle; // 备注 private String orderRemark; private String addressId; private OrderProjectVO orderProjectVO; public OrderVO() { } public OrderVO(Integer id, String orderNum, String payOrderNum, Double orderAmount, Integer invoice, String invoiceTitle, String orderRemark, String addressId, OrderProjectVO orderProjectVO) { this.id = id; this.orderNum = orderNum; this.payOrderNum = payOrderNum; this.orderAmount = orderAmount; this.invoice = invoice; this.invoiceTitle = invoiceTitle; this.orderRemark = orderRemark; this.addressId = addressId; this.orderProjectVO = orderProjectVO; }
修改t_order表
增加address_id字段
支付功能流程
思路
操作1:搭建环境
基础环境
参照order-consumer
支付接口调用所需环境
<!-- https://mvnrepository.com/artifact/com.alipay.sdk/alipay-sdk-java --> <dependency> <groupId>com.alipay.sdk</groupId> <artifactId>alipay-sdk-java</artifactId> <version>3.3.49.ALL</version> </dependency>
创建PayProperties类维护支付接口参数
@Component @ConfigurationProperties(prefix = "ali.pay") public class PayProperties { private String appId; private String merchantPrivateKey; private String alipayPublicKey; private String notifyUrl; private String returnUrl; private String signType; private String charset; private String gatewayUrl; public PayProperties() { } public PayProperties(String appId, String merchantPrivateKey, String alipayPublicKey, String notifyUrl, String returnUrl, String signType, String charset, String gatewayUrl) { this.appId = appId; this.merchantPrivateKey = merchantPrivateKey; this.alipayPublicKey = alipayPublicKey; this.notifyUrl = notifyUrl; this.returnUrl = returnUrl; this.signType = signType; this.charset = charset; this.gatewayUrl = gatewayUrl; }
配置支付参数
操作2:提交订单表单
构造页面不可见的表单
<!-- 为了收集当前页面中的所有数据,构造空表单 --> <form id="summaryForm" action="pay/generate/order" method="post"></f
给“立即付款”按钮绑定单击响应函数
$("#payButton").click(function(){ // 1.收集所有要提交的表单项的数据 var addressId = $("[name=addressId]:checked").val(); var invoice = $("[name=invoiceRadio]:checked").val(); var invoiceTitle = $.trim($("[name=invoiceTitle]").val()); var remark = $.trim($("[name=remark]").val()); // 2.将上面收集到的表单数据填充到空表单中并提交 $("#summaryForm") .append("<input type='hidden' name='addressId' value='"+addressId+"'/>") .append("<input type='hidden' name='invoice' value='"+invoice+"'/>") .append("<input type='hidden' name='invoiceTitle' value='"+invoiceTitle+"'/>") .append("<input type='hidden' name='orderRemark' value='"+remark+"'/>") .submit(); });
操作3:生成订单号
支付宝对订单号的要求
格式设计
当前时间(年月日小时分秒)+用户ID
时间格式:yyyyMMddHHmmss
用户ID:通过UUID生成
Controller
//让当前方法返回响应体,在浏览器页面上显示支付宝支付页面 @ResponseBody @RequestMapping("/generate/order") public String generateOrder(HttpSession session, OrderVO orderVO) throws AlipayApiException { // 1.从session中获取orderProjectVO OrderProjectVO orderProjectVO= (OrderProjectVO) session.getAttribute("orderProjectVO"); // 2.将orderProjectVO对象和orderVO对象封装到一起 orderVO.setOrderProjectVO(orderProjectVO); // 3.生成订单并设置到orderVO对象中 String time = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()); String user = UUID.randomUUID().toString().replace("-","").toUpperCase(); String orderNum = time + user; orderVO.setOrderNum(orderNum); // 4.计算订单金额并设置到orderVO对象中 Double orderAmount = (double) orderProjectVO.getSupportPrice() * orderProjectVO.getReturnCount() + orderProjectVO.getFreight(); orderVO.setOrderAmount(orderAmount); // 将OrderVO对象存入Session域中 session.setAttribute("orderVO",orderVO); // 5.调用支付宝封装方法 return sendRequestToAliPay(orderNum,orderAmount,orderProjectVO.getProjectName(),orderProjectVO.getReturnContent()); }
调用支付宝接口的专门方法
/** * 为了调用支付宝接口封装的方法 * @param outTradeNo 外部订单号 * @param totalAmount 订单金额 * @param subject 订单标题 * @param body 描述 * @return 返回页面上显示的登录界面 * @throws AlipayApiException */ private String sendRequestToAliPay( String outTradeNo, Double totalAmount, String subject, String body) throws AlipayApiException { //获得初始化的AlipayClient AlipayClient alipayClient = new DefaultAlipayClient( payProperties.getGatewayUrl(), payProperties.getAppId(), payProperties.getMerchantPrivateKey(), "json", payProperties.getCharset(), payProperties.getAlipayPublicKey(), payProperties.getSignType()); //设置请求参数 AlipayTradePagePayRequest alipayRequest = new AlipayTradePagePayRequest(); alipayRequest.setReturnUrl(payProperties.getReturnUrl()); alipayRequest.setNotifyUrl(payProperties.getNotifyUrl()); //商户订单号,商户网站订单系统中唯一订单号,必填 // String out_trade_no = new String(request.getParameter("WIDout_trade_no").getBytes("ISO-8859-1"),"UTF-8"); //付款金额,必填 // String total_amount = new String(request.getParameter("WIDtotal_amount").getBytes("ISO-8859-1"),"UTF-8"); // //订单名称,必填 // String subject = new String(request.getParameter("WIDsubject").getBytes("ISO-8859-1"),"UTF-8"); // //商品描述,可空 // String body = new String(request.getParameter("WIDbody").getBytes("ISO-8859-1"),"UTF-8"); alipayRequest.setBizContent("{\"out_trade_no\":\""+ outTradeNo +"\"," + "\"total_amount\":\""+ totalAmount +"\"," + "\"subject\":\""+ subject +"\"," + "\"body\":\""+ body +"\"," + "\"product_code\":\"FAST_INSTANT_TRADE_PAY\"}"); //若想给BizContent增加其他可选请求参数,以增加自定义超时时间参数timeout_express来举例说明 //alipayRequest.setBizContent("{\"out_trade_no\":\""+ out_trade_no +"\"," // + "\"total_amount\":\""+ total_amount +"\"," // + "\"subject\":\""+ subject +"\"," // + "\"body\":\""+ body +"\"," // + "\"timeout_express\":\"10m\"," // + "\"product_code\":\"FAST_INSTANT_TRADE_PAY\"}"); //请求参数可查阅【电脑网站支付的API文档-alipay.trade.page.pay-请求参数】章节 //请求 return alipayClient.pageExecute(alipayRequest).getBody(); }
操作4:returnUrl方法
@ResponseBody @RequestMapping("/return") public String returnUrlMethod( HttpServletRequest request, HttpSession session) throws AlipayApiException, UnsupportedEncodingException { //获取支付宝GET过来反馈信息 Map<String,String> params = new HashMap<String,String>(); Map<String,String[]> requestParams = request.getParameterMap(); for (Iterator<String> iter = requestParams.keySet().iterator(); iter.hasNext();) { String name = (String) iter.next(); String[] values = (String[]) requestParams.get(name); String valueStr = ""; for (int i = 0; i < values.length; i++) { valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ","; } //乱码解决,这段代码在出现乱码时使用 valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8"); params.put(name, valueStr); } boolean signVerified = AlipaySignature.rsaCheckV1( params, payProperties.getAlipayPublicKey(), payProperties.getCharset(), payProperties.getSignType()); //调用SDK验证签名 //——请在这里编写您的程序(以下代码仅作参考)—— if(signVerified) { //商户订单号 String orderNum = new String(request.getParameter("out_trade_no").getBytes("ISO-8859-1"),"UTF-8"); //支付宝交易号 String payOrderNum = new String(request.getParameter("trade_no").getBytes("ISO-8859-1"),"UTF-8"); //付款金额 String orderAmount = new String(request.getParameter("total_amount").getBytes("ISO-8859-1"),"UTF-8"); // 保存到数据库 // 从Session获取OrderVO对象 OrderVO orderVO = (OrderVO) session.getAttribute("orderVO"); orderVO.setPayOrderNum(payOrderNum); // 发送给mysql远程接口 ResultEntity<String> resultEntity = mySQLRemoteService.saveOrderRemote(orderVO); logger.info("Order save result="+resultEntity.getResult()); return "trade_no:"+payOrderNum+"<br/>out_trade_no:"+orderNum+"<br/>total_amount:"+orderAmount; }else { // 页面显示信息,验签失败 return "验签失败"; } }
操作5:notifyUrl方法
@RequestMapping("/notify") public void notifyUrlMethod(HttpServletRequest request) throws UnsupportedEncodingException, AlipayApiException { //获取支付宝POST过来反馈信息 Map<String,String> params = new HashMap<String,String>(); Map<String,String[]> requestParams = request.getParameterMap(); for (Iterator<String> iter = requestParams.keySet().iterator(); iter.hasNext();) { String name = (String) iter.next(); String[] values = (String[]) requestParams.get(name); String valueStr = ""; for (int i = 0; i < values.length; i++) { valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ","; } //乱码解决,这段代码在出现乱码时使用 valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8"); params.put(name, valueStr); } boolean signVerified = AlipaySignature.rsaCheckV1( params, payProperties.getAlipayPublicKey(), payProperties.getCharset(), payProperties.getSignType()); //调用SDK验证签名 //——请在这里编写您的程序(以下代码仅作参考)—— /* 实际验证过程建议商户务必添加以下校验: 1、需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号, 2、判断total_amount是否确实为该订单的实际金额(即商户订单创建时的金额), 3、校验通知中的seller_id(或者seller_email) 是否为out_trade_no这笔单据的对应的操作方(有的时候,一个商户可能有多个seller_id/seller_email) 4、验证app_id是否为该商户本身。 */ if(signVerified) {//验证成功 //商户订单号 String out_trade_no = new String(request.getParameter("out_trade_no").getBytes("ISO-8859-1"),"UTF-8"); //支付宝交易号 String trade_no = new String(request.getParameter("trade_no").getBytes("ISO-8859-1"),"UTF-8"); //交易状态 String trade_status = new String(request.getParameter("trade_status").getBytes("ISO-8859-1"),"UTF-8"); logger.info("trade_status="+trade_status); logger.info("out_trade_no="+out_trade_no); logger.info("trade_no="+trade_no); // if(trade_status.equals("TRADE_FINISHED")){ // //判断该笔订单是否在商户网站中已经做过处理 // //如果没有做过处理,根据订单号(out_trade_no)在商户网站的订单系统中查到该笔订单的详细,并执行商户的业务程序 // //如果有做过处理,不执行商户的业务程序 // // //注意: // //退款日期超过可退款期限后(如三个月可退款),支付宝系统发送该交易状态通知 // }else if (trade_status.equals("TRADE_SUCCESS")){ // //判断该笔订单是否在商户网站中已经做过处理 // //如果没有做过处理,根据订单号(out_trade_no)在商户网站的订单系统中查到该笔订单的详细,并执行商户的业务程序 // //如果有做过处理,不执行商户的业务程序 // // //注意: // //付款完成后,支付宝系统发送该交易状态通知 // } // // out.println("success"); // // }else {//验证失败 // out.println("fail"); // // //调试用,写文本函数记录程序运行情况是否正常 // //String sWord = AlipaySignature.getSignCheckContentV1(params); // //AlipayConfig.logResult(sWord); }else { logger.info("验证失败"); } }
订单信息保存到数据库
思路
SQL设置
关键点:保存OrderPO时获取自增主键
<insert id="insert" parameterType="com.example.entity.po.OrderPO" useGeneratedKeys="true" keyProperty="id"> insert into t_order (id, order_num, pay_order_num, order_amount, invoice, invoice_title, order_remark, address_id) values (#{id,jdbcType=INTEGER}, #{orderNum,jdbcType=CHAR}, #{payOrderNum,jdbcType=CHAR}, #{orderAmount,jdbcType=DOUBLE}, #{invoice,jdbcType=INTEGER}, #{invoiceTitle,jdbcType=CHAR}, #{orderRemark,jdbcType=CHAR}, #{addressId,jdbcType=CHAR}) </insert>
Linux等环境软件安装