zoukankan      html  css  js  c++  java
  • 【SSH网上商城项目实战23】完成在线支付功能

     

      转自: https://blog.csdn.net/eson_15/article/details/51464415 

    上一节我们做好了支付页面的显示,从上一节支付页面显示的jsp代码中可以看出,当用户点击确认支付时,会跳到${shop}/pay_goBank.action的action,也就是说,提交后我们得在payAction中的goBank方法中处理一些逻辑(即21节demo中的那个流程图的逻辑),即获得明文,将明文加密成签名(密文)然后再去访问易宝服务器,易宝连接银行,完成支付。 
      但是考虑到MVC设计模式,我们会将上面这些业务逻辑放到Service层中处理,所以下面我们来实现21节中那个demo的支付逻辑。

    1. 先写一个Model来封装参数

      首先我们需要写一个Model(SendData)来接收jsp页面传来的参数,以及自己添加其他需要发送的参数,这些参数是易宝官方文档规定的,我们严格按照文档上的规定即可:

    /*
     * 商城到易宝支付发送的信息,封装为实体
     * */
    public class SendData implements Serializable {
    
        private static final long serialVersionUID = -6304103739907145812L;
        //(*)表示必填字段
        private String p0_Cmd; //业务类型(*),固定为:Buy
        private String p1_MerId;//商户编号(*)
        private String p2_Order;//商户订单号
        private String p3_Amt;//支付金额
        private String p4_Cur;//交易币种(*)
        private String p5_Pid;//商品名称
        private String p6_Pcat;//商品种类
        private String p7_Pdesc;//商品描述
        private String p8_Url;//商户接收支付成功数据的地址
        private String p9_SAF;//送货地址
        private String pa_MP;//商户扩展信息
        private String pd_FrpId;//支付通道编码,即银行
        private String pr_NeedResponse;//应答机制
    
        //省略get和set方法
    }

      以上这些属性是要传到易宝的所有参数,可以选择自己所需要的参数,并非所有的都传,视情况而定,一般除了p5, p6, p7外,其他我们都会传过去。下面我们来看看Action的代码:

    2. payAction的实现

      在payAction中,我们主要是将jsp页面传进来的部分参数封装到model里,然后调用service层的方法初始化其他参数,如下:

    @Controller("payAction")
    @Scope("prototype")
    public class PayAction extends BaseAction<Object> implements ParameterAware{
    
        public String goBank() {
            //1. 补全参数:P2 p3 pd Pa,需要从session中获取
            Forder forder = (Forder) session.get("oldForder");
            User user = (User) session.get("user");
            model.setP2_Order(forder.getId().toString()); //商户订单号
            model.setP3_Amt(forder.getTotal().toString()); //支付金额
            model.setPa_MP(user.getEmail() + "," + user.getPhone()); //商户扩展信息
            //2. 对参数进行追加        
            //3. 加密获取签名     
            //4. 存储到request域中
            payService.saveDataToRequest(request, model); //2,3,4的业务逻辑交给service层来处理
            //5. 跳转到支付页面        
            return "pay";
        }
    }

      接下来我们来写Service层的代码,service层主要实现上面2,3,4步骤的逻辑:

    3. Service层的实现

      具体代码如下:

    //payService接口
    public interface PayService {
    
        // 把加密后的信息存储到requestMap中
        public abstract Map<String, Object> saveDataToRequest(
                Map<String, Object> request, SendData sendData);
    
        //把返回的数据进行加密得到密文,并与传回来的密文比较,(我们后面再来实现)
        public boolean checkBackData(BackData backData);
    }
    
    //payServiceImpl实现类
    @Service("payService")
    public class PayServiceImpl implements PayService {
        //密钥
        @Value("#{prop.key}")
        private String key; 
        @Value("#{prop.p1_MerId}")
        //商户账号(不是订单号)
        private String p1_MerId; 
        //支付成功的返回地址
        @Value("#{prop.p8_Url}")
        private String p8_Url; 
        //以上三个属性都是固定值,我将它们放到pay.properties配置文件中了,直接使用Spring注解@Value获得
    
        // 补全SendData的数据, P2 p3 pd Pa 是前台注入,不需要在这补了,已经在Action中拿到了
        private SendData finishSendData(SendData sendData) {
                sendData.setP0_Cmd("Buy");
                sendData.setP1_MerId(p1_MerId);
                sendData.setP4_Cur("CNY");
                sendData.setP5_Pid("");
                sendData.setP6_Pcat("");
                sendData.setP7_Pdesc("");
                sendData.setP8_Url(p8_Url);
                sendData.setP9_SAF("0");
                sendData.setPr_NeedResponse("0");
                return sendData;
            }
    
            // 完成数据的追加,返回追加的明文
            private String joinSendDataParam(SendData sendData) {
                // 填充的所有数据
                sendData = this.finishSendData(sendData);
                StringBuffer infoBuffer = new StringBuffer();
                infoBuffer.append(sendData.getP0_Cmd());
                infoBuffer.append(sendData.getP1_MerId());
                infoBuffer.append(sendData.getP2_Order());
                infoBuffer.append(sendData.getP3_Amt());
                infoBuffer.append(sendData.getP4_Cur());
                infoBuffer.append(sendData.getP5_Pid());
                infoBuffer.append(sendData.getP6_Pcat());
                infoBuffer.append(sendData.getP7_Pdesc());
                infoBuffer.append(sendData.getP8_Url());
                infoBuffer.append(sendData.getP9_SAF());
                infoBuffer.append(sendData.getPa_MP());
                infoBuffer.append(sendData.getPd_FrpId());
                infoBuffer.append(sendData.getPr_NeedResponse());
                return infoBuffer.toString();
            }
    
            // 把加密后的信息存储到requestMap中
            @Override
            public Map<String, Object> saveDataToRequest(Map<String, Object> request,
                    SendData sendData) {
                // 返回了被追加的字符串(即明文)
                String joinParam = joinSendDataParam(sendData);
                request.put("p0_Cmd", sendData.getP0_Cmd());
                request.put("p1_MerId", sendData.getP1_MerId());
                request.put("p2_Order", sendData.getP2_Order());
                request.put("p3_Amt", sendData.getP3_Amt());
                request.put("p4_Cur", sendData.getP4_Cur());
                request.put("p5_Pid", sendData.getP5_Pid());
                request.put("p6_Pcat", sendData.getP6_Pcat());
                request.put("p7_Pdesc", sendData.getP7_Pdesc());
                request.put("p8_Url", sendData.getP8_Url());
                request.put("p9_SAF", sendData.getP9_SAF());
                request.put("pa_MP", sendData.getPa_MP());
                request.put("pd_FrpId", sendData.getPd_FrpId());
                request.put("pr_NeedResponse", sendData.getPr_NeedResponse());
                request.put("hmac", DigestUtil.hmacSign(joinParam, key));//追加之后的签名(密文)
                return request;
            }   
    }

      我们可以看到,其实跟前面那个demo中的servlet实现原理是相同的,上面代码中用到了pay.properties文件和注解,下面看一下pay.properties文件和beans.xml中的配置:

    #pay.properties
    key=w0P75wMZ203fr46r5i70V556WHFa94j14yW5J6vuh4yo3nRl5jsqF3c41677
    p1_MerId=10000940764
    p8_Url=https://www.hao123.com
    <!-- beans.xml -->
    <bean id="prop" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
         <property name="locations">
            <array>
                <value>classpath:public.properties</value><!--之前用到的-->
                <value>classpath:pay.properties</value>
            </array>
         </property>
    </bean>

      好了,现在Action和Service层都写好了,我们再来配置一下struts.xml文件:

    4. struts.xml配置和pay.jsp页面

    <action name="pay_*" class="payAction" method="{1}">
            <result name="pay">/user/pay.jsp</result>
    </action>

      struts.xml的配置很简单,主要就是根据返回值跳转到user/pay.jsp页面,将上面设置好的明文(参数)和密文(签名)发送到易宝服务器端即可:

    <div class="payskip-inner">
        <div>
            <span>订单号:
                </span><strong>${requestScope.p2_Order }</strong><span>&#91;请您记住这个号码,以便在付款和查询时使用&#93;
            </span>
        </div>
        <div>
            <span>支付金额:</span><strong>¥${requestScope.p3_Amt }</strong>
        </div>
        <div>
            <span>支付方式:</span><img class="zffs" src="${shop }/files/yibao.jpg" alt="" />
        </div>
        <div>
            <span>支付银行:</span><img class="zfyh" src="${shop }/files/bankImages/${pd_FrpId}.gif" alt="" />
        </div>
        <div>
            <form name="yeepay" action='https://www.yeepay.com/app-merchant-proxy/node' method='POST' target="_blank">
                <input type='hidden' name='p0_Cmd'   value='${requestScope.p0_Cmd}'>
                <input type='hidden' name='p1_MerId' value='${requestScope.p1_MerId}'>
                <input type='hidden' name='p2_Order' value='${requestScope.p2_Order}'>
                <input type='hidden' name='p3_Amt'   value='${requestScope.p3_Amt}'>
                <input type='hidden' name='p4_Cur'   value='${requestScope.p4_Cur}'>
                <input type='hidden' name='p5_Pid'   value='${requestScope.p5_Pid}'>
                <input type='hidden' name='p6_Pcat'  value='${requestScope.p6_Pcat}'>
                <input type='hidden' name='p7_Pdesc' value='${requestScope.p7_Pdesc}'>
                <input type='hidden' name='p8_Url'   value='${requestScope.p8_Url}'>
                <input type='hidden' name='p9_SAF'   value='${requestScope.p9_SAF}'>
                <input type='hidden' name='pa_MP'    value='${requestScope.pa_MP}'>
                <input type='hidden' name='pd_FrpId' value='${requestScope.pd_FrpId}'>
                <input type="hidden" name="pr_NeedResponse"  value="${requestScope.pr_NeedResponse}">
                <input type='hidden' name='hmac' value='${requestScope.hmac}'>
    
                <div class="pay-inner">
                     <input type="submit" style=" 80px; height: 40px;" value="立即支付" />
                </div>
            </form>
        </div>
    </div>

      pay.jsp中会将我们要传的明文和相应的密文全部发送给易宝服务器,易宝那边会根据这些明文,以同样的方式加密成密文,然后与我们传过去的密文进行匹配,如果相同说明数据安全,传输过程中没有被篡改,正常跳转到支付页面,然后就正常支付即可;如果不相同,则不允许支付,弹出个友好的提示页面。 
      到这里其实已经对这种在线支付的流程很清楚了,道理很简单,无非就是我把参数追加在一起形成,再加个密生成密文,然后把这些参数和密文发给第三方,他也用同样的加密方式加个密,跟我发过去的比较一下即可,至于如何调用银行接口,就不是我们要管的事了,这由第三方负责,包括一些安全方面的东西,这样的好处事每个人都能专注自己的事:我只要跟第三方对接好即可,把有用信息传给他;第三方只需要专注如何跟不同的银行对接,这给开发带来了很大的方便。因为跟不同的银行对接的接口肯定都不一样,如果我们直接去跟银行对接,那成本太高,而且不便于维护,银行那边升个级,我就得跟着升级……有了第三方,我们永远不用管了,升级是第三方的事,我们和第三方的接口是不会变的~

  • 相关阅读:
    leetcode 18 4Sum
    leetcode 71 Simplify Path
    leetcode 10 Regular Expression Matching
    leetcode 30 Substring with Concatenation of All Words
    leetcode 355 Design Twitte
    leetcode LRU Cache
    leetcode 3Sum
    leetcode Letter Combinations of a Phone Number
    leetcode Remove Nth Node From End of List
    leetcode Valid Parentheses
  • 原文地址:https://www.cnblogs.com/sunshine5683/p/9916900.html
Copyright © 2011-2022 走看看