爱旅行项目中实现支付宝支付

本文档详细介绍了在SSM框架上集成支付宝支付的步骤,包括配置文件、支付接口实现、业务逻辑处理以及页面展示。通过创建AlipayConfig、AlipaymentController等组件,实现了订单确认、支付请求发送、异步和同步通知等功能。同时,涉及订单状态管理,如支付成功或失败后的订单状态更新。最后,提供了测试方法,包括模拟订单、部署war包和检查支付结果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在ssm框架的基础上,按照以下步骤完成支付功能
1.配置文件
applicationContext-alipay.xml

<bean class="cn.itrip.trade.config.AlipayConfig">
		<property name="appID" value="沙箱账号中的appid"/>
		<property name="rsaPrivateKey" value="应用私钥"/>
		<property name="notifyUrl" value="http://服务器ip:端口/tradedemo/api/notify"/>
		<property name="returnUrl" value="http://服务器ip:端口/tradedemo/api/return"/>
		<property name="url" value="沙箱账号中的支付宝网关"/>
		<property name="charset" value="GBK"/>
		<property name="format" value="json"/>
		<property name="alipayPublicKey" value="支付宝公钥"/>
		<property name="logPath" value="/logs"/>
		<property name="signType" value="RSA2"/>
		<property name="paymentSuccessUrl" value="http://服务器ip:端口/tradedemo/success.jsp"/>
		<property name="paymentFailureUrl" value="http://服务器ip:端口/tradedemo/fail.jsp"/>
	</bean>

2.AlipayConfig.java

public class AlipayConfig {
    // 商户appid
    private  String appID;
    // 私钥 pkcs8格式的
    private  String rsaPrivateKey ;
    // 服务器异步通知页面路径 需http://或者https://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问
    private  String notifyUrl;
    //public static String notify_url = "http://itrip.project.bdqn.cn/trade/api/notify";
    // 页面跳转同步通知页面路径 需http://或者https://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问 商户可以自定义同步跳转地址
    private  String returnUrl ;
    // 请求网关地址
    private  String url ;
    // 编码
    private  String charset ;
    // 返回格式
    private  String format ;
    // 支付宝公钥
    private  String alipayPublicKey;
    // 日志记录目录
    private  String logPath ;
    // RSA2
    private  String signType ;

    private  String paymentSuccessUrl;

    private  String paymentFailureUrl;
...省略get set方法
    }

3.在AlipaymentController.java中完成订单确认,发送支付请求,异步通知,同步通知

@Controller
@RequestMapping("/api")
public class AlipaymentController {
    @Resource
    private OrderService orderService;

    @Resource
    private AlipayConfig alipayConfig;

    //订单确认
    @RequestMapping(value = "/prepay/{orderNo}", method = RequestMethod.GET)
    public String prePay(@PathVariable String orderNo, ModelMap model) {
        try {
            ItripHotelOrder order = orderService.loadItripHotelOrder(orderNo);
            //System.out.println("=======>order:"+order.getOrderNo());
            if (!EmptyUtils.isEmpty(order)) {
                model.addAttribute("hotelName", order.getHotelName());
                model.addAttribute("roomId", order.getRoomId());
                model.addAttribute("count", order.getCount());
                model.addAttribute("payAmount", order.getPayAmount());
                return "pay";
            }else
                return "notfound";
        } catch (Exception e) {
            e.printStackTrace();
            return "error";
        }
    }

    //发送支付请求
    @RequestMapping(value = "/pay", method = RequestMethod.POST)
    public void pay(@RequestParam String WIDout_trade_no,
                    @RequestParam String WIDsubject,
                    @RequestParam String WIDtotal_amount,
                    HttpServletResponse response) {
        // 超时时间 可空
        String timeout_express = "2m";
        // 销售产品码 必填
        String product_code = "QUICK_WAP_PAY";
        /**********************/
        // SDK 公共请求类,包含公共请求参数,以及封装了签名与验签,开发者无需关注签名与验签
        // 调用RSA签名方式
        AlipayClient client = new DefaultAlipayClient(alipayConfig.getUrl(),
                alipayConfig.getAppID(), alipayConfig.getRsaPrivateKey(),
                alipayConfig.getFormat(), alipayConfig.getCharset(),
                alipayConfig.getAlipayPublicKey(), alipayConfig.getSignType());

        AlipayTradeWapPayRequest alipay_request = new AlipayTradeWapPayRequest();

        // 封装请求支付信息
        AlipayTradeWapPayModel model = new AlipayTradeWapPayModel();
        model.setOutTradeNo(WIDout_trade_no);
        model.setSubject("subject");
        model.setTotalAmount(WIDtotal_amount);
        model.setTimeoutExpress(timeout_express);
        model.setProductCode(product_code);

        alipay_request.setBizModel(model);
        // 设置异步通知地址
        alipay_request.setNotifyUrl(alipayConfig.getNotifyUrl());
        // 设置同步地址
        alipay_request.setReturnUrl(alipayConfig.getReturnUrl());
        // form表单生产
        String form = "";
        try {
            // 调用SDK生成表单
            form = client.pageExecute(alipay_request).getBody();
            System.out.println(form);
            response.setContentType("text/html;charset="
                    + alipayConfig.getCharset());
            response.getWriter().write(form);// 直接将完整的表单html输出到页面
            response.getWriter().flush();
            response.getWriter().close();
        } catch (AlipayApiException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    //异步通知
    @RequestMapping(value = "/notify",method=RequestMethod.POST)
    public void trackPaymentStatus(HttpServletRequest request,
                                   HttpServletResponse response) {
        try {
            // 获取支付宝POST过来反馈信息
            Map<String, String> params = new HashMap<String, String>();
            Map requestParams = request.getParameterMap();
            for (Iterator 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] + ",";
                }
                // 乱码解决,这段代码在出现乱码时使用。如果mysign和sign不相等也可以使用这段代码转化
                // valueStr = new String(valueStr.getBytes("ISO-8859-1"), "gbk");
                params.put(name, valueStr);
            }
            // 获取支付宝的通知返回参数,可参考技术文档中页面跳转同步通知参数列表(以下仅供参考)//
            // 商户订单号
            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");

            // 获取支付宝的通知返回参数,可参考技术文档中页面跳转同步通知参数列表(以上仅供参考)//
            // 计算得出通知验证结果
            // boolean AlipaySignature.rsaCheckV1(Map<String, String> params, String
            // publicKey, String charset, String sign_type)
            boolean verify_result = AlipaySignature.rsaCheckV1(params,
                    alipayConfig.getAlipayPublicKey(),
                    alipayConfig.getCharset(), "RSA2");

            if (verify_result) {// 验证成功
                // 
                // 请在这里加上商户的业务逻辑程序代码

                //即时到账普通版,那么这时的交易状态值为:  TRADE_FINISHED;如果是即时到账高级版,此时的交易状态值就为:TRADE_SUCCESS
                //收到TRADE_FINISHED请求后,这笔订单就结束了,支付宝不会再主动请求商户网站了;收到TRADE_SUCCESS请求后,后续一定还有至少一条通知记录,即TRADE_FINISHED。
                if (trade_status.equals("TRADE_FINISHED")) {
                    // 判断该笔订单是否在商户网站中已经做过处理
                    // 如果没有做过处理,根据订单号(out_trade_no)在商户网站的订单系统中查到该笔订单的详细,并执行商户的业务程序
                    // 请务必判断请求时的total_fee、seller_id与通知时获取的total_fee、seller_id为一致的
                    // 如果有做过处理,不执行商户的业务程序
                    if(!orderService.processed(out_trade_no))
                    {
                        orderService.paySuccess(out_trade_no, 2,trade_no);
                    }
                    // 注意:
                    // 如果签约的是可退款协议,退款日期超过可退款期限后(如三个月可退款),支付宝系统发送该交易状态通知
                    // 如果没有签约可退款协议,那么付款完成后,支付宝系统发送该交易状态通知。
                } else if (trade_status.equals("TRADE_SUCCESS")) {
                    // 判断该笔订单是否在商户网站中已经做过处理
                    // 如果没有做过处理,根据订单号(out_trade_no)在商户网站的订单系统中查到该笔订单的详细,并执行商户的业务程序
                    // 请务必判断请求时的total_fee、seller_id与通知时获取的total_fee、seller_id为一致的
                    // 如果有做过处理,不执行商户的业务程序
                    if(!orderService.processed(out_trade_no))
                    {
                        orderService.paySuccess(out_trade_no, 2,trade_no);
                    }

                    // 注意:
                    // 如果签约的是可退款协议,那么付款完成后,支付宝系统发送该交易状态通知。
                }

                response.getWriter().println("success"); // 请不要修改或删除

                // 
            } else {// 验证失败
                orderService.payFailed(out_trade_no, 1,trade_no);
                response.getWriter().println("fail");
            }
        } catch (UnsupportedEncodingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (AlipayApiException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    @RequestMapping(value = "/return", method = RequestMethod.GET)
    public void callback(HttpServletRequest request,
                         HttpServletResponse response) {
        System.out.println("=========>同步通知");
        try {
            //获取支付宝GET过来反馈信息
            Map<String,String> params = new HashMap<String,String>();
            Map requestParams = request.getParameterMap();
            for (Iterator 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] + ",";
                }
                //乱码解决,这段代码在出现乱码时使用。如果mysign和sign不相等也可以使用这段代码转化
                valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8");
                params.put(name, valueStr);
            }

            //获取支付宝的通知返回参数,可参考技术文档中页面跳转同步通知参数列表(以下仅供参考)//
            //商户订单号
            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");

            //获取支付宝的通知返回参数,可参考技术文档中页面跳转同步通知参数列表(以上仅供参考)//
            //计算得出通知验证结果
            //boolean AlipaySignature.rsaCheckV1(Map<String, String> params, String publicKey, String charset, String sign_type)
            boolean verify_result = AlipaySignature.rsaCheckV1(params,
                    alipayConfig.getAlipayPublicKey(),
                    alipayConfig.getCharset(), "RSA2");

            if(verify_result){//验证成功
                String id=orderService.loadItripHotelOrder(out_trade_no).getId().toString();
                //提示支付成功

                response.sendRedirect(
                        String.format(alipayConfig.getPaymentSuccessUrl(),out_trade_no,id));
            }else{
                //提示支付失败
                response.sendRedirect(alipayConfig.getPaymentFailureUrl());
            }
        } catch (UnsupportedEncodingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (AlipayApiException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

4.在Controller层需要依赖业务层的方法,所以需要在业务层完成加载酒店订单,判断订单是否已经被处理过,支付成功修改订单状态,支付失败修改订单状态。
接口OrderService.java

public interface OrderService {
    /**
     * 加载酒店订单
     * @param  orderNo
     * @return
     * @throws Exception
     */
    public ItripHotelOrder loadItripHotelOrder(String orderNo) throws Exception;

    /**
     * 判断该订单是否已被处理过(被更新为已支付状态)
     * @param orderNo
     * @return
     * @throws Exception
     */
    public boolean processed(String orderNo) throws Exception;
    /**
     * 支付成功
     * @param orderNo 订单编号
     * @param payType 支付方式:1:支付宝 2:微信 3:到店付
     * @param tradeNo 支付平台返回的交易码
     * @throws Exception
     */
    public void paySuccess(String orderNo, int payType, String tradeNo) throws Exception;
    /**
     * 支付失败
     * @param orderNo 订单编号
     * @param payType 支付方式:1:支付宝 2:微信 3:到店付
     * @param tradeNo 支付平台返回的交易码
     * @throws Exception
     */
    public void payFailed(String orderNo, int payType, String tradeNo) throws Exception;

}

实现类OrderServiceImpl.java

@Service
public class OrderServiceImpl implements OrderService {
    @Resource
    private ItripHotelOrderMapper itripHotelOrderMapper;
    @Resource
    private SystemConfig systemConfig;

    @Override
    public ItripHotelOrder loadItripHotelOrder(String orderNo) throws Exception {
        Map map = new HashMap();
        map.put("orderNo",orderNo);
        List<ItripHotelOrder> orderList = itripHotelOrderMapper.getItripHotelOrderListByMap(map);
        if(orderList.size()>0){
            return orderList.get(0);
        }
        return null;
    }

    @Override
    public void paySuccess(String orderNo, int payType,String tradeNo) throws Exception {
        ItripHotelOrder itripHotelOrder=this.loadItripHotelOrder(orderNo);
        itripHotelOrder.setOrderStatus(2);//支付成功
        itripHotelOrder.setPayType(payType);
        itripHotelOrder.setTradeNo(tradeNo);//交易号(如支付宝交易号)
        itripHotelOrderMapper.updateItripHotelOrder(itripHotelOrder);

        //增加订单后续待处理记录
        /*ItripTradeEnds itripTradeEnds=new ItripTradeEnds();
        itripTradeEnds.setId(itripHotelOrder.getId());
        itripTradeEnds.setOrderNo(itripHotelOrder.getOrderNo());
        itripTradeEndsMapper.insertItripTradeEnds(itripTradeEnds);*/
        //通知业务模块后续处理
        //sendGet(systemConfig.getTradeEndsUrl(),"orderNo="+orderNo);
    }

    @Override
    public void payFailed(String orderNo, int payType,String tradeNo) throws Exception {
        ItripHotelOrder itripHotelOrder=this.loadItripHotelOrder(orderNo);
        itripHotelOrder.setOrderStatus(1);//支付状态:已取消
        itripHotelOrder.setPayType(payType);
        itripHotelOrder.setTradeNo(tradeNo);//交易号(如支付宝交易号)
        itripHotelOrderMapper.updateItripHotelOrder(itripHotelOrder);
    }

    @Override
    public boolean processed(String orderNo) throws Exception {
        // TODO Auto-generated method stub
        ItripHotelOrder itripHotelOrder=this.loadItripHotelOrder(orderNo);
        return itripHotelOrder.getOrderStatus().equals(2)
                &&!EmptyUtils.isEmpty(itripHotelOrder.getTradeNo());
    }
    }

5.页面
确认支付页面pay.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>

<!DOCTYPE html>
<html>
	<head>
	<title>支付宝手机网站支付接口</title>
	<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<style>
    *{
        margin:0;
        padding:0;
    }
    ul,ol{
        list-style:none;
    }
    body{
        font-family: "Helvetica Neue",Helvetica,Arial,"Lucida Grande",sans-serif;
    }
    .hidden{
        display:none;
    }
    .new-btn-login-sp{
        padding: 1px;
        display: inline-block;
        width: 75%;
    }
    .new-btn-login {
        background-color: #02aaf1;
        color: #FFFFFF;
        font-weight: bold;
        border: none;
        width: 100%;
        height: 30px;
        border-radius: 5px;
        font-size: 16px;
    }
    #main{
        width:100%;
        margin:0 auto;
        font-size:14px;
    }
    .red-star{
        color:#f00;
        width:10px;
        display:inline-block;
    }
    .null-star{
        color:#fff;
    }
    .content{
        margin-top:5px;
    }
    .content dt{
        width:100px;
        display:inline-block;
        float: left;
        margin-left: 20px;
        color: #666;
        font-size: 13px;
        margin-top: 8px;
    }
    .content dd{
        margin-left:120px;
        margin-bottom:5px;
    }
    .content dd input {
        width: 85%;
        height: 28px;
        border: 0;
        -webkit-border-radius: 0;
        -webkit-appearance: none;
    }
    #foot{
        margin-top:10px;
        position: absolute;
        bottom: 15px;
        width: 100%;
    }
    .foot-ul{
        width: 100%;
    }
    .foot-ul li {
        width: 100%;
        text-align:center;
        color: #666;
    }
    .note-help {
        color: #999999;
        font-size: 12px;
        line-height: 130%;
        margin-top: 5px;
        width: 100%;
        display: block;
    }
    #btn-dd{
        margin: 20px;
        text-align: center;
    }
    .foot-ul{
        width: 100%;
    }
    .one_line{
        display: block;
        height: 1px;
        border: 0;
        border-top: 1px solid #eeeeee;
        width: 100%;
        margin-left: 20px;
    }
    .am-header {
        display: -webkit-box;
        display: -ms-flexbox;
        display: box;
        width: 100%;
        position: relative;
        padding: 7px 0;
        -webkit-box-sizing: border-box;
        -ms-box-sizing: border-box;
        box-sizing: border-box;
        background: #1D222D;
        height: 50px;
        text-align: center;
        -webkit-box-pack: center;
        -ms-flex-pack: center;
        box-pack: center;
        -webkit-box-align: center;
        -ms-flex-align: center;
        box-align: center;
    }
    .am-header h1 {
        -webkit-box-flex: 1;
        -ms-flex: 1;
        box-flex: 1;
        line-height: 18px;
        text-align: center;
        font-size: 18px;
        font-weight: 300;
        color: #fff;
    }
</style>
</head>
<body text=#000000 bgColor="#ffffff" leftMargin=0 topMargin=4>
<header class="am-header">
        <h1>确认订单信息</h1>
</header>
<div id="main">
        <form name=alipayment action='../pay' method="post">
            <div id="body" style="clear:left">
                <dl class="content">
                    <dt>订单编号:</dt>
                    <dd>
                        ${orderNo}
                        <input type="hidden" name="WIDout_trade_no" value="${orderNo}">
                        <input type="hidden" name="WIDsubject" value="${hotelName}">
                        <input type="hidden" name="WIDtotal_amount" value="${payAmount}">
                    </dd>
                    <hr class="one_line">
                    <dt>酒店名称:</dt>
                    <dd>
                        ${hotelName}
                    </dd>
                    <hr class="one_line">
                    <dt>付款金额:</dt>
                    <dd>
                        	¥${payAmount}
                    </dd>
                    <hr class="one_line"/>
                    <dt>订房描述:</dt>
                    <dd>
                        	房间ID:${roomId},数量:${count}
                    </dd>
                    <hr class="one_line">
                    <dt></dt>
                    <dd id="btn-dd">
                        <span class="new-btn-login-sp">
                            <button class="new-btn-login" type="submit" style="text-align:center;">确 认</button>
                        </span>
                        <span class="note-help">如果您点击“确认”按钮,即表示您同意该次的执行操作。</span>
                    </dd>
                </dl>
            </div>
		</form>
        <div id="foot">
			<ul class="foot-ul">
				<li>
					版权信息
				</li>
			</ul>
		</div>
	</div>
</body>

</html>

支付失败页面:failure.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<html>
<body>
<h2>支付失败!</h2>
</body>
</html>

支付成功页面:success.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<html>
<body>
<h2>支付成功!</h2>
</body>
</html>

6.测试需要将itrip-trade模块打成war包上传到云服务器
需要在数据中订单表中插入一条数据,表示用户的下单,该订单状态为0(未支付)

INSERT INTO `itrip_hotel_order`(`id`,`userId`,`orderNo`,`hotelId`,`hotelName`,`roomId`, `count`,`bookingDays`,`payAmount`,`checkInDate`,`checkOutDate`,`orderStatus`, `creationDate`) 
VALUES (302,29,'D10000012019022301114314aff8',1,'北京首都大酒店',2,1,1,600,NOW(),DATE_ADD(NOW(),INTERVAL 1 DAY),0,NOW())

浏览器地址栏输入:
http://服务器ip:端口/tradedemo/api/prepay/需要支付的订单号
完成整个支付过程后,查看是否跳转到支付成功页success.jsp,及数据库中订单状态orderStatus是否修改为2,交易号tradeNo是否有值。如果正确说明整个支付代码无误。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值